Logo Search packages:      
Sourcecode: ipsec-tools version File versions

isakmp.c

/* $Id: isakmp.c,v 1.32.2.4 2005/04/21 08:57:17 monas 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 <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/queue.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#include <netdb.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>

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

#include "remoteconf.h"
#include "localconf.h"
#include "grabmyaddr.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "oakley.h"
#include "evt.h"
#include "handler.h"
#include "ipsec_doi.h"
#include "pfkey.h"
#include "crypto_openssl.h"
#include "policy.h"
#include "isakmp_ident.h"
#include "isakmp_agg.h"
#include "isakmp_base.h"
#include "isakmp_quick.h"
#include "isakmp_inf.h"
#include "isakmp_newg.h"
#ifdef ENABLE_HYBRID
#include "isakmp_xauth.h"
#include "isakmp_cfg.h"
#endif
#ifdef ENABLE_FRAG
#include "isakmp_frag.h"
#endif
#include "strnames.h"

#ifdef ENABLE_NATT
# include "nattraversal.h"
# ifdef __linux__
#  include <linux/udp.h>
#  ifndef SOL_UDP
#   define SOL_UDP 17
#  endif
# endif /* __linux__ */
# if defined(__NetBSD__) || defined(__FreeBSD__)
#  include <netinet/in.h>
#  include <netinet/udp.h>
#  define SOL_UDP IPPROTO_UDP
# endif /* __NetBSD__ / __FreeBSD__ */
#endif

static int nostate1 __P((struct ph1handle *, vchar_t *));
static int nostate2 __P((struct ph2handle *, vchar_t *));

extern caddr_t val2str(const char *, size_t);

static int (*ph1exchange[][2][PHASE1ST_MAX])
      __P((struct ph1handle *, vchar_t *)) = {
 /* error */
 { {}, {}, },
 /* Identity Protection exchange */
 {
  { nostate1, ident_i1send, nostate1, ident_i2recv, ident_i2send,
    ident_i3recv, ident_i3send, ident_i4recv, ident_i4send, nostate1, },
  { nostate1, ident_r1recv, ident_r1send, ident_r2recv, ident_r2send,
    ident_r3recv, ident_r3send, nostate1, nostate1, nostate1, },
 },
 /* Aggressive exchange */
 {
  { nostate1, agg_i1send, nostate1, agg_i2recv, agg_i2send,
    nostate1, nostate1, nostate1, nostate1, nostate1, },
  { nostate1, agg_r1recv, agg_r1send, agg_r2recv, agg_r2send,
    nostate1, nostate1, nostate1, nostate1, nostate1, },
 },
 /* Base exchange */
 {
  { nostate1, base_i1send, nostate1, base_i2recv, base_i2send,
    base_i3recv, base_i3send, nostate1, nostate1, nostate1, },
  { nostate1, base_r1recv, base_r1send, base_r2recv, base_r2send,
    nostate1, nostate1, nostate1, nostate1, nostate1, },
 },
};

static int (*ph2exchange[][2][PHASE2ST_MAX])
      __P((struct ph2handle *, vchar_t *)) = {
 /* error */
 { {}, {}, },
 /* Quick mode for IKE*/
 {
  { nostate2, nostate2, quick_i1prep, nostate2, quick_i1send,
    quick_i2recv, quick_i2send, quick_i3recv, nostate2, nostate2, },
  { nostate2, quick_r1recv, quick_r1prep, nostate2, quick_r2send,
    quick_r3recv, quick_r3prep, quick_r3send, nostate2, nostate2, }
 },
};

static u_char r_ck0[] = { 0,0,0,0,0,0,0,0 }; /* used to verify the r_ck. */
 
static int isakmp_main __P((vchar_t *, struct sockaddr *, struct sockaddr *));
static int ph1_main __P((struct ph1handle *, vchar_t *));
static int quick_main __P((struct ph2handle *, vchar_t *));
static int isakmp_ph1begin_r __P((vchar_t *,
      struct sockaddr *, struct sockaddr *, u_int8_t));
static int isakmp_ph2begin_i __P((struct ph1handle *, struct ph2handle *));
static int isakmp_ph2begin_r __P((struct ph1handle *, vchar_t *));
static int etypesw1 __P((int));
static int etypesw2 __P((int));
#ifdef ENABLE_FRAG
static int frag_handler(struct ph1handle *, 
    vchar_t *, struct sockaddr *, struct sockaddr *);
#endif

extern char **environ;

/*
 * isakmp packet handler
 */
int
isakmp_handler(so_isakmp)
      int so_isakmp;
{
      struct isakmp isakmp;
      union {
            char        buf[sizeof (isakmp) + 4];
            u_int32_t   non_esp[2];
      } x;
      struct sockaddr_storage remote;
      struct sockaddr_storage local;
      unsigned int remote_len = sizeof(remote);
      unsigned int local_len = sizeof(local);
      int len = 0, extralen = 0;
      u_short port;
      vchar_t *buf = NULL, *tmpbuf = NULL;
      int error = -1;

      /* read message by MSG_PEEK */
      while ((len = recvfromto(so_isakmp, x.buf, sizeof(x),
                MSG_PEEK, (struct sockaddr *)&remote, &remote_len,
                (struct sockaddr *)&local, &local_len)) < 0) {
            if (errno == EINTR)
                  continue;
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to receive isakmp packet: %s\n",
                  strerror (errno));
            goto end;
      }

      /* keep-alive packet - ignore */
      if (len == 1 && (x.buf[0]&0xff) == 0xff)
            goto end;

#ifdef ENABLE_NATT
      /* we don't know about portchange yet, 
         look for non-esp marker instead */
      if (x.non_esp[0] == 0 && x.non_esp[1] != 0)
            extralen = NON_ESP_MARKER_LEN;
#endif

      /* now we know if there is an extra non-esp 
         marker at the beginning or not */
      memcpy ((char *)&isakmp, x.buf + extralen, sizeof (isakmp));

      /* check isakmp header length, as well as sanity of header length */
      if (len < sizeof(isakmp) || ntohl(isakmp.len) < sizeof(isakmp)) {
            plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
                  "packet shorter than isakmp header size (%u, %u, %zu)\n",
                  len, ntohl(isakmp.len), sizeof(isakmp));
            /* dummy receive */
            if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp),
                      0, (struct sockaddr *)&remote, &remote_len)) < 0) {
                  plog(LLV_ERROR, LOCATION, NULL,
                        "failed to receive isakmp packet: %s\n",
                        strerror (errno));
            }
            goto end;
      }

      /* reject it if the size is tooooo big. */
      if (ntohl(isakmp.len) > 0xffff) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "the length in the isakmp header is too big.\n");
            if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp),
                      0, (struct sockaddr *)&remote, &remote_len)) < 0) {
                  plog(LLV_ERROR, LOCATION, NULL,
                        "failed to receive isakmp packet: %s\n",
                        strerror (errno));
            }
            goto end;
      }

      /* read real message */
      if ((tmpbuf = vmalloc(ntohl(isakmp.len) + extralen)) == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate reading buffer (%u Bytes)\n",
                  ntohl(isakmp.len) + extralen);
            /* dummy receive */
            if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp),
                      0, (struct sockaddr *)&remote, &remote_len)) < 0) {
                  plog(LLV_ERROR, LOCATION, NULL,
                        "failed to receive isakmp packet: %s\n", 
                        strerror (errno));
            }
            goto end;
      }

      while ((len = recvfromto(so_isakmp, (char *)tmpbuf->v, tmpbuf->l,
                          0, (struct sockaddr *)&remote, &remote_len,
                          (struct sockaddr *)&local, &local_len)) < 0) {
            if (errno == EINTR)
                  continue;
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to receive isakmp packet: %s\n",
                  strerror (errno));
            goto end;
      }

      if ((buf = vmalloc(len - extralen)) == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate reading buffer (%u Bytes)\n",
                  (len - extralen));
            goto end;
      }
      
      memcpy (buf->v, tmpbuf->v + extralen, buf->l);

      vfree (tmpbuf);

      len -= extralen;
      
      if (len != buf->l) {
            plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
                  "received invalid length (%d != %zu), why ?\n",
                  len, buf->l);
            goto end;
      }

      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
      plog(LLV_DEBUG, LOCATION, NULL,
            "%d bytes message received %s\n",
            len, saddr2str_fromto("from %s to %s", 
                  (struct sockaddr *)&remote,
                  (struct sockaddr *)&local));
      plogdump(LLV_DEBUG, buf->v, buf->l);

      /* avoid packets with malicious port/address */
      switch (remote.ss_family) {
      case AF_INET:
            port = ((struct sockaddr_in *)&remote)->sin_port;
            break;
#ifdef INET6
      case AF_INET6:
            port = ((struct sockaddr_in6 *)&remote)->sin6_port;
            break;
#endif
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid family: %d\n", remote.ss_family);
            goto end;
      }
      if (port == 0) {
            plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
                  "src port == 0 (valid as UDP but not with IKE)\n");
            goto end;
      }

      /* XXX: check sender whether to be allowed or not to accept */

      /* XXX: I don't know how to check isakmp half connection attack. */

      /* simply reply if the packet was processed. */
      if (check_recvdpkt((struct sockaddr *)&remote,
                  (struct sockaddr *)&local, buf)) {
            plog(LLV_NOTIFY, LOCATION, NULL,
                  "the packet is retransmitted by %s.\n",
                  saddr2str((struct sockaddr *)&remote));
            error = 0;
            goto end;
      }

      /* isakmp main routine */
      if (isakmp_main(buf, (struct sockaddr *)&remote,
                  (struct sockaddr *)&local) != 0) goto end;

      error = 0;

end:
      if (buf != NULL)
            vfree(buf);

      return(error);
}

/*
 * main processing to handle isakmp payload
 */
static int
isakmp_main(msg, remote, local)
      vchar_t *msg;
      struct sockaddr *remote, *local;
{
      struct isakmp *isakmp = (struct isakmp *)msg->v;
      isakmp_index *index = (isakmp_index *)isakmp;
      u_int32_t msgid = isakmp->msgid;
      struct ph1handle *iph1;

#ifdef HAVE_PRINT_ISAKMP_C
      isakmp_printpacket(msg, remote, local, 0);
#endif

      /* the initiator's cookie must not be zero */
      if (memcmp(&isakmp->i_ck, r_ck0, sizeof(cookie_t)) == 0) {
            plog(LLV_ERROR, LOCATION, remote,
                  "malformed cookie received.\n");
            return -1;
      }

      /* Check the Major and Minor Version fields. */
      /*
       * XXX Is is right to check version here ?
       * I think it may no be here because the version depends
       * on exchange status.
       */
      if (isakmp->v < ISAKMP_VERSION_NUMBER) {
            if (ISAKMP_GETMAJORV(isakmp->v) < ISAKMP_MAJOR_VERSION) {
                  plog(LLV_ERROR, LOCATION, remote,
                        "invalid major version %d.\n",
                        ISAKMP_GETMAJORV(isakmp->v));
                  return -1;
            }
#if ISAKMP_MINOR_VERSION > 0
            if (ISAKMP_GETMINORV(isakmp->v) < ISAKMP_MINOR_VERSION) {
                  plog(LLV_ERROR, LOCATION, remote,
                        "invalid minor version %d.\n",
                        ISAKMP_GETMINORV(isakmp->v));
                  return -1;
            }
#endif
      }

      /* check the Flags field. */
      /* XXX How is the exclusive check, E and A ? */
      if (isakmp->flags & ~(ISAKMP_FLAG_E | ISAKMP_FLAG_C | ISAKMP_FLAG_A)) {
            plog(LLV_ERROR, LOCATION, remote,
                  "invalid flag 0x%02x.\n", isakmp->flags);
            return -1;
      }

      /* ignore commit bit. */
      if (ISSET(isakmp->flags, ISAKMP_FLAG_C)) {
            if (isakmp->msgid == 0) {
                  isakmp_info_send_nx(isakmp, remote, local,
                        ISAKMP_NTYPE_INVALID_FLAGS, NULL);
                  plog(LLV_ERROR, LOCATION, remote,
                        "Commit bit on phase1 forbidden.\n");
                  return -1;
            }
      }

      iph1 = getph1byindex(index);
      if (iph1 != NULL) {
            /* validity check */
            if (memcmp(&isakmp->r_ck, r_ck0, sizeof(cookie_t)) == 0 &&
                iph1->side == INITIATOR) {
                  plog(LLV_DEBUG, LOCATION, remote,
                        "malformed cookie received or "
                        "the initiator's cookies collide.\n");
                  return -1;
            }

#ifdef ENABLE_NATT
            /* Floating ports for NAT-T */
            if ((iph1->natt_flags & NAT_DETECTED) &&
                ! (iph1->natt_flags & NAT_PORTS_CHANGED) &&
                ((cmpsaddrstrict(iph1->remote, remote) != 0) ||
                (cmpsaddrstrict(iph1->local, local) != 0)))
            {
                  /* prevent memory leak */
                  racoon_free (iph1->remote);
                  racoon_free (iph1->local);

                  /* copy-in new addresses */
                  iph1->remote = dupsaddr(remote);
                  iph1->local = dupsaddr(local);

                  /* set the flag to prevent further port floating
                     (FIXME: should we allow it? E.g. when the NAT gw 
                      is rebooted?) */
                  iph1->natt_flags |= NAT_PORTS_CHANGED;
                  
                  /* print some neat info */
                  plog (LLV_INFO, LOCATION, NULL, 
                        "NAT-T: ports changed to: %s\n",
                        saddr2str_fromto ("%s<->%s", iph1->remote, iph1->local));

                  natt_keepalive_add_ph1 (iph1);
            }
#endif

            /* must be same addresses in one stream of a phase at least. */
            if (cmpsaddrstrict(iph1->remote, remote) != 0) {
                  char *saddr_db, *saddr_act;

                  saddr_db = strdup(saddr2str(iph1->remote));
                  saddr_act = strdup(saddr2str(remote));

                  plog(LLV_WARNING, LOCATION, remote,
                        "remote address mismatched. db=%s, act=%s\n",
                        saddr_db, saddr_act);

                  racoon_free(saddr_db);
                  racoon_free(saddr_act);
            }

            /*
             * don't check of exchange type here because other type will be
             * with same index, for example, informational exchange.
             */

            /* XXX more acceptable check */
      }

      switch (isakmp->etype) {
      case ISAKMP_ETYPE_IDENT:
      case ISAKMP_ETYPE_AGG:
      case ISAKMP_ETYPE_BASE:
            /* phase 1 validity check */
            if (isakmp->msgid != 0) {
                  plog(LLV_ERROR, LOCATION, remote,
                        "message id should be zero in phase1.\n");
                  return -1;
            }

            /* search for isakmp status record of phase 1 */
            if (iph1 == NULL) {
                  /*
                   * the packet must be the 1st message from a initiator
                   * or the 2nd message from the responder.
                   */

                  /* search for phase1 handle by index without r_ck */
                  iph1 = getph1byindex0(index);
                  if (iph1 == NULL) {
                        /*it must be the 1st message from a initiator.*/
                        if (memcmp(&isakmp->r_ck, r_ck0,
                              sizeof(cookie_t)) != 0) {

                              plog(LLV_DEBUG, LOCATION, remote,
                                    "malformed cookie received "
                                    "or the spi expired.\n");
                              return -1;
                        }

                        /* it must be responder's 1st exchange. */
                        if (isakmp_ph1begin_r(msg, remote, local,
                              isakmp->etype) < 0)
                              return -1;
                        break;

                        /*NOTREACHED*/
                  }

                  /* it must be the 2nd message from the responder. */
                  if (iph1->side != INITIATOR) {
                        plog(LLV_DEBUG, LOCATION, remote,
                              "malformed cookie received. "
                              "it has to be as the initiator.  %s\n",
                              isakmp_pindex(&iph1->index, 0));
                        return -1;
                  }
            }

            /*
             * Don't delete phase 1 handler when the exchange type
             * in handler is not equal to packet's one because of no
             * authencication completed.
             */
            if (iph1->etype != isakmp->etype) {
                  plog(LLV_ERROR, LOCATION, iph1->remote,
                        "exchange type is mismatched: "
                        "db=%s packet=%s, ignore it.\n",
                        s_isakmp_etype(iph1->etype),
                        s_isakmp_etype(isakmp->etype));
                  return -1;
            }

#ifdef ENABLE_FRAG
            if (isakmp->np == ISAKMP_NPTYPE_FRAG)
                  return frag_handler(iph1, msg, remote, local);
#endif

            /* call main process of phase 1 */
            if (ph1_main(iph1, msg) < 0) {
                  plog(LLV_ERROR, LOCATION, iph1->remote,
                        "phase1 negotiation failed.\n");
                  remph1(iph1);
                  delph1(iph1);
                  return -1;
            }
            break;

      case ISAKMP_ETYPE_AUTH:
            plog(LLV_INFO, LOCATION, remote,
                  "unsupported exchange %d received.\n",
                  isakmp->etype);
            break;

      case ISAKMP_ETYPE_INFO:
      case ISAKMP_ETYPE_ACKINFO:
            /*
             * iph1 must be present for Information message.
             * if iph1 is null then trying to get the phase1 status
             * as the packet from responder againt initiator's 1st
             * exchange in phase 1.
             * NOTE: We think such informational exchange should be ignored.
             */
            if (iph1 == NULL) {
                  iph1 = getph1byindex0(index);
                  if (iph1 == NULL) {
                        plog(LLV_ERROR, LOCATION, remote,
                              "unknown Informational "
                              "exchange received.\n");
                        return -1;
                  }
                  if (cmpsaddrstrict(iph1->remote, remote) != 0) {
                        plog(LLV_WARNING, LOCATION, remote,
                              "remote address mismatched. "
                              "db=%s\n",
                              saddr2str(iph1->remote));
                  }
            }

#ifdef ENABLE_FRAG
            if (isakmp->np == ISAKMP_NPTYPE_FRAG)
                  return frag_handler(iph1, msg, remote, local);
#endif

            if (isakmp_info_recv(iph1, msg) < 0)
                  return -1;
            break;

      case ISAKMP_ETYPE_QUICK:
      {
            struct ph2handle *iph2;

            if (iph1 == NULL) {
                  isakmp_info_send_nx(isakmp, remote, local,
                        ISAKMP_NTYPE_INVALID_COOKIE, NULL);
                  plog(LLV_ERROR, LOCATION, remote,
                        "can't start the quick mode, "
                        "there is no ISAKMP-SA, %s\n",
                        isakmp_pindex((isakmp_index *)&isakmp->i_ck,
                              isakmp->msgid));
                  return -1;
            }

#ifdef ENABLE_FRAG
            if (isakmp->np == ISAKMP_NPTYPE_FRAG)
                  return frag_handler(iph1, msg, remote, local);
#endif

            /* check status of phase 1 whether negotiated or not. */
            if (iph1->status != PHASE1ST_ESTABLISHED) {
                  plog(LLV_ERROR, LOCATION, remote,
                        "can't start the quick mode, "
                        "there is no valid ISAKMP-SA, %s\n",
                        isakmp_pindex(&iph1->index, iph1->msgid));
                  return -1;
            }

            /* search isakmp phase 2 stauts record. */
            iph2 = getph2bymsgid(iph1, msgid);
            if (iph2 == NULL) {
                  /* it must be new negotiation as responder */
                  if (isakmp_ph2begin_r(iph1, msg) < 0)
                        return -1;
                  return 0;
                  /*NOTREACHED*/
            }

            /* commit bit. */
            /* XXX
             * we keep to set commit bit during negotiation.
             * When SA is configured, bit will be reset.
             * XXX
             * don't initiate commit bit.  should be fixed in the future.
             */
            if (ISSET(isakmp->flags, ISAKMP_FLAG_C))
                  iph2->flags |= ISAKMP_FLAG_C;

            /* call main process of quick mode */
            if (quick_main(iph2, msg) < 0) {
                  plog(LLV_ERROR, LOCATION, iph1->remote,
                        "phase2 negotiation failed.\n");
                  unbindph12(iph2);
                  remph2(iph2);
                  delph2(iph2);
                  return -1;
            }
      }
            break;

      case ISAKMP_ETYPE_NEWGRP:
            if (iph1 == NULL) {
                  plog(LLV_ERROR, LOCATION, remote,
                        "Unknown new group mode exchange, "
                        "there is no ISAKMP-SA.\n");
                  return -1;
            }

#ifdef ENABLE_FRAG
            if (isakmp->np == ISAKMP_NPTYPE_FRAG)
                  return frag_handler(iph1, msg, remote, local);
#endif

            isakmp_newgroup_r(iph1, msg);
            break;

#ifdef ENABLE_HYBRID
      case ISAKMP_ETYPE_CFG:
            if (iph1 == NULL) {
                  plog(LLV_ERROR, LOCATION, NULL,
                       "mode config %d from %s, "
                       "but we have no ISAKMP-SA.\n",
                       isakmp->etype, saddr2str(remote));
                  return -1;
            }

#ifdef ENABLE_FRAG
            if (isakmp->np == ISAKMP_NPTYPE_FRAG)
                  return frag_handler(iph1, msg, remote, local);
#endif

            isakmp_cfg_r(iph1, msg);
            break;
#endif       

      case ISAKMP_ETYPE_NONE:
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "Invalid exchange type %d from %s.\n",
                  isakmp->etype, saddr2str(remote));
            return -1;
      }

      return 0;
}

/*
 * main function of phase 1.
 */
static int
ph1_main(iph1, msg)
      struct ph1handle *iph1;
      vchar_t *msg;
{
      int error;
#ifdef ENABLE_STATS
      struct timeval start, end;
#endif

      /* ignore a packet */
      if (iph1->status == PHASE1ST_ESTABLISHED)
            return 0;

#ifdef ENABLE_STATS
      gettimeofday(&start, NULL);
#endif
      /* receive */
      if (ph1exchange[etypesw1(iph1->etype)]
                   [iph1->side]
                   [iph1->status] == NULL) {
            plog(LLV_ERROR, LOCATION, iph1->remote,
                  "why isn't the function defined.\n");
            return -1;
      }
      error = (ph1exchange[etypesw1(iph1->etype)]
                      [iph1->side]
                      [iph1->status])(iph1, msg);
      if (error != 0) {
#if 0
            /* XXX
             * When an invalid packet is received on phase1, it should
             * be selected to process this packet.  That is to respond
             * with a notify and delete phase 1 handler, OR not to respond
             * and keep phase 1 handler.
             */
            plog(LLV_ERROR, LOCATION, iph1->remote,
                  "failed to pre-process packet.\n");
            return -1;
#else
            /* ignore the error and keep phase 1 handler */
            return 0;
#endif
      }

      /* free resend buffer */
      if (iph1->sendbuf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "no buffer found as sendbuf\n"); 
            return -1;
      }
      VPTRINIT(iph1->sendbuf);

      /* turn off schedule */
      if (iph1->scr)
            SCHED_KILL(iph1->scr);

      /* send */
      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
      if ((ph1exchange[etypesw1(iph1->etype)]
                  [iph1->side]
                  [iph1->status])(iph1, msg) != 0) {
            plog(LLV_ERROR, LOCATION, iph1->remote,
                  "failed to process packet.\n");
            return -1;
      }

#ifdef ENABLE_STATS
      gettimeofday(&end, NULL);
      syslog(LOG_NOTICE, "%s(%s): %8.6f",
            "phase1", s_isakmp_state(iph1->etype, iph1->side, iph1->status),
            timedelta(&start, &end));
#endif
      if (iph1->status == PHASE1ST_ESTABLISHED) {

#ifdef ENABLE_STATS
            gettimeofday(&iph1->end, NULL);
            syslog(LOG_NOTICE, "%s(%s): %8.6f",
                  "phase1", s_isakmp_etype(iph1->etype),
                  timedelta(&iph1->start, &iph1->end));
#endif

            /* save created date. */
            (void)time(&iph1->created);

            /* add to the schedule to expire, and seve back pointer. */
            iph1->sce = sched_new(iph1->approval->lifetime,
                isakmp_ph1expire_stub, iph1);
#ifdef ENABLE_HYBRID
            if (iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) {
                  switch(iph1->approval->authmethod) {
                  case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_I:
                  case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_I:
                        xauth_sendreq(iph1);
                        /* XXX Don't process INITIAL_CONTACT */
                        iph1->rmconf->ini_contact = 0;
                        break;
                  default:
                        break;
                  }
            }
#endif
#ifdef ENABLE_DPD
            /* Schedule the r_u_there.... */
            if(iph1->dpd_support && iph1->rmconf->dpd_interval)
                  isakmp_sched_r_u(iph1, 0);
#endif

            /* INITIAL-CONTACT processing */
            /* don't anything if local test mode. */
            if (!f_local
             && iph1->rmconf->ini_contact && !getcontacted(iph1->remote)) {
                  /* send INITIAL-CONTACT */
                  isakmp_info_send_n1(iph1,
                              ISAKMP_NTYPE_INITIAL_CONTACT, NULL);
                  /* insert a node into contacted list. */
                  if (inscontacted(iph1->remote) == -1) {
                        plog(LLV_ERROR, LOCATION, iph1->remote,
                              "failed to add contacted list.\n");
                        /* ignore */
                  }
            }

            log_ph1established(iph1);
            plog(LLV_DEBUG, LOCATION, NULL, "===\n");

            /* 
             * SA up shell script hook: do it now,except if
             * ISAKMP mode config was requested. In the later
             * case it is done when we receive the configuration.
             */
            if ((iph1->status == PHASE1ST_ESTABLISHED) &&
                !iph1->rmconf->mode_cfg)
                  script_hook(iph1, SCRIPT_PHASE1_UP);      
      }

      return 0;
}

/*
 * main function of quick mode.
 */
static int
quick_main(iph2, msg)
      struct ph2handle *iph2;
      vchar_t *msg;
{
      struct isakmp *isakmp = (struct isakmp *)msg->v;
      int error;
#ifdef ENABLE_STATS
      struct timeval start, end;
#endif

      /* ignore a packet */
      if (iph2->status == PHASE2ST_ESTABLISHED
       || iph2->status == PHASE2ST_GETSPISENT)
            return 0;

#ifdef ENABLE_STATS
      gettimeofday(&start, NULL);
#endif

      /* receive */
      if (ph2exchange[etypesw2(isakmp->etype)]
                   [iph2->side]
                   [iph2->status] == NULL) {
            plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
                  "why isn't the function defined.\n");
            return -1;
      }
      error = (ph2exchange[etypesw2(isakmp->etype)]
                      [iph2->side]
                      [iph2->status])(iph2, msg);
      if (error != 0) {
            plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
                  "failed to pre-process packet.\n");
            if (error == ISAKMP_INTERNAL_ERROR)
                  return 0;
            isakmp_info_send_n1(iph2->ph1, error, NULL);
            return -1;
      }

      /* when using commit bit, status will be reached here. */
      if (iph2->status == PHASE2ST_ADDSA)
            return 0;

      /* free resend buffer */
      if (iph2->sendbuf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "no buffer found as sendbuf\n"); 
            return -1;
      }
      VPTRINIT(iph2->sendbuf);

      /* turn off schedule */
      if (iph2->scr)
            SCHED_KILL(iph2->scr);

      /* send */
      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
      if ((ph2exchange[etypesw2(isakmp->etype)]
                  [iph2->side]
                  [iph2->status])(iph2, msg) != 0) {
            plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
                  "failed to process packet.\n");
            return -1;
      }

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

      return 0;
}

/* new negotiation of phase 1 for initiator */
int
isakmp_ph1begin_i(rmconf, remote, local)
      struct remoteconf *rmconf;
      struct sockaddr *remote, *local;
{
      struct ph1handle *iph1;
#ifdef ENABLE_STATS
      struct timeval start, end;
#endif

      /* get new entry to isakmp status table. */
      iph1 = newph1();
      if (iph1 == NULL)
            return -1;

      iph1->status = PHASE1ST_START;
      iph1->rmconf = rmconf;
      iph1->side = INITIATOR;
      iph1->version = ISAKMP_VERSION_NUMBER;
      iph1->msgid = 0;
      iph1->flags = 0;
      iph1->ph2cnt = 0;
#ifdef HAVE_GSSAPI
      iph1->gssapi_state = NULL;
#endif
#ifdef ENABLE_HYBRID
      if ((iph1->mode_cfg = isakmp_cfg_mkstate()) == NULL)
            return -1;
#endif
#ifdef ENABLE_FRAG
      iph1->frag = 0;
      iph1->frag_chain = NULL;
#endif
      iph1->approval = NULL;

      /* XXX copy remote address */
      if (copy_ph1addresses(iph1, rmconf, remote, local) < 0)
            return -1;

      (void)insph1(iph1);

      /* start phase 1 exchange */
      iph1->etype = rmconf->etypes->type;

      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
    {
      char *a;

      a = strdup(saddr2str(iph1->local));
      plog(LLV_INFO, LOCATION, NULL,
            "initiate new phase 1 negotiation: %s<=>%s\n",
            a, saddr2str(iph1->remote));
      racoon_free(a);
    }
      plog(LLV_INFO, LOCATION, NULL,
            "begin %s mode.\n",
            s_isakmp_etype(iph1->etype));

#ifdef ENABLE_STATS
      gettimeofday(&iph1->start, NULL);
      gettimeofday(&start, NULL);
#endif
      /* start exchange */
      if ((ph1exchange[etypesw1(iph1->etype)]
                  [iph1->side]
                  [iph1->status])(iph1, NULL) != 0) {
            /* failed to start phase 1 negotiation */
            remph1(iph1);
            delph1(iph1);

            return -1;
      }

#ifdef ENABLE_STATS
      gettimeofday(&end, NULL);
      syslog(LOG_NOTICE, "%s(%s): %8.6f",
            "phase1",
            s_isakmp_state(iph1->etype, iph1->side, iph1->status),
            timedelta(&start, &end));
#endif

      return 0;
}

/* new negotiation of phase 1 for responder */
static int
isakmp_ph1begin_r(msg, remote, local, etype)
      vchar_t *msg;
      struct sockaddr *remote, *local;
      u_int8_t etype;
{
      struct isakmp *isakmp = (struct isakmp *)msg->v;
      struct remoteconf *rmconf;
      struct ph1handle *iph1;
      struct etypes *etypeok;
#ifdef ENABLE_STATS
      struct timeval start, end;
#endif

      /* look for my configuration */
      rmconf = getrmconf(remote);
      if (rmconf == NULL) {
            plog(LLV_ERROR, LOCATION, remote,
                  "couldn't find "
                  "configuration.\n");
            return -1;
      }

      /* check to be acceptable exchange type */
      etypeok = check_etypeok(rmconf, etype);
      if (etypeok == NULL) {
            plog(LLV_ERROR, LOCATION, remote,
                  "not acceptable %s mode\n", s_isakmp_etype(etype));
            return -1;
      }

      /* get new entry to isakmp status table. */
      iph1 = newph1();
      if (iph1 == NULL)
            return -1;

      memcpy(&iph1->index.i_ck, &isakmp->i_ck, sizeof(iph1->index.i_ck));
      iph1->status = PHASE1ST_START;
      iph1->rmconf = rmconf;
      iph1->flags = 0;
      iph1->side = RESPONDER;
      iph1->etype = etypeok->type;
      iph1->version = isakmp->v;
      iph1->msgid = 0;
#ifdef HAVE_GSSAPI
      iph1->gssapi_state = NULL;
#endif
#ifdef ENABLE_HYBRID
      if ((iph1->mode_cfg = isakmp_cfg_mkstate()) == NULL)
            return -1;
#endif
#ifdef ENABLE_FRAG
      iph1->frag = 0;
      iph1->frag_chain = NULL;
#endif
      iph1->approval = NULL;

      /* copy remote address */
      if (copy_ph1addresses(iph1, rmconf, remote, local) < 0)
            return -1;

      (void)insph1(iph1);

      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
    {
      char *a;

      a = strdup(saddr2str(iph1->local));
      plog(LLV_INFO, LOCATION, NULL,
            "respond new phase 1 negotiation: %s<=>%s\n",
            a, saddr2str(iph1->remote));
      racoon_free(a);
    }
      plog(LLV_INFO, LOCATION, NULL,
            "begin %s mode.\n", s_isakmp_etype(etype));

#ifdef ENABLE_STATS
      gettimeofday(&iph1->start, NULL);
      gettimeofday(&start, NULL);
#endif
      /* start exchange */
      if ((ph1exchange[etypesw1(iph1->etype)]
                      [iph1->side]
                      [iph1->status])(iph1, msg) < 0
       || (ph1exchange[etypesw1(iph1->etype)]
                  [iph1->side]
                  [iph1->status])(iph1, msg) < 0) {
            plog(LLV_ERROR, LOCATION, remote,
                  "failed to process packet.\n");
            remph1(iph1);
            delph1(iph1);
            return -1;
      }
#ifdef ENABLE_STATS
      gettimeofday(&end, NULL);
      syslog(LOG_NOTICE, "%s(%s): %8.6f",
            "phase1",
            s_isakmp_state(iph1->etype, iph1->side, iph1->status),
            timedelta(&start, &end));
#endif

      return 0;
}

/* new negotiation of phase 2 for initiator */
static int
isakmp_ph2begin_i(iph1, iph2)
      struct ph1handle *iph1;
      struct ph2handle *iph2;
{
#ifdef ENABLE_HYBRID
      if (xauth_check(iph1) != 0) {
            plog(LLV_ERROR, LOCATION, NULL,
                "Attempt to start phase 2 whereas Xauth failed\n");
            return -1;
      }
#endif

      /* found ISAKMP-SA. */
      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
      plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n");
    {
      char *a;
      a = strdup(saddr2str(iph2->src));
      plog(LLV_INFO, LOCATION, NULL,
            "initiate new phase 2 negotiation: %s<=>%s\n",
            a, saddr2str(iph2->dst));
      racoon_free(a);
    }

#ifdef ENABLE_STATS
      gettimeofday(&iph2->start, NULL);
#endif
      /* found isakmp-sa */
      bindph12(iph1, iph2);
      iph2->status = PHASE2ST_STATUS2;

      if ((ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)]
                   [iph2->side]
                   [iph2->status])(iph2, NULL) < 0) {
            unbindph12(iph2);
            /* release ipsecsa handler due to internal error. */
            remph2(iph2);
            delph2(iph2);
            return -1;
      }
      return 0;
}

/* new negotiation of phase 2 for responder */
static int
isakmp_ph2begin_r(iph1, msg)
      struct ph1handle *iph1;
      vchar_t *msg;
{
      struct isakmp *isakmp = (struct isakmp *)msg->v;
      struct ph2handle *iph2 = 0;
      int error;
#ifdef ENABLE_STATS
      struct timeval start, end;
#endif
#ifdef ENABLE_HYBRID
      if (xauth_check(iph1) != 0) {
            plog(LLV_ERROR, LOCATION, NULL,
                "Attempt to start phase 2 whereas Xauth failed\n");
            return -1;
      }
#endif

      iph2 = newph2();
      if (iph2 == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to allocate phase2 entry.\n");
            return -1;
      }

      iph2->ph1 = iph1;
      iph2->side = RESPONDER;
      iph2->status = PHASE2ST_START;
      iph2->flags = isakmp->flags;
      iph2->msgid = isakmp->msgid;
      iph2->seq = pk_getseq();
      iph2->ivm = oakley_newiv2(iph1, iph2->msgid);
      if (iph2->ivm == NULL) {
            delph2(iph2);
            return -1;
      }
      iph2->dst = dupsaddr(iph1->remote); /* XXX should be considered */
      if (iph2->dst == NULL) {
            delph2(iph2);
            return -1;
      }
      switch (iph2->dst->sa_family) {
      case AF_INET:
            ((struct sockaddr_in *)iph2->dst)->sin_port = 0;
            break;
#ifdef INET6
      case AF_INET6:
            ((struct sockaddr_in6 *)iph2->dst)->sin6_port = 0;
            break;
#endif
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid family: %d\n", iph2->dst->sa_family);
            delph2(iph2);
            return -1;
      }

      iph2->src = dupsaddr(iph1->local);  /* XXX should be considered */
      if (iph2->src == NULL) {
            delph2(iph2);
            return -1;
      }
      switch (iph2->src->sa_family) {
      case AF_INET:
            ((struct sockaddr_in *)iph2->src)->sin_port = 0;
            break;
#ifdef INET6
      case AF_INET6:
            ((struct sockaddr_in6 *)iph2->src)->sin6_port = 0;
            break;
#endif
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid family: %d\n", iph2->src->sa_family);
            delph2(iph2);
            return -1;
      }

      /* add new entry to isakmp status table */
      insph2(iph2);
      bindph12(iph1, iph2);

      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
    {
      char *a;

      a = strdup(saddr2str(iph2->src));
      plog(LLV_INFO, LOCATION, NULL,
            "respond new phase 2 negotiation: %s<=>%s\n",
            a, saddr2str(iph2->dst));
      racoon_free(a);
    }

#ifdef ENABLE_STATS
      gettimeofday(&start, NULL);
#endif

      error = (ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)]
                         [iph2->side]
                         [iph2->status])(iph2, msg);
      if (error != 0) {
            plog(LLV_ERROR, LOCATION, iph1->remote,
                  "failed to pre-process packet.\n");
            if (error != ISAKMP_INTERNAL_ERROR)
                  isakmp_info_send_n1(iph2->ph1, error, NULL);
            /*
             * release handler because it's wrong that ph2handle is kept
             * after failed to check message for responder's.
             */
            unbindph12(iph2);
            remph2(iph2);
            delph2(iph2);
            return -1;
      }

      /* send */
      plog(LLV_DEBUG, LOCATION, NULL, "===\n");
      if ((ph2exchange[etypesw2(isakmp->etype)]
                  [iph2->side]
                  [iph2->status])(iph2, msg) < 0) {
            plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
                  "failed to process packet.\n");
            /* don't release handler */
            return -1;
      }
#ifdef ENABLE_STATS
      gettimeofday(&end, NULL);
      syslog(LOG_NOTICE, "%s(%s): %8.6f",
            "phase2",
            s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status),
            timedelta(&start, &end));
#endif

      return 0;
}

/*
 * parse ISAKMP payloads, without ISAKMP base header.
 */
vchar_t *
isakmp_parsewoh(np0, gen, len)
      int np0;
      struct isakmp_gen *gen;
      int len;
{
      u_char np = np0 & 0xff;
      int tlen, plen;
      vchar_t *result;
      struct isakmp_parse_t *p, *ep;

      plog(LLV_DEBUG, LOCATION, NULL, "begin.\n");

      /*
       * 5 is a magic number, but any value larger than 2 should be fine
       * as we do vrealloc() in the following loop.
       */
      result = vmalloc(sizeof(struct isakmp_parse_t) * 5);
      if (result == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get buffer.\n");
            return NULL;
      }
      p = (struct isakmp_parse_t *)result->v;
      ep = (struct isakmp_parse_t *)(result->v + result->l - sizeof(*ep));

      tlen = len;

      /* parse through general headers */
      while (0 < tlen && np != ISAKMP_NPTYPE_NONE) {
            if (tlen <= sizeof(struct isakmp_gen)) {
                  /* don't send information, see isakmp_ident_r1() */
                  plog(LLV_ERROR, LOCATION, NULL,
                        "invalid length of payload\n");
                  vfree(result);
                  return NULL;
            }

            plog(LLV_DEBUG, LOCATION, NULL,
                  "seen nptype=%u(%s)\n", np, s_isakmp_nptype(np));

            p->type = np;
            p->len = ntohs(gen->len);
            if (p->len < sizeof(struct isakmp_gen) || p->len > tlen) {
                  plog(LLV_DEBUG, LOCATION, NULL,
                        "invalid length of payload\n");
                  vfree(result);
                  return NULL;
            }
            p->ptr = gen;
            p++;
            if (ep <= p) {
                  int off;

                  off = p - (struct isakmp_parse_t *)result->v;
                  result = vrealloc(result, result->l * 2);
                  if (result == NULL) {
                        plog(LLV_DEBUG, LOCATION, NULL,
                              "failed to realloc buffer.\n");
                        vfree(result);
                        return NULL;
                  }
                  ep = (struct isakmp_parse_t *)
                        (result->v + result->l - sizeof(*ep));
                  p = (struct isakmp_parse_t *)result->v;
                  p += off;
            }

            np = gen->np;
            plen = ntohs(gen->len);
            gen = (struct isakmp_gen *)((caddr_t)gen + plen);
            tlen -= plen;
      }
      p->type = ISAKMP_NPTYPE_NONE;
      p->len = 0;
      p->ptr = NULL;

      plog(LLV_DEBUG, LOCATION, NULL, "succeed.\n");

      return result;
}

/*
 * parse ISAKMP payloads, including ISAKMP base header.
 */
vchar_t *
isakmp_parse(buf)
      vchar_t *buf;
{
      struct isakmp *isakmp = (struct isakmp *)buf->v;
      struct isakmp_gen *gen;
      int tlen;
      vchar_t *result;
      u_char np;

      np = isakmp->np;
      gen = (struct isakmp_gen *)(buf->v + sizeof(*isakmp));
      tlen = buf->l - sizeof(struct isakmp);
      result = isakmp_parsewoh(np, gen, tlen);

      return result;
}

/* %%% */
int
isakmp_init()
{
      /* initialize a isakmp status table */
      initph1tree();
      initph2tree();
      initctdtree();
      init_recvdpkt();

      if (isakmp_open() < 0)
            goto err;

      return(0);

err:
      isakmp_close();
      return(-1);
}

/*
 * make strings containing i_cookie + r_cookie + msgid
 */
const char *
isakmp_pindex(index, msgid)
      const isakmp_index *index;
      const u_int32_t msgid;
{
      static char buf[64];
      const u_char *p;
      int i, j;

      memset(buf, 0, sizeof(buf));

      /* copy index */
      p = (const u_char *)index;
      for (j = 0, i = 0; i < sizeof(isakmp_index); i++) {
            snprintf((char *)&buf[j], sizeof(buf) - j, "%02x", p[i]);
            j += 2;
            switch (i) {
            case 7:
                  buf[j++] = ':';
            }
      }

      if (msgid == 0)
            return buf;

      /* copy msgid */
      snprintf((char *)&buf[j], sizeof(buf) - j, ":%08x", ntohs(msgid));

      return buf;
}

/* open ISAKMP sockets. */
int
isakmp_open()
{
      const int yes = 1;
      int ifnum = 0, encap_ifnum = 0;
#ifdef INET6
      int pktinfo;
#endif
      struct myaddrs *p;

      for (p = lcconf->myaddrs; p; p = p->next) {
            if (!p->addr)
                  continue;

            /* warn if wildcard address - should we forbid this? */
            switch (p->addr->sa_family) {
            case AF_INET:
                  if (((struct sockaddr_in *)p->addr)->sin_addr.s_addr == 0)
                        plog(LLV_WARNING, LOCATION, NULL,
                              "listening to wildcard address,"
                              "broadcast IKE packet may kill you\n");
                  break;
#ifdef INET6
            case AF_INET6:
                  if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)p->addr)->sin6_addr))
                        plog(LLV_WARNING, LOCATION, NULL,
                              "listening to wildcard address, "
                              "broadcast IKE packet may kill you\n");
                  break;
#endif
            default:
                  plog(LLV_ERROR, LOCATION, NULL,
                        "unsupported address family %d\n",
                        lcconf->default_af);
                  goto err_and_next;
            }

#ifdef INET6
            if (p->addr->sa_family == AF_INET6 &&
                IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)
                                  p->addr)->sin6_addr))
            {
                  plog(LLV_DEBUG, LOCATION, NULL, 
                        "Ignoring multicast address %s\n",
                        saddr2str(p->addr));
                        racoon_free(p->addr);
                        p->addr = NULL;
                  continue;
            }
#endif

            if ((p->sock = socket(p->addr->sa_family, SOCK_DGRAM, 0)) < 0) {
                  plog(LLV_ERROR, LOCATION, NULL,
                        "socket (%s)\n", strerror(errno));
                  goto err_and_next;
            }

            /* receive my interface address on inbound packets. */
            switch (p->addr->sa_family) {
            case AF_INET:
                  if (setsockopt(p->sock, IPPROTO_IP,
#ifdef __linux__
                               IP_PKTINFO,
#else
                               IP_RECVDSTADDR,
#endif
                              (const void *)&yes, sizeof(yes)) < 0) {
                        plog(LLV_ERROR, LOCATION, NULL,
                              "setsockopt (%s)\n", strerror(errno));
                        goto err_and_next;
                  }
                  break;
#ifdef INET6
            case AF_INET6:
#ifdef INET6_ADVAPI
#ifdef IPV6_RECVPKTINFO
                  pktinfo = IPV6_RECVPKTINFO;
#else  /* old adv. API */
                  pktinfo = IPV6_PKTINFO;
#endif /* IPV6_RECVPKTINFO */
#else
                  pktinfo = IPV6_RECVDSTADDR;
#endif
                  if (setsockopt(p->sock, IPPROTO_IPV6, pktinfo,
                              (const void *)&yes, sizeof(yes)) < 0)
                  {
                        plog(LLV_ERROR, LOCATION, NULL,
                              "setsockopt(%d): %s\n",
                              pktinfo, strerror(errno));
                        goto err_and_next;
                  }
                  break;
#endif
            }

#ifdef IPV6_USE_MIN_MTU
            if (p->addr->sa_family == AF_INET6 &&
                setsockopt(p->sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
                (void *)&yes, sizeof(yes)) < 0) {
                  plog(LLV_ERROR, LOCATION, NULL,
                      "setsockopt (%s)\n", strerror(errno));
                  return -1;
            }
#endif

            if (setsockopt_bypass(p->sock, p->addr->sa_family) < 0)
                  goto err_and_next;

            if (bind(p->sock, p->addr, sysdep_sa_len(p->addr)) < 0) {
                  plog(LLV_ERROR, LOCATION, p->addr,
                        "failed to bind to address %s (%s).\n",
                        saddr2str(p->addr), strerror(errno));
                  close(p->sock);
                  goto err_and_next;
            }

            ifnum++;

            plog(LLV_INFO, LOCATION, NULL,
                  "%s used as isakmp port (fd=%d)\n",
                  saddr2str(p->addr), p->sock);

#ifdef ENABLE_NATT
            if (p->addr->sa_family == AF_INET) {
                  int option = -1;


                  if(p->udp_encap)
                        option = UDP_ENCAP_ESPINUDP;
#if defined(ENABLE_NATT_00) || defined(ENABLE_NATT_01)
                  else
                        option = UDP_ENCAP_ESPINUDP_NON_IKE;
#endif
                  if(option != -1){
                        if (setsockopt (p->sock, SOL_UDP, UDP_ENCAP,
                                                &option, sizeof (option)) < 0) {
                              plog(LLV_ERROR, LOCATION, NULL,
                                     "setsockopt(%s): %s\n",
                                     option == UDP_ENCAP_ESPINUDP ? "UDP_ENCAP_ESPINUDP" : "UDP_ENCAP_ESPINUDP_NON_IKE",
                                     strerror(errno));
                              goto err_and_next;
                        }
                        else {
                              plog(LLV_INFO, LOCATION, NULL,
                                     "%s used for NAT-T\n",
                                     saddr2str(p->addr));
                              encap_ifnum++;
                        }
                  }
            }
#endif

            continue;

      err_and_next:
            racoon_free(p->addr);
            p->addr = NULL;
            if (! lcconf->autograbaddr && lcconf->strict_address)
                  return -1;
            continue;
      }

      if (!ifnum) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "no address could be bound.\n");
            return -1;
      }

#ifdef ENABLE_NATT
      if (natt_enabled_in_rmconf() && !encap_ifnum) {
            plog(LLV_WARNING, LOCATION, NULL, 
                  "NAT-T is enabled in at least one remote{} section,\n");
            plog(LLV_WARNING, LOCATION, NULL, 
                  "but no 'isakmp_natt' address was specified!\n");
      }
#endif

      return 0;
}

void
isakmp_close()
{
      struct myaddrs *p, *next;

      for (p = lcconf->myaddrs; p; p = next) {
            next = p->next;

            if (!p->addr) {
                  racoon_free(p);
                  continue;
            }
            close(p->sock);
            racoon_free(p->addr);
            racoon_free(p);
      }

      lcconf->myaddrs = NULL;
}

int
isakmp_send(iph1, sbuf)
      struct ph1handle *iph1;
      vchar_t *sbuf;
{
      int len = 0;
      int s;
      vchar_t *vbuf = NULL;

#ifdef ENABLE_NATT
      size_t extralen = NON_ESP_MARKER_USE(iph1) * NON_ESP_MARKER_LEN;

#ifdef ENABLE_FRAG
      /* 
       * Do not add the non ESP marker for a packet that will
       * be fragmented. The non ESP marker should appear in 
       * all fragment's packets, but not in the fragmented packet
       */
      if (iph1->frag && sbuf->l > ISAKMP_FRAG_MAXLEN) 
            extralen = 0;
#endif
      if (extralen)
            plog (LLV_DEBUG, LOCATION, NULL, "Adding NON-ESP marker\n");

      /* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker) 
         must added just before the packet itself. For this we must 
         allocate a new buffer and release it at the end. */
      if (extralen) {
            vbuf = vmalloc (sbuf->l + extralen);
            *(u_int32_t *)vbuf->v = 0;
            memcpy (vbuf->v + extralen, sbuf->v, sbuf->l);
      }
      else
            vbuf = sbuf;
#else
      vbuf = sbuf;
#endif

      /* select the socket to be sent */
      s = getsockmyaddr(iph1->local);
      if (s == -1)
            return -1;

      plog (LLV_DEBUG, LOCATION, NULL, "%zu bytes %s\n", vbuf->l, 
            saddr2str_fromto("from %s to %s", iph1->local, iph1->remote));

#ifdef ENABLE_FRAG
      if (iph1->frag && vbuf->l > ISAKMP_FRAG_MAXLEN) {
            if (isakmp_sendfrags(iph1, vbuf) == -1) {
                  plog(LLV_ERROR, LOCATION, NULL, 
                      "isakmp_sendfrags failed\n");
                  return -1;
            }
      } else 
#endif
      {
            len = sendfromto(s, vbuf->v, vbuf->l,
                iph1->local, iph1->remote, lcconf->count_persend);
            if (len == -1) {
                  plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
                  return -1;
            }
      }

      return 0;
}

/* called from scheduler */
void
isakmp_ph1resend_stub(p)
      void *p;
{
      (void)isakmp_ph1resend((struct ph1handle *)p);
}

int
isakmp_ph1resend(iph1)
      struct ph1handle *iph1;
{
      if (iph1->retry_counter < 0) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "phase1 negotiation failed due to time up. %s\n",
                  isakmp_pindex(&iph1->index, iph1->msgid));
            EVT_PUSH(iph1->local, iph1->remote, 
                EVTT_PEER_NO_RESPONSE, NULL);

            remph1(iph1);
            delph1(iph1);
            return -1;
      }

      if (isakmp_send(iph1, iph1->sendbuf) < 0)
            return -1;

      plog(LLV_DEBUG, LOCATION, NULL,
            "resend phase1 packet %s\n",
            isakmp_pindex(&iph1->index, iph1->msgid));

      iph1->retry_counter--;

      iph1->scr = sched_new(iph1->rmconf->retry_interval,
            isakmp_ph1resend_stub, iph1);

      return 0;
}

/* called from scheduler */
void
isakmp_ph2resend_stub(p)
      void *p;
{

      (void)isakmp_ph2resend((struct ph2handle *)p);
}

int
isakmp_ph2resend(iph2)
      struct ph2handle *iph2;
{
      if (iph2->retry_counter < 0) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "phase2 negotiation failed due to time up. %s\n",
                        isakmp_pindex(&iph2->ph1->index, iph2->msgid));
            EVT_PUSH(iph2->src, iph2->dst, EVTT_PEER_NO_RESPONSE, NULL);
            unbindph12(iph2);
            remph2(iph2);
            delph2(iph2);
            return -1;
      }

      if (isakmp_send(iph2->ph1, iph2->sendbuf) < 0)
            return -1;

      plog(LLV_DEBUG, LOCATION, NULL,
            "resend phase2 packet %s\n",
            isakmp_pindex(&iph2->ph1->index, iph2->msgid));

      iph2->retry_counter--;

      iph2->scr = sched_new(iph2->ph1->rmconf->retry_interval,
            isakmp_ph2resend_stub, iph2);

      return 0;
}

/* called from scheduler */
void
isakmp_ph1expire_stub(p)
      void *p;
{

      isakmp_ph1expire((struct ph1handle *)p);
}

void
isakmp_ph1expire(iph1)
      struct ph1handle *iph1;
{
      char *src, *dst;

      SCHED_KILL(iph1->sce);

      if(iph1->status != PHASE1ST_EXPIRED){
            src = strdup(saddr2str(iph1->local));
            dst = strdup(saddr2str(iph1->remote));
            plog(LLV_INFO, LOCATION, NULL,
                   "ISAKMP-SA expired %s-%s spi:%s\n",
                   src, dst,
                   isakmp_pindex(&iph1->index, 0));
            racoon_free(src);
            racoon_free(dst);
            iph1->status = PHASE1ST_EXPIRED;
      }

      /*
       * the phase1 deletion is postponed until there is no phase2.
       */
      if (LIST_FIRST(&iph1->ph2tree) != NULL) {
            iph1->sce = sched_new(1, isakmp_ph1expire_stub, iph1);
            return;
      }

      iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1);
}

/* called from scheduler */
void
isakmp_ph1delete_stub(p)
      void *p;
{

      isakmp_ph1delete((struct ph1handle *)p);
}

void
isakmp_ph1delete(iph1)
      struct ph1handle *iph1;
{
      char *src, *dst;

      SCHED_KILL(iph1->sce);

      if (LIST_FIRST(&iph1->ph2tree) != NULL) {
            iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1);
            return;
      }

      /* don't re-negosiation when the phase 1 SA expires. */

      src = strdup(saddr2str(iph1->local));
      dst = strdup(saddr2str(iph1->remote));
      plog(LLV_INFO, LOCATION, NULL,
            "ISAKMP-SA deleted %s-%s spi:%s\n",
            src, dst, isakmp_pindex(&iph1->index, 0));
      EVT_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_DOWN, NULL);
      racoon_free(src);
      racoon_free(dst);

      remph1(iph1);
      delph1(iph1);

      return;
}

/* called from scheduler.
 * this function will call only isakmp_ph2delete().
 * phase 2 handler remain forever if kernel doesn't cry a expire of phase 2 SA
 * by something cause.  That's why this function is called after phase 2 SA
 * expires in the userland.
 */
void
isakmp_ph2expire_stub(p)
      void *p;
{

      isakmp_ph2expire((struct ph2handle *)p);
}

void
isakmp_ph2expire(iph2)
      struct ph2handle *iph2;
{
      char *src, *dst;

      SCHED_KILL(iph2->sce);

      src = strdup(saddrwop2str(iph2->src));
      dst = strdup(saddrwop2str(iph2->dst));
      plog(LLV_INFO, LOCATION, NULL,
            "phase2 sa expired %s-%s\n", src, dst);
      racoon_free(src);
      racoon_free(dst);

      iph2->status = PHASE2ST_EXPIRED;

      iph2->sce = sched_new(1, isakmp_ph2delete_stub, iph2);

      return;
}

/* called from scheduler */
void
isakmp_ph2delete_stub(p)
      void *p;
{

      isakmp_ph2delete((struct ph2handle *)p);
}

void
isakmp_ph2delete(iph2)
      struct ph2handle *iph2;
{
      char *src, *dst;

      SCHED_KILL(iph2->sce);

      src = strdup(saddrwop2str(iph2->src));
      dst = strdup(saddrwop2str(iph2->dst));
      plog(LLV_INFO, LOCATION, NULL,
            "phase2 sa deleted %s-%s\n", src, dst);
      racoon_free(src);
      racoon_free(dst);

      unbindph12(iph2);
      remph2(iph2);
      delph2(iph2);

      return;
}

/* %%%
 * Interface between PF_KEYv2 and ISAKMP
 */
/*
 * receive ACQUIRE from kernel, and begin either phase1 or phase2.
 * if phase1 has been finished, begin phase2.
 */
int
isakmp_post_acquire(iph2)
      struct ph2handle *iph2;
{
      struct remoteconf *rmconf;
      struct ph1handle *iph1 = NULL;

      /* search appropreate configuration with masking port. */
      rmconf = getrmconf(iph2->dst);
      if (rmconf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "no configuration found for %s.\n",
                  saddrwop2str(iph2->dst));
            return -1;
      }

      /* if passive mode, ignore the acquire message */
      if (rmconf->passive) {
            plog(LLV_DEBUG, LOCATION, NULL,
                  "because of passive mode, "
                  "ignore the acquire message for %s.\n",
                  saddrwop2str(iph2->dst));
            return 0;
      }

      /* search isakmp status table by address with masking port */
      iph1 = getph1byaddr(iph2->src, iph2->dst);

      /* no ISAKMP-SA found. */
      if (iph1 == NULL) {
            struct sched *sc;

            iph2->retry_checkph1 = lcconf->retry_checkph1;
            sc = sched_new(1, isakmp_chkph1there_stub, iph2);
            plog(LLV_INFO, LOCATION, NULL,
                  "IPsec-SA request for %s queued "
                  "due to no phase1 found.\n",
                  saddrwop2str(iph2->dst));

            /* start phase 1 negotiation as a initiator. */
            if (isakmp_ph1begin_i(rmconf, iph2->dst, iph2->src) < 0) {
                  SCHED_KILL(sc);
                  return -1;
            }

            return 0;
            /*NOTREACHED*/
      }

      /* found ISAKMP-SA, but on negotiation. */
      if (iph1->status != PHASE1ST_ESTABLISHED) {
            iph2->retry_checkph1 = lcconf->retry_checkph1;
            sched_new(1, isakmp_chkph1there_stub, iph2);
            plog(LLV_INFO, LOCATION, iph2->dst,
                  "request for establishing IPsec-SA was queued "
                  "due to no phase1 found.\n");
            return 0;
            /*NOTREACHED*/
      }

      /* found established ISAKMP-SA */
      /* i.e. iph1->status == PHASE1ST_ESTABLISHED */

      /* found ISAKMP-SA. */
      plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n");

      /* begin quick mode */
      if (isakmp_ph2begin_i(iph1, iph2))
            return -1;

      return 0;
}

/*
 * receive GETSPI from kernel.
 */
int
isakmp_post_getspi(iph2)
      struct ph2handle *iph2;
{
#ifdef ENABLE_STATS
      struct timeval start, end;
#endif

      /* don't process it because there is no suitable phase1-sa. */
      if (iph2->ph1->status == PHASE1ST_EXPIRED) {
            plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
                  "the negotiation is stopped, "
                  "because there is no suitable ISAKMP-SA.\n");
            return -1;
      }

#ifdef ENABLE_STATS
      gettimeofday(&start, NULL);
#endif
      if ((ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)]
                      [iph2->side]
                      [iph2->status])(iph2, NULL) != 0)
            return -1;
#ifdef ENABLE_STATS
      gettimeofday(&end, NULL);
      syslog(LOG_NOTICE, "%s(%s): %8.6f",
            "phase2",
            s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status),
            timedelta(&start, &end));
#endif

      return 0;
}

/* called by scheduler */
void
isakmp_chkph1there_stub(p)
      void *p;
{
      isakmp_chkph1there((struct ph2handle *)p);
}

void
isakmp_chkph1there(iph2)
      struct ph2handle *iph2;
{
      struct ph1handle *iph1;

      iph2->retry_checkph1--;
      if (iph2->retry_checkph1 < 0) {
            plog(LLV_ERROR, LOCATION, iph2->dst,
                  "phase2 negotiation failed "
                  "due to time up waiting for phase1. %s\n",
                  sadbsecas2str(iph2->dst, iph2->src,
                        iph2->satype, 0, 0));
            plog(LLV_INFO, LOCATION, NULL,
                  "delete phase 2 handler.\n");

            /* send acquire to kernel as error */
            pk_sendeacquire(iph2);

            unbindph12(iph2);
            remph2(iph2);
            delph2(iph2);

            return;
      }

      iph1 = getph1byaddr(iph2->src, iph2->dst);

      /* XXX Even if ph1 as responder is there, should we not start
       * phase 2 negotiation ? */
      if (iph1 != NULL
       && iph1->status == PHASE1ST_ESTABLISHED) {
            /* found isakmp-sa */
            /* begin quick mode */
            (void)isakmp_ph2begin_i(iph1, iph2);
            return;
      }

      /* no isakmp-sa found */
      sched_new(1, isakmp_chkph1there_stub, iph2);

      return;
}

/* copy variable data into ALLOCATED buffer. */
caddr_t
isakmp_set_attr_v(buf, type, val, len)
      caddr_t buf;
      int type;
      caddr_t val;
      int len;
{
      struct isakmp_data *data;

      data = (struct isakmp_data *)buf;
      data->type = htons((u_int16_t)type | ISAKMP_GEN_TLV);
      data->lorv = htons((u_int16_t)len);
      memcpy(data + 1, val, len);

      return buf + sizeof(*data) + len;
}

/* copy fixed length data into ALLOCATED buffer. */
caddr_t
isakmp_set_attr_l(buf, type, val)
      caddr_t buf;
      int type;
      u_int32_t val;
{
      struct isakmp_data *data;

      data = (struct isakmp_data *)buf;
      data->type = htons((u_int16_t)type | ISAKMP_GEN_TV);
      data->lorv = htons((u_int16_t)val);

      return buf + sizeof(*data);
}

/* add a variable data attribute to the buffer by reallocating it. */
vchar_t *
isakmp_add_attr_v(buf0, type, val, len)
      vchar_t *buf0;
      int type;
      caddr_t val;
      int len;
{
      vchar_t *buf = NULL;
      struct isakmp_data *data;
      int tlen;
      int oldlen = 0;

      tlen = sizeof(*data) + len;

      if (buf0) {
            oldlen = buf0->l;
            buf = vrealloc(buf0, oldlen + tlen);
      } else
            buf = vmalloc(tlen);
      if (!buf) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get a attribute buffer.\n");
            return NULL;
      }

      data = (struct isakmp_data *)(buf->v + oldlen);
      data->type = htons((u_int16_t)type | ISAKMP_GEN_TLV);
      data->lorv = htons((u_int16_t)len);
      memcpy(data + 1, val, len);

      return buf;
}

/* add a fixed data attribute to the buffer by reallocating it. */
vchar_t *
isakmp_add_attr_l(buf0, type, val)
      vchar_t *buf0;
      int type;
      u_int32_t val;
{
      vchar_t *buf = NULL;
      struct isakmp_data *data;
      int tlen;
      int oldlen = 0;

      tlen = sizeof(*data);

      if (buf0) {
            oldlen = buf0->l;
            buf = vrealloc(buf0, oldlen + tlen);
      } else
            buf = vmalloc(tlen);
      if (!buf) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get a attribute buffer.\n");
            return NULL;
      }

      data = (struct isakmp_data *)(buf->v + oldlen);
      data->type = htons((u_int16_t)type | ISAKMP_GEN_TV);
      data->lorv = htons((u_int16_t)val);

      return buf;
}

/*
 * calculate cookie and set.
 */
int
isakmp_newcookie(place, remote, local)
      caddr_t place;
      struct sockaddr *remote;
      struct sockaddr *local;
{
      vchar_t *buf = NULL, *buf2 = NULL;
      char *p;
      int blen;
      int alen;
      caddr_t sa1, sa2;
      time_t t;
      int error = -1;
      u_short port;


      if (remote->sa_family != local->sa_family) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "address family mismatch, remote:%d local:%d\n",
                  remote->sa_family, local->sa_family);
            goto end;
      }
      switch (remote->sa_family) {
      case AF_INET:
            alen = sizeof(struct in_addr);
            sa1 = (caddr_t)&((struct sockaddr_in *)remote)->sin_addr;
            sa2 = (caddr_t)&((struct sockaddr_in *)local)->sin_addr;
            break;
#ifdef INET6
      case AF_INET6:
            alen = sizeof(struct in_addr);
            sa1 = (caddr_t)&((struct sockaddr_in6 *)remote)->sin6_addr;
            sa2 = (caddr_t)&((struct sockaddr_in6 *)local)->sin6_addr;
            break;
#endif
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid family: %d\n", remote->sa_family);
            goto end;
      }
      blen = (alen + sizeof(u_short)) * 2
            + sizeof(time_t) + lcconf->secret_size;
      buf = vmalloc(blen);
      if (buf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get a cookie.\n");
            goto end;
      }
      p = buf->v;

      /* copy my address */
      memcpy(p, sa1, alen);
      p += alen;
      port = ((struct sockaddr_in *)remote)->sin_port;
      memcpy(p, &port, sizeof(u_short));
      p += sizeof(u_short);

      /* copy target address */
      memcpy(p, sa2, alen);
      p += alen;
      port = ((struct sockaddr_in *)local)->sin_port;
      memcpy(p, &port, sizeof(u_short));
      p += sizeof(u_short);

      /* copy time */
      t = time(0);
      memcpy(p, (caddr_t)&t, sizeof(t));
      p += sizeof(t);

      /* copy random value */
      buf2 = eay_set_random(lcconf->secret_size);
      if (buf2 == NULL)
            goto end;
      memcpy(p, buf2->v, lcconf->secret_size);
      p += lcconf->secret_size;
      vfree(buf2);

      buf2 = eay_sha1_one(buf);
      memcpy(place, buf2->v, sizeof(cookie_t));

      sa1 = val2str(place, sizeof (cookie_t));
      plog(LLV_DEBUG, LOCATION, NULL, "new cookie:\n%s\n", sa1);
      racoon_free(sa1);

      error = 0;
end:
      if (buf != NULL)
            vfree(buf);
      if (buf2 != NULL)
            vfree(buf2);
      return error;
}

/*
 * save partner's(payload) data into phhandle.
 */
int
isakmp_p2ph(buf, gen)
      vchar_t **buf;
      struct isakmp_gen *gen;
{
      /* XXX to be checked in each functions for logging. */
      if (*buf) {
            plog(LLV_WARNING, LOCATION, NULL,
                  "ignore this payload, same payload type exist.\n");
            return -1;
      }

      *buf = vmalloc(ntohs(gen->len) - sizeof(*gen));
      if (*buf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get buffer.\n");
            return -1;
      }
      memcpy((*buf)->v, gen + 1, (*buf)->l);

      return 0;
}

u_int32_t
isakmp_newmsgid2(iph1)
      struct ph1handle *iph1;
{
      u_int32_t msgid2;

      do {
            msgid2 = eay_random();
      } while (getph2bymsgid(iph1, msgid2));

      return msgid2;
}

/*
 * set values into allocated buffer of isakmp header for phase 1
 */
static caddr_t
set_isakmp_header(vbuf, iph1, nptype, etype, flags, msgid)
      vchar_t *vbuf;
      struct ph1handle *iph1;
      int nptype;
      u_int8_t etype;
      u_int8_t flags;
      u_int32_t msgid;
{
      struct isakmp *isakmp;

      if (vbuf->l < sizeof(*isakmp))
            return NULL;

      isakmp = (struct isakmp *)vbuf->v;

      memcpy(&isakmp->i_ck, &iph1->index.i_ck, sizeof(cookie_t));
      memcpy(&isakmp->r_ck, &iph1->index.r_ck, sizeof(cookie_t));
      isakmp->np = nptype;
      isakmp->v = iph1->version;
      isakmp->etype = etype;
      isakmp->flags = flags;
      isakmp->msgid = msgid;
      isakmp->len = htonl(vbuf->l);

      return vbuf->v + sizeof(*isakmp);
}

/*
 * set values into allocated buffer of isakmp header for phase 1
 */
caddr_t
set_isakmp_header1(vbuf, iph1, nptype)
      vchar_t *vbuf;
      struct ph1handle *iph1;
      int nptype;
{
      return set_isakmp_header (vbuf, iph1, nptype, iph1->etype, iph1->flags, iph1->msgid);
}

/*
 * set values into allocated buffer of isakmp header for phase 2
 */
caddr_t
set_isakmp_header2(vbuf, iph2, nptype)
      vchar_t *vbuf;
      struct ph2handle *iph2;
      int nptype;
{
      return set_isakmp_header (vbuf, iph2->ph1, nptype, ISAKMP_ETYPE_QUICK, iph2->flags, iph2->msgid);
}

/*
 * set values into allocated buffer of isakmp payload.
 */
caddr_t
set_isakmp_payload(buf, src, nptype)
      caddr_t buf;
      vchar_t *src;
      int nptype;
{
      struct isakmp_gen *gen;
      caddr_t p = buf;

      plog(LLV_DEBUG, LOCATION, NULL, "add payload of len %zu, next type %d\n",
          src->l, nptype);

      gen = (struct isakmp_gen *)p;
      gen->np = nptype;
      gen->len = htons(sizeof(*gen) + src->l);
      p += sizeof(*gen);
      memcpy(p, src->v, src->l);
      p += src->l;

      return p;
}

static int
etypesw1(etype)
      int etype;
{
      switch (etype) {
      case ISAKMP_ETYPE_IDENT:
            return 1;
      case ISAKMP_ETYPE_AGG:
            return 2;
      case ISAKMP_ETYPE_BASE:
            return 3;
      default:
            return 0;
      }
      /*NOTREACHED*/
}

static int
etypesw2(etype)
      int etype;
{
      switch (etype) {
      case ISAKMP_ETYPE_QUICK:
            return 1;
      default:
            return 0;
      }
      /*NOTREACHED*/
}

#ifdef HAVE_PRINT_ISAKMP_C
/* for print-isakmp.c */
char *snapend;
extern void isakmp_print __P((const u_char *, u_int, const u_char *));

char *getname __P((const u_char *));
#ifdef INET6
char *getname6 __P((const u_char *));
#endif
int safeputchar __P((int));

/*
 * Return a name for the IP address pointed to by ap.  This address
 * is assumed to be in network byte order.
 */
char *
getname(ap)
      const u_char *ap;
{
      struct sockaddr_in addr;
      static char ntop_buf[NI_MAXHOST];

      memset(&addr, 0, sizeof(addr));
#ifndef __linux__
      addr.sin_len = sizeof(struct sockaddr_in);
#endif
      addr.sin_family = AF_INET;
      memcpy(&addr.sin_addr, ap, sizeof(addr.sin_addr));
      if (getnameinfo((struct sockaddr *)&addr, sizeof(addr),
                  ntop_buf, sizeof(ntop_buf), NULL, 0,
                  NI_NUMERICHOST | niflags))
            strlcpy(ntop_buf, "?", sizeof(ntop_buf));

      return ntop_buf;
}

#ifdef INET6
/*
 * Return a name for the IP6 address pointed to by ap.  This address
 * is assumed to be in network byte order.
 */
char *
getname6(ap)
      const u_char *ap;
{
      struct sockaddr_in6 addr;
      static char ntop_buf[NI_MAXHOST];

      memset(&addr, 0, sizeof(addr));
      addr.sin6_len = sizeof(struct sockaddr_in6);
      addr.sin6_family = AF_INET6;
      memcpy(&addr.sin6_addr, ap, sizeof(addr.sin6_addr));
      if (getnameinfo((struct sockaddr *)&addr, addr.sin6_len,
                  ntop_buf, sizeof(ntop_buf), NULL, 0,
                  NI_NUMERICHOST | niflags))
            strlcpy(ntop_buf, "?", sizeof(ntop_buf));

      return ntop_buf;
}
#endif /* INET6 */

int
safeputchar(c)
      int c;
{
      unsigned char ch;

      ch = (unsigned char)(c & 0xff);
      if (c < 0x80 && isprint(c))
            return printf("%c", c & 0xff);
      else
            return printf("\\%03o", c & 0xff);
}

void
isakmp_printpacket(msg, from, my, decoded)
      vchar_t *msg;
      struct sockaddr *from;
      struct sockaddr *my;
      int decoded;
{
#ifdef YIPS_DEBUG
      struct timeval tv;
      int s;
      char hostbuf[NI_MAXHOST];
      char portbuf[NI_MAXSERV];
      struct isakmp *isakmp;
      vchar_t *buf;
#endif

      if (loglevel < LLV_DEBUG)
            return;

#ifdef YIPS_DEBUG
      plog(LLV_DEBUG, LOCATION, NULL, "begin.\n");

      gettimeofday(&tv, NULL);
      s = tv.tv_sec % 3600;
      printf("%02d:%02d.%06u ", s / 60, s % 60, (u_int32_t)tv.tv_usec);

      if (from) {
            if (getnameinfo(from, sysdep_sa_len(from), hostbuf, sizeof(hostbuf),
                        portbuf, sizeof(portbuf),
                        NI_NUMERICHOST | NI_NUMERICSERV | niflags)) {
                  strlcpy(hostbuf, "?", sizeof(hostbuf));
                  strlcpy(portbuf, "?", sizeof(portbuf));
            }
            printf("%s:%s", hostbuf, portbuf);
      } else
            printf("?");
      printf(" -> ");
      if (my) {
            if (getnameinfo(my, sysdep_sa_len(my), hostbuf, sizeof(hostbuf),
                        portbuf, sizeof(portbuf),
                        NI_NUMERICHOST | NI_NUMERICSERV | niflags)) {
                  strlcpy(hostbuf, "?", sizeof(hostbuf));
                  strlcpy(portbuf, "?", sizeof(portbuf));
            }
            printf("%s:%s", hostbuf, portbuf);
      } else
            printf("?");
      printf(": ");

      buf = vdup(msg);
      if (!buf) {
            printf("(malloc fail)\n");
            return;
      }
      if (decoded) {
            isakmp = (struct isakmp *)buf->v;
            if (isakmp->flags & ISAKMP_FLAG_E) {
#if 0
                  int pad;
                  pad = *(u_char *)(buf->v + buf->l - 1);
                  if (buf->l < pad && 2 < vflag)
                        printf("(wrong padding)");
#endif
                  isakmp->flags &= ~ISAKMP_FLAG_E;
            }
      }

      snapend = buf->v + buf->l;
      isakmp_print(buf->v, buf->l, NULL);
      vfree(buf);
      printf("\n");
      fflush(stdout);

      return;
#endif
}
#endif /*HAVE_PRINT_ISAKMP_C*/

int
copy_ph1addresses(iph1, rmconf, remote, local)
      struct ph1handle *iph1;
      struct remoteconf *rmconf;
      struct sockaddr *remote, *local;
{
      u_short *port = NULL;

      /* address portion must be grabbed from real remote address "remote" */
      iph1->remote = dupsaddr(remote);
      if (iph1->remote == NULL) {
            delph1(iph1);
            return -1;
      }

      /*
       * if remote has no port # (in case of initiator - from ACQUIRE msg)
       * - if remote.conf specifies port #, use that
       * - if remote.conf does not, use 500
       * if remote has port # (in case of responder - from recvfrom(2))
       * respect content of "remote".
       */
      switch (iph1->remote->sa_family) {
      case AF_INET:
            port = &((struct sockaddr_in *)iph1->remote)->sin_port;
            if (*port)
                  break;
            *port = ((struct sockaddr_in *)rmconf->remote)->sin_port;
            if (*port)
                  break;
            *port = htons(PORT_ISAKMP);
            break;
#ifdef INET6
      case AF_INET6:
            port = &((struct sockaddr_in6 *)iph1->remote)->sin6_port;
            if (*port)
                  break;
            *port = ((struct sockaddr_in6 *)rmconf->remote)->sin6_port;
            if (*port)
                  break;
            *port = htons(PORT_ISAKMP);
            break;
#endif
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid family: %d\n", iph1->remote->sa_family);
            return -1;
      }

      if (local == NULL)
            iph1->local = getlocaladdr(iph1->remote);
      else
            iph1->local = dupsaddr(local);
      if (iph1->local == NULL) {
            delph1(iph1);
            return -1;
      }
      switch (iph1->local->sa_family) {
      case AF_INET:
            port = &((struct sockaddr_in *)iph1->local)->sin_port;
            if (*port)
                  break;
            *port = ((struct sockaddr_in *)local)->sin_port;
            if (*port)
                  break;
            *port = getmyaddrsport(iph1->local);
            break;
#ifdef INET6
      case AF_INET6:
            port = &((struct sockaddr_in6 *)iph1->local)->sin6_port;
            if (*port)
                  break;
            *port = ((struct sockaddr_in6 *)local)->sin6_port;
            if (*port)
                  break;
            *port = getmyaddrsport(iph1->local);
            break;
#endif
      default:
            plog(LLV_ERROR, LOCATION, NULL,
                  "invalid family: %d\n", iph1->local->sa_family);
            delph1(iph1);
            return -1;
      }

      return 0;
}

static int
nostate1(iph1, msg)
      struct ph1handle *iph1;
      vchar_t *msg;
{
      plog(LLV_ERROR, LOCATION, iph1->remote, "wrong state %u.\n",
                  iph1->status);
      return -1;
}

static int
nostate2(iph2, msg)
      struct ph2handle *iph2;
      vchar_t *msg;
{
      plog(LLV_ERROR, LOCATION, iph2->ph1->remote, "wrong state %u.\n",
            iph2->status);
      return -1;
}

void
log_ph1established(iph1)
      const struct ph1handle *iph1;
{
      char *src, *dst;

      src = strdup(saddr2str(iph1->local));
      dst = strdup(saddr2str(iph1->remote));
      plog(LLV_INFO, LOCATION, NULL,
            "ISAKMP-SA established %s-%s spi:%s\n",
            src, dst,
            isakmp_pindex(&iph1->index, 0));
      EVT_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_UP, NULL);
      racoon_free(src);
      racoon_free(dst);

      return;
}

struct payload_list *
isakmp_plist_append (struct payload_list *plist, vchar_t *payload, int payload_type)
{
      if (! plist) {
            plist = racoon_malloc (sizeof (struct payload_list));
            plist->prev = NULL;
      }
      else {
            plist->next = racoon_malloc (sizeof (struct payload_list));
            plist->next->prev = plist;
            plist = plist->next;
      }

      plist->next = NULL;
      plist->payload = payload;
      plist->payload_type = payload_type;

      return plist;
}

vchar_t * 
isakmp_plist_set_all (struct payload_list **plist, struct ph1handle *iph1)
{
      struct payload_list *ptr = *plist, *first;
      size_t tlen = sizeof (struct isakmp), n = 0;
      vchar_t *buf;
      char *p;

      /* Seek to the first item.  */
      while (ptr->prev) ptr = ptr->prev;
      first = ptr;
      
      /* Compute the whole length.  */
      while (ptr) {
            tlen += ptr->payload->l + sizeof (struct isakmp_gen);
            ptr = ptr->next;
      }

      buf = vmalloc(tlen);
      if (buf == NULL) {
            plog(LLV_ERROR, LOCATION, NULL,
                  "failed to get buffer to send.\n");
            goto end;
      }

      ptr = first;

      p = set_isakmp_header1(buf, iph1, ptr->payload_type);
      if (p == NULL)
            goto end;

      while (ptr)
      {
            p = set_isakmp_payload (p, ptr->payload, ptr->next ? ptr->next->payload_type : ISAKMP_NPTYPE_NONE);
            first = ptr;
            ptr = ptr->next;
            racoon_free (first);
            /* ptr->prev = NULL; first = NULL; ... omitted.  */
            n++;
      }

      *plist = NULL;

      return buf;
end:
      return NULL;
}

#ifdef ENABLE_FRAG
int 
frag_handler(iph1, msg, remote, local)
      struct ph1handle *iph1;
      vchar_t *msg;
      struct sockaddr *remote;
      struct sockaddr *local;
{
      vchar_t *newmsg;

      if (isakmp_frag_extract(iph1, msg) == 1) {
            if ((newmsg = isakmp_frag_reassembly(iph1)) == NULL) {
                  plog(LLV_ERROR, LOCATION, remote, 
                      "Packet reassembly failed\n");
                  return -1;
            }
            return isakmp_main(newmsg, remote, local);
      }

      return 0;
}
#endif

void
script_hook(iph1, script)
      struct ph1handle *iph1;
      int script;
{
#define IP_MAX 40
      char addrstr[IP_MAX];
      char *argv[3];
      struct sockaddr_in *sin;

      if (iph1->rmconf->script[script] == NULL)
            return;

#ifdef ENABLE_HYBRID
      isakmp_cfg_setenv(iph1);
#endif

      /* local address */
      sin = (struct sockaddr_in *)iph1->local;
      inet_ntop(sin->sin_family, &sin->sin_addr, addrstr, IP_MAX);
      setenv("LOCAL_ADDR", addrstr, 1);


      /* Peer address */
      sin = (struct sockaddr_in *)iph1->remote;
      inet_ntop(sin->sin_family, &sin->sin_addr, addrstr, IP_MAX);
      setenv("REMOTE_ADDR", addrstr, 1);

      argv[0] = iph1->rmconf->script[script]->v;
      argv[1] = script_names[script];
      argv[2] = NULL;

      switch (fork()) {
      case 0:
            execve(argv[0], argv, environ);
            plog(LLV_ERROR, LOCATION, NULL, "execve(\"%s\") failed: %s\n",
                argv[0], strerror(errno));
            return;
            break;
      case -1:
            plog(LLV_ERROR, LOCATION, NULL, "Cannot fork: %s\n",
                strerror(errno));
            return;
            break;
      default:
            break;
      }

      return;
}

Generated by  Doxygen 1.6.0   Back to index