--- /dev/null
+/* $Id: pfkey.c,v 1.27.2.3 2005/02/18 10:09:55 vanhu Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef ENABLE_NATT
+# ifdef __linux__
+# include <linux/udp.h>
+# endif
+# if defined(__NetBSD__) || defined(__FreeBSD__)
+# 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>
+#ifndef HAVE_NETINET6_IPSEC
+#include <netinet/ipsec.h>
+#else
+#include <netinet6/ipsec.h>
+#endif
+
+#include "libpfkey.h"
+
+#include "var.h"
+#include "misc.h"
+#include "vmbuf.h"
+#include "plog.h"
+#include "sockmisc.h"
+#include "debug.h"
+
+#include "schedule.h"
+#include "localconf.h"
+#include "remoteconf.h"
+#include "isakmp_var.h"
+#include "isakmp.h"
+#include "isakmp_inf.h"
+#include "ipsec_doi.h"
+#include "oakley.h"
+#include "pfkey.h"
+#include "handler.h"
+#include "policy.h"
+#include "algorithm.h"
+#include "sainfo.h"
+#include "proposal.h"
+#include "admin.h"
+#include "strnames.h"
+#include "backupsa.h"
+#include "gcmalloc.h"
+#include "nattraversal.h"
+#include "crypto_openssl.h"
+#include "grabmyaddr.h"
+
+#if defined(SADB_X_EALG_RIJNDAELCBC) && !defined(SADB_X_EALG_AESCBC)
+#define SADB_X_EALG_AESCBC SADB_X_EALG_RIJNDAELCBC
+#endif
+
+/* prototype */
+static u_int ipsecdoi2pfkey_aalg __P((u_int));
+static u_int ipsecdoi2pfkey_ealg __P((u_int));
+static u_int ipsecdoi2pfkey_calg __P((u_int));
+static u_int ipsecdoi2pfkey_alg __P((u_int, u_int));
+static u_int keylen_aalg __P((u_int));
+static u_int keylen_ealg __P((u_int, int));
+
+static int pk_recvgetspi __P((caddr_t *));
+static int pk_recvupdate __P((caddr_t *));
+static int pk_recvadd __P((caddr_t *));
+static int pk_recvdelete __P((caddr_t *));
+static int pk_recvacquire __P((caddr_t *));
+static int pk_recvexpire __P((caddr_t *));
+static int pk_recvflush __P((caddr_t *));
+static int getsadbpolicy __P((caddr_t *, int *, int, struct ph2handle *));
+static int pk_recvspdupdate __P((caddr_t *));
+static int pk_recvspdadd __P((caddr_t *));
+static int pk_recvspddelete __P((caddr_t *));
+static int pk_recvspdexpire __P((caddr_t *));
+static int pk_recvspdget __P((caddr_t *));
+static int pk_recvspddump __P((caddr_t *));
+static int pk_recvspdflush __P((caddr_t *));
+static struct sadb_msg *pk_recv __P((int, int *));
+
+static int (*pkrecvf[]) __P((caddr_t *)) = {
+NULL,
+pk_recvgetspi,
+pk_recvupdate,
+pk_recvadd,
+pk_recvdelete,
+NULL, /* SADB_GET */
+pk_recvacquire,
+NULL, /* SABD_REGISTER */
+pk_recvexpire,
+pk_recvflush,
+NULL, /* SADB_DUMP */
+NULL, /* SADB_X_PROMISC */
+NULL, /* SADB_X_PCHANGE */
+pk_recvspdupdate,
+pk_recvspdadd,
+pk_recvspddelete,
+pk_recvspdget,
+NULL, /* SADB_X_SPDACQUIRE */
+pk_recvspddump,
+pk_recvspdflush,
+NULL, /* SADB_X_SPDSETIDX */
+pk_recvspdexpire,
+NULL, /* SADB_X_SPDDELETE2 */
+NULL, /* SADB_X_NAT_T_NEW_MAPPING */
+};
+
+static int addnewsp __P((caddr_t *));
+
+/* 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
+ */
+int
+pfkey_handler()
+{
+ 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(lcconf->sock_pfkey, &len);
+ if (msg == NULL) {
+ if (len < 0) {
+ 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, "get 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 = 0;
+end:
+ if (msg)
+ racoon_free(msg);
+ return(error);
+}
+
+/*
+ * dump SADB
+ */
+vchar_t *
+pfkey_dump_sadb(satype)
+ int satype;
+{
+ int s = -1;
+ vchar_t *buf = NULL;
+ pid_t pid = getpid();
+ struct sadb_msg *msg = NULL;
+ size_t bl, ml;
+ int len;
+
+ if ((s = pfkey_open()) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed pfkey open: %s\n",
+ ipsec_strerror());
+ return NULL;
+ }
+
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_dump\n");
+ if (pfkey_send_dump(s, satype) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed dump: %s\n", ipsec_strerror());
+ goto fail;
+ }
+
+ while (1) {
+ if (msg)
+ racoon_free(msg);
+ msg = pk_recv(s, &len);
+ if (msg == NULL) {
+ if (len < 0)
+ goto done;
+ else
+ continue;
+ }
+
+ if (msg->sadb_msg_type != SADB_DUMP || msg->sadb_msg_pid != pid)
+ 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);
+ if (s >= 0)
+ close(s);
+ return buf;
+}
+
+#ifdef ENABLE_ADMINPORT
+/*
+ * flush SADB
+ */
+void
+pfkey_flush_sadb(proto)
+ u_int proto;
+{
+ int satype;
+
+ /* convert to SADB_SATYPE */
+ if ((satype = admin2pfkey_proto(proto)) < 0)
+ return;
+
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_flush\n");
+ if (pfkey_send_flush(lcconf->sock_pfkey, satype) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send flush (%s)\n", ipsec_strerror());
+ return;
+ }
+
+ return;
+}
+#endif
+
+/*
+ * These are the SATYPEs that we manage. We register to get
+ * PF_KEY messages related to these SATYPEs, and we also use
+ * this list to determine which SATYPEs to delete SAs for when
+ * we receive an INITIAL-CONTACT.
+ */
+const struct pfkey_satype pfkey_satypes[] = {
+ { SADB_SATYPE_AH, "AH" },
+ { SADB_SATYPE_ESP, "ESP" },
+ { SADB_X_SATYPE_IPCOMP, "IPCOMP" },
+};
+const int pfkey_nsatypes =
+ sizeof(pfkey_satypes) / sizeof(pfkey_satypes[0]);
+
+/*
+ * PF_KEY initialization
+ */
+int
+pfkey_init()
+{
+ int i, reg_fail;
+
+ if ((lcconf->sock_pfkey = pfkey_open()) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed pfkey open (%s)\n", ipsec_strerror());
+ return -1;
+ }
+
+ 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
+ return 0;
+}
+
+/* %%% for conversion */
+/* IPSECDOI_ATTR_AUTH -> SADB_AALG */
+static u_int
+ipsecdoi2pfkey_aalg(hashtype)
+ u_int hashtype;
+{
+ switch (hashtype) {
+ case IPSECDOI_ATTR_AUTH_HMAC_MD5:
+ return SADB_AALG_MD5HMAC;
+ case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
+ return SADB_AALG_SHA1HMAC;
+ case IPSECDOI_ATTR_AUTH_KPDK: /* need special care */
+ return SADB_AALG_NONE;
+
+ /* not supported */
+ case IPSECDOI_ATTR_AUTH_DES_MAC:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Not supported hash type: %u\n", hashtype);
+ return ~0;
+
+ case 0: /* reserved */
+ default:
+ return SADB_AALG_NONE;
+
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid hash type: %u\n", hashtype);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* IPSECDOI_ESP -> SADB_EALG */
+static u_int
+ipsecdoi2pfkey_ealg(t_id)
+ u_int t_id;
+{
+ switch (t_id) {
+ case IPSECDOI_ESP_DES_IV64: /* sa_flags |= SADB_X_EXT_OLD */
+ return SADB_EALG_DESCBC;
+ case IPSECDOI_ESP_DES:
+ return SADB_EALG_DESCBC;
+ case IPSECDOI_ESP_3DES:
+ return SADB_EALG_3DESCBC;
+#ifdef SADB_X_EALG_RC5CBC
+ case IPSECDOI_ESP_RC5:
+ return SADB_X_EALG_RC5CBC;
+#endif
+ case IPSECDOI_ESP_CAST:
+ return SADB_X_EALG_CAST128CBC;
+ case IPSECDOI_ESP_BLOWFISH:
+ return SADB_X_EALG_BLOWFISHCBC;
+ case IPSECDOI_ESP_DES_IV32: /* flags |= (SADB_X_EXT_OLD|
+ SADB_X_EXT_IV4B)*/
+ return SADB_EALG_DESCBC;
+ case IPSECDOI_ESP_NULL:
+ return SADB_EALG_NULL;
+#ifdef SADB_X_EALG_AESCBC
+ case IPSECDOI_ESP_AES:
+ return SADB_X_EALG_AESCBC;
+#endif
+#ifdef SADB_X_EALG_TWOFISHCBC
+ case IPSECDOI_ESP_TWOFISH:
+ return SADB_X_EALG_TWOFISHCBC;
+#endif
+
+ /* not supported */
+ case IPSECDOI_ESP_3IDEA:
+ case IPSECDOI_ESP_IDEA:
+ case IPSECDOI_ESP_RC4:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Not supported transform: %u\n", t_id);
+ return ~0;
+
+ case 0: /* reserved */
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid transform id: %u\n", t_id);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* IPCOMP -> SADB_CALG */
+static u_int
+ipsecdoi2pfkey_calg(t_id)
+ u_int t_id;
+{
+ switch (t_id) {
+ case IPSECDOI_IPCOMP_OUI:
+ return SADB_X_CALG_OUI;
+ case IPSECDOI_IPCOMP_DEFLATE:
+ return SADB_X_CALG_DEFLATE;
+ case IPSECDOI_IPCOMP_LZS:
+ return SADB_X_CALG_LZS;
+
+ case 0: /* reserved */
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid transform id: %u\n", t_id);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* IPSECDOI_PROTO -> SADB_SATYPE */
+u_int
+ipsecdoi2pfkey_proto(proto)
+ u_int proto;
+{
+ switch (proto) {
+ case IPSECDOI_PROTO_IPSEC_AH:
+ return SADB_SATYPE_AH;
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ return SADB_SATYPE_ESP;
+ case IPSECDOI_PROTO_IPCOMP:
+ return SADB_X_SATYPE_IPCOMP;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid ipsec_doi proto: %u\n", proto);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+static u_int
+ipsecdoi2pfkey_alg(algclass, type)
+ u_int algclass, type;
+{
+ switch (algclass) {
+ case IPSECDOI_ATTR_AUTH:
+ return ipsecdoi2pfkey_aalg(type);
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ return ipsecdoi2pfkey_ealg(type);
+ case IPSECDOI_PROTO_IPCOMP:
+ return ipsecdoi2pfkey_calg(type);
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid ipsec_doi algclass: %u\n", algclass);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* SADB_SATYPE -> IPSECDOI_PROTO */
+u_int
+pfkey2ipsecdoi_proto(satype)
+ u_int satype;
+{
+ switch (satype) {
+ case SADB_SATYPE_AH:
+ return IPSECDOI_PROTO_IPSEC_AH;
+ case SADB_SATYPE_ESP:
+ return IPSECDOI_PROTO_IPSEC_ESP;
+ case SADB_X_SATYPE_IPCOMP:
+ return IPSECDOI_PROTO_IPCOMP;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "Invalid pfkey proto: %u\n", satype);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
+u_int
+ipsecdoi2pfkey_mode(mode)
+ u_int mode;
+{
+ switch (mode) {
+ case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
+#ifdef ENABLE_NATT
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
+#endif
+ return IPSEC_MODE_TUNNEL;
+ case IPSECDOI_ATTR_ENC_MODE_TRNS:
+#ifdef ENABLE_NATT
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
+ case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
+#endif
+ return IPSEC_MODE_TRANSPORT;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
+u_int
+pfkey2ipsecdoi_mode(mode)
+ u_int mode;
+{
+ switch (mode) {
+ case IPSEC_MODE_TUNNEL:
+ return IPSECDOI_ATTR_ENC_MODE_TUNNEL;
+ case IPSEC_MODE_TRANSPORT:
+ return IPSECDOI_ATTR_ENC_MODE_TRNS;
+ case IPSEC_MODE_ANY:
+ return IPSECDOI_ATTR_ENC_MODE_ANY;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode);
+ return ~0;
+ }
+ /*NOTREACHED*/
+}
+
+/* default key length for encryption algorithm */
+static u_int
+keylen_aalg(hashtype)
+ u_int hashtype;
+{
+ int res;
+
+ if (hashtype == 0)
+ return SADB_AALG_NONE;
+
+ res = alg_ipsec_hmacdef_hashlen(hashtype);
+ if (res == -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid hmac algorithm %u.\n", hashtype);
+ return ~0;
+ }
+ return res;
+}
+
+/* default key length for encryption algorithm */
+static u_int
+keylen_ealg(enctype, encklen)
+ u_int enctype;
+ int encklen;
+{
+ int res;
+
+ res = alg_ipsec_encdef_keylen(enctype, encklen);
+ if (res == -1) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encryption algorithm %u.\n", enctype);
+ return ~0;
+ }
+ return res;
+}
+
+int
+pfkey_convertfromipsecdoi(proto_id, t_id, hashtype,
+ e_type, e_keylen, a_type, a_keylen, flags)
+ u_int proto_id;
+ u_int t_id;
+ u_int hashtype;
+ u_int *e_type;
+ u_int *e_keylen;
+ u_int *a_type;
+ u_int *a_keylen;
+ u_int *flags;
+{
+ *flags = 0;
+ switch (proto_id) {
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ if ((*e_type = ipsecdoi2pfkey_ealg(t_id)) == ~0)
+ goto bad;
+ if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0)
+ goto bad;
+ *e_keylen >>= 3;
+
+ if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0)
+ goto bad;
+ if ((*a_keylen = keylen_aalg(hashtype)) == ~0)
+ goto bad;
+ *a_keylen >>= 3;
+
+ if (*e_type == SADB_EALG_NONE) {
+ plog(LLV_ERROR, LOCATION, NULL, "no ESP algorithm.\n");
+ goto bad;
+ }
+ break;
+
+ case IPSECDOI_PROTO_IPSEC_AH:
+ if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0)
+ goto bad;
+ if ((*a_keylen = keylen_aalg(hashtype)) == ~0)
+ goto bad;
+ *a_keylen >>= 3;
+
+ if (t_id == IPSECDOI_ATTR_AUTH_HMAC_MD5
+ && hashtype == IPSECDOI_ATTR_AUTH_KPDK) {
+ /* AH_MD5 + Auth(KPDK) = RFC1826 keyed-MD5 */
+ *a_type = SADB_X_AALG_MD5;
+ *flags |= SADB_X_EXT_OLD;
+ }
+ *e_type = SADB_EALG_NONE;
+ *e_keylen = 0;
+ if (*a_type == SADB_AALG_NONE) {
+ plog(LLV_ERROR, LOCATION, NULL, "no AH algorithm.\n");
+ goto bad;
+ }
+ break;
+
+ case IPSECDOI_PROTO_IPCOMP:
+ if ((*e_type = ipsecdoi2pfkey_calg(t_id)) == ~0)
+ goto bad;
+ *e_keylen = 0;
+
+ *flags = SADB_X_EXT_RAWCPI;
+
+ *a_type = SADB_AALG_NONE;
+ *a_keylen = 0;
+ if (*e_type == SADB_X_CALG_NONE) {
+ plog(LLV_ERROR, LOCATION, NULL, "no IPCOMP algorithm.\n");
+ goto bad;
+ }
+ break;
+
+ default:
+ plog(LLV_ERROR, LOCATION, NULL, "unknown IPsec protocol.\n");
+ goto bad;
+ }
+
+ return 0;
+
+ bad:
+ errno = EINVAL;
+ return -1;
+}
+
+/* called from scheduler */
+void
+pfkey_timeover_stub(p)
+ void *p;
+{
+
+ pfkey_timeover((struct ph2handle *)p);
+}
+
+void
+pfkey_timeover(iph2)
+ struct ph2handle *iph2;
+{
+ plog(LLV_ERROR, LOCATION, NULL,
+ "%s give up to get IPsec-SA due to time up to wait.\n",
+ saddrwop2str(iph2->dst));
+ SCHED_KILL(iph2->sce);
+
+ /* If initiator side, send error to kernel by SADB_ACQUIRE. */
+ if (iph2->side == INITIATOR)
+ pk_sendeacquire(iph2);
+
+ unbindph12(iph2);
+ remph2(iph2);
+ delph2(iph2);
+
+ return;
+}
+
+/*%%%*/
+/* send getspi message per ipsec protocol per remote address */
+/*
+ * the local address and remote address in ph1handle are dealed
+ * with destination address and source address respectively.
+ * Because SPI is decided by responder.
+ */
+int
+pk_sendgetspi(iph2)
+ struct ph2handle *iph2;
+{
+ struct sockaddr *src = NULL, *dst = NULL;
+ u_int satype, mode;
+ struct saprop *pp;
+ struct saproto *pr;
+ u_int32_t minspi, maxspi;
+ int proxy = 0;
+
+ if (iph2->side == INITIATOR) {
+ pp = iph2->proposal;
+ proxy = iph2->ph1->rmconf->support_proxy;
+ } else {
+ pp = iph2->approval;
+ if (iph2->sainfo && iph2->sainfo->id_i)
+ proxy = 1;
+ }
+
+ /* for mobile IPv6 */
+ if (proxy && iph2->src_id && iph2->dst_id &&
+ ipsecdoi_transportmode(pp)) {
+ src = iph2->src_id;
+ dst = iph2->dst_id;
+ } else {
+ src = iph2->src;
+ dst = iph2->dst;
+ }
+
+ for (pr = pp->head; pr != NULL; pr = pr->next) {
+
+ /* validity check */
+ satype = ipsecdoi2pfkey_proto(pr->proto_id);
+ if (satype == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", pr->proto_id);
+ 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 = ntohl (0x100);
+ maxspi = ntohl (0xffff);
+ }
+ else {
+ minspi = 0;
+ maxspi = 0;
+ }
+ mode = ipsecdoi2pfkey_mode(pr->encmode);
+ if (mode == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encmode %d\n", pr->encmode);
+ return -1;
+ }
+
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_getspi\n");
+ if (pfkey_send_getspi(
+ lcconf->sock_pfkey,
+ satype,
+ mode,
+ dst, /* src of SA */
+ src, /* dst of SA */
+ minspi, maxspi,
+ pr->reqid_in, iph2->seq) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "ipseclib failed send getspi (%s)\n",
+ ipsec_strerror());
+ return -1;
+ }
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "pfkey GETSPI sent: %s\n",
+ sadbsecas2str(dst, src, satype, 0, mode));
+ }
+
+ return 0;
+}
+
+/*
+ * receive GETSPI from kernel.
+ */
+static int
+pk_recvgetspi(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_sa *sa;
+ struct ph2handle *iph2;
+ struct sockaddr *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) {
+ 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];
+ dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); /* note SA dir */
+
+ /* 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(iph2->dst, iph2->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(iph2->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");
+ unbindph12(iph2);
+ remph2(iph2);
+ delph2(iph2);
+ iph2 = NULL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * set inbound SA
+ */
+int
+pk_sendupdate(iph2)
+ struct ph2handle *iph2;
+{
+ struct saproto *pr;
+ struct sockaddr *src = NULL, *dst = NULL;
+ int e_type, e_keylen, a_type, a_keylen, flags;
+ u_int satype, mode;
+ u_int64_t lifebyte = 0;
+ u_int wsize = 4; /* XXX static size of window */
+ int proxy = 0;
+ struct ph2natt natt;
+
+ /* sanity check */
+ if (iph2->approval == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no approvaled SAs found.\n");
+ }
+
+ if (iph2->side == INITIATOR)
+ proxy = iph2->ph1->rmconf->support_proxy;
+ else if (iph2->sainfo && iph2->sainfo->id_i)
+ proxy = 1;
+
+ /* for mobile IPv6 */
+ if (proxy && iph2->src_id && iph2->dst_id &&
+ ipsecdoi_transportmode(iph2->approval)) {
+ src = iph2->src_id;
+ dst = iph2->dst_id;
+ } else {
+ src = iph2->src;
+ dst = iph2->dst;
+ }
+
+ for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
+ /* validity check */
+ satype = ipsecdoi2pfkey_proto(pr->proto_id);
+ if (satype == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", pr->proto_id);
+ return -1;
+ }
+ else if (satype == SADB_X_SATYPE_IPCOMP) {
+ /* IPCOMP has no replay window */
+ wsize = 0;
+ }
+#ifdef ENABLE_SAMODE_UNSPECIFIED
+ mode = IPSEC_MODE_ANY;
+#else
+ mode = ipsecdoi2pfkey_mode(pr->encmode);
+ if (mode == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encmode %d\n", pr->encmode);
+ return -1;
+ }
+#endif
+
+ /* set algorithm type and key length */
+ e_keylen = pr->head->encklen;
+ if (pfkey_convertfromipsecdoi(
+ pr->proto_id,
+ pr->head->trns_id,
+ pr->head->authtype,
+ &e_type, &e_keylen,
+ &a_type, &a_keylen, &flags) < 0)
+ return -1;
+
+#if 0
+ lifebyte = iph2->approval->lifebyte * 1024,
+#else
+ lifebyte = 0;
+#endif
+
+#ifdef ENABLE_NATT
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_update_nat\n");
+ if (pr->udp_encap) {
+ memset (&natt, 0, sizeof (natt));
+ natt.type = iph2->ph1->natt_options->encaps_type;
+ natt.sport = extract_port (iph2->ph1->remote);
+ natt.dport = extract_port (iph2->ph1->local);
+ natt.oa = NULL; // FIXME: Here comes OA!!!
+ }
+ else
+ memset (&natt, 0, sizeof (natt));
+
+ if (pfkey_send_update_nat(
+ lcconf->sock_pfkey,
+ satype,
+ mode,
+ dst,
+ src,
+ pr->spi,
+ pr->reqid_in,
+ wsize,
+ pr->keymat->v,
+ e_type, e_keylen, a_type, a_keylen, flags,
+ 0, lifebyte, iph2->approval->lifetime, 0,
+ iph2->seq,
+ natt.type, natt.sport, natt.dport, natt.oa) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send update_nat (%s)\n",
+ ipsec_strerror());
+ return -1;
+ }
+#else
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_update\n");
+ if (pfkey_send_update(
+ lcconf->sock_pfkey,
+ satype,
+ mode,
+ dst,
+ src,
+ pr->spi,
+ pr->reqid_in,
+ wsize,
+ pr->keymat->v,
+ e_type, e_keylen, a_type, a_keylen, flags,
+ 0, lifebyte, iph2->approval->lifetime, 0,
+ iph2->seq) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send update (%s)\n",
+ ipsec_strerror());
+ return -1;
+ }
+#endif /* ENABLE_NATT */
+
+ 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(satype, mode, dst, src,
+ pr->spi, pr->reqid_in, 4,
+ pr->keymat->v,
+ e_type, e_keylen, a_type, a_keylen, flags,
+ 0, iph2->approval->lifebyte * 1024,
+ iph2->approval->lifetime, 0,
+ iph2->seq) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "backuped SA failed: %s\n",
+ sadbsecas2str(dst, src,
+ satype, pr->spi, mode));
+ }
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "backuped SA: %s\n",
+ sadbsecas2str(dst, src,
+ satype, pr->spi, mode));
+ }
+
+ return 0;
+}
+
+static int
+pk_recvupdate(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_sa *sa;
+ struct sockaddr *src, *dst;
+ struct ph2handle *iph2;
+ u_int proto_id, encmode, sa_mode;
+ int incomplete = 0;
+ struct saproto *pr;
+
+ /* ignore this message because of local test mode. */
+ if (f_local)
+ return 0;
+
+ /* sanity check */
+ if (mhp[0] == NULL
+ || mhp[SADB_EXT_SA] == NULL
+ || mhp[SADB_EXT_ADDRESS_SRC] == NULL
+ || mhp[SADB_EXT_ADDRESS_DST] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb update message passed.\n");
+ return -1;
+ }
+ msg = (struct sadb_msg *)mhp[0];
+ src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
+ dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
+ sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
+
+ sa_mode = mhp[SADB_X_EXT_SA2] == NULL
+ ? IPSEC_MODE_ANY
+ : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+
+ /* the message has to be processed or not ? */
+ if (msg->sadb_msg_pid != getpid()) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "%s message is not interesting "
+ "because pid %d is not mine.\n",
+ s_pfkey_type(msg->sadb_msg_type),
+ msg->sadb_msg_pid);
+ return -1;
+ }
+
+ iph2 = getph2byseq(msg->sadb_msg_seq);
+ if (iph2 == NULL) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "seq %d of %s message not interesting.\n",
+ msg->sadb_msg_seq,
+ s_pfkey_type(msg->sadb_msg_type));
+ return -1;
+ }
+
+ if (iph2->status != PHASE2ST_ADDSA) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "status mismatch (db:%d msg:%d)\n",
+ iph2->status, PHASE2ST_ADDSA);
+ return -1;
+ }
+
+ /* check to complete all keys ? */
+ for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
+ proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
+ if (proto_id == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", msg->sadb_msg_satype);
+ return -1;
+ }
+ encmode = pfkey2ipsecdoi_mode(sa_mode);
+ if (encmode == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encmode %d\n", sa_mode);
+ return -1;
+ }
+
+ if (pr->proto_id == proto_id
+ && pr->spi == sa->sadb_sa_spi) {
+ pr->ok = 1;
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "pfkey UPDATE succeeded: %s\n",
+ sadbsecas2str(iph2->dst, iph2->src,
+ msg->sadb_msg_satype,
+ sa->sadb_sa_spi,
+ sa_mode));
+
+ plog(LLV_INFO, LOCATION, NULL,
+ "IPsec-SA established: %s\n",
+ sadbsecas2str(iph2->dst, iph2->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_KILL(iph2->sce);
+
+ /* update status */
+ iph2->status = PHASE2ST_ESTABLISHED;
+
+#ifdef ENABLE_STATS
+ gettimeofday(&iph2->end, NULL);
+ syslog(LOG_NOTICE, "%s(%s): %8.6f",
+ "phase2", "quick", timedelta(&iph2->start, &iph2->end));
+#endif
+
+ /* count up */
+ iph2->ph1->ph2cnt++;
+
+ /* turn off schedule */
+ if (iph2->scr)
+ SCHED_KILL(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.
+ */
+ /* XXX ???
+ */
+/* unbindph12(iph2);*/
+
+ iph2->sce = sched_new(iph2->approval->lifetime,
+ isakmp_ph2expire_stub, iph2);
+
+ plog(LLV_DEBUG, LOCATION, NULL, "===\n");
+ return 0;
+}
+
+/*
+ * set outbound SA
+ */
+int
+pk_sendadd(iph2)
+ struct ph2handle *iph2;
+{
+ struct saproto *pr;
+ struct sockaddr *src = NULL, *dst = NULL;
+ int e_type, e_keylen, a_type, a_keylen, flags;
+ u_int satype, mode;
+ u_int64_t lifebyte = 0;
+ u_int wsize = 4; /* XXX static size of window */
+ int proxy = 0;
+ struct ph2natt natt;
+
+ /* sanity check */
+ if (iph2->approval == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no approvaled SAs found.\n");
+ }
+
+ if (iph2->side == INITIATOR)
+ proxy = iph2->ph1->rmconf->support_proxy;
+ else if (iph2->sainfo && iph2->sainfo->id_i)
+ proxy = 1;
+
+ /* for mobile IPv6 */
+ if (proxy && iph2->src_id && iph2->dst_id &&
+ ipsecdoi_transportmode(iph2->approval)) {
+ src = iph2->src_id;
+ dst = iph2->dst_id;
+ } else {
+ src = iph2->src;
+ dst = iph2->dst;
+ }
+
+ for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
+ /* validity check */
+ satype = ipsecdoi2pfkey_proto(pr->proto_id);
+ if (satype == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", pr->proto_id);
+ return -1;
+ }
+ else if (satype == SADB_X_SATYPE_IPCOMP) {
+ /* no replay window for IPCOMP */
+ wsize = 0;
+ }
+#ifdef ENABLE_SAMODE_UNSPECIFIED
+ mode = IPSEC_MODE_ANY;
+#else
+ mode = ipsecdoi2pfkey_mode(pr->encmode);
+ if (mode == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encmode %d\n", pr->encmode);
+ return -1;
+ }
+#endif
+
+ /* set algorithm type and key length */
+ e_keylen = pr->head->encklen;
+ if (pfkey_convertfromipsecdoi(
+ pr->proto_id,
+ pr->head->trns_id,
+ pr->head->authtype,
+ &e_type, &e_keylen,
+ &a_type, &a_keylen, &flags) < 0)
+ return -1;
+
+#if 0
+ lifebyte = iph2->approval->lifebyte * 1024,
+#else
+ lifebyte = 0;
+#endif
+
+#ifdef ENABLE_NATT
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add_nat\n");
+
+ if (pr->udp_encap) {
+ memset (&natt, 0, sizeof (natt));
+ natt.type = UDP_ENCAP_ESPINUDP;
+ natt.sport = extract_port (iph2->ph1->local);
+ natt.dport = extract_port (iph2->ph1->remote);
+ natt.oa = NULL; // FIXME: Here comes OA!!!
+ }
+ else
+ memset (&natt, 0, sizeof (natt));
+
+ if (pfkey_send_add_nat(
+ lcconf->sock_pfkey,
+ satype,
+ mode,
+ src,
+ dst,
+ pr->spi_p,
+ pr->reqid_out,
+ wsize,
+ pr->keymat_p->v,
+ e_type, e_keylen, a_type, a_keylen, flags,
+ 0, lifebyte, iph2->approval->lifetime, 0,
+ iph2->seq,
+ natt.type, natt.sport, natt.dport, natt.oa) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send add_nat (%s)\n",
+ ipsec_strerror());
+ return -1;
+ }
+#else
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add\n");
+
+ if (pfkey_send_add(
+ lcconf->sock_pfkey,
+ satype,
+ mode,
+ src,
+ dst,
+ pr->spi_p,
+ pr->reqid_out,
+ wsize,
+ pr->keymat_p->v,
+ e_type, e_keylen, a_type, a_keylen, flags,
+ 0, lifebyte, iph2->approval->lifetime, 0,
+ iph2->seq) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send add (%s)\n",
+ ipsec_strerror());
+ return -1;
+ }
+#endif /* ENABLE_NATT */
+
+ 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(satype, mode, src, dst,
+ pr->spi_p, pr->reqid_out, 4,
+ pr->keymat_p->v,
+ e_type, e_keylen, a_type, a_keylen, flags,
+ 0, iph2->approval->lifebyte * 1024,
+ iph2->approval->lifetime, 0,
+ iph2->seq) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "backuped SA failed: %s\n",
+ sadbsecas2str(src, dst,
+ satype, pr->spi_p, mode));
+ }
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "backuped SA: %s\n",
+ sadbsecas2str(src, dst,
+ satype, pr->spi_p, mode));
+ }
+
+ return 0;
+}
+
+static int
+pk_recvadd(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_sa *sa;
+ struct sockaddr *src, *dst;
+ struct ph2handle *iph2;
+ u_int sa_mode;
+
+ /* ignore this message because of local test mode. */
+ if (f_local)
+ return 0;
+
+ /* sanity check */
+ if (mhp[0] == NULL
+ || mhp[SADB_EXT_SA] == NULL
+ || mhp[SADB_EXT_ADDRESS_SRC] == NULL
+ || mhp[SADB_EXT_ADDRESS_DST] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb add message passed.\n");
+ return -1;
+ }
+ msg = (struct sadb_msg *)mhp[0];
+ 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(iph2->src, iph2->dst,
+ msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode));
+
+ plog(LLV_DEBUG, LOCATION, NULL, "===\n");
+ return 0;
+}
+
+static int
+pk_recvexpire(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_sa *sa;
+ struct sockaddr *src, *dst;
+ struct ph2handle *iph2;
+ u_int proto_id, sa_mode;
+
+ /* sanity check */
+ if (mhp[0] == NULL
+ || mhp[SADB_EXT_SA] == NULL
+ || mhp[SADB_EXT_ADDRESS_SRC] == NULL
+ || mhp[SADB_EXT_ADDRESS_DST] == NULL
+ || (mhp[SADB_EXT_LIFETIME_HARD] != NULL
+ && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb expire message passed.\n");
+ return -1;
+ }
+ msg = (struct sadb_msg *)mhp[0];
+ sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
+ src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
+ dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
+
+ sa_mode = mhp[SADB_X_EXT_SA2] == NULL
+ ? IPSEC_MODE_ANY
+ : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+
+ proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
+ if (proto_id == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", msg->sadb_msg_satype);
+ return -1;
+ }
+
+ plog(LLV_INFO, LOCATION, NULL,
+ "IPsec-SA expired: %s\n",
+ sadbsecas2str(src, dst,
+ msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode));
+
+ iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
+ if (iph2 == NULL) {
+ /*
+ * Ignore it because two expire messages are come up.
+ * phase2 handler has been deleted already when 2nd message
+ * is received.
+ */
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "no such a SA found: %s\n",
+ sadbsecas2str(src, dst,
+ msg->sadb_msg_satype, sa->sadb_sa_spi,
+ sa_mode));
+ return 0;
+ }
+ if (iph2->status != PHASE2ST_ESTABLISHED) {
+ /*
+ * If the status is not equal to PHASE2ST_ESTABLISHED,
+ * racoon ignores this expire message. There are two reason.
+ * One is that the phase 2 probably starts because there is
+ * a potential that racoon receives the acquire message
+ * without receiving a expire message. Another is that racoon
+ * may receive the multiple expire messages from the kernel.
+ */
+ plog(LLV_WARNING, LOCATION, NULL,
+ "the expire message is received "
+ "but the handler has not been established.\n");
+ return 0;
+ }
+
+ /* turn off the timer for calling isakmp_ph2expire() */
+ SCHED_KILL(iph2->sce);
+
+ iph2->status = PHASE2ST_EXPIRED;
+
+ /* INITIATOR, begin phase 2 exchange. */
+ /* allocate buffer for status management of pfkey message */
+ if (iph2->side == INITIATOR) {
+
+ initph2(iph2);
+
+ /* update status for re-use */
+ iph2->status = PHASE2ST_STATUS2;
+
+ /* start isakmp initiation by using ident exchange */
+ if (isakmp_post_acquire(iph2) < 0) {
+ plog(LLV_ERROR, LOCATION, iph2->dst,
+ "failed to begin ipsec sa "
+ "re-negotication.\n");
+ unbindph12(iph2);
+ remph2(iph2);
+ delph2(iph2);
+ return -1;
+ }
+
+ return 0;
+ /*NOTREACHED*/
+ }
+
+ /* If not received SADB_EXPIRE, INITIATOR delete ph2handle. */
+ /* RESPONDER always delete ph2handle, keep silent. RESPONDER doesn't
+ * manage IPsec SA, so delete the list */
+ unbindph12(iph2);
+ remph2(iph2);
+ delph2(iph2);
+
+ return 0;
+}
+
+static int
+pk_recvacquire(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_x_policy *xpl;
+ struct secpolicy *sp_out = NULL, *sp_in = NULL;
+#define MAXNESTEDSA 5 /* XXX */
+ struct ph2handle *iph2[MAXNESTEDSA];
+ int n; /* # of phase 2 handler */
+
+ /* 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];
+
+ /* 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 is multicast address */
+ {
+ struct sockaddr *sa = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
+
+ if ((sa->sa_family == AF_INET
+ && IN_MULTICAST(ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr)))
+#ifdef INET6
+ || (sa->sa_family == AF_INET6
+ && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sa)->sin6_addr))
+#endif
+ ) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "ignore due to multicast address: %s.\n",
+ saddrwop2str(sa));
+ return 0;
+ }
+ }
+
+ /* ignore, if we do not listen on source address */
+ {
+ /* reasons behind:
+ * - if we'll contact peer from address we do not listen -
+ * we will be unable to complete negotiation;
+ * - if we'll negotiate using address we're listening -
+ * remote peer will send packets to address different
+ * than one in the policy, so kernel will drop them;
+ * => therefore this acquire is not for us! --Aidas
+ */
+ struct sockaddr *sa = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
+ struct myaddrs *p;
+ int do_listen = 0;
+ for (p = lcconf->myaddrs; p; p = p->next) {
+ if (!cmpsaddrwop(p->addr, sa)) {
+ do_listen = 1;
+ break;
+ }
+ }
+
+ if (!do_listen) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "ignore because do not listen on source address : %s.\n",
+ saddrwop2str(sa));
+ return 0;
+ }
+ }
+
+ /*
+ * If there is a phase 2 handler against the policy identifier in
+ * the acquire message, and if
+ * 1. its state is less than PHASE2ST_ESTABLISHED, then racoon
+ * should ignore such a acquire message because the phase 2
+ * is just negotiating.
+ * 2. its state is equal to PHASE2ST_ESTABLISHED, then racoon
+ * has to prcesss such a acquire message because racoon may
+ * lost the expire message.
+ */
+ iph2[0] = getph2byspid(xpl->sadb_x_policy_id);
+ if (iph2[0] != NULL) {
+ if (iph2[0]->status < PHASE2ST_ESTABLISHED) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "ignore the acquire because ph2 found\n");
+ return -1;
+ }
+ if (iph2[0]->status == PHASE2ST_EXPIRED)
+ iph2[0] = NULL;
+ /*FALLTHROUGH*/
+ }
+
+ /* 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));
+
+ /* get inbound policy */
+ {
+ struct policyindex 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;
+
+ 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));
+ }
+ }
+
+ memset(iph2, 0, MAXNESTEDSA);
+
+ n = 0;
+
+ /* allocate a phase 2 */
+ iph2[n] = newph2();
+ if (iph2[n] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to allocate phase2 entry.\n");
+ return -1;
+ }
+ iph2[n]->side = INITIATOR;
+ iph2[n]->spid = xpl->sadb_x_policy_id;
+ iph2[n]->satype = msg->sadb_msg_satype;
+ iph2[n]->seq = msg->sadb_msg_seq;
+ iph2[n]->status = PHASE2ST_STATUS2;
+
+ /* set end addresses of SA */
+ iph2[n]->dst = dupsaddr(PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]));
+ if (iph2[n]->dst == NULL) {
+ delph2(iph2[n]);
+ return -1;
+ }
+ iph2[n]->src = dupsaddr(PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]));
+ if (iph2[n]->src == NULL) {
+ delph2(iph2[n]);
+ return -1;
+ }
+
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "new acquire %s\n", spidx2str(&sp_out->spidx));
+
+ /* get sainfo */
+ {
+ vchar_t *idsrc, *iddst;
+
+ idsrc = ipsecdoi_sockaddr2id((struct sockaddr *)&sp_out->spidx.src,
+ sp_out->spidx.prefs, sp_out->spidx.ul_proto);
+ if (idsrc == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID for %s\n",
+ spidx2str(&sp_out->spidx));
+ delph2(iph2[n]);
+ return -1;
+ }
+ iddst = ipsecdoi_sockaddr2id((struct sockaddr *)&sp_out->spidx.dst,
+ sp_out->spidx.prefd, sp_out->spidx.ul_proto);
+ if (iddst == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get ID for %s\n",
+ spidx2str(&sp_out->spidx));
+ vfree(idsrc);
+ delph2(iph2[n]);
+ return -1;
+ }
+ iph2[n]->sainfo = getsainfo(idsrc, iddst, NULL);
+ vfree(idsrc);
+ vfree(iddst);
+ if (iph2[n]->sainfo == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get sainfo.\n");
+ delph2(iph2[n]);
+ return -1;
+ /* XXX should use the algorithm list from register message */
+ }
+ }
+
+ if (set_proposal_from_policy(iph2[n], sp_out, sp_in) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to create saprop.\n");
+ delph2(iph2[n]);
+ return -1;
+ }
+ insph2(iph2[n]);
+
+ /* start isakmp initiation by using ident exchange */
+ /* XXX should be looped if there are multiple phase 2 handler. */
+ if (isakmp_post_acquire(iph2[n]) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to begin ipsec sa negotication.\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (n >= 0) {
+ unbindph12(iph2[n]);
+ remph2(iph2[n]);
+ delph2(iph2[n]);
+ iph2[n] = NULL;
+ n--;
+ }
+ return -1;
+}
+
+static int
+pk_recvdelete(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_sa *sa;
+ struct sockaddr *src, *dst;
+ struct ph2handle *iph2 = NULL;
+ u_int proto_id;
+
+ /* ignore this message because of local test mode. */
+ if (f_local)
+ return 0;
+
+ /* sanity check */
+ if (mhp[0] == NULL
+ || mhp[SADB_EXT_SA] == NULL
+ || mhp[SADB_EXT_ADDRESS_SRC] == NULL
+ || mhp[SADB_EXT_ADDRESS_DST] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb acquire message passed.\n");
+ return -1;
+ }
+ msg = (struct sadb_msg *)mhp[0];
+ sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
+ src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
+ dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
+
+ /* the message has to be processed or not ? */
+ if (msg->sadb_msg_pid == getpid()) {
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "%s message is not interesting "
+ "because the message was originated by me.\n",
+ s_pfkey_type(msg->sadb_msg_type));
+ return -1;
+ }
+
+ proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
+ if (proto_id == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", msg->sadb_msg_satype);
+ return -1;
+ }
+
+ iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
+ if (iph2 == NULL) {
+ /* ignore */
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no iph2 found: %s\n",
+ sadbsecas2str(src, dst, msg->sadb_msg_satype,
+ sa->sadb_sa_spi, IPSEC_MODE_ANY));
+ return 0;
+ }
+
+ plog(LLV_ERROR, LOCATION, NULL,
+ "pfkey DELETE received: %s\n",
+ sadbsecas2str(iph2->src, iph2->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);
+
+ unbindph12(iph2);
+ remph2(iph2);
+ delph2(iph2);
+
+ return 0;
+}
+
+static int
+pk_recvflush(mhp)
+ caddr_t *mhp;
+{
+ /* ignore this message because of local test mode. */
+ if (f_local)
+ return 0;
+
+ /* sanity check */
+ if (mhp[0] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb acquire message passed.\n");
+ return -1;
+ }
+
+ flushph2();
+
+ return 0;
+}
+
+static int
+getsadbpolicy(policy0, policylen0, type, iph2)
+ caddr_t *policy0;
+ int *policylen0, type;
+ struct ph2handle *iph2;
+{
+ struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
+ struct sadb_x_policy *xpl;
+ struct sadb_x_ipsecrequest *xisr;
+ struct saproto *pr;
+ caddr_t policy, p;
+ int policylen;
+ int xisrlen;
+ u_int satype, mode;
+
+ /* get policy buffer size */
+ policylen = sizeof(struct sadb_x_policy);
+ if (type != SADB_X_SPDDELETE) {
+ for (pr = iph2->approval->head; pr; pr = pr->next) {
+ xisrlen = sizeof(*xisr);
+ if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) {
+ xisrlen += (sysdep_sa_len(iph2->src)
+ + sysdep_sa_len(iph2->dst));
+ }
+
+ policylen += PFKEY_ALIGN8(xisrlen);
+ }
+ }
+
+ /* make policy structure */
+ policy = racoon_malloc(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
+
+ /* 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 + 1);
+
+ for (pr = iph2->approval->head; pr; pr = pr->next) {
+
+ satype = doi2ipproto(pr->proto_id);
+ if (satype == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid proto_id %d\n", pr->proto_id);
+ goto err;
+ }
+ mode = ipsecdoi2pfkey_mode(pr->encmode);
+ if (mode == ~0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid encmode %d\n", pr->encmode);
+ goto err;
+ }
+
+ /*
+ * the policy level cannot be unique because the policy
+ * is defined later than SA, so req_id cannot be bound to SA.
+ */
+ xisr->sadb_x_ipsecrequest_proto = satype;
+ xisr->sadb_x_ipsecrequest_mode = mode;
+ 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(iph2->src);
+ dst_len = sysdep_sa_len(iph2->dst);
+ xisrlen += src_len + dst_len;
+
+ memcpy(p, iph2->src, src_len);
+ p += src_len;
+
+ memcpy(p, iph2->dst, dst_len);
+ p += dst_len;
+ }
+
+ xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(xisrlen);
+ }
+
+end:
+ *policy0 = policy;
+ *policylen0 = policylen;
+
+ return 0;
+
+err:
+ if (policy)
+ racoon_free(policy);
+
+ return -1;
+}
+
+int
+pk_sendspdupdate2(iph2)
+ struct ph2handle *iph2;
+{
+ struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
+ caddr_t policy = NULL;
+ int policylen = 0;
+ u_int64_t ltime, vtime;
+
+ ltime = iph2->approval->lifetime;
+ vtime = 0;
+
+ if (getsadbpolicy(&policy, &policylen, SADB_X_SPDUPDATE, iph2)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "getting sadb policy failed.\n");
+ return -1;
+ }
+
+ if (pfkey_send_spdupdate2(
+ lcconf->sock_pfkey,
+ (struct sockaddr *)&spidx->src,
+ spidx->prefs,
+ (struct sockaddr *)&spidx->dst,
+ spidx->prefd,
+ spidx->ul_proto,
+ ltime, vtime,
+ policy, policylen, 0) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send spdupdate2 (%s)\n",
+ ipsec_strerror());
+ goto end;
+ }
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdupdate2\n");
+
+end:
+ if (policy)
+ racoon_free(policy);
+
+ return 0;
+}
+
+static int
+pk_recvspdupdate(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_address *saddr, *daddr;
+ struct sadb_x_policy *xpl;
+ struct policyindex spidx;
+ struct secpolicy *sp;
+
+ /* 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];
+
+#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,
+ &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,
+ &spidx);
+#endif
+
+ sp = getsp(&spidx);
+ if (sp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "such policy does not already exist: %s\n",
+ spidx2str(&spidx));
+ } else {
+ remsp(sp);
+ delsp(sp);
+ }
+
+ if (addnewsp(mhp) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * this function has to be used by responder side.
+ */
+int
+pk_sendspdadd2(iph2)
+ struct ph2handle *iph2;
+{
+ struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
+ caddr_t policy = NULL;
+ int policylen = 0;
+ u_int64_t ltime, vtime;
+
+ ltime = iph2->approval->lifetime;
+ vtime = 0;
+
+ if (getsadbpolicy(&policy, &policylen, SADB_X_SPDADD, iph2)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "getting sadb policy failed.\n");
+ return -1;
+ }
+
+ if (pfkey_send_spdadd2(
+ lcconf->sock_pfkey,
+ (struct sockaddr *)&spidx->src,
+ spidx->prefs,
+ (struct sockaddr *)&spidx->dst,
+ spidx->prefd,
+ spidx->ul_proto,
+ ltime, vtime,
+ policy, policylen, 0) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send spdadd2 (%s)\n",
+ ipsec_strerror());
+ goto end;
+ }
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdadd2\n");
+
+end:
+ if (policy)
+ racoon_free(policy);
+
+ return 0;
+}
+
+static int
+pk_recvspdadd(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_address *saddr, *daddr;
+ struct sadb_x_policy *xpl;
+ struct policyindex spidx;
+ struct secpolicy *sp;
+
+ /* 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];
+
+#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,
+ &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,
+ &spidx);
+#endif
+
+ sp = getsp(&spidx);
+ if (sp != NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "such policy already exists. "
+ "anyway replace it: %s\n",
+ spidx2str(&spidx));
+ remsp(sp);
+ delsp(sp);
+ }
+
+ if (addnewsp(mhp) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * this function has to be used by responder side.
+ */
+int
+pk_sendspddelete(iph2)
+ struct ph2handle *iph2;
+{
+ struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
+ caddr_t policy = NULL;
+ int policylen;
+
+ if (getsadbpolicy(&policy, &policylen, SADB_X_SPDDELETE, iph2)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "getting sadb policy failed.\n");
+ return -1;
+ }
+
+ if (pfkey_send_spddelete(
+ lcconf->sock_pfkey,
+ (struct sockaddr *)&spidx->src,
+ spidx->prefs,
+ (struct sockaddr *)&spidx->dst,
+ spidx->prefd,
+ spidx->ul_proto,
+ policy, policylen, 0) < 0) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "libipsec failed send spddelete (%s)\n",
+ ipsec_strerror());
+ goto end;
+ }
+ plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spddelete\n");
+
+end:
+ if (policy)
+ racoon_free(policy);
+
+ return 0;
+}
+
+static int
+pk_recvspddelete(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_address *saddr, *daddr;
+ struct sadb_x_policy *xpl;
+ struct policyindex spidx;
+ struct secpolicy *sp;
+
+ /* 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];
+
+#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,
+ &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,
+ &spidx);
+#endif
+
+ sp = getsp(&spidx);
+ if (sp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no policy found: %s\n",
+ spidx2str(&spidx));
+ return -1;
+ }
+
+ remsp(sp);
+ delsp(sp);
+
+ return 0;
+}
+
+static int
+pk_recvspdexpire(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_address *saddr, *daddr;
+ struct sadb_x_policy *xpl;
+ struct policyindex spidx;
+ struct secpolicy *sp;
+
+ /* 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];
+
+#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,
+ &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,
+ &spidx);
+#endif
+
+ sp = getsp(&spidx);
+ if (sp == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "no policy found: %s\n",
+ spidx2str(&spidx));
+ return -1;
+ }
+
+ remsp(sp);
+ delsp(sp);
+
+ return 0;
+}
+
+static int
+pk_recvspdget(mhp)
+ caddr_t *mhp;
+{
+ /* sanity check */
+ if (mhp[0] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb spdget message passed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+pk_recvspddump(mhp)
+ caddr_t *mhp;
+{
+ struct sadb_msg *msg;
+ struct sadb_address *saddr, *daddr;
+ struct sadb_x_policy *xpl;
+ struct policyindex spidx;
+ struct secpolicy *sp;
+
+ /* sanity check */
+ if (mhp[0] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb spddump message passed.\n");
+ return -1;
+ }
+ msg = (struct sadb_msg *)mhp[0];
+
+ saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
+ daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
+ xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
+
+ 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,
+ &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,
+ &spidx);
+#endif
+
+ sp = getsp(&spidx);
+ if (sp != NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "such policy already exists. "
+ "anyway replace it: %s\n",
+ spidx2str(&spidx));
+ remsp(sp);
+ delsp(sp);
+ }
+
+ if (addnewsp(mhp) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int
+pk_recvspdflush(mhp)
+ caddr_t *mhp;
+{
+ /* sanity check */
+ if (mhp[0] == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "inappropriate sadb spdflush message passed.\n");
+ return -1;
+ }
+
+ flushsp();
+
+ return 0;
+}
+
+/*
+ * send error against acquire message to kenrel.
+ */
+int
+pk_sendeacquire(iph2)
+ struct ph2handle *iph2;
+{
+ struct sadb_msg *newmsg;
+ int len;
+
+ len = sizeof(struct sadb_msg);
+ newmsg = racoon_calloc(1, len);
+ if (newmsg == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get buffer to send acquire.\n");
+ return -1;
+ }
+
+ memset(newmsg, 0, len);
+ newmsg->sadb_msg_version = PF_KEY_V2;
+ newmsg->sadb_msg_type = SADB_ACQUIRE;
+ newmsg->sadb_msg_errno = ENOENT; /* XXX */
+ newmsg->sadb_msg_satype = iph2->satype;
+ newmsg->sadb_msg_len = PFKEY_UNIT64(len);
+ newmsg->sadb_msg_reserved = 0;
+ newmsg->sadb_msg_seq = iph2->seq;
+ newmsg->sadb_msg_pid = (u_int32_t)getpid();
+
+ /* send message */
+ len = pfkey_send(lcconf->sock_pfkey, newmsg, len);
+
+ racoon_free(newmsg);
+
+ return 0;
+}
+
+/*
+ * check if the algorithm is supported or not.
+ * OUT 0: ok
+ * -1: ng
+ */
+int
+pk_checkalg(class, calg, keylen)
+ int class, calg, keylen;
+{
+ int sup, error;
+ u_int alg;
+ struct sadb_alg alg0;
+
+ switch (algclass2doi(class)) {
+ case IPSECDOI_PROTO_IPSEC_ESP:
+ sup = SADB_EXT_SUPPORTED_ENCRYPT;
+ break;
+ case IPSECDOI_ATTR_AUTH:
+ sup = SADB_EXT_SUPPORTED_AUTH;
+ break;
+ case IPSECDOI_PROTO_IPCOMP:
+ plog(LLV_DEBUG, LOCATION, NULL,
+ "compression algorithm can not be checked "
+ "because sadb message doesn't support it.\n");
+ return 0;
+ default:
+ plog(LLV_ERROR, LOCATION, NULL,
+ "invalid algorithm class.\n");
+ return -1;
+ }
+ alg = ipsecdoi2pfkey_alg(algclass2doi(class), algtype2doi(class, calg));
+ if (alg == ~0)
+ return -1;
+
+ if (keylen == 0) {
+ if (ipsec_get_keylen(sup, alg, &alg0)) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "%s.\n", ipsec_strerror());
+ return -1;
+ }
+ keylen = alg0.sadb_alg_minbits;
+ }
+
+ error = ipsec_check_keylen(sup, alg, keylen);
+ if (error)
+ plog(LLV_ERROR, LOCATION, NULL,
+ "%s.\n", ipsec_strerror());
+
+ return error;
+}
+
+/*
+ * differences with pfkey_recv() in libipsec/pfkey.c:
+ * - never performs busy wait loop.
+ * - returns NULL and set *lenp to negative on fatal failures
+ * - returns NULL and set *lenp to non-negative on non-fatal failures
+ * - returns non-NULL on success
+ */
+static struct sadb_msg *
+pk_recv(so, lenp)
+ int so;
+ int *lenp;
+{
+ struct sadb_msg buf, *newmsg;
+ int reallen;
+
+ *lenp = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK);
+ if (*lenp < 0)
+ return NULL; /*fatal*/
+ else if (*lenp < sizeof(buf))
+ return NULL;
+
+ reallen = PFKEY_UNUNIT64(buf.sadb_msg_len);
+ if ((newmsg = racoon_calloc(1, reallen)) == NULL)
+ return NULL;
+
+ *lenp = recv(so, (caddr_t)newmsg, reallen, MSG_PEEK);
+ if (*lenp < 0) {
+ racoon_free(newmsg);
+ return NULL; /*fatal*/
+ } else if (*lenp != reallen) {
+ racoon_free(newmsg);
+ return NULL;
+ }
+
+ *lenp = recv(so, (caddr_t)newmsg, reallen, 0);
+ if (*lenp < 0) {
+ racoon_free(newmsg);
+ return NULL; /*fatal*/
+ } else if (*lenp != reallen) {
+ racoon_free(newmsg);
+ return NULL;
+ }
+
+ return newmsg;
+}
+
+/* see handler.h */
+u_int32_t
+pk_getseq()
+{
+ return eay_random();
+}
+
+static int
+addnewsp(mhp)
+ caddr_t *mhp;
+{
+ struct secpolicy *new;
+ struct sadb_address *saddr, *daddr;
+ struct sadb_x_policy *xpl;
+
+ /* 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");
+ 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];
+
+#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");
+ return -1;
+ }
+
+ 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");
+ return -1;
+ }
+
+ 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");
+ return -1;
+ }
+
+ /* allocate request buffer */
+ *p_isr = newipsecreq();
+ if (*p_isr == NULL) {
+ plog(LLV_ERROR, LOCATION, NULL,
+ "failed to get new ipsecreq.\n");
+ return -1;
+ }
+
+ /* 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);
+ return -1;
+ }
+ (*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);
+ return -1;
+ }
+ (*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);
+ return -1;
+ }
+ (*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");
+ 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,
+ &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,
+ &new->spidx);
+#endif
+
+ inssp(new);
+
+ return 0;
+}
+
+/* proto/mode/src->dst spi */
+const char *
+sadbsecas2str(src, dst, proto, spi, mode)
+ struct sockaddr *src, *dst;
+ int proto;
+ u_int32_t spi;
+ int mode;
+{
+ static char buf[256];
+ u_int doi_proto, doi_mode = 0;
+ char *p;
+ int blen, i;
+
+ doi_proto = pfkey2ipsecdoi_proto(proto);
+ if (doi_proto == ~0)
+ return NULL;
+ if (mode) {
+ doi_mode = pfkey2ipsecdoi_mode(mode);
+ if (doi_mode == ~0)
+ return NULL;
+ }
+
+ blen = sizeof(buf) - 1;
+ p = buf;
+
+ i = snprintf(p, blen, "%s%s%s ",
+ s_ipsecdoi_proto(doi_proto),
+ mode ? "/" : "",
+ mode ? s_ipsecdoi_encmode(doi_mode) : "");
+ if (i < 0 || i >= blen)
+ return NULL;
+ p += i;
+ blen -= i;
+
+ i = snprintf(p, blen, "%s->", saddrwop2str(src));
+ if (i < 0 || i >= blen)
+ return NULL;
+ p += i;
+ blen -= i;
+
+ i = snprintf(p, blen, "%s ", saddrwop2str(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;
+}