Logo Search packages:      
Sourcecode: openswan version File versions  Download package

kernel_pfkey.c

/* pfkey interface to the kernel's IPsec mechanism
 * Copyright (C) 1997 Angelos D. Keromytis.
 * Copyright (C) 1998-2002  D. Hugh Redelmeier.
 * Copyright (C) 2003 Herbert Xu.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * RCSID $Id: kernel_pfkey.c,v 1.22 2005-07-08 17:55:28 mcr Exp $
 */

#ifdef KLIPS

#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

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

#include <openswan.h>
#include <pfkeyv2.h>
#include <pfkey.h>

#include "constants.h"
#include "oswlog.h"

#include "defs.h"
#include "id.h"
#include "connections.h"
#include "kernel.h"
#include "kernel_pfkey.h"
#include "log.h"
#include "whack.h"      /* for RC_LOG_SERIOUS */
#ifdef NAT_TRAVERSAL
#include "packet.h"  /* for pb_stream in nat_traversal.h */
#include "nat_traversal.h"
#endif

#include "alg_info.h"
#include "kernel_alg.h"


static int pfkeyfd = NULL_FD;

typedef u_int32_t pfkey_seq_t;
static pfkey_seq_t pfkey_seq = 0;   /* sequence number for our PF_KEY messages */

static pid_t pid;

#define NE(x) { x, #x } /* Name Entry -- shorthand for sparse_names */

static sparse_names pfkey_type_names = {
      NE(SADB_RESERVED),
      NE(SADB_GETSPI),
      NE(SADB_UPDATE),
      NE(SADB_ADD),
      NE(SADB_DELETE),
      NE(SADB_GET),
      NE(SADB_ACQUIRE),
      NE(SADB_REGISTER),
      NE(SADB_EXPIRE),
      NE(SADB_FLUSH),
      NE(SADB_DUMP),
      NE(SADB_X_PROMISC),
      NE(SADB_X_PCHANGE),
      NE(SADB_X_GRPSA),
      NE(SADB_X_ADDFLOW),
      NE(SADB_X_DELFLOW),
      NE(SADB_X_DEBUG),
#ifdef NAT_TRAVERSAL
      NE(SADB_X_NAT_T_NEW_MAPPING),
#endif
      NE(SADB_MAX),     
      { 0, sparse_end }
};

#ifdef NEVER /* not needed yet */
static sparse_names pfkey_ext_names = {
      NE(SADB_EXT_RESERVED),
      NE(SADB_EXT_SA),
      NE(SADB_EXT_LIFETIME_CURRENT),
      NE(SADB_EXT_LIFETIME_HARD),
      NE(SADB_EXT_LIFETIME_SOFT),
      NE(SADB_EXT_ADDRESS_SRC),
      NE(SADB_EXT_ADDRESS_DST),
      NE(SADB_EXT_ADDRESS_PROXY),
      NE(SADB_EXT_KEY_AUTH),
      NE(SADB_EXT_KEY_ENCRYPT),
      NE(SADB_EXT_IDENTITY_SRC),
      NE(SADB_EXT_IDENTITY_DST),
      NE(SADB_EXT_SENSITIVITY),
      NE(SADB_EXT_PROPOSAL),
      NE(SADB_EXT_SUPPORTED_AUTH),
      NE(SADB_EXT_SUPPORTED_ENCRYPT),
      NE(SADB_EXT_SPIRANGE),
      NE(SADB_X_EXT_KMPRIVATE),
      NE(SADB_X_EXT_SATYPE2),
      NE(SADB_X_EXT_SA2),
      NE(SADB_X_EXT_ADDRESS_DST2),
      NE(SADB_X_EXT_ADDRESS_SRC_FLOW),
      NE(SADB_X_EXT_ADDRESS_DST_FLOW),
      NE(SADB_X_EXT_ADDRESS_SRC_MASK),
      NE(SADB_X_EXT_ADDRESS_DST_MASK),
      NE(SADB_X_EXT_DEBUG),
      { 0, sparse_end }
};
#endif /* NEVER */

#undef NE

void
init_pfkey(void)
{
    pid = getpid();

    /* open PF_KEY socket */

    pfkeyfd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);

    if (pfkeyfd == -1)
      exit_log_errno((e, "socket() in init_pfkeyfd()"));

#ifdef NEVER      /* apparently unsupported! */
    if (fcntl(pfkeyfd, F_SETFL, O_NONBLOCK) != 0)
      exit_log_errno((e, "fcntl(O_NONBLOCK) in init_pfkeyfd()"));
#endif
    if (fcntl(pfkeyfd, F_SETFD, FD_CLOEXEC) != 0)
      exit_log_errno((e, "fcntl(FD_CLOEXEC) in init_pfkeyfd()"));

    DBG(DBG_KLIPS,
      DBG_log("process %u listening for PF_KEY_V2 on file descriptor %d", (unsigned)pid, pfkeyfd));
}

/* Kinds of PF_KEY message from the kernel:
 * - response to a request from us
 *   + ACK/NAK
 *   + Register: indicates transforms supported by kernel
 *   + SPI requested by getspi
 * - Acquire, requesting us to deal with trapped clear packet
 * - expiration of of one of our SAs
 * - messages to other processes
 *
 * To minimize the effect on the event-driven structure of Pluto,
 * responses are dealt with synchronously.  We hope that the Kernel
 * produces them synchronously.  We must "read ahead" in the PF_KEY
 * stream, saving Acquire and Expiry messages that are encountered.
 * We ignore messages to other processes.
 */

typedef union {
      unsigned char bytes[PFKEYv2_MAX_MSGSIZE];
      struct sadb_msg msg;
    } pfkey_buf;

/* queue of unprocessed PF_KEY messages input from kernel
 * Note that the pfkey_buf may be partly allocated, reflecting
 * the variable length nature of the messages.  So the link field
 * must come first.
 */
typedef struct pfkey_item {
      struct pfkey_item *next;
      pfkey_buf buf;
    } pfkey_item;

static pfkey_item *pfkey_iq_head = NULL;  /* oldest */
static pfkey_item *pfkey_iq_tail;   /* youngest */

static bool
pfkey_input_ready(void)
{
    fd_set readfds;
    int ndes;
    struct timeval tm;

    tm.tv_sec = 0;      /* don't wait at all */
    tm.tv_usec = 0;

    FD_ZERO(&readfds);  /* we only care about pfkeyfd */
    FD_SET(pfkeyfd, &readfds);

    do {
      ndes = select(pfkeyfd + 1, &readfds, NULL, NULL, &tm);
    } while (ndes == -1 && errno == EINTR);

    if (ndes < 0)
    {
      log_errno((e, "select() failed in pfkey_get()"));
      return FALSE;
    }

    if (ndes == 0)
      return FALSE;     /* nothing to read */

    passert(ndes == 1 && FD_ISSET(pfkeyfd, &readfds));
    return TRUE;
}

/* get a PF_KEY message from kernel.
 * Returns TRUE is message found, FALSE if no message pending,
 * and aborts or keeps trying when an error is encountered.
 * The only validation of the message is that the message length
 * received matches that in the message header, and that the message
 * is for this process.
 */
static bool
pfkey_get(pfkey_buf *buf)
{
    for (;;)
    {
      /* len must be less than PFKEYv2_MAX_MSGSIZE,
       * so it should fit in an int.  We use this fact when printing it.
       */
      ssize_t len;

      if (!pfkey_input_ready())
          return FALSE;

      len = read(pfkeyfd, buf->bytes, sizeof(buf->bytes));

      if (len < 0)
      {
          if (errno == EAGAIN)
            return FALSE;

          log_errno((e, "read() failed in pfkey_get()"));
          return FALSE;
      }
      else if ((size_t) len < sizeof(buf->msg))
      {
          plog("pfkey_get read truncated PF_KEY message: %d bytes; ignoring message"
            , (int) len);
      }
      else if ((size_t) len != buf->msg.sadb_msg_len * IPSEC_PFKEYv2_ALIGN)
      {
          plog("pfkey_get read PF_KEY message with length %d that doesn't equal sadb_msg_len %u * %u; ignoring message"
            , (int) len
            , (unsigned) buf->msg.sadb_msg_len
            , (unsigned) IPSEC_PFKEYv2_ALIGN);
      }
      else if (!(buf->msg.sadb_msg_pid == (unsigned)pid
      /*    for now, unsolicited messages can be: 
       *    SADB_ACQUIRE, SADB_REGISTER, SADB_X_NAT_T_NEW_MAPPING
       */
      || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_ACQUIRE)
#ifdef KERNEL_ALG
      || (buf->msg.sadb_msg_type == SADB_REGISTER)
#endif
#ifdef NAT_TRAVERSAL
      || (buf->msg.sadb_msg_pid == 0 && buf->msg.sadb_msg_type == SADB_X_NAT_T_NEW_MAPPING)
#endif
      ))
      {
          /* not for us: ignore */
          DBG(DBG_KLIPS,
            DBG_log("pfkey_get: ignoring PF_KEY %s message %u for process %u"
                , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type)
                , buf->msg.sadb_msg_seq
                , buf->msg.sadb_msg_pid));
      }
      else
      {
          DBG(DBG_KLIPS,
            DBG_log("pfkey_get: %s message %u"
                , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type)
                , buf->msg.sadb_msg_seq));
          return TRUE;
      }
    }
}

/* get a response to a specific message */
static bool
pfkey_get_response(pfkey_buf *buf, pfkey_seq_t seq)
{
    while (pfkey_get(buf))
    {
      if (buf->msg.sadb_msg_pid == (unsigned)pid
      && buf->msg.sadb_msg_seq == seq)
      {
          return TRUE;
      }
      else
      {
          /* Not for us: queue it. */
          size_t bl = buf->msg.sadb_msg_len * IPSEC_PFKEYv2_ALIGN;
          pfkey_item *it = alloc_bytes(offsetof(pfkey_item, buf) + bl, "pfkey_item");

          memcpy(&it->buf, buf, bl);

          it->next = NULL;
          if (pfkey_iq_head == NULL)
          {
            pfkey_iq_head = it;
          }
          else
          {
            pfkey_iq_tail->next = it;
          }
          pfkey_iq_tail = it;
      }
    }
    return FALSE;
}

/* Process a SADB_REGISTER message from the kernel.
 * This will be a response to one of ours, but it may be asynchronous
 * (if kernel modules are loaded and unloaded).
 * Some sanity checking has already been performed.
 */
static void
klips_pfkey_register_response(const struct sadb_msg *msg)
{
    /* Find out what the kernel can support.
     * In fact, the only question at the moment
     * is whether it can support IPcomp.
     * So we ignore the rest.
     * ??? we really should pay attention to what transforms are supported.
     */
    switch (msg->sadb_msg_satype)
    {
    case SADB_SATYPE_AH:
      break;
    case SADB_SATYPE_ESP:
#ifdef KERNEL_ALG
      kernel_alg_register_pfkey(msg, sizeof (pfkey_buf));
#endif
      break;
    case SADB_X_SATYPE_COMP:
      /* ??? There ought to be an extension to list the
       * supported algorithms, but RFC 2367 doesn't
       * list one for IPcomp.  KLIPS uses SADB_X_CALG_DEFLATE.
       * Since we only implement deflate, we'll assume this.
       */
      can_do_IPcomp = TRUE;
      break;
    case SADB_X_SATYPE_IPIP:
      break;
    default:
      break;
    }
}

/* Processs a SADB_ACQUIRE message from KLIPS.
 * Try to build an opportunistic connection!
 * See RFC 2367 "PF_KEY Key Management API, Version 2" 3.1.6
 * <base, address(SD), (address(P)), (identity(SD),) (sensitivity,) proposal>
 * - extensions for source and data IP addresses
 * - optional extensions for identity [not useful for us?]
 * - optional extension for sensitivity [not useful for us?]
 * - expension for proposal [not useful for us?]
 *
 * ??? We must use the sequence number in creating an SA.
 * We actually need to create up to 4 SAs each way.  Which one?
 * I guess it depends on the protocol present in the sadb_msg_satype.
 * For now, we'll ignore this requirement.
 *
 * ??? We need some mechanism to make sure that multiple ACQUIRE messages
 * don't cause a whole bunch of redundant negotiations.
 */
static void
process_pfkey_acquire(pfkey_buf *buf, struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
    struct sadb_address *srcx = (void *) extensions[SADB_EXT_ADDRESS_SRC];
    struct sadb_address *dstx = (void *) extensions[SADB_EXT_ADDRESS_DST];
    int src_proto = srcx->sadb_address_proto;
    int dst_proto = dstx->sadb_address_proto;
    ip_address *src = (ip_address*)&srcx[1];
    ip_address *dst = (ip_address*)&dstx[1];
    ip_subnet ours, his;
    err_t ugh = NULL;

    /* assumption: we're only catching our own outgoing packets
     * so source is our end and destination is the other end.
     * Verifying this is not actually convenient.
     *
     * This stylized control structure yields a complaint or
     * desired results.  For compactness, a pointer value is
     * treated as a boolean.  Logically, the structure is:
     * keep going as long as things are OK.
     */

    if (buf->msg.sadb_msg_pid == 0  /* we only wish to hear from kernel */
      && !(ugh = src_proto == dst_proto? NULL : "src and dst protocols differ")
      && !(ugh = addrtypeof(src) == addrtypeof(dst)? NULL : "conflicting address types")
      && !(ugh = addrtosubnet(src, &ours))
      && !(ugh = addrtosubnet(dst, &his)))
      record_and_initiate_opportunistic(&ours, &his, 0, "%acquire-pfkey");

    if (ugh != NULL)
      plog("SADB_ACQUIRE message from KLIPS malformed: %s", ugh);

}

/* Handle PF_KEY messages from the kernel that are not dealt with
 * synchronously.  In other words, all but responses to PF_KEY messages
 * that we sent.
 */
static void
pfkey_async(pfkey_buf *buf)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];

    if (pfkey_msg_parse(&buf->msg, NULL, extensions, EXT_BITS_OUT))
    {
      plog("pfkey_async:"
          " unparseable PF_KEY message:"
          " %s len=%d, errno=%d, seq=%d, pid=%d; message ignored"
          , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type)
          , buf->msg.sadb_msg_len
          , buf->msg.sadb_msg_errno
          , buf->msg.sadb_msg_seq
          , buf->msg.sadb_msg_pid);
    }
    else
    {
      DBG(DBG_CONTROL | DBG_KLIPS, DBG_log("pfkey_async:"
          " %s len=%u, errno=%u, satype=%u, seq=%u, pid=%u"
          , sparse_val_show(pfkey_type_names, buf->msg.sadb_msg_type)
          , buf->msg.sadb_msg_len
          , buf->msg.sadb_msg_errno
          , buf->msg.sadb_msg_satype
          , buf->msg.sadb_msg_seq
          , buf->msg.sadb_msg_pid));

      switch (buf->msg.sadb_msg_type)
      {
      case SADB_REGISTER:
          kernel_ops->pfkey_register_response(&buf->msg);
          break;
      case SADB_ACQUIRE:
          /* to simulate loss of ACQUIRE, delete this call */
          process_pfkey_acquire(buf, extensions);
          break;
#ifdef NAT_TRAVERSAL
      case SADB_X_NAT_T_NEW_MAPPING:
          process_pfkey_nat_t_new_mapping(&(buf->msg), extensions);
          break;
#endif
      default:
          /* ignored */
          break;
      }
    }
}

/* asynchronous messages from our queue */
static void
pfkey_dequeue(void)
{
    while (pfkey_iq_head != NULL)
    {
      pfkey_item *it = pfkey_iq_head;

      pfkey_async(&it->buf);
      pfkey_iq_head = it->next;
      pfree(it);
    }

    /* Handle any orphaned holds, but only if no pfkey input is pending.
     * For each, we initiate Opportunistic.
     * note: we don't need to advance the pointer because
     * record_and_initiate_opportunistic will remove the current
     * record each time we call it.
     */
    while (orphaned_holds != NULL && !pfkey_input_ready())
      record_and_initiate_opportunistic(&orphaned_holds->ours
                              , &orphaned_holds->his
                              , orphaned_holds->transport_proto
                              , "%hold found-pfkey");

}

/* asynchronous messages directly from PF_KEY socket */
static void
pfkey_event(void)
{
    pfkey_buf buf;

    if (pfkey_get(&buf))
      pfkey_async(&buf);
}

static bool
pfkey_build(int error
, const char *description
, const char *text_said
, struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
    if (error == 0)
    {
      return TRUE;
    }
    else
    {
      loglog(RC_LOG_SERIOUS, "building of %s %s failed, code %d"
          , description, text_said, error);
      pfkey_extensions_free(extensions);
      return FALSE;
    }
}

/* pfkey_extensions_init + pfkey_build + pfkey_msg_hdr_build */
static bool
pfkey_msg_start(u_int8_t msg_type
, u_int8_t satype
, const char *description
, const char *text_said
, struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
    pfkey_extensions_init(extensions);
    return pfkey_build(pfkey_msg_hdr_build(&extensions[0], msg_type
          , satype, 0, ++pfkey_seq, pid)
      , description, text_said, extensions);
}

/* pfkey_build + pfkey_address_build */
static bool
pfkeyext_address(u_int16_t exttype
, const ip_address *address
, const char *description
, const char *text_said
, struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
    /* the following variable is only needed to silence
     * a warning caused by the fact that the argument
     * to sockaddrof is NOT pointer to const!
     */
    ip_address t = *address;

    return pfkey_build(pfkey_address_build(extensions + exttype
          , exttype, 0, 0, sockaddrof(&t))
      , description, text_said, extensions);
}

/* pfkey_build + pfkey_x_protocol_build */
static bool
pfkeyext_protocol(int transport_proto
, const char *description
, const char *text_said
, struct sadb_ext *extensions[SADB_EXT_MAX + 1])
{
    return (transport_proto == 0)
        ? TRUE
        : pfkey_build(pfkey_x_protocol_build(extensions + SADB_X_EXT_PROTOCOL,
                                             transport_proto),
                      description, text_said, extensions);
}


/* Finish (building, sending, accepting response for) PF_KEY message.
 * If response isn't NULL, the response from the kernel will be
 * placed there (and its errno field will not be examined).
 * Returns TRUE iff all appears well.
 */
static bool
finish_pfkey_msg(struct sadb_ext *extensions[SADB_EXT_MAX + 1]
, const char *description
, const char *text_said
, pfkey_buf *response)
{
    struct sadb_msg *pfkey_msg;
    bool success = TRUE;
    int error;

    error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN);

    if (error != 0)
    {
      loglog(RC_LOG_SERIOUS, "pfkey_msg_build of %s %s failed, code %d"
          , description, text_said, error);
      success = FALSE;
    }
    else
    {
      size_t len = pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN;

      DBG(DBG_KLIPS,
          DBG_log("finish_pfkey_msg: %s message %u for %s %s"
            , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type)
            , pfkey_msg->sadb_msg_seq
            , description, text_said);
          DBG_dump(NULL, (void *) pfkey_msg, len));

      if (kern_interface != NO_KERNEL)
      {
          ssize_t r = write(pfkeyfd, pfkey_msg, len);
          int e1 = errno;

          if (r != (ssize_t)len)
          {
            if (r < 0)
            {
              switch(e1) {
              case ESRCH:
                if(pfkey_msg->sadb_msg_type == SADB_DELETE) {
                  success=TRUE;
                }
                else {
                  goto logerr;
                }
                break;

              case ENOENT:
                loglog(RC_LOG_SERIOUS, "requested algorithm is not available in the kernel");
                success=FALSE;
                break;
                
              default:
              logerr:
                log_errno_routine(e1, "pfkey write() of %s message %u"
                              " for %s %s failed"
                              , sparse_val_show(pfkey_type_names
                                          , pfkey_msg->sadb_msg_type)
                              , pfkey_msg->sadb_msg_seq
                              , description, text_said);
                success = FALSE;
              }
            }
            else
            {
                loglog(RC_LOG_SERIOUS
                  , "ERROR: pfkey write() of %s message %u"
                    " for %s %s truncated: %ld instead of %ld"
                  , sparse_val_show(pfkey_type_names
                      , pfkey_msg->sadb_msg_type)
                  , pfkey_msg->sadb_msg_seq
                  , description, text_said
                  , (long)r, (long)len);
                success = FALSE;
            }

            /* if we were compiled with debugging, but we haven't already
             * dumped the KLIPS command, do so.
             */
#ifdef DEBUG
            if ((cur_debugging & DBG_KLIPS) == 0)
                DBG_dump(NULL, (void *) pfkey_msg, len);
#endif
          }
          else
          {
            /* Check response from KLIPS.
             * It ought to be an echo, perhaps with additional info.
             * If the caller wants it, response will point to space.
             */
            pfkey_buf b;
            pfkey_buf *bp = response != NULL? response : &b;

            if (!pfkey_get_response(bp, ((struct sadb_msg *) extensions[0])->sadb_msg_seq))
            {
                loglog(RC_LOG_SERIOUS
                  , "ERROR: no response to our PF_KEY %s message for %s %s"
                  , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type)
                  , description, text_said);
                success = FALSE;
            }
            else if (pfkey_msg->sadb_msg_type != bp->msg.sadb_msg_type)
            {
                loglog(RC_LOG_SERIOUS
                  , "Openswan ERROR: response to our PF_KEY %s message for %s %s was of wrong type (%s)"
                  , sparse_name(pfkey_type_names, pfkey_msg->sadb_msg_type)
                  , description, text_said
                  , sparse_val_show(pfkey_type_names, bp->msg.sadb_msg_type));
                success = FALSE;
            }
            else if (response == NULL && bp->msg.sadb_msg_errno != 0)
            {
                /* KLIPS is signalling a problem */
                loglog(RC_LOG_SERIOUS
                  , "ERROR: PF_KEY %s response for %s %s included errno %u: %s"
                  , sparse_val_show(pfkey_type_names, pfkey_msg->sadb_msg_type)
                  , description, text_said
                  , (unsigned) bp->msg.sadb_msg_errno
                  , strerror(bp->msg.sadb_msg_errno));
                success = FALSE;
            }
          }
      }
    }

    /* all paths must exit this way to free resources */
    pfkey_extensions_free(extensions);
    pfkey_msg_free(&pfkey_msg);
    return success;
}

/*  register SA types that can be negotiated */
void
pfkey_register_proto(unsigned satype, const char *satypename)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
    pfkey_buf pfb;

    if (!(pfkey_msg_start(SADB_REGISTER
      , satype
      , satypename, NULL, extensions)
    && finish_pfkey_msg(extensions, satypename, "", &pfb)))
    {
      /* ??? should this be loglog */
      plog("no KLIPS support for %s", satypename);
    }
    else
    {
      kernel_ops->pfkey_register_response(&pfb.msg);
      DBG(DBG_KLIPS,
          DBG_log("%s registered with kernel.", satypename));
    }
}

static void
klips_pfkey_register(void)
{
    pfkey_register_proto(SADB_SATYPE_AH, "AH");
    pfkey_register_proto(SADB_SATYPE_ESP, "ESP");
    can_do_IPcomp = FALSE;  /* until we get a response from KLIPS */
    pfkey_register_proto(SADB_X_SATYPE_COMP, "IPCOMP");
    pfkey_register_proto(SADB_X_SATYPE_IPIP, "IPIP");
}

static bool
pfkey_raw_eroute(const ip_address *this_host
             , const ip_subnet *this_client
             , const ip_address *that_host
             , const ip_subnet *that_client
             , ipsec_spi_t spi
             , unsigned int proto UNUSED
             , unsigned int transport_proto
             , unsigned int satype
             , const struct pfkey_proto_info *proto_info UNUSED
             , time_t use_lifetime UNUSED
             , unsigned int op
             , const char *text_said)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
    ip_address
      sflow_ska,
      dflow_ska,
      smask_ska,
      dmask_ska;

    int sport = ntohs(portof(&this_client->addr));
    int dport = ntohs(portof(&that_client->addr));

    networkof(this_client, &sflow_ska);
    maskof(this_client, &smask_ska);
    setportof(sport ? ~0:0, &smask_ska);

    networkof(that_client, &dflow_ska);
    maskof(that_client, &dmask_ska);
    setportof(dport ? ~0:0, &dmask_ska);

    if (!pfkey_msg_start(op & ERO_MASK, satype
                   , "pfkey_msg_hdr flow", text_said, extensions))
    {
      return FALSE;
    }

    if (op != ERO_DELETE)
    {
      if (!(pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
                               , SADB_EXT_SA
                               , spi      /* in network order */
                               , 0, 0, 0, 0, op >> ERO_FLAG_SHIFT)
                    , "pfkey_sa add flow", text_said, extensions)

          && pfkeyext_address(SADB_EXT_ADDRESS_SRC, this_host
            , "pfkey_addr_s add flow", text_said, extensions)

          && pfkeyext_address(SADB_EXT_ADDRESS_DST, that_host
                        , "pfkey_addr_d add flow", text_said
                        , extensions)))
      {
          return FALSE;
      }
    }

    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_SRC_FLOW, &sflow_ska
                    , "pfkey_addr_sflow", text_said, extensions))
    {
      return FALSE;
    }

    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_DST_FLOW, &dflow_ska
                  , "pfkey_addr_dflow", text_said, extensions))
    {
      return FALSE;
    }

    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_SRC_MASK, &smask_ska
                  , "pfkey_addr_smask", text_said, extensions))
    {
      return FALSE;
    }

    if (!pfkeyext_address(SADB_X_EXT_ADDRESS_DST_MASK, &dmask_ska
                  , "pfkey_addr_dmask", text_said, extensions))
    {
      return FALSE;
    }

    if (!pfkeyext_protocol(transport_proto
                  , "pfkey_x_protocol", text_said, extensions))
    {
      return FALSE;
    }

    return finish_pfkey_msg(extensions, "flow", text_said, NULL);
}

static bool
pfkey_add_sa(const struct kernel_sa *sa, bool replace)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];
    bool success = FALSE;

    success = pfkey_msg_start(replace ? SADB_UPDATE : SADB_ADD, sa->satype
                        , "pfkey_msg_hdr Add SA"
                        , sa->text_said, extensions);

    if(!success) return FALSE;

    success = pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
                               , SADB_EXT_SA
                               , sa->spi  /* in network order */
                               , sa->replay_window, SADB_SASTATE_MATURE
                               , sa->authalg, sa->encalg, 0)
                    , "pfkey_sa Add SA", sa->text_said, extensions);
    if(!success) return FALSE;

    success = pfkeyext_address(SADB_EXT_ADDRESS_SRC, sa->src
                         , "pfkey_addr_s Add SA"
                         , sa->text_said, extensions);
    if(!success) return FALSE;

    success = pfkeyext_address(SADB_EXT_ADDRESS_DST, sa->dst
                         , "pfkey_addr_d Add SA", sa->text_said
                         , extensions);
    if(!success) return FALSE;

    if(sa->authkeylen != 0) {
      success = pfkey_build(pfkey_key_build(&extensions[SADB_EXT_KEY_AUTH]
                                    , SADB_EXT_KEY_AUTH
                                    , sa->authkeylen * BITS_PER_BYTE
                                    , sa->authkey)
                        , "pfkey_key_a Add SA"
                        , sa->text_said, extensions);
      if(!success) return FALSE;
    }
      
    if(sa->enckeylen != 0) {
      success = pfkey_build(pfkey_key_build(&extensions[SADB_EXT_KEY_ENCRYPT]
                                    , SADB_EXT_KEY_ENCRYPT
                                    , sa->enckeylen * BITS_PER_BYTE
                                    , sa->enckey)
                        , "pfkey_key_e Add SA"
                        , sa->text_said, extensions);
      if(!success) return FALSE;
    }
      

#ifdef NAT_TRAVERSAL
    if(sa->natt_type != 0) {
      success = pfkey_build(pfkey_x_nat_t_type_build(
                          &extensions[SADB_X_EXT_NAT_T_TYPE]
                          , sa->natt_type),
                        "pfkey_nat_t_type Add ESP SA"
                        ,  sa->text_said, extensions);
      DBG(DBG_KLIPS
          , DBG_log("setting natt_type to %d\n", sa->natt_type));
      if(!success) return FALSE;

      if(sa->natt_sport != 0) {
        success = pfkey_build(pfkey_x_nat_t_port_build(
                          &extensions[SADB_X_EXT_NAT_T_SPORT]
                          , SADB_X_EXT_NAT_T_SPORT,
                          sa->natt_sport)
                        , "pfkey_nat_t_sport Add ESP SA"
                        , sa->text_said, extensions);
        DBG(DBG_KLIPS
            , DBG_log("setting natt_sport to %d\n", sa->natt_sport));
        if(!success) return FALSE;
      }
      
      if(sa->natt_dport != 0) {
        success = pfkey_build(pfkey_x_nat_t_port_build(
                          &extensions[SADB_X_EXT_NAT_T_DPORT]
                          , SADB_X_EXT_NAT_T_DPORT
                          , sa->natt_dport)
                        , "pfkey_nat_t_dport Add ESP SA"
                        , sa->text_said, extensions);
        DBG(DBG_KLIPS
            , DBG_log("setting natt_dport to %d\n", sa->natt_dport));
        if(!success) return FALSE;
      }
      

      if(sa->natt_type!=0 && !isanyaddr(sa->natt_oa)) {
        success = pfkeyext_address(SADB_X_EXT_NAT_T_OA, sa->natt_oa
                             , "pfkey_nat_t_oa Add ESP SA"
                             , sa->text_said, extensions);
        DBG(DBG_KLIPS
            , DBG_log("setting nat_oa to %s\n", ip_str(sa->natt_oa)));
        if(!success) return FALSE;
      }
    }
#endif

    return finish_pfkey_msg(extensions, "Add SA", sa->text_said, NULL);
}


static bool
pfkey_grp_sa(const struct kernel_sa *sa0, const struct kernel_sa *sa1)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];

    return pfkey_msg_start(SADB_X_GRPSA, sa1->satype
      , "pfkey_msg_hdr group", sa1->text_said, extensions)

    && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
          , SADB_EXT_SA
          , sa1->spi    /* in network order */
          , 0, 0, 0, 0, 0)
      , "pfkey_sa group", sa1->text_said, extensions)

    && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa1->dst
      , "pfkey_addr_d group", sa1->text_said, extensions)

    && pfkey_build(pfkey_x_satype_build(&extensions[SADB_X_EXT_SATYPE2]
          , sa0->satype)
      , "pfkey_satype group", sa0->text_said, extensions)

    && pfkey_build(pfkey_sa_build(&extensions[SADB_X_EXT_SA2]
          , SADB_X_EXT_SA2
          , sa0->spi    /* in network order */
          , 0, 0, 0, 0, 0)
      , "pfkey_sa2 group", sa0->text_said, extensions)

    && pfkeyext_address(SADB_X_EXT_ADDRESS_DST2, sa0->dst
      , "pfkey_addr_d2 group", sa0->text_said, extensions)

    && finish_pfkey_msg(extensions, "group", sa1->text_said, NULL);
}

static bool
pfkey_del_sa(const struct kernel_sa *sa)
{
    struct sadb_ext *extensions[SADB_EXT_MAX + 1];

    return pfkey_msg_start(SADB_DELETE, proto2satype(sa->proto)
      , "pfkey_msg_hdr delete SA", sa->text_said, extensions)

    && pfkey_build(pfkey_sa_build(&extensions[SADB_EXT_SA]
          , SADB_EXT_SA
          , sa->spi     /* in host order */
          , 0, SADB_SASTATE_MATURE, 0, 0, 0)
      , "pfkey_sa delete SA", sa->text_said, extensions)

    && pfkeyext_address(SADB_EXT_ADDRESS_SRC, sa->src
      , "pfkey_addr_s delete SA", sa->text_said, extensions)

    && pfkeyext_address(SADB_EXT_ADDRESS_DST, sa->dst
      , "pfkey_addr_d delete SA", sa->text_said, extensions)

    && finish_pfkey_msg(extensions, "Delete SA", sa->text_said, NULL);
}

void
pfkey_close(void)
{
    while (pfkey_iq_head != NULL)
    {
      pfkey_item *it = pfkey_iq_head;

      pfkey_iq_head = it->next;
      pfree(it);
    }

    close(pfkeyfd);
    pfkeyfd = NULL_FD;
}

const struct kernel_ops klips_kernel_ops = {
      type: KERNEL_TYPE_KLIPS,
      async_fdp: &pfkeyfd,
      replay_window: 64,

      pfkey_register: klips_pfkey_register,
      pfkey_register_response: klips_pfkey_register_response,
      process_queue: pfkey_dequeue,
      process_msg: pfkey_event,
      raw_eroute: pfkey_raw_eroute,
      add_sa: pfkey_add_sa,
      grp_sa: pfkey_grp_sa,
      del_sa: pfkey_del_sa,
      get_spi: NULL,
        inbound_eroute: FALSE,
      policy_lifetime: FALSE,
      init: NULL,
      docommand: do_command_linux,
      opname: "pfkey"
};
#endif /* KLIPS */

Generated by  Doxygen 1.6.0   Back to index