Merge branch 'ikev1-clean' into ikev1-master
authorMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:56:18 +0000 (17:56 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:57:53 +0000 (17:57 +0100)
Conflicts:
configure.in
man/ipsec.conf.5.in
src/libcharon/daemon.c
src/libcharon/plugins/eap_ttls/eap_ttls_peer.c
src/libcharon/plugins/eap_radius/eap_radius_accounting.c
src/libcharon/plugins/eap_radius/eap_radius_forward.c
src/libcharon/plugins/farp/farp_listener.c
src/libcharon/sa/ike_sa.c
src/libcharon/sa/keymat.c
src/libcharon/sa/task_manager.c
src/libcharon/sa/trap_manager.c
src/libstrongswan/plugins/x509/x509_cert.c
src/libstrongswan/utils.h

Applied lost changes of moved files keymat.c and task_manager.c.
Updated listener_t.message hook signature in new plugins.

39 files changed:
1  2 
configure.in
man/ipsec.conf.5.in
src/libcharon/Makefile.am
src/libcharon/daemon.c
src/libcharon/encoding/payloads/notify_payload.c
src/libcharon/encoding/payloads/notify_payload.h
src/libcharon/plugins/eap_radius/eap_radius_accounting.c
src/libcharon/plugins/eap_radius/eap_radius_forward.c
src/libcharon/plugins/eap_ttls/eap_ttls_peer.c
src/libcharon/plugins/ha/ha_dispatcher.c
src/libcharon/plugins/ha/ha_ike.c
src/libcharon/plugins/ha/ha_message.c
src/libcharon/plugins/ha/ha_message.h
src/libcharon/plugins/radattr/radattr_listener.c
src/libcharon/plugins/stroke/stroke_control.c
src/libcharon/plugins/stroke/stroke_cred.c
src/libcharon/plugins/stroke/stroke_list.c
src/libcharon/plugins/stroke/stroke_socket.c
src/libcharon/sa/ike_sa.c
src/libcharon/sa/ike_sa.h
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
src/libcharon/sa/ikev2/tasks/ike_mobike.c
src/libcharon/sa/keymat.c
src/libcharon/sa/trap_manager.c
src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
src/libstrongswan/asn1/oid.txt
src/libstrongswan/credentials/auth_cfg.h
src/libstrongswan/credentials/certificates/x509.h
src/libstrongswan/credentials/credential_manager.c
src/libstrongswan/credentials/credential_manager.h
src/libstrongswan/plugins/x509/x509_cert.c
src/libstrongswan/utils.h
src/pki/commands/issue.c
src/pki/commands/self.c
src/pluto/keys.c
src/starter/confread.c
src/starter/confread.h
src/stroke/stroke.c

diff --cc configure.in
index f0f41c0,1d28cfa..b99487a
mode 100644,100755..100755
@@@ -130,8 -129,9 +130,10 @@@ ARG_ENABL_SET([eap-ttls],       [enabl
  ARG_ENABL_SET([eap-peap],       [enable EAP PEAP authentication module.])
  ARG_ENABL_SET([eap-tnc],        [enable EAP TNC trusted network connect module.])
  ARG_ENABL_SET([eap-radius],     [enable RADIUS proxy authentication module.])
+ ARG_ENABL_SET([xauth-generic],  [enable generic XAuth backend.])
+ ARG_ENABL_SET([xauth-eap],      [enable XAuth backend using EAP methods to verify passwords.])
  ARG_ENABL_SET([tnc-ifmap],      [enable TNC IF-MAP module.])
 +ARG_ENABL_SET([tnc-pdp],        [enable TNC policy decision point module.])
  ARG_ENABL_SET([tnc-imc],        [enable TNC IMC module.])
  ARG_ENABL_SET([tnc-imv],        [enable TNC IMV module.])
  ARG_ENABL_SET([tnccs-11],       [enable TNCCS 1.1 protocol module.])
@@@ -871,8 -845,9 +875,10 @@@ ADD_PLUGIN([eap-tls],              [c l
  ADD_PLUGIN([eap-ttls],             [c libcharon])
  ADD_PLUGIN([eap-peap],             [c libcharon])
  ADD_PLUGIN([eap-tnc],              [c libcharon])
+ ADD_PLUGIN([xauth-generic],        [c libcharon])
+ ADD_PLUGIN([xauth-eap],            [c libcharon])
  ADD_PLUGIN([tnc-ifmap],            [c libcharon])
 +ADD_PLUGIN([tnc-pdp],              [c libcharon])
  ADD_PLUGIN([tnc-imc],              [c libcharon])
  ADD_PLUGIN([tnc-imv],              [c libcharon])
  ADD_PLUGIN([tnc-tnccs],            [c libcharon])
@@@ -995,8 -966,9 +1001,10 @@@ AM_CONDITIONAL(USE_EAP_TTLS, test x$eap
  AM_CONDITIONAL(USE_EAP_PEAP, test x$eap_peap = xtrue)
  AM_CONDITIONAL(USE_EAP_TNC, test x$eap_tnc = xtrue)
  AM_CONDITIONAL(USE_EAP_RADIUS, test x$eap_radius = xtrue)
+ AM_CONDITIONAL(USE_XAUTH_GENERIC, test x$xauth_generic = xtrue)
+ AM_CONDITIONAL(USE_XAUTH_EAP, test x$xauth_eap = xtrue)
  AM_CONDITIONAL(USE_TNC_IFMAP, test x$tnc_ifmap = xtrue)
 +AM_CONDITIONAL(USE_TNC_PDP, test x$tnc_pdp = xtrue)
  AM_CONDITIONAL(USE_TNC_IMC, test x$tnc_imc = xtrue)
  AM_CONDITIONAL(USE_TNC_IMV, test x$tnc_imv = xtrue)
  AM_CONDITIONAL(USE_TNC_TNCCS, test x$tnc_tnccs = xtrue)
@@@ -1174,8 -1150,9 +1189,10 @@@ AC_OUTPUT
        src/libcharon/plugins/eap_peap/Makefile
        src/libcharon/plugins/eap_tnc/Makefile
        src/libcharon/plugins/eap_radius/Makefile
+       src/libcharon/plugins/xauth_generic/Makefile
+       src/libcharon/plugins/xauth_eap/Makefile
        src/libcharon/plugins/tnc_ifmap/Makefile
 +      src/libcharon/plugins/tnc_pdp/Makefile
        src/libcharon/plugins/tnc_imc/Makefile
        src/libcharon/plugins/tnc_imv/Makefile
        src/libcharon/plugins/tnc_tnccs/Makefile
@@@ -1026,14 -889,10 +889,10 @@@ signifying the special Mobile IPv6 tran
  .BR passthrough ,
  signifying that no IPsec processing should be done at all;
  .BR drop ,
- signifying that packets should be discarded; and
- .BR reject ,
- signifying that packets should be discarded and a diagnostic ICMP returned
- .RB ( reject
- is currently not supported by the NETKEY stack of the Linux 2.6 kernel).
+ signifying that packets should be discarded.
  .TP
  .BR xauth " = " client " | server"
 -specifies the role in the XAUTH protocol if activated by
 +specifies the role in the XAuth protocol if activated by
  .B authby=xauthpsk
  or
  .B authby=xauthrsasig.
@@@ -128,6 -122,11 +128,7 @@@ static void destroy(private_daemon_t *t
        DESTROY_IF(this->public.ike_sa_manager);
        DESTROY_IF(this->public.controller);
        DESTROY_IF(this->public.eap);
 -#ifdef ME
 -      DESTROY_IF(this->public.connect_manager);
 -      DESTROY_IF(this->public.mediation_manager);
 -#endif /* ME */
+       DESTROY_IF(this->public.xauth);
        DESTROY_IF(this->public.backends);
        DESTROY_IF(this->public.socket);
  
index fef2949,ade2d94..4115344
mode 100644,100755..100755
@@@ -99,9 -106,16 +106,16 @@@ ENUM_NEXT(notify_type_names, INITIAL_CO
        "IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED",
        "IKEV2_MESSAGE_ID_SYNC",
        "IPSEC_REPLAY_COUNTER_SYNC");
- ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, IPSEC_REPLAY_COUNTER_SYNC,
+ ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
+       "INITIAL_CONTACT");
+ ENUM_NEXT(notify_type_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1,
+       "DPD_R_U_THERE",
+       "DPD_R_U_THERE_ACK");
+ ENUM_NEXT(notify_type_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK,
+       "UNITY_LOAD_BALANCE");
+ ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE,
        "USE_BEET_MODE");
 -ENUM_NEXT(notify_type_names, ME_MEDIATION, ME_RESPONSE, USE_BEET_MODE,
 +ENUM_NEXT(notify_type_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE,
        "ME_MEDIATION",
        "ME_ENDPOINT",
        "ME_CALLBACK",
@@@ -187,9 -207,16 +208,16 @@@ ENUM_NEXT(notify_type_short_names, INIT
        "RPL_CTR_SYN_SUP",
        "MSG_ID_SYN",
        "RPL_CTR_SYN");
- ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, IPSEC_REPLAY_COUNTER_SYNC,
+ ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
+       "INITIAL_CONTACT");
+ ENUM_NEXT(notify_type_short_names, DPD_R_U_THERE, DPD_R_U_THERE_ACK, INITIAL_CONTACT_IKEV1,
+       "DPD",
+       "DPD_ACK");
+ ENUM_NEXT(notify_type_short_names, UNITY_LOAD_BALANCE, UNITY_LOAD_BALANCE, DPD_R_U_THERE_ACK,
+       "UNITY_LB");
+ ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, UNITY_LOAD_BALANCE,
        "BEET_MODE");
 -ENUM_NEXT(notify_type_short_names, ME_MEDIATION, ME_RESPONSE, USE_BEET_MODE,
 +ENUM_NEXT(notify_type_short_names, ME_MEDIATION, RADIUS_ATTRIBUTE, USE_BEET_MODE,
        "ME_MED",
        "ME_EP",
        "ME_CB",
index 45be227,0000000..243c763
mode 100644,000000..100644
--- /dev/null
@@@ -1,339 -1,0 +1,339 @@@
-       message_t *message, bool incoming)
 +/*
 + * Copyright (C) 2012 Martin Willi
 + * Copyright (C) 2012 revosec AG
 + *
 + * 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.
 + */
 +
 +#include "eap_radius_accounting.h"
 +#include "eap_radius_plugin.h"
 +
 +#include <time.h>
 +
 +#include <radius_message.h>
 +#include <radius_client.h>
 +#include <daemon.h>
 +#include <utils/hashtable.h>
 +#include <threading/mutex.h>
 +
 +typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t;
 +
 +/**
 + * Private data of an eap_radius_accounting_t object.
 + */
 +struct private_eap_radius_accounting_t {
 +
 +      /**
 +       * Public eap_radius_accounting_t interface.
 +       */
 +      eap_radius_accounting_t public;
 +
 +      /**
 +       * Hashtable with sessions, IKE_SA unique id => entry_t
 +       */
 +      hashtable_t *sessions;
 +
 +      /**
 +       * Mutex to lock sessions
 +       */
 +      mutex_t *mutex;
 +
 +      /**
 +       * Session ID prefix
 +       */
 +      u_int32_t prefix;
 +};
 +
 +/**
 + * Hashtable entry with usage stats
 + */
 +typedef struct {
 +      /** RADIUS accounting session ID */
 +      char sid[16];
 +      /** number of octets sent */
 +      u_int64_t sent;
 +      /** number of octets received */
 +      u_int64_t received;
 +      /** session creation time */
 +      time_t created;
 +} entry_t;
 +
 +/**
 + * Accounting message status types
 + */
 +typedef enum {
 +      ACCT_STATUS_START = 1,
 +      ACCT_STATUS_STOP = 2,
 +      ACCT_STATUS_INTERIM_UPDATE = 3,
 +      ACCT_STATUS_ACCOUNTING_ON = 7,
 +      ACCT_STATUS_ACCOUNTING_OFF = 8,
 +} radius_acct_status_t;
 +
 +/**
 + * Hashtable hash function
 + */
 +static u_int hash(uintptr_t key)
 +{
 +      return key;
 +}
 +
 +/**
 + * Hashtable equals function
 + */
 +static bool equals(uintptr_t a, uintptr_t b)
 +{
 +      return a == b;
 +}
 +
 +/**
 + * Update usage counter when a CHILD_SA rekeys/goes down
 + */
 +static void update_usage(private_eap_radius_accounting_t *this,
 +                                               ike_sa_t *ike_sa, child_sa_t *child_sa)
 +{
 +      u_int64_t sent, received;
 +      entry_t *entry;
 +
 +      child_sa->get_usestats(child_sa, FALSE, NULL, &sent);
 +      child_sa->get_usestats(child_sa, TRUE, NULL, &received);
 +
 +      this->mutex->lock(this->mutex);
 +      entry = this->sessions->get(this->sessions,
 +                                                              (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
 +      if (entry)
 +      {
 +              entry->sent += sent;
 +              entry->received += received;
 +      }
 +      this->mutex->unlock(this->mutex);
 +}
 +
 +/**
 + * Send a RADIUS message, wait for response
 + */
 +static bool send_message(private_eap_radius_accounting_t *this,
 +                                               radius_message_t *request)
 +{
 +      radius_message_t *response;
 +      radius_client_t *client;
 +      bool ack = FALSE;
 +
 +      client = eap_radius_create_client();
 +      if (client)
 +      {
 +              response = client->request(client, request);
 +              if (response)
 +              {
 +                      ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
 +                      response->destroy(response);
 +              }
 +              else
 +              {
 +                      charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING);
 +              }
 +              client->destroy(client);
 +      }
 +      return ack;
 +}
 +
 +/**
 + * Add common IKE_SA parameters to RADIUS account message
 + */
 +static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa)
 +{
 +      host_t *vip;
 +      char buf[64];
 +      chunk_t data;
 +
 +      snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
 +      message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf)));
 +      snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa));
 +      message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf)));
 +      vip = ike_sa->get_virtual_ip(ike_sa, FALSE);
 +      if (vip && vip->get_family(vip) == AF_INET)
 +      {
 +              message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip));
 +      }
 +      if (vip && vip->get_family(vip) == AF_INET6)
 +      {
 +              /* we currently assign /128 prefixes, only (reserved, length) */
 +              data = chunk_from_chars(0, 128);
 +              data = chunk_cata("cc", data, vip->get_address(vip));
 +              message->add(message, RAT_FRAMED_IPV6_PREFIX, data);
 +      }
 +}
 +
 +/**
 + * Send an accounting start message
 + */
 +static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
 +{
 +      radius_message_t *message;
 +      entry_t *entry;
 +      u_int32_t id, value;
 +
 +      id = ike_sa->get_unique_id(ike_sa);
 +      INIT(entry,
 +              .created = time_monotonic(NULL),
 +      );
 +      snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id);
 +
 +      message = radius_message_create(RMC_ACCOUNTING_REQUEST);
 +      value = htonl(ACCT_STATUS_START);
 +      message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
 +      message->add(message, RAT_ACCT_SESSION_ID,
 +                               chunk_create(entry->sid, strlen(entry->sid)));
 +      add_ike_sa_parameters(message, ike_sa);
 +      if (send_message(this, message))
 +      {
 +              this->mutex->lock(this->mutex);
 +              entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry);
 +              this->mutex->unlock(this->mutex);
 +              free(entry);
 +      }
 +      message->destroy(message);
 +}
 +
 +/**
 + * Send an account stop message
 + */
 +static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
 +{
 +      radius_message_t *message;
 +      entry_t *entry;
 +      u_int32_t id, value;
 +
 +      id = ike_sa->get_unique_id(ike_sa);
 +      this->mutex->lock(this->mutex);
 +      entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id);
 +      this->mutex->unlock(this->mutex);
 +      if (entry)
 +      {
 +              message = radius_message_create(RMC_ACCOUNTING_REQUEST);
 +              value = htonl(ACCT_STATUS_STOP);
 +              message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
 +              message->add(message, RAT_ACCT_SESSION_ID,
 +                                       chunk_create(entry->sid, strlen(entry->sid)));
 +              add_ike_sa_parameters(message, ike_sa);
 +              value = htonl(entry->sent);
 +              message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
 +              value = htonl(entry->sent >> 32);
 +              if (value)
 +              {
 +                      message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
 +                                               chunk_from_thing(value));
 +              }
 +              value = htonl(entry->received);
 +              message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
 +              value = htonl(entry->received >> 32);
 +              if (value)
 +              {
 +                      message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
 +                                               chunk_from_thing(value));
 +              }
 +              value = htonl(time_monotonic(NULL) - entry->created);
 +              message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
 +
 +              send_message(this, message);
 +              message->destroy(message);
 +              free(entry);
 +      }
 +}
 +
 +METHOD(listener_t, ike_updown, bool,
 +      private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
 +{
 +      if (!up)
 +      {
 +              enumerator_t *enumerator;
 +              child_sa_t *child_sa;
 +
 +              /* update usage for all children just before sending stop */
 +              enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
 +              while (enumerator->enumerate(enumerator, &child_sa))
 +              {
 +                      update_usage(this, ike_sa, child_sa);
 +              }
 +              enumerator->destroy(enumerator);
 +
 +              send_stop(this, ike_sa);
 +      }
 +      return TRUE;
 +}
 +
 +METHOD(listener_t, message_hook, bool,
 +      private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
-       if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
++      message_t *message, bool incoming, bool plain)
 +{
 +      /* start accounting here, virtual IP now is set */
++      if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
 +              message->get_exchange_type(message) == IKE_AUTH &&
 +              !incoming && !message->get_request(message))
 +      {
 +              send_start(this, ike_sa);
 +      }
 +      return TRUE;
 +}
 +
 +METHOD(listener_t, child_rekey, bool,
 +      private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
 +      child_sa_t *old, child_sa_t *new)
 +{
 +      update_usage(this, ike_sa, old);
 +
 +      return TRUE;
 +}
 +
 +METHOD(listener_t, child_updown, bool,
 +      private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
 +      child_sa_t *child_sa, bool up)
 +{
 +      if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
 +      {
 +              update_usage(this, ike_sa, child_sa);
 +      }
 +      return TRUE;
 +}
 +
 +METHOD(eap_radius_accounting_t, destroy, void,
 +      private_eap_radius_accounting_t *this)
 +{
 +      this->mutex->destroy(this->mutex);
 +      this->sessions->destroy(this->sessions);
 +      free(this);
 +}
 +
 +/**
 + * See header
 + */
 +eap_radius_accounting_t *eap_radius_accounting_create()
 +{
 +      private_eap_radius_accounting_t *this;
 +
 +      INIT(this,
 +              .public = {
 +                      .listener = {
 +                              .ike_updown = _ike_updown,
 +                              .message = _message_hook,
 +                              .child_updown = _child_updown,
 +                              .child_rekey = _child_rekey,
 +                      },
 +                      .destroy = _destroy,
 +              },
 +              /* use system time as Session ID prefix */
 +              .prefix = (u_int32_t)time(NULL),
 +              .sessions = hashtable_create((hashtable_hash_t)hash,
 +                                                                       (hashtable_equals_t)equals, 32),
 +              .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
 +      );
 +
 +      return &this->public;
 +}
index cb4ca74,0000000..16701bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,458 -1,0 +1,458 @@@
-       ike_sa_t *ike_sa, message_t *message, bool incoming)
 +/*
 + * Copyright (C) 2012 Martin Willi
 + * Copyright (C) 2012 revosec AG
 + *
 + * 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.
 + */
 +
 +#include "eap_radius_forward.h"
 +
 +#include <daemon.h>
 +#include <utils/linked_list.h>
 +#include <utils/hashtable.h>
 +#include <threading/mutex.h>
 +
 +typedef struct private_eap_radius_forward_t private_eap_radius_forward_t;
 +
 +/**
 + * Private data of an eap_radius_forward_t object.
 + */
 +struct private_eap_radius_forward_t {
 +
 +      /**
 +       * Public eap_radius_forward_t interface.
 +       */
 +      eap_radius_forward_t public;
 +
 +      /**
 +       * List of attribute types to copy from IKE, as attr_t
 +       */
 +      linked_list_t *from_attr;
 +
 +      /**
 +       * List of attribute types to copy to IKE, as attr_t
 +       */
 +      linked_list_t *to_attr;
 +
 +      /**
 +       * Queued to forward from IKE, unique_id => linked_list_t of chunk_t
 +       */
 +      hashtable_t *from;
 +
 +      /**
 +       * Queued to forward to IKE, unique_id => linked_list_t of chunk_t
 +       */
 +      hashtable_t *to;
 +
 +      /**
 +       * Mutex to lock concurrent access to hashtables
 +       */
 +      mutex_t *mutex;
 +};
 +
 +/**
 + * RADIUS attribute selector
 + */
 +typedef struct {
 +      /** vendor ID, 0 for standard attributes */
 +      u_int32_t vendor;
 +      /** attribute type */
 +      u_int8_t type;
 +} attr_t;
 +
 +/**
 + * Single instance of this
 + */
 +static private_eap_radius_forward_t *singleton = NULL;
 +
 +/**
 + * Hashtable hash function
 + */
 +static u_int hash(uintptr_t key)
 +{
 +      return key;
 +}
 +
 +/**
 + * Hashtable equals function
 + */
 +static bool equals(uintptr_t a, uintptr_t b)
 +{
 +      return a == b;
 +}
 +
 +/**
 + * Free a queue entry
 + */
 +static void free_attribute(chunk_t *chunk)
 +{
 +      free(chunk->ptr);
 +      free(chunk);
 +}
 +
 +/**
 + * Lookup/create an attribute queue from a table
 + */
 +static linked_list_t *lookup_queue(private_eap_radius_forward_t *this,
 +                                                                 hashtable_t *table)
 +{
 +      linked_list_t *queue = NULL;
 +      ike_sa_t *ike_sa;
 +      uintptr_t id;
 +
 +      ike_sa = charon->bus->get_sa(charon->bus);
 +      if (ike_sa && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN))
 +      {
 +              id = ike_sa->get_unique_id(ike_sa);
 +              this->mutex->lock(this->mutex);
 +              queue = table->get(table, (void*)id);
 +              if (!queue)
 +              {
 +                      queue = linked_list_create();
 +                      table->put(table, (void*)id, queue);
 +              }
 +              this->mutex->unlock(this->mutex);
 +      }
 +      return queue;
 +}
 +
 +/**
 + * Remove attribute queue from table
 + */
 +static void remove_queue(private_eap_radius_forward_t *this,
 +                                               hashtable_t *table, ike_sa_t *ike_sa)
 +{
 +      linked_list_t *queue;
 +
 +      this->mutex->lock(this->mutex);
 +      queue = table->remove(table, (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
 +      this->mutex->unlock(this->mutex);
 +      if (queue)
 +      {
 +              queue->destroy_function(queue, (void*)free_attribute);
 +      }
 +}
 +
 +/**
 + * Check if RADIUS attribute is contained in selector
 + */
 +static bool is_attribute_selected(linked_list_t *selector,
 +                                                                radius_attribute_type_t type, chunk_t data)
 +{
 +      enumerator_t *enumerator;
 +      u_int32_t vendor = 0;
 +      attr_t *sel;
 +      bool found = FALSE;
 +
 +      if (type == RAT_VENDOR_SPECIFIC)
 +      {
 +              if (data.len < 4)
 +              {
 +                      return FALSE;
 +              }
 +              vendor = untoh32(data.ptr);
 +      }
 +      enumerator = selector->create_enumerator(selector);
 +      while (!found && enumerator->enumerate(enumerator, &sel))
 +      {
 +              if (sel->vendor == vendor)
 +              {
 +                      if (vendor)
 +                      {
 +                              if (sel->type == 0)
 +                              {       /* any of that vendor is fine */
 +                                      found = TRUE;
 +                              }
 +                              else if (data.len > 4 && data.ptr[4] == sel->type)
 +                              {       /* vendor specific type field, as defined in RFC 2865 */
 +                                      found = TRUE;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              if (sel->type == type)
 +                              {
 +                                      found = TRUE;
 +                              }
 +                      }
 +              }
 +      }
 +      enumerator->destroy(enumerator);
 +
 +      return found;
 +}
 +
 +/**
 + * Copy RADIUS attributes from queue to a RADIUS message
 + */
 +static void queue2radius(linked_list_t *queue, radius_message_t *message)
 +{
 +      chunk_t *data;
 +
 +      while (queue->remove_last(queue, (void**)&data) == SUCCESS)
 +      {
 +              if (data->len >= 2)
 +              {
 +                      message->add(message, data->ptr[0], chunk_skip(*data, 2));
 +              }
 +              free_attribute(data);
 +      }
 +}
 +
 +/**
 + * Copy RADIUS attributes from a RADIUS message to the queue
 + */
 +static void radius2queue(radius_message_t *message, linked_list_t *queue,
 +                                               linked_list_t *selector)
 +{
 +      enumerator_t *enumerator;
 +      int type;
 +      chunk_t data, hdr, *ptr;
 +
 +      enumerator = message->create_enumerator(message);
 +      while (enumerator->enumerate(enumerator, &type, &data))
 +      {
 +              if (is_attribute_selected(selector, type, data))
 +              {
 +                      hdr = chunk_alloc(2);
 +                      hdr.ptr[0] = type;
 +                      hdr.ptr[1] = data.len + 2;
 +
 +                      INIT(ptr);
 +                      *ptr = chunk_cat("mc", hdr, data);
 +                      queue->insert_last(queue, ptr);
 +              }
 +      }
 +      enumerator->destroy(enumerator);
 +}
 +
 +/**
 + * Copy RADIUS attribute nofifies from IKE message to queue
 + */
 +static void ike2queue(message_t *message, linked_list_t *queue,
 +                                        linked_list_t *selector)
 +{
 +      enumerator_t *enumerator;
 +      payload_t *payload;
 +      notify_payload_t *notify;
 +      chunk_t data, *ptr;
 +
 +      enumerator = message->create_payload_enumerator(message);
 +      while (enumerator->enumerate(enumerator, &payload))
 +      {
 +              if (payload->get_type(payload) == NOTIFY)
 +              {
 +                      notify = (notify_payload_t*)payload;
 +                      if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE)
 +                      {
 +                              data = notify->get_notification_data(notify);
 +                              if (data.len >= 2 && is_attribute_selected(selector,
 +                                                                              data.ptr[0], chunk_skip(data, 2)))
 +                              {
 +                                      INIT(ptr);
 +                                      *ptr = chunk_clone(data);
 +                                      queue->insert_last(queue, ptr);
 +                              }
 +                      }
 +              }
 +      }
 +      enumerator->destroy(enumerator);
 +}
 +
 +/**
 + * Copy RADUIS attributes from queue to IKE message notifies
 + */
 +static void queue2ike(linked_list_t *queue, message_t *message)
 +{
 +      chunk_t *data;
 +
 +      while (queue->remove_last(queue, (void**)&data) == SUCCESS)
 +      {
 +              message->add_notify(message, FALSE, RADIUS_ATTRIBUTE, *data);
 +              free_attribute(data);
 +      }
 +}
 +
 +/**
 + * See header.
 + */
 +void eap_radius_forward_from_ike(radius_message_t *request)
 +{
 +      private_eap_radius_forward_t *this = singleton;
 +      linked_list_t *queue;
 +
 +      if (this)
 +      {
 +              queue = lookup_queue(this, this->from);
 +              if (queue)
 +              {
 +                      queue2radius(queue, request);
 +              }
 +      }
 +}
 +
 +/**
 + * See header.
 + */
 +void eap_radius_forward_to_ike(radius_message_t *response)
 +{
 +      private_eap_radius_forward_t *this = singleton;
 +      linked_list_t *queue;
 +
 +      if (this)
 +      {
 +              queue = lookup_queue(this, this->to);
 +              if (queue)
 +              {
 +                      radius2queue(response, queue, this->to_attr);
 +              }
 +      }
 +}
 +
 +METHOD(listener_t, message, bool,
 +      private_eap_radius_forward_t *this,
-       if (message->get_exchange_type(message) == IKE_AUTH)
++      ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain)
 +{
 +      linked_list_t *queue;
 +
++      if (plain && message->get_exchange_type(message) == IKE_AUTH)
 +      {
 +              if (incoming)
 +              {
 +                      queue = lookup_queue(this, this->from);
 +                      if (queue)
 +                      {
 +                              ike2queue(message, queue, this->from_attr);
 +                      }
 +              }
 +              else
 +              {
 +                      queue = lookup_queue(this, this->to);
 +                      if (queue)
 +                      {
 +                              queue2ike(queue, message);
 +                      }
 +              }
 +      }
 +      return TRUE;
 +}
 +
 +METHOD(listener_t, ike_updown, bool,
 +      private_eap_radius_forward_t *this, ike_sa_t *ike_sa, bool up)
 +{
 +      /* up or down, we don't need the state anymore */
 +      remove_queue(this, this->from, ike_sa);
 +      remove_queue(this, this->to, ike_sa);
 +      return TRUE;
 +}
 +
 +/**
 + * Parse a selector string to a list of attr_t selectors
 + */
 +static linked_list_t* parse_selector(char *selector)
 +{
 +      enumerator_t *enumerator;
 +      linked_list_t *list;
 +      char *token, *pos;
 +
 +      list = linked_list_create();
 +      enumerator = enumerator_create_token(selector, ",", " ");
 +      while (enumerator->enumerate(enumerator, &token))
 +      {
 +              int type, vendor = 0;
 +              attr_t *attr;
 +
 +              pos = strchr(token, ':');
 +              if (pos)
 +              {
 +                      *(pos++) = 0;
 +                      vendor = atoi(token);
 +                      token = pos;
 +              }
 +              type = enum_from_name(radius_attribute_type_names, token);
 +              if (type == -1)
 +              {
 +                      type = atoi(token);
 +              }
 +              if (vendor == 0 && type == 0)
 +              {
 +                      DBG1(DBG_CFG, "ignoring unknown RADIUS attribute type '%s'", token);
 +              }
 +              else
 +              {
 +                      INIT(attr,
 +                              .type = type,
 +                              .vendor = vendor,
 +                      );
 +                      list->insert_last(list, attr);
 +                      if (!vendor)
 +                      {
 +                              DBG1(DBG_IKE, "forwarding RADIUS attribute %N",
 +                                       radius_attribute_type_names, type);
 +                      }
 +                      else
 +                      {
 +                              DBG1(DBG_IKE, "forwarding RADIUS VSA %d-%d", vendor, type);
 +                      }
 +              }
 +      }
 +      enumerator->destroy(enumerator);
 +      return list;
 +}
 +
 +METHOD(eap_radius_forward_t, destroy, void,
 +      private_eap_radius_forward_t *this)
 +{
 +      this->from_attr->destroy_function(this->from_attr, free);
 +      this->to_attr->destroy_function(this->to_attr, free);
 +      this->from->destroy(this->from);
 +      this->to->destroy(this->to);
 +      this->mutex->destroy(this->mutex);
 +      free(this);
 +      singleton = NULL;
 +}
 +
 +/**
 + * See header
 + */
 +eap_radius_forward_t *eap_radius_forward_create()
 +{
 +      private_eap_radius_forward_t *this;
 +
 +      INIT(this,
 +              .public = {
 +                      .listener = {
 +                              .message = _message,
 +                              .ike_updown = _ike_updown,
 +                      },
 +                      .destroy = _destroy,
 +              },
 +              .from_attr = parse_selector(lib->settings->get_str(lib->settings,
 +                                              "charon.plugins.eap-radius.forward.ike_to_radius", "")),
 +              .to_attr = parse_selector(lib->settings->get_str(lib->settings,
 +                                              "charon.plugins.eap-radius.forward.radius_to_ike", "")),
 +              .from = hashtable_create((hashtable_hash_t)hash,
 +                                              (hashtable_equals_t)equals, 8),
 +              .to = hashtable_create((hashtable_hash_t)hash,
 +                                              (hashtable_equals_t)equals, 8),
 +              .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
 +      );
 +
 +      if (this->from_attr->get_count(this->from_attr) == 0 &&
 +              this->to_attr->get_count(this->to_attr) == 0)
 +      {
 +              destroy(this);
 +              return NULL;
 +      }
 +
 +      singleton = this;
 +      return &this->public;
 +}
@@@ -18,8 -18,8 +18,8 @@@
  
  #include <debug.h>
  #include <daemon.h>
 -
 +#include <radius_message.h>
- #include <sa/authenticators/eap/eap_method.h>
+ #include <sa/eap/eap_method.h>
  
  typedef struct private_eap_ttls_peer_t private_eap_ttls_peer_t;
  
Simple merge
Simple merge
Simple merge
index 94b718a,0000000..88ab605
mode 100644,000000..100644
--- /dev/null
@@@ -1,221 -1,0 +1,221 @@@
-       ike_sa_t *ike_sa, message_t *message, bool incoming)
 +/*
 + * Copyright (C) 2012 Martin Willi
 + * Copyright (C) 2012 revosec AG
 + *
 + * 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.
 + */
 +
 +#include "radattr_listener.h"
 +
 +#include <sys/types.h>
 +#include <sys/stat.h>
 +#include <unistd.h>
 +#include <fcntl.h>
 +#include <sys/mman.h>
 +#include <errno.h>
 +
 +#include <daemon.h>
 +
 +#include <radius_message.h>
 +
 +/**
 + * Maximum size of an attribute to add
 + */
 +#define MAX_ATTR_SIZE 1024
 +
 +typedef struct private_radattr_listener_t private_radattr_listener_t;
 +
 +/**
 + * Private data of an radattr_listener_t object.
 + */
 +struct private_radattr_listener_t {
 +
 +      /**
 +       * Public radattr_listener_t interface.
 +       */
 +      radattr_listener_t public;
 +
 +      /**
 +       * Directory to look for attribute files
 +       */
 +      char *dir;
 +
 +      /**
 +       * IKE_AUTH message ID to attribute
 +       */
 +      int mid;
 +};
 +
 +/**
 + * Print RADIUS attributes found in IKE message notifies
 + */
 +static void print_radius_attributes(private_radattr_listener_t *this,
 +                                                                      message_t *message)
 +{
 +      radius_attribute_type_t type;
 +      enumerator_t *enumerator;
 +      notify_payload_t *notify;
 +      payload_t *payload;
 +      chunk_t data;
 +
 +      enumerator = message->create_payload_enumerator(message);
 +      while (enumerator->enumerate(enumerator, &payload))
 +      {
 +              if (payload->get_type(payload) == NOTIFY)
 +              {
 +                      notify = (notify_payload_t*)payload;
 +                      if (notify->get_notify_type(notify) == RADIUS_ATTRIBUTE)
 +                      {
 +                              data = notify->get_notification_data(notify);
 +                              if (data.len >= 2)
 +                              {
 +                                      type = data.ptr[0];
 +                                      data = chunk_skip(data, 2);
 +                                      if (chunk_printable(data, NULL, 0))
 +                                      {
 +                                              DBG1(DBG_IKE, "received RADIUS %N: %.*s",
 +                                                       radius_attribute_type_names, type,
 +                                                       (int)data.len, data.ptr);
 +                                      }
 +                                      else
 +                                      {
 +                                              DBG1(DBG_IKE, "received RADIUS %N: %#B",
 +                                                       radius_attribute_type_names, type, &data);
 +
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +      enumerator->destroy(enumerator);
 +}
 +
 +/**
 + * Add a RADIUS attribute from a client-ID specific file to an IKE message
 + */
 +static void add_radius_attribute(private_radattr_listener_t *this,
 +                                                               ike_sa_t *ike_sa, message_t *message)
 +{
 +      if (this->dir &&
 +              (this->mid == -1 || message->get_message_id(message) == this->mid))
 +      {
 +              identification_t *id;
 +              auth_cfg_t *auth;
 +              char path[PATH_MAX];
 +              chunk_t data;
 +              struct stat sb;
 +              void *addr;
 +              int fd;
 +
 +              auth = ike_sa->get_auth_cfg(ike_sa, TRUE);
 +              id = auth->get(auth, AUTH_RULE_EAP_IDENTITY);
 +              if (!id)
 +              {
 +                      id = ike_sa->get_my_id(ike_sa);
 +              }
 +
 +              snprintf(path, sizeof(path), "%s/%Y", this->dir, id);
 +              fd = open(path, O_RDONLY);
 +              if (fd != -1)
 +              {
 +                      if (fstat(fd, &sb) != -1)
 +                      {
 +                              if (sb.st_size <= MAX_ATTR_SIZE)
 +                              {
 +                                      addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 +                                      if (addr != MAP_FAILED)
 +                                      {
 +                                              data = chunk_create(addr, sb.st_size);
 +                                              if (data.len >= 2)
 +                                              {
 +                                                      DBG1(DBG_CFG, "adding RADIUS %N attribute",
 +                                                               radius_attribute_type_names, data.ptr[0]);
 +                                                      message->add_notify(message, FALSE,
 +                                                                                              RADIUS_ATTRIBUTE, data);
 +                                              }
 +                                              munmap(addr, sb.st_size);
 +                                      }
 +                                      else
 +                                      {
 +                                              DBG1(DBG_CFG, "mapping RADIUS attribute '%s' failed: %s",
 +                                                       path, strerror(errno));
 +                                      }
 +                              }
 +                              else
 +                              {
 +                                      DBG1(DBG_CFG, "RADIUS attribute '%s' exceeds size limit",
 +                                               path);
 +                              }
 +                      }
 +                      else
 +                      {
 +                              DBG1(DBG_CFG, "fstat RADIUS attribute '%s' failed: %s",
 +                                       path, strerror(errno));
 +                      }
 +                      close(fd);
 +              }
 +              else
 +              {
 +                      DBG1(DBG_CFG, "reading RADIUS attribute '%s' failed: %s",
 +                               path, strerror(errno));
 +              }
 +      }
 +}
 +
 +METHOD(listener_t, message, bool,
 +      private_radattr_listener_t *this,
-       if (ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN) &&
++      ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain)
 +{
++      if (plain && ike_sa->supports_extension(ike_sa, EXT_STRONGSWAN) &&
 +              message->get_exchange_type(message) == IKE_AUTH &&
 +              message->get_payload(message, EXTENSIBLE_AUTHENTICATION))
 +      {
 +              if (incoming)
 +              {
 +                      print_radius_attributes(this, message);
 +              }
 +              else
 +              {
 +                      add_radius_attribute(this, ike_sa, message);
 +              }
 +      }
 +      return TRUE;
 +}
 +
 +
 +METHOD(radattr_listener_t, destroy, void,
 +      private_radattr_listener_t *this)
 +{
 +      free(this);
 +}
 +
 +/**
 + * See header
 + */
 +radattr_listener_t *radattr_listener_create()
 +{
 +      private_radattr_listener_t *this;
 +
 +      INIT(this,
 +              .public = {
 +                      .listener = {
 +                              .message = _message,
 +                      },
 +                      .destroy = _destroy,
 +              },
 +              .dir = lib->settings->get_str(lib->settings,
 +                                                                        "charon.plugins.radattr.dir", NULL),
 +              .mid = lib->settings->get_int(lib->settings,
 +                                                                        "charon.plugins.radattr.message_id", -1),
 +      );
 +
 +      return &this->public;
 +}
  #include <processing/jobs/send_dpd_job.h>
  #include <processing/jobs/send_keepalive_job.h>
  #include <processing/jobs/rekey_ike_sa_job.h>
- #include <encoding/payloads/unknown_payload.h>
++#include <sa/ikev2/tasks/ike_auth_lifetime.h>
  
  #ifdef ME
- #include <sa/tasks/ike_me.h>
+ #include <sa/ikev2/tasks/ike_me.h>
  #include <processing/jobs/initiate_mediation_job.h>
  #endif
  
@@@ -1559,18 -1394,10 +1396,16 @@@ METHOD(ike_sa_t, reauth, status_t
                }
                else
                {
 -                      DBG1(DBG_IKE, "reauthenticating actively");
 +                      DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d] actively",
 +                               get_name(this), this->unique_id);
                }
        }
-       task = (task_t*)ike_reauth_create(&this->public);
-       this->task_manager->queue_task(this->task_manager, task);
 +      else
 +      {
 +              DBG0(DBG_IKE, "reauthenticating IKE_SA %s[%d]",
 +                       get_name(this), this->unique_id);
 +      }
+       this->task_manager->queue_ike_reauth(this->task_manager);
        return this->task_manager->initiate(this->task_manager);
  }
  
@@@ -1760,8 -1558,7 +1566,8 @@@ METHOD(ike_sa_t, retransmit, status_t
                                        DBG1(DBG_IKE, "peer not responding, trying again (%d/%d)",
                                                 this->keyingtry + 1, tries);
                                        reset(this);
-                                       requeue_init_tasks(this);
 +                                      resolve_hosts(this);
+                                       this->task_manager->queue_ike(this->task_manager);
                                        return this->task_manager->initiate(this->task_manager);
                                }
                                DBG1(DBG_IKE, "establishing IKE_SA failed, peer not responding");
        return SUCCESS;
  }
  
 -METHOD(ike_sa_t, set_auth_lifetime, void,
 +METHOD(ike_sa_t, set_auth_lifetime, status_t,
        private_ike_sa_t *this, u_int32_t lifetime)
  {
 -      u_int32_t reduction = this->peer_cfg->get_over_time(this->peer_cfg);
 -      u_int32_t reauth_time = time_monotonic(NULL) + lifetime - reduction;
 +      u_int32_t diff, hard, soft, now;
 +      ike_auth_lifetime_t *task;
 +      bool send_update;
 +
 +      diff = this->peer_cfg->get_over_time(this->peer_cfg);
 +      now = time_monotonic(NULL);
 +      hard = now + lifetime;
 +      soft = hard - diff;
  
 -      if (lifetime < reduction)
 +      /* check if we have to send an AUTH_LIFETIME to enforce the new lifetime.
 +       * We send the notify in IKE_AUTH if not yet ESTABLISHED. */
-       send_update = this->state == IKE_ESTABLISHED &&
++      send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 &&
 +                                !has_condition(this, COND_ORIGINAL_INITIATOR) &&
 +                                (this->other_virtual_ip != NULL ||
 +                                has_condition(this, COND_EAP_AUTHENTICATED));
 +
 +      if (lifetime < diff)
        {
 -              DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, starting reauthentication",
 -                       lifetime);
 -              lib->processor->queue_job(lib->processor,
 +              this->stats[STAT_REAUTH] = now;
 +
 +              if (!send_update)
 +              {
 +                      DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, "
 +                               "starting reauthentication", lifetime);
 +                      lib->processor->queue_job(lib->processor,
                                        (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE));
 +              }
        }
        else if (this->stats[STAT_REAUTH] == 0 ||
 -                       this->stats[STAT_REAUTH] > reauth_time)
 +                       this->stats[STAT_REAUTH] > soft)
        {
 -              this->stats[STAT_REAUTH] = reauth_time;
 -              DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling reauthentication"
 -                       " in %ds", lifetime, lifetime - reduction);
 -              lib->scheduler->schedule_job(lib->scheduler,
 +              this->stats[STAT_REAUTH] = soft;
 +              if (!send_update)
 +              {
 +                      DBG1(DBG_IKE, "received AUTH_LIFETIME of %ds, scheduling "
 +                               "reauthentication in %ds", lifetime, lifetime - diff);
 +                      lib->scheduler->schedule_job(lib->scheduler,
                                                (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, TRUE),
 -                                              lifetime - reduction);
 +                                              lifetime - diff);
 +              }
        }
        else
        {
Simple merge
index 0000000,d12f5c9..ba7fdd2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1475 +1,1479 @@@
 -      this->queued_tasks->destroy_offset(this->queued_tasks,
 -                                                                              offsetof(task_t, destroy));
 -      this->queued_tasks = linked_list_create();
+ /*
+  * Copyright (C) 2007-2011 Tobias Brunner
+  * Copyright (C) 2007-2010 Martin Willi
+  * Hochschule fuer Technik Rapperswil
+  *
+  * 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.
+  */
+ #include "task_manager_v2.h"
+ #include <math.h>
+ #include <daemon.h>
+ #include <sa/ikev2/tasks/ike_init.h>
+ #include <sa/ikev2/tasks/ike_natd.h>
+ #include <sa/ikev2/tasks/ike_mobike.h>
+ #include <sa/ikev2/tasks/ike_auth.h>
+ #include <sa/ikev2/tasks/ike_auth_lifetime.h>
+ #include <sa/ikev2/tasks/ike_cert_pre.h>
+ #include <sa/ikev2/tasks/ike_cert_post.h>
+ #include <sa/ikev2/tasks/ike_rekey.h>
+ #include <sa/ikev2/tasks/ike_reauth.h>
+ #include <sa/ikev2/tasks/ike_delete.h>
+ #include <sa/ikev2/tasks/ike_config.h>
+ #include <sa/ikev2/tasks/ike_dpd.h>
+ #include <sa/ikev2/tasks/ike_vendor.h>
+ #include <sa/ikev2/tasks/child_create.h>
+ #include <sa/ikev2/tasks/child_rekey.h>
+ #include <sa/ikev2/tasks/child_delete.h>
+ #include <encoding/payloads/delete_payload.h>
+ #include <encoding/payloads/unknown_payload.h>
+ #include <processing/jobs/retransmit_job.h>
+ #include <processing/jobs/delete_ike_sa_job.h>
+ #ifdef ME
+ #include <sa/ikev2/tasks/ike_me.h>
+ #endif
+ typedef struct exchange_t exchange_t;
+ /**
+  * An exchange in the air, used do detect and handle retransmission
+  */
+ struct exchange_t {
+       /**
+        * Message ID used for this transaction
+        */
+       u_int32_t mid;
+       /**
+        * generated packet for retransmission
+        */
+       packet_t *packet;
+ };
+ typedef struct private_task_manager_t private_task_manager_t;
+ /**
+  * private data of the task manager
+  */
+ struct private_task_manager_t {
+       /**
+        * public functions
+        */
+       task_manager_v2_t public;
+       /**
+        * associated IKE_SA we are serving
+        */
+       ike_sa_t *ike_sa;
+       /**
+        * Exchange we are currently handling as responder
+        */
+       struct {
+               /**
+                * Message ID of the exchange
+                */
+               u_int32_t mid;
+               /**
+                * packet for retransmission
+                */
+               packet_t *packet;
+       } responding;
+       /**
+        * Exchange we are currently handling as initiator
+        */
+       struct {
+               /**
+                * Message ID of the exchange
+                */
+               u_int32_t mid;
+               /**
+                * how many times we have retransmitted so far
+                */
+               u_int retransmitted;
+               /**
+                * packet for retransmission
+                */
+               packet_t *packet;
+               /**
+                * type of the initated exchange
+                */
+               exchange_type_t type;
+       } initiating;
+       /**
+        * List of queued tasks not yet in action
+        */
+       linked_list_t *queued_tasks;
+       /**
+        * List of active tasks, initiated by ourselve
+        */
+       linked_list_t *active_tasks;
+       /**
+        * List of tasks initiated by peer
+        */
+       linked_list_t *passive_tasks;
+       /**
+        * the task manager has been reset
+        */
+       bool reset;
+       /**
+        * Number of times we retransmit messages before giving up
+        */
+       u_int retransmit_tries;
+       /**
+        * Retransmission timeout
+        */
+       double retransmit_timeout;
+       /**
+        * Base to calculate retransmission timeout
+        */
+       double retransmit_base;
+ };
+ /**
+  * flush all tasks in the task manager
+  */
+ static void flush(private_task_manager_t *this)
+ {
 -                              charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
 -                              /* FALL */
 -                      case DESTROY_ME:
+       this->passive_tasks->destroy_offset(this->passive_tasks,
+                                                                               offsetof(task_t, destroy));
+       this->passive_tasks = linked_list_create();
+       this->active_tasks->destroy_offset(this->active_tasks,
+                                                                               offsetof(task_t, destroy));
+       this->active_tasks = linked_list_create();
++      this->queued_tasks->destroy_offset(this->queued_tasks,
++                                                                              offsetof(task_t, destroy));
++      this->queued_tasks = linked_list_create();
+ }
+ /**
+  * move a task of a specific type from the queue to the active list
+  */
+ static bool activate_task(private_task_manager_t *this, task_type_t type)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       bool found = FALSE;
+       enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, (void**)&task))
+       {
+               if (task->get_type(task) == type)
+               {
+                       DBG2(DBG_IKE, "  activating %N task", task_type_names, type);
+                       this->queued_tasks->remove_at(this->queued_tasks, enumerator);
+                       this->active_tasks->insert_last(this->active_tasks, task);
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+ }
+ METHOD(task_manager_t, retransmit, status_t,
+       private_task_manager_t *this, u_int32_t message_id)
+ {
+       if (this->initiating.packet && message_id == this->initiating.mid)
+       {
+               u_int32_t timeout;
+               job_t *job;
+               enumerator_t *enumerator;
+               packet_t *packet;
+               task_t *task;
+               ike_mobike_t *mobike = NULL;
+               /* check if we are retransmitting a MOBIKE routability check */
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void*)&task))
+               {
+                       if (task->get_type(task) == TASK_IKE_MOBIKE)
+                       {
+                               mobike = (ike_mobike_t*)task;
+                               if (!mobike->is_probing(mobike))
+                               {
+                                       mobike = NULL;
+                               }
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
+               if (mobike == NULL)
+               {
+                       if (this->initiating.retransmitted <= this->retransmit_tries)
+                       {
+                               timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 *
+                                       pow(this->retransmit_base, this->initiating.retransmitted));
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "giving up after %d retransmits",
+                                        this->initiating.retransmitted - 1);
+                               if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+                               {
+                                       charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               }
+                               return DESTROY_ME;
+                       }
+                       if (this->initiating.retransmitted)
+                       {
+                               DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
+                                        this->initiating.retransmitted, message_id);
+                       }
+                       packet = this->initiating.packet->clone(this->initiating.packet);
+                       charon->sender->send(charon->sender, packet);
+               }
+               else
+               {       /* for routeability checks, we use a more aggressive behavior */
+                       if (this->initiating.retransmitted <= ROUTEABILITY_CHECK_TRIES)
+                       {
+                               timeout = ROUTEABILITY_CHECK_INTERVAL;
+                       }
+                       else
+                       {
+                               DBG1(DBG_IKE, "giving up after %d path probings",
+                                        this->initiating.retransmitted - 1);
+                               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               return DESTROY_ME;
+                       }
+                       if (this->initiating.retransmitted)
+                       {
+                               DBG1(DBG_IKE, "path probing attempt %d",
+                                        this->initiating.retransmitted);
+                       }
+                       mobike->transmit(mobike, this->initiating.packet);
+               }
+               this->initiating.retransmitted++;
+               job = (job_t*)retransmit_job_create(this->initiating.mid,
+                                                                                       this->ike_sa->get_id(this->ike_sa));
+               lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout);
+       }
+       return SUCCESS;
+ }
+ METHOD(task_manager_t, initiate, status_t,
+       private_task_manager_t *this)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       message_t *message;
+       host_t *me, *other;
+       status_t status;
+       exchange_type_t exchange = 0;
+       if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
+       {
+               DBG2(DBG_IKE, "delaying task initiation, %N exchange in progress",
+                               exchange_type_names, this->initiating.type);
+               /* do not initiate if we already have a message in the air */
+               return SUCCESS;
+       }
+       if (this->active_tasks->get_count(this->active_tasks) == 0)
+       {
+               DBG2(DBG_IKE, "activating new tasks");
+               switch (this->ike_sa->get_state(this->ike_sa))
+               {
+                       case IKE_CREATED:
+                               activate_task(this, TASK_IKE_VENDOR);
+                               if (activate_task(this, TASK_IKE_INIT))
+                               {
+                                       this->initiating.mid = 0;
+                                       exchange = IKE_SA_INIT;
+                                       activate_task(this, TASK_IKE_NATD);
+                                       activate_task(this, TASK_IKE_CERT_PRE);
+ #ifdef ME
+                                       /* this task has to be activated before the TASK_IKE_AUTH
+                                        * task, because that task pregenerates the packet after
+                                        * which no payloads can be added to the message anymore.
+                                        */
+                                       activate_task(this, TASK_IKE_ME);
+ #endif /* ME */
+                                       activate_task(this, TASK_IKE_AUTH);
+                                       activate_task(this, TASK_IKE_CERT_POST);
+                                       activate_task(this, TASK_IKE_CONFIG);
+                                       activate_task(this, TASK_CHILD_CREATE);
+                                       activate_task(this, TASK_IKE_AUTH_LIFETIME);
+                                       activate_task(this, TASK_IKE_MOBIKE);
+                               }
+                               break;
+                       case IKE_ESTABLISHED:
+                               if (activate_task(this, TASK_CHILD_CREATE))
+                               {
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_CHILD_DELETE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_CHILD_REKEY))
+                               {
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_DELETE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_REKEY))
+                               {
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_REAUTH))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_MOBIKE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                               if (activate_task(this, TASK_IKE_DPD))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
++                              if (activate_task(this, TASK_IKE_AUTH_LIFETIME))
++                              {
++                                      exchange = INFORMATIONAL;
++                                      break;
++                              }
+ #ifdef ME
+                               if (activate_task(this, TASK_IKE_ME))
+                               {
+                                       exchange = ME_CONNECT;
+                                       break;
+                               }
+ #endif /* ME */
+                       case IKE_REKEYING:
+                               if (activate_task(this, TASK_IKE_DELETE))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
+                       case IKE_DELETING:
+                       default:
+                               break;
+               }
+       }
+       else
+       {
+               DBG2(DBG_IKE, "reinitiating already active tasks");
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&task))
+               {
+                       DBG2(DBG_IKE, "  %N task", task_type_names, task->get_type(task));
+                       switch (task->get_type(task))
+                       {
+                               case TASK_IKE_INIT:
+                                       exchange = IKE_SA_INIT;
+                                       break;
+                               case TASK_IKE_AUTH:
+                                       exchange = IKE_AUTH;
+                                       break;
+                               case TASK_CHILD_CREATE:
+                               case TASK_CHILD_REKEY:
+                               case TASK_IKE_REKEY:
+                                       exchange = CREATE_CHILD_SA;
+                                       break;
+                               case TASK_IKE_MOBIKE:
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               default:
+                                       continue;
+                       }
+                       break;
+               }
+               enumerator->destroy(enumerator);
+       }
+       if (exchange == 0)
+       {
+               DBG2(DBG_IKE, "nothing to initiate");
+               /* nothing to do yet... */
+               return SUCCESS;
+       }
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       message->set_message_id(message, this->initiating.mid);
+       message->set_source(message, me->clone(me));
+       message->set_destination(message, other->clone(other));
+       message->set_exchange_type(message, exchange);
+       this->initiating.type = exchange;
+       this->initiating.retransmitted = 0;
+       enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->build(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               task->destroy(task);
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs another exchange */
+                               break;
+                       case FAILED:
+                       default:
+                               if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+                               {
+                                       charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               }
+                               /* FALL */
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               enumerator->destroy(enumerator);
+                               message->destroy(message);
+                               flush(this);
+                               return DESTROY_ME;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* update exchange type if a task changed it */
+       this->initiating.type = message->get_exchange_type(message);
+       status = this->ike_sa->generate_message(this->ike_sa, message,
+                                                                                       &this->initiating.packet);
+       if (status != SUCCESS)
+       {
+               /* message generation failed. There is nothing more to do than to
+                * close the SA */
+               message->destroy(message);
+               flush(this);
+               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       message->destroy(message);
+       return retransmit(this, this->initiating.mid);
+ }
+ /**
+  * handle an incoming response message
+  */
+ static status_t process_response(private_task_manager_t *this,
+                                                                message_t *message)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       if (message->get_exchange_type(message) != this->initiating.type)
+       {
+               DBG1(DBG_IKE, "received %N response, but expected %N",
+                        exchange_type_names, message->get_exchange_type(message),
+                        exchange_type_names, this->initiating.type);
+               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       /* catch if we get resetted while processing */
+       this->reset = FALSE;
+       enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->process(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               task->destroy(task);
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs another exchange */
+                               break;
+                       case FAILED:
+                       default:
+                               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               /* FALL */
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               enumerator->destroy(enumerator);
+                               task->destroy(task);
+                               return DESTROY_ME;
+               }
+               if (this->reset)
+               {       /* start all over again if we were reset */
+                       this->reset = FALSE;
+                       enumerator->destroy(enumerator);
+                       return initiate(this);
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->initiating.mid++;
+       this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+       this->initiating.packet->destroy(this->initiating.packet);
+       this->initiating.packet = NULL;
+       return initiate(this);
+ }
+ /**
+  * handle exchange collisions
+  */
+ static bool handle_collisions(private_task_manager_t *this, task_t *task)
+ {
+       enumerator_t *enumerator;
+       task_t *active;
+       task_type_t type;
+       type = task->get_type(task);
+       /* do we have to check  */
+       if (type == TASK_IKE_REKEY || type == TASK_CHILD_REKEY ||
+               type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE ||
+               type == TASK_IKE_REAUTH)
+       {
+               /* find an exchange collision, and notify these tasks */
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&active))
+               {
+                       switch (active->get_type(active))
+                       {
+                               case TASK_IKE_REKEY:
+                                       if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE ||
+                                               type == TASK_IKE_REAUTH)
+                                       {
+                                               ike_rekey_t *rekey = (ike_rekey_t*)active;
+                                               rekey->collide(rekey, task);
+                                               break;
+                                       }
+                                       continue;
+                               case TASK_CHILD_REKEY:
+                                       if (type == TASK_CHILD_REKEY || type == TASK_CHILD_DELETE)
+                                       {
+                                               child_rekey_t *rekey = (child_rekey_t*)active;
+                                               rekey->collide(rekey, task);
+                                               break;
+                                       }
+                                       continue;
+                               default:
+                                       continue;
+                       }
+                       enumerator->destroy(enumerator);
+                       return TRUE;
+               }
+               enumerator->destroy(enumerator);
+       }
+       return FALSE;
+ }
+ /**
+  * build a response depending on the "passive" task list
+  */
+ static status_t build_response(private_task_manager_t *this, message_t *request)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       message_t *message;
+       host_t *me, *other;
+       bool delete = FALSE;
+       status_t status;
+       me = request->get_destination(request);
+       other = request->get_source(request);
+       message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       message->set_exchange_type(message, request->get_exchange_type(request));
+       /* send response along the path the request came in */
+       message->set_source(message, me->clone(me));
+       message->set_destination(message, other->clone(other));
+       message->set_message_id(message, this->responding.mid);
+       message->set_request(message, FALSE);
+       enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->build(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+                               if (!handle_collisions(this, task))
+                               {
+                                       task->destroy(task);
+                               }
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs another exchange */
+                               if (handle_collisions(this, task))
+                               {
+                                       this->passive_tasks->remove_at(this->passive_tasks,
+                                                                                                  enumerator);
+                               }
+                               break;
++                      case DESTROY_ME:
+                       case FAILED:
+                       default:
+                               /* destroy IKE_SA, but SEND response first */
+                               delete = TRUE;
+                               break;
+               }
+               if (delete)
+               {
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* remove resonder SPI if IKE_SA_INIT failed */
+       if (delete && request->get_exchange_type(request) == IKE_SA_INIT)
+       {
+               ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa);
+               id->set_responder_spi(id, 0);
+       }
+       /* message complete, send it */
+       DESTROY_IF(this->responding.packet);
+       this->responding.packet = NULL;
+       status = this->ike_sa->generate_message(this->ike_sa, message,
+                                                                                       &this->responding.packet);
+       message->destroy(message);
+       if (status != SUCCESS)
+       {
+               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       charon->sender->send(charon->sender,
+                                                this->responding.packet->clone(this->responding.packet));
+       if (delete)
+       {
++              charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+               return DESTROY_ME;
+       }
+       return SUCCESS;
+ }
+ /**
+  * handle an incoming request message
+  */
+ static status_t process_request(private_task_manager_t *this,
+                                                               message_t *message)
+ {
+       enumerator_t *enumerator;
+       task_t *task = NULL;
+       payload_t *payload;
+       notify_payload_t *notify;
+       delete_payload_t *delete;
+       if (this->passive_tasks->get_count(this->passive_tasks) == 0)
+       {       /* create tasks depending on request type, if not already some queued */
+               switch (message->get_exchange_type(message))
+               {
+                       case IKE_SA_INIT:
+                       {
+                               task = (task_t*)ike_vendor_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+ #ifdef ME
+                               task = (task_t*)ike_me_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+ #endif /* ME */
+                               task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_cert_post_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_config_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE,
+                                                                                                       NULL, NULL);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               task = (task_t*)ike_mobike_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               break;
+                       }
+                       case CREATE_CHILD_SA:
+                       {       /* FIXME: we should prevent this on mediation connections */
+                               bool notify_found = FALSE, ts_found = FALSE;
+                               enumerator = message->create_payload_enumerator(message);
+                               while (enumerator->enumerate(enumerator, &payload))
+                               {
+                                       switch (payload->get_type(payload))
+                                       {
+                                               case NOTIFY:
+                                               {       /* if we find a rekey notify, its CHILD_SA rekeying */
+                                                       notify = (notify_payload_t*)payload;
+                                                       if (notify->get_notify_type(notify) == REKEY_SA &&
+                                                               (notify->get_protocol_id(notify) == PROTO_AH ||
+                                                                notify->get_protocol_id(notify) == PROTO_ESP))
+                                                       {
+                                                               notify_found = TRUE;
+                                                       }
+                                                       break;
+                                               }
+                                               case TRAFFIC_SELECTOR_INITIATOR:
+                                               case TRAFFIC_SELECTOR_RESPONDER:
+                                               {       /* if we don't find a TS, its IKE rekeying */
+                                                       ts_found = TRUE;
+                                                       break;
+                                               }
+                                               default:
+                                                       break;
+                                       }
+                               }
+                               enumerator->destroy(enumerator);
+                               if (ts_found)
+                               {
+                                       if (notify_found)
+                                       {
+                                               task = (task_t*)child_rekey_create(this->ike_sa,
+                                                                                                                  PROTO_NONE, 0);
+                                       }
+                                       else
+                                       {
+                                               task = (task_t*)child_create_create(this->ike_sa, NULL,
+                                                                                                                       FALSE, NULL, NULL);
+                                       }
+                               }
+                               else
+                               {
+                                       task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
+                               }
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               break;
+                       }
+                       case INFORMATIONAL:
+                       {
+                               enumerator = message->create_payload_enumerator(message);
+                               while (enumerator->enumerate(enumerator, &payload))
+                               {
+                                       switch (payload->get_type(payload))
+                                       {
+                                               case NOTIFY:
+                                               {
+                                                       notify = (notify_payload_t*)payload;
+                                                       switch (notify->get_notify_type(notify))
+                                                       {
+                                                               case ADDITIONAL_IP4_ADDRESS:
+                                                               case ADDITIONAL_IP6_ADDRESS:
+                                                               case NO_ADDITIONAL_ADDRESSES:
+                                                               case UPDATE_SA_ADDRESSES:
+                                                               case NO_NATS_ALLOWED:
+                                                               case UNACCEPTABLE_ADDRESSES:
+                                                               case UNEXPECTED_NAT_DETECTED:
+                                                               case COOKIE2:
+                                                               case NAT_DETECTION_SOURCE_IP:
+                                                               case NAT_DETECTION_DESTINATION_IP:
+                                                                       task = (task_t*)ike_mobike_create(
+                                                                                                                       this->ike_sa, FALSE);
+                                                                       break;
+                                                               case AUTH_LIFETIME:
+                                                                       task = (task_t*)ike_auth_lifetime_create(
+                                                                                                                       this->ike_sa, FALSE);
+                                                                       break;
+                                                               default:
+                                                                       break;
+                                                       }
+                                                       break;
+                                               }
+                                               case DELETE:
+                                               {
+                                                       delete = (delete_payload_t*)payload;
+                                                       if (delete->get_protocol_id(delete) == PROTO_IKE)
+                                                       {
+                                                               task = (task_t*)ike_delete_create(this->ike_sa,
+                                                                                                                               FALSE);
+                                                       }
+                                                       else
+                                                       {
+                                                               task = (task_t*)child_delete_create(this->ike_sa,
+                                                                                                               PROTO_NONE, 0, FALSE);
+                                                       }
+                                                       break;
+                                               }
+                                               default:
+                                                       break;
+                                       }
+                                       if (task)
+                                       {
+                                               break;
+                                       }
+                               }
+                               enumerator->destroy(enumerator);
+                               if (task == NULL)
+                               {
+                                       task = (task_t*)ike_dpd_create(FALSE);
+                               }
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                               break;
+                       }
+ #ifdef ME
+                       case ME_CONNECT:
+                       {
+                               task = (task_t*)ike_me_create(this->ike_sa, FALSE);
+                               this->passive_tasks->insert_last(this->passive_tasks, task);
+                       }
+ #endif /* ME */
+                       default:
+                               break;
+               }
+       }
+       /* let the tasks process the message */
+       enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+       while (enumerator->enumerate(enumerator, (void*)&task))
+       {
+               switch (task->process(task, message))
+               {
+                       case SUCCESS:
+                               /* task completed, remove it */
+                               this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+                               task->destroy(task);
+                               break;
+                       case NEED_MORE:
+                               /* processed, but task needs at least another call to build() */
+                               break;
+                       case FAILED:
+                       default:
+                               charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                               /* FALL */
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               this->passive_tasks->remove_at(this->passive_tasks, enumerator);
+                               enumerator->destroy(enumerator);
+                               task->destroy(task);
+                               return DESTROY_ME;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return build_response(this, message);
+ }
+ METHOD(task_manager_t, incr_mid, void,
+       private_task_manager_t *this, bool initiate)
+ {
+       if (initiate)
+       {
+               this->initiating.mid++;
+       }
+       else
+       {
+               this->responding.mid++;
+       }
+ }
+ /**
+  * Send a notify back to the sender
+  */
+ static void send_notify_response(private_task_manager_t *this,
+                                                                message_t *request, notify_type_t type,
+                                                                chunk_t data)
+ {
+       message_t *response;
+       packet_t *packet;
+       host_t *me, *other;
+       response = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION);
+       response->set_exchange_type(response, request->get_exchange_type(request));
+       response->set_request(response, FALSE);
+       response->set_message_id(response, request->get_message_id(request));
+       response->add_notify(response, FALSE, type, data);
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       if (me->is_anyaddr(me))
+       {
+               me = request->get_destination(request);
+               this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
+       }
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       if (other->is_anyaddr(other))
+       {
+               other = request->get_source(request);
+               this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+       }
+       response->set_source(response, me->clone(me));
+       response->set_destination(response, other->clone(other));
+       if (this->ike_sa->generate_message(this->ike_sa, response,
+                                                                          &packet) == SUCCESS)
+       {
+               charon->sender->send(charon->sender, packet);
+       }
+       response->destroy(response);
+ }
+ /**
+  * Parse the given message and verify that it is valid.
+  */
+ static status_t parse_message(private_task_manager_t *this, message_t *msg)
+ {
+       status_t status;
+       u_int8_t type = 0;
+       status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
+       if (status == SUCCESS)
+       {       /* check for unsupported critical payloads */
+               enumerator_t *enumerator;
+               unknown_payload_t *unknown;
+               payload_t *payload;
+               enumerator = msg->create_payload_enumerator(msg);
+               while (enumerator->enumerate(enumerator, &payload))
+               {
+                       unknown = (unknown_payload_t*)payload;
+                       type = payload->get_type(payload);
+                       if (!payload_is_known(type) &&
+                               unknown->is_critical(unknown))
+                       {
+                               DBG1(DBG_ENC, "payload type %N is not supported, "
+                                        "but its critical!", payload_type_names, type);
+                               status = NOT_SUPPORTED;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+       if (status != SUCCESS)
+       {
+               bool is_request = msg->get_request(msg);
+               switch (status)
+               {
+                       case NOT_SUPPORTED:
+                               DBG1(DBG_IKE, "critical unknown payloads found");
+                               if (is_request)
+                               {
+                                       send_notify_response(this, msg,
+                                                                                UNSUPPORTED_CRITICAL_PAYLOAD,
+                                                                                chunk_from_thing(type));
+                                       incr_mid(this, FALSE);
+                               }
+                               break;
+                       case PARSE_ERROR:
+                               DBG1(DBG_IKE, "message parsing failed");
+                               if (is_request)
+                               {
+                                       send_notify_response(this, msg,
+                                                                                INVALID_SYNTAX, chunk_empty);
+                                       incr_mid(this, FALSE);
+                               }
+                               break;
+                       case VERIFY_ERROR:
+                               DBG1(DBG_IKE, "message verification failed");
+                               if (is_request)
+                               {
+                                       send_notify_response(this, msg,
+                                                                                INVALID_SYNTAX, chunk_empty);
+                                       incr_mid(this, FALSE);
+                               }
+                               break;
+                       case FAILED:
+                               DBG1(DBG_IKE, "integrity check failed");
+                               /* ignored */
+                               break;
+                       case INVALID_STATE:
+                               DBG1(DBG_IKE, "found encrypted message, but no keys available");
+                       default:
+                               break;
+               }
+               DBG1(DBG_IKE, "%N %s with message ID %d processing failed",
+                        exchange_type_names, msg->get_exchange_type(msg),
+                        is_request ? "request" : "response",
+                        msg->get_message_id(msg));
+               if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED)
+               {       /* invalid initiation attempt, close SA */
+                       return DESTROY_ME;
+               }
+       }
+       return status;
+ }
+ METHOD(task_manager_t, process_message, status_t,
+       private_task_manager_t *this, message_t *msg)
+ {
+       host_t *me, *other;
+       status_t status;
+       u_int32_t mid;
+       charon->bus->message(charon->bus, msg, TRUE, FALSE);
+       status = parse_message(this, msg);
+       if (status != SUCCESS)
+       {
+               return status;
+       }
+       me = msg->get_destination(msg);
+       other = msg->get_source(msg);
+       /* if this IKE_SA is virgin, we check for a config */
+       if (this->ike_sa->get_ike_cfg(this->ike_sa) == NULL)
+       {
+               ike_sa_id_t *ike_sa_id;
+               ike_cfg_t *ike_cfg;
+               job_t *job;
+               ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other);
+               if (ike_cfg == NULL)
+               {
+                       /* no config found for these hosts, destroy */
+                       DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N",
+                                me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
+                       send_notify_response(this, msg,
+                                                                NO_PROPOSAL_CHOSEN, chunk_empty);
+                       return DESTROY_ME;
+               }
+               this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg);
+               ike_cfg->destroy(ike_cfg);
+               /* add a timeout if peer does not establish it completely */
+               ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+               job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE);
+               lib->scheduler->schedule_job(lib->scheduler, job,
+                               lib->settings->get_int(lib->settings,
+                                       "charon.half_open_timeout",  HALF_OPEN_IKE_SA_TIMEOUT));
+       }
+       this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND,
+                                                               time_monotonic(NULL));
+       mid = msg->get_message_id(msg);
+       if (msg->get_request(msg))
+       {
+               if (mid == this->responding.mid)
+               {
+                       if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+                               this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+                               msg->get_exchange_type(msg) != IKE_SA_INIT)
+                       {       /* only do host updates based on verified messages */
+                               if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+                               {       /* with MOBIKE, we do no implicit updates */
+                                       this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
+                               }
+                       }
+                       charon->bus->message(charon->bus, msg, TRUE, TRUE);
+                       if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+                       {       /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+                               return SUCCESS;
+                       }
+                       if (process_request(this, msg) != SUCCESS)
+                       {
+                               flush(this);
+                               return DESTROY_ME;
+                       }
+                       this->responding.mid++;
+               }
+               else if ((mid == this->responding.mid - 1) && this->responding.packet)
+               {
+                       packet_t *clone;
+                       host_t *host;
+                       DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+                                "retransmitting response", mid);
+                       clone = this->responding.packet->clone(this->responding.packet);
+                       host = msg->get_destination(msg);
+                       clone->set_source(clone, host->clone(host));
+                       host = msg->get_source(msg);
+                       clone->set_destination(clone, host->clone(host));
+                       charon->sender->send(charon->sender, clone);
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+                                mid, this->responding.mid);
+               }
+       }
+       else
+       {
+               if (mid == this->initiating.mid)
+               {
+                       if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
+                               this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
+                               msg->get_exchange_type(msg) != IKE_SA_INIT)
+                       {       /* only do host updates based on verified messages */
+                               if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+                               {       /* with MOBIKE, we do no implicit updates */
+                                       this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE);
+                               }
+                       }
+                       charon->bus->message(charon->bus, msg, TRUE, TRUE);
+                       if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
+                       {       /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
+                               return SUCCESS;
+                       }
+                       if (process_response(this, msg) != SUCCESS)
+                       {
+                               flush(this);
+                               return DESTROY_ME;
+                       }
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
+                                mid, this->initiating.mid);
+                       return SUCCESS;
+               }
+       }
+       return SUCCESS;
+ }
+ METHOD(task_manager_t, queue_task, void,
+       private_task_manager_t *this, task_t *task)
+ {
+       if (task->get_type(task) == TASK_IKE_MOBIKE)
+       {       /*  there is no need to queue more than one mobike task */
+               enumerator_t *enumerator;
+               task_t *current;
+               enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&current))
+               {
+                       if (current->get_type(current) == TASK_IKE_MOBIKE)
+                       {
+                               enumerator->destroy(enumerator);
+                               task->destroy(task);
+                               return;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+       DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+       this->queued_tasks->insert_last(this->queued_tasks, task);
+ }
+ /**
+  * Check if a given task has been queued already
+  */
+ static bool has_queued(private_task_manager_t *this, task_type_t type)
+ {
+       enumerator_t *enumerator;
+       bool found = FALSE;
+       task_t *task;
+       enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (task->get_type(task) == type)
+               {
+                       found = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return found;
+ }
+ METHOD(task_manager_t, queue_ike, void,
+       private_task_manager_t *this)
+ {
+       if (!has_queued(this, TASK_IKE_VENDOR))
+       {
+               queue_task(this, (task_t*)ike_vendor_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_INIT))
+       {
+               queue_task(this, (task_t*)ike_init_create(this->ike_sa, TRUE, NULL));
+       }
+       if (!has_queued(this, TASK_IKE_NATD))
+       {
+               queue_task(this, (task_t*)ike_natd_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_CERT_PRE))
+       {
+               queue_task(this, (task_t*)ike_cert_pre_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_AUTH))
+       {
+               queue_task(this, (task_t*)ike_auth_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_CERT_POST))
+       {
+               queue_task(this, (task_t*)ike_cert_post_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_CONFIG))
+       {
+               queue_task(this, (task_t*)ike_config_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_AUTH_LIFETIME))
+       {
+               queue_task(this, (task_t*)ike_auth_lifetime_create(this->ike_sa, TRUE));
+       }
+       if (!has_queued(this, TASK_IKE_MOBIKE))
+       {
+               peer_cfg_t *peer_cfg;
+               peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+               if (peer_cfg->use_mobike(peer_cfg))
+               {
+                       queue_task(this, (task_t*)ike_mobike_create(this->ike_sa, TRUE));
+               }
+       }
+ #ifdef ME
+       if (!has_queued(this, TASK_IKE_ME))
+       {
+               queue_task(this, (task_t*)ike_me_create(this->ike_sa, TRUE));
+       }
+ #endif /* ME */
+ }
+ METHOD(task_manager_t, queue_ike_rekey, void,
+       private_task_manager_t *this)
+ {
+       queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE));
+ }
+ METHOD(task_manager_t, queue_ike_reauth, void,
+       private_task_manager_t *this)
+ {
+       queue_task(this, (task_t*)ike_reauth_create(this->ike_sa));
+ }
+ METHOD(task_manager_t, queue_ike_delete, void,
+       private_task_manager_t *this)
+ {
+       queue_task(this, (task_t*)ike_delete_create(this->ike_sa, TRUE));
+ }
+ METHOD(task_manager_t, queue_mobike, void,
+       private_task_manager_t *this, bool roam, bool address)
+ {
+       ike_mobike_t *mobike;
+       mobike = ike_mobike_create(this->ike_sa, TRUE);
+       if (roam)
+       {
+               mobike->roam(mobike, address);
+       }
+       else
+       {
+               mobike->addresses(mobike);
+       }
+       queue_task(this, &mobike->task);
+ }
+ METHOD(task_manager_t, queue_child, void,
+       private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid,
+       traffic_selector_t *tsi, traffic_selector_t *tsr)
+ {
+       child_create_t *task;
+       task = child_create_create(this->ike_sa, cfg, FALSE, tsi, tsr);
+       if (reqid)
+       {
+               task->use_reqid(task, reqid);
+       }
+       queue_task(this, &task->task);
+ }
+ METHOD(task_manager_t, queue_child_rekey, void,
+       private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi)
+ {
+       queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi));
+ }
+ METHOD(task_manager_t, queue_child_delete, void,
+       private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+       bool expired)
+ {
+       queue_task(this, (task_t*)child_delete_create(this->ike_sa,
+                                                                                                 protocol, spi, expired));
+ }
+ METHOD(task_manager_t, queue_dpd, void,
+       private_task_manager_t *this)
+ {
+       ike_mobike_t *mobike;
+       if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE) &&
+               this->ike_sa->has_condition(this->ike_sa, COND_NAT_HERE))
+       {
+               /* use mobike enabled DPD to detect NAT mapping changes */
+               mobike = ike_mobike_create(this->ike_sa, TRUE);
+               mobike->dpd(mobike);
+               queue_task(this, &mobike->task);
+       }
+       else
+       {
+               queue_task(this, (task_t*)ike_dpd_create(TRUE));
+       }
+ }
+ METHOD(task_manager_t, adopt_tasks, void,
+       private_task_manager_t *this, task_manager_t *other_public)
+ {
+       private_task_manager_t *other = (private_task_manager_t*)other_public;
+       task_t *task;
+       /* move queued tasks from other to this */
+       while (other->queued_tasks->remove_last(other->queued_tasks,
+                                                                                               (void**)&task) == SUCCESS)
+       {
+               DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
+               task->migrate(task, this->ike_sa);
+               this->queued_tasks->insert_first(this->queued_tasks, task);
+       }
+ }
+ METHOD(task_manager_t, busy, bool,
+       private_task_manager_t *this)
+ {
+       return (this->active_tasks->get_count(this->active_tasks) > 0);
+ }
+ METHOD(task_manager_t, reset, void,
+       private_task_manager_t *this, u_int32_t initiate, u_int32_t respond)
+ {
+       enumerator_t *enumerator;
+       task_t *task;
+       /* reset message counters and retransmit packets */
+       DESTROY_IF(this->responding.packet);
+       DESTROY_IF(this->initiating.packet);
+       this->responding.packet = NULL;
+       this->initiating.packet = NULL;
+       if (initiate != UINT_MAX)
+       {
+               this->initiating.mid = initiate;
+       }
+       if (respond != UINT_MAX)
+       {
+               this->responding.mid = respond;
+       }
+       this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+       /* reset queued tasks */
+       enumerator = this->queued_tasks->create_enumerator(this->queued_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               task->migrate(task, this->ike_sa);
+       }
+       enumerator->destroy(enumerator);
+       /* reset active tasks */
+       while (this->active_tasks->remove_last(this->active_tasks,
+                                                                                  (void**)&task) == SUCCESS)
+       {
+               task->migrate(task, this->ike_sa);
+               this->queued_tasks->insert_first(this->queued_tasks, task);
+       }
+       this->reset = TRUE;
+ }
+ METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
+       private_task_manager_t *this, task_queue_t queue)
+ {
+       switch (queue)
+       {
+               case TASK_QUEUE_ACTIVE:
+                       return this->active_tasks->create_enumerator(this->active_tasks);
+               case TASK_QUEUE_PASSIVE:
+                       return this->passive_tasks->create_enumerator(this->passive_tasks);
+               case TASK_QUEUE_QUEUED:
+                       return this->queued_tasks->create_enumerator(this->queued_tasks);
+               default:
+                       return enumerator_create_empty();
+       }
+ }
+ METHOD(task_manager_t, destroy, void,
+       private_task_manager_t *this)
+ {
+       flush(this);
+       this->active_tasks->destroy(this->active_tasks);
+       this->queued_tasks->destroy(this->queued_tasks);
+       this->passive_tasks->destroy(this->passive_tasks);
+       DESTROY_IF(this->responding.packet);
+       DESTROY_IF(this->initiating.packet);
+       free(this);
+ }
+ /*
+  * see header file
+  */
+ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
+ {
+       private_task_manager_t *this;
+       INIT(this,
+               .public = {
+                       .task_manager = {
+                               .process_message = _process_message,
+                               .queue_task = _queue_task,
+                               .queue_ike = _queue_ike,
+                               .queue_ike_rekey = _queue_ike_rekey,
+                               .queue_ike_reauth = _queue_ike_reauth,
+                               .queue_ike_delete = _queue_ike_delete,
+                               .queue_mobike = _queue_mobike,
+                               .queue_child = _queue_child,
+                               .queue_child_rekey = _queue_child_rekey,
+                               .queue_child_delete = _queue_child_delete,
+                               .queue_dpd = _queue_dpd,
+                               .initiate = _initiate,
+                               .retransmit = _retransmit,
+                               .incr_mid = _incr_mid,
+                               .reset = _reset,
+                               .adopt_tasks = _adopt_tasks,
+                               .busy = _busy,
+                               .create_task_enumerator = _create_task_enumerator,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .initiating.type = EXCHANGE_TYPE_UNDEFINED,
+               .queued_tasks = linked_list_create(),
+               .active_tasks = linked_list_create(),
+               .passive_tasks = linked_list_create(),
+               .retransmit_tries = lib->settings->get_int(lib->settings,
+                                                               "charon.retransmit_tries", RETRANSMIT_TRIES),
+               .retransmit_timeout = lib->settings->get_double(lib->settings,
+                                                               "charon.retransmit_timeout", RETRANSMIT_TIMEOUT),
+               .retransmit_base = lib->settings->get_double(lib->settings,
+                                                               "charon.retransmit_base", RETRANSMIT_BASE),
+       );
+       return &this->public;
+ }
index 0000000,0de2efd..7583710
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,528 +1,528 @@@
 -       * wheter this is the final authentication round
+ /*
+  * Copyright (C) 2008 Tobias Brunner
+  * Copyright (C) 2006-2009 Martin Willi
+  * Hochschule fuer Technik Rapperswil
+  *
+  * 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.
+  */
+ #include "ike_cert_pre.h"
+ #include <daemon.h>
+ #include <sa/ike_sa.h>
+ #include <encoding/payloads/cert_payload.h>
+ #include <encoding/payloads/certreq_payload.h>
+ #include <credentials/certificates/x509.h>
+ typedef struct private_ike_cert_pre_t private_ike_cert_pre_t;
+ /**
+  * Private members of a ike_cert_pre_t task.
+  */
+ struct private_ike_cert_pre_t {
+       /**
+        * Public methods and task_t interface.
+        */
+       ike_cert_pre_t public;
+       /**
+        * Assigned IKE_SA.
+        */
+       ike_sa_t *ike_sa;
+       /**
+        * Are we the initiator?
+        */
+       bool initiator;
+       /**
+        * Do we accept HTTP certificate lookup requests
+        */
+       bool do_http_lookup;
+       /**
++       * whether this is the final authentication round
+        */
+       bool final;
+ };
+ /**
+  * read certificate requests
+  */
+ static void process_certreqs(private_ike_cert_pre_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       payload_t *payload;
+       auth_cfg_t *auth;
+       auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               switch (payload->get_type(payload))
+               {
+                       case CERTIFICATE_REQUEST:
+                       {
+                               certreq_payload_t *certreq = (certreq_payload_t*)payload;
+                               enumerator_t *enumerator;
+                               u_int unknown = 0;
+                               chunk_t keyid;
+                               this->ike_sa->set_condition(this->ike_sa, COND_CERTREQ_SEEN, TRUE);
+                               if (certreq->get_cert_type(certreq) != CERT_X509)
+                               {
+                                       DBG1(DBG_IKE, "cert payload %N not supported - ignored",
+                                                certificate_type_names, certreq->get_cert_type(certreq));
+                                       break;
+                               }
+                               enumerator = certreq->create_keyid_enumerator(certreq);
+                               while (enumerator->enumerate(enumerator, &keyid))
+                               {
+                                       identification_t *id;
+                                       certificate_t *cert;
+                                       id = identification_create_from_encoding(ID_KEY_ID, keyid);
+                                       cert = lib->credmgr->get_cert(lib->credmgr,
+                                                                                                 CERT_X509, KEY_ANY, id, TRUE);
+                                       if (cert)
+                                       {
+                                               DBG1(DBG_IKE, "received cert request for \"%Y\"",
+                                                        cert->get_subject(cert));
+                                               auth->add(auth, AUTH_RULE_CA_CERT, cert);
+                                       }
+                                       else
+                                       {
+                                               DBG2(DBG_IKE, "received cert request for unknown ca "
+                                                                         "with keyid %Y", id);
+                                               unknown++;
+                                       }
+                                       id->destroy(id);
+                               }
+                               enumerator->destroy(enumerator);
+                               if (unknown)
+                               {
+                                       DBG1(DBG_IKE, "received %u cert requests for an unknown ca",
+                                                unknown);
+                               }
+                               break;
+                       }
+                       case NOTIFY:
+                       {
+                               notify_payload_t *notify = (notify_payload_t*)payload;
+                               /* we only handle one type of notify here */
+                               if (notify->get_notify_type(notify) == HTTP_CERT_LOOKUP_SUPPORTED)
+                               {
+                                       this->ike_sa->enable_extension(this->ike_sa, EXT_HASH_AND_URL);
+                               }
+                               break;
+                       }
+                       default:
+                               /* ignore other payloads here, these are handled elsewhere */
+                               break;
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ /**
+  * tries to extract a certificate from the cert payload or the credential
+  * manager (based on the hash of a "Hash and URL" encoded cert).
+  * Note: the returned certificate (if any) has to be destroyed
+  */
+ static certificate_t *try_get_cert(cert_payload_t *cert_payload)
+ {
+       certificate_t *cert = NULL;
+       switch (cert_payload->get_cert_encoding(cert_payload))
+       {
+               case ENC_X509_SIGNATURE:
+               {
+                       cert = cert_payload->get_cert(cert_payload);
+                       break;
+               }
+               case ENC_X509_HASH_AND_URL:
+               {
+                       identification_t *id;
+                       chunk_t hash = cert_payload->get_hash(cert_payload);
+                       if (!hash.ptr)
+                       {
+                               /* invalid "Hash and URL" data (logged elsewhere) */
+                               break;
+                       }
+                       id = identification_create_from_encoding(ID_KEY_ID, hash);
+                       cert = lib->credmgr->get_cert(lib->credmgr,
+                                                                                 CERT_X509, KEY_ANY, id, FALSE);
+                       id->destroy(id);
+                       break;
+               }
+               default:
+               {
+                       break;
+               }
+       }
+       return cert;
+ }
+ /**
+  * import certificates
+  */
+ static void process_certs(private_ike_cert_pre_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       payload_t *payload;
+       auth_cfg_t *auth;
+       bool first = TRUE;
+       auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               if (payload->get_type(payload) == CERTIFICATE)
+               {
+                       cert_payload_t *cert_payload;
+                       cert_encoding_t encoding;
+                       certificate_t *cert;
+                       char *url;
+                       cert_payload = (cert_payload_t*)payload;
+                       encoding = cert_payload->get_cert_encoding(cert_payload);
+                       switch (encoding)
+                       {
+                               case ENC_X509_HASH_AND_URL:
+                               {
+                                       if (!this->do_http_lookup)
+                                       {
+                                               DBG1(DBG_IKE, "received hash-and-url encoded cert, but"
+                                                               " we don't accept them, ignore");
+                                               break;
+                                       }
+                                       /* FALL */
+                               }
+                               case ENC_X509_SIGNATURE:
+                               {
+                                       cert = try_get_cert(cert_payload);
+                                       if (cert)
+                                       {
+                                               if (first)
+                                               {       /* the first is an end entity certificate */
+                                                       DBG1(DBG_IKE, "received end entity cert \"%Y\"",
+                                                                cert->get_subject(cert));
+                                                       auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert);
+                                                       first = FALSE;
+                                               }
+                                               else
+                                               {
+                                                       DBG1(DBG_IKE, "received issuer cert \"%Y\"",
+                                                                cert->get_subject(cert));
+                                                       auth->add(auth, AUTH_HELPER_IM_CERT, cert);
+                                               }
+                                       }
+                                       else if (encoding == ENC_X509_HASH_AND_URL)
+                                       {
+                                               /* we fetch the certificate not yet, but only if
+                                                * it is really needed during authentication */
+                                               url = cert_payload->get_url(cert_payload);
+                                               if (!url)
+                                               {
+                                                       DBG1(DBG_IKE, "received invalid hash-and-url "
+                                                                "encoded cert, ignore");
+                                                       break;
+                                               }
+                                               url = strdup(url);
+                                               if (first)
+                                               {       /* first URL is for an end entity certificate */
+                                                       DBG1(DBG_IKE, "received hash-and-url for end"
+                                                                " entity cert \"%s\"", url);
+                                                       auth->add(auth, AUTH_HELPER_SUBJECT_HASH_URL, url);
+                                                       first = FALSE;
+                                               }
+                                               else
+                                               {
+                                                       DBG1(DBG_IKE, "received hash-and-url for issuer"
+                                                                       " cert \"%s\"", url);
+                                                       auth->add(auth, AUTH_HELPER_IM_HASH_URL, url);
+                                               }
+                                       }
+                                       break;
+                               }
+                               case ENC_CRL:
+                                       cert = cert_payload->get_cert(cert_payload);
+                                       if (cert)
+                                       {
+                                               DBG1(DBG_IKE, "received CRL \"%Y\"",
+                                                        cert->get_subject(cert));
+                                               auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert);
+                                       }
+                                       break;
+                               case ENC_PKCS7_WRAPPED_X509:
+                               case ENC_PGP:
+                               case ENC_DNS_SIGNED_KEY:
+                               case ENC_KERBEROS_TOKEN:
+                               case ENC_ARL:
+                               case ENC_SPKI:
+                               case ENC_X509_ATTRIBUTE:
+                               case ENC_RAW_RSA_KEY:
+                               case ENC_X509_HASH_AND_URL_BUNDLE:
+                               case ENC_OCSP_CONTENT:
+                               default:
+                                       DBG1(DBG_ENC, "certificate encoding %N not supported",
+                                                cert_encoding_names, encoding);
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ /**
+  * add the keyid of a certificate to the certificate request payload
+  */
+ static void add_certreq(certreq_payload_t **req, certificate_t *cert)
+ {
+       switch (cert->get_type(cert))
+       {
+               case CERT_X509:
+               {
+                       public_key_t *public;
+                       chunk_t keyid;
+                       x509_t *x509 = (x509_t*)cert;
+                       if (!(x509->get_flags(x509) & X509_CA))
+                       {       /* no CA cert, skip */
+                               break;
+                       }
+                       public = cert->get_public_key(cert);
+                       if (!public)
+                       {
+                               break;
+                       }
+                       if (*req == NULL)
+                       {
+                               *req = certreq_payload_create_type(CERT_X509);
+                       }
+                       if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &keyid))
+                       {
+                               (*req)->add_keyid(*req, keyid);
+                               DBG1(DBG_IKE, "sending cert request for \"%Y\"",
+                                        cert->get_subject(cert));
+                       }
+                       public->destroy(public);
+                       break;
+               }
+               default:
+                       break;
+       }
+ }
+ /**
+  * add a auth_cfg's CA certificates to the certificate request
+  */
+ static void add_certreqs(certreq_payload_t **req, auth_cfg_t *auth)
+ {
+       enumerator_t *enumerator;
+       auth_rule_t type;
+       void *value;
+       enumerator = auth->create_enumerator(auth);
+       while (enumerator->enumerate(enumerator, &type, &value))
+       {
+               switch (type)
+               {
+                       case AUTH_RULE_CA_CERT:
+                               add_certreq(req, (certificate_t*)value);
+                               break;
+                       default:
+                               break;
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ /**
+  * build certificate requests
+  */
+ static void build_certreqs(private_ike_cert_pre_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       ike_cfg_t *ike_cfg;
+       peer_cfg_t *peer_cfg;
+       certificate_t *cert;
+       auth_cfg_t *auth;
+       certreq_payload_t *req = NULL;
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+       if (!ike_cfg->send_certreq(ike_cfg))
+       {
+               return;
+       }
+       /* check if we require a specific CA for that peer */
+       peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+       if (peer_cfg)
+       {
+               enumerator = peer_cfg->create_auth_cfg_enumerator(peer_cfg, FALSE);
+               while (enumerator->enumerate(enumerator, &auth))
+               {
+                       add_certreqs(&req, auth);
+               }
+               enumerator->destroy(enumerator);
+       }
+       if (!req)
+       {
+               /* otherwise add all trusted CA certificates */
+               enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+                                                                                               CERT_ANY, KEY_ANY, NULL, TRUE);
+               while (enumerator->enumerate(enumerator, &cert))
+               {
+                       add_certreq(&req, cert);
+               }
+               enumerator->destroy(enumerator);
+       }
+       if (req)
+       {
+               message->add_payload(message, (payload_t*)req);
+               if (lib->settings->get_bool(lib->settings, "charon.hash_and_url", FALSE))
+               {
+                       message->add_notify(message, FALSE, HTTP_CERT_LOOKUP_SUPPORTED,
+                                                               chunk_empty);
+                       this->do_http_lookup = TRUE;
+               }
+       }
+ }
+ /**
+  * Check if this is the final authentication round
+  */
+ static bool final_auth(message_t *message)
+ {
+       /* we check for an AUTH payload without a ANOTHER_AUTH_FOLLOWS notify */
+       if (message->get_payload(message, AUTHENTICATION) == NULL)
+       {
+               return FALSE;
+       }
+       if (message->get_notify(message, ANOTHER_AUTH_FOLLOWS))
+       {
+               return FALSE;
+       }
+       return TRUE;
+ }
+ METHOD(task_t, build_i, status_t,
+       private_ike_cert_pre_t *this, message_t *message)
+ {
+       if (message->get_message_id(message) == 1)
+       {       /* initiator sends CERTREQs in first IKE_AUTH */
+               build_certreqs(this, message);
+       }
+       return NEED_MORE;
+ }
+ METHOD(task_t, process_r, status_t,
+       private_ike_cert_pre_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) != IKE_SA_INIT)
+       {       /* handle certreqs/certs in any IKE_AUTH, just in case */
+               process_certreqs(this, message);
+               process_certs(this, message);
+       }
+       this->final = final_auth(message);
+       return NEED_MORE;
+ }
+ METHOD(task_t, build_r, status_t,
+       private_ike_cert_pre_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) == IKE_SA_INIT)
+       {
+               build_certreqs(this, message);
+       }
+       if (this->final)
+       {
+               return SUCCESS;
+       }
+       return NEED_MORE;
+ }
+ METHOD(task_t, process_i, status_t,
+       private_ike_cert_pre_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) == IKE_SA_INIT)
+       {
+               process_certreqs(this, message);
+       }
+       process_certs(this, message);
+       if (final_auth(message))
+       {
+               return SUCCESS;
+       }
+       return NEED_MORE;
+ }
+ METHOD(task_t, get_type, task_type_t,
+       private_ike_cert_pre_t *this)
+ {
+       return TASK_IKE_CERT_PRE;
+ }
+ METHOD(task_t, migrate, void,
+       private_ike_cert_pre_t *this, ike_sa_t *ike_sa)
+ {
+       this->ike_sa = ike_sa;
+ }
+ METHOD(task_t, destroy, void,
+       private_ike_cert_pre_t *this)
+ {
+       free(this);
+ }
+ /*
+  * Described in header.
+  */
+ ike_cert_pre_t *ike_cert_pre_create(ike_sa_t *ike_sa, bool initiator)
+ {
+       private_ike_cert_pre_t *this;
+       INIT(this,
+               .public = {
+                       .task = {
+                               .get_type = _get_type,
+                               .migrate = _migrate,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+               .initiator = initiator,
+       );
+       if (initiator)
+       {
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
+       }
+       else
+       {
+               this->public.task.build = _build_r;
+               this->public.task.process = _process_r;
+       }
+       return &this->public;
+ }
index 0000000,c533506..3777140
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,651 +1,648 @@@
 -                                      this->ike_sa->remove_additional_addresses(this->ike_sa);
+ /*
++ * Copyright (C) 2010-2012 Tobias Brunner
+  * Copyright (C) 2007 Martin Willi
+  * Hochschule fuer Technik Rapperswil
+  *
+  * 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.
+  */
+ #include "ike_mobike.h"
+ #include <string.h>
+ #include <hydra.h>
+ #include <daemon.h>
+ #include <sa/ikev2/tasks/ike_natd.h>
+ #include <encoding/payloads/notify_payload.h>
+ #define COOKIE2_SIZE 16
+ #define MAX_ADDITIONAL_ADDRS 8
+ typedef struct private_ike_mobike_t private_ike_mobike_t;
+ /**
+  * Private members of a ike_mobike_t task.
+  */
+ struct private_ike_mobike_t {
+       /**
+        * Public methods and task_t interface.
+        */
+       ike_mobike_t public;
+       /**
+        * Assigned IKE_SA.
+        */
+       ike_sa_t *ike_sa;
+       /**
+        * Are we the initiator?
+        */
+       bool initiator;
+       /**
+        * cookie2 value to verify new addresses
+        */
+       chunk_t cookie2;
+       /**
+        * NAT discovery reusing the TASK_IKE_NATD task
+        */
+       ike_natd_t *natd;
+       /**
+        * use task to update addresses
+        */
+       bool update;
+       /**
+        * do routability check
+        */
+       bool check;
+       /**
+        * include address list update
+        */
+       bool address;
+       /**
+        * additional addresses got updated
+        */
+       bool addresses_updated;
+ };
+ /**
+  * read notifys from message and evaluate them
+  */
+ static void process_payloads(private_ike_mobike_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       payload_t *payload;
+       bool first = TRUE;
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               int family = AF_INET;
+               notify_payload_t *notify;
+               chunk_t data;
+               host_t *host;
+               if (payload->get_type(payload) != NOTIFY)
+               {
+                       continue;
+               }
+               notify = (notify_payload_t*)payload;
+               switch (notify->get_notify_type(notify))
+               {
+                       case MOBIKE_SUPPORTED:
+                       {
+                               peer_cfg_t *peer_cfg;
+                               peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+                               if (!this->initiator &&
+                                       peer_cfg && !peer_cfg->use_mobike(peer_cfg))
+                               {
+                                       DBG1(DBG_IKE, "peer supports MOBIKE, but disabled in config");
+                               }
+                               else
+                               {
+                                       DBG1(DBG_IKE, "peer supports MOBIKE");
+                                       this->ike_sa->enable_extension(this->ike_sa, EXT_MOBIKE);
+                               }
+                               break;
+                       }
+                       case COOKIE2:
+                       {
+                               chunk_free(&this->cookie2);
+                               this->cookie2 = chunk_clone(notify->get_notification_data(notify));
+                               break;
+                       }
+                       case ADDITIONAL_IP6_ADDRESS:
+                       {
+                               family = AF_INET6;
+                               /* fall through */
+                       }
+                       case ADDITIONAL_IP4_ADDRESS:
+                       {
+                               if (first)
+                               {       /* an ADDITIONAL_*_ADDRESS means replace, so flush once */
 -                              this->ike_sa->add_additional_address(this->ike_sa, host);
++                                      this->ike_sa->clear_peer_addresses(this->ike_sa);
+                                       first = FALSE;
++                                      /* add the peer's current address to the list */
++                                      host = this->ike_sa->get_other_host(this->ike_sa);
++                                      this->ike_sa->add_peer_address(this->ike_sa,
++                                                                                                 host->clone(host));
+                               }
+                               data = notify->get_notification_data(notify);
+                               host = host_create_from_chunk(family, data, 0);
+                               DBG2(DBG_IKE, "got additional MOBIKE peer address: %H", host);
 -                              this->ike_sa->remove_additional_addresses(this->ike_sa);
++                              this->ike_sa->add_peer_address(this->ike_sa, host);
+                               this->addresses_updated = TRUE;
+                               break;
+                       }
+                       case UPDATE_SA_ADDRESSES:
+                       {
+                               this->update = TRUE;
+                               break;
+                       }
+                       case NO_ADDITIONAL_ADDRESSES:
+                       {
 -      me = hydra->kernel_interface->get_source_addr(
 -                                                                      hydra->kernel_interface, other_old, NULL);
 -      if (me)
 -      {
 -              apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg));
 -              DBG1(DBG_IKE, "checking original path %#H - %#H", me, other_old);
 -              copy = packet->clone(packet);
 -              copy->set_source(copy, me);
 -              charon->sender->send(charon->sender, copy);
 -      }
 -
 -      enumerator = this->ike_sa->create_additional_address_enumerator(this->ike_sa);
++                              this->ike_sa->clear_peer_addresses(this->ike_sa);
++                              /* add the peer's current address to the list */
++                              host = this->ike_sa->get_other_host(this->ike_sa);
++                              this->ike_sa->add_peer_address(this->ike_sa, host->clone(host));
+                               this->addresses_updated = TRUE;
+                               break;
+                       }
+                       case NAT_DETECTION_SOURCE_IP:
+                       case NAT_DETECTION_DESTINATION_IP:
+                       {
+                               /* NAT check in this MOBIKE exchange, create subtask for it */
+                               if (this->natd == NULL)
+                               {
+                                       this->natd = ike_natd_create(this->ike_sa, this->initiator);
+                               }
+                               break;
+                       }
+                       default:
+                               break;
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ /**
+  * Add ADDITIONAL_*_ADDRESS notifys depending on our address list
+  */
+ static void build_address_list(private_ike_mobike_t *this, message_t *message)
+ {
+       enumerator_t *enumerator;
+       host_t *host, *me;
+       notify_type_t type;
+       int added = 0;
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       enumerator = hydra->kernel_interface->create_address_enumerator(
+                                                                               hydra->kernel_interface, FALSE, FALSE);
+       while (enumerator->enumerate(enumerator, (void**)&host))
+       {
+               if (me->ip_equals(me, host))
+               {       /* "ADDITIONAL" means do not include IKE_SAs host */
+                       continue;
+               }
+               switch (host->get_family(host))
+               {
+                       case AF_INET:
+                               type = ADDITIONAL_IP4_ADDRESS;
+                               break;
+                       case AF_INET6:
+                               type = ADDITIONAL_IP6_ADDRESS;
+                               break;
+                       default:
+                               continue;
+               }
+               message->add_notify(message, FALSE, type, host->get_address(host));
+               if (++added >= MAX_ADDITIONAL_ADDRS)
+               {       /* limit number of notifys, some implementations do not like too
+                        * many of them (f.e. strongSwan ;-) */
+                       break;
+               }
+       }
+       if (!added)
+       {
+               message->add_notify(message, FALSE, NO_ADDITIONAL_ADDRESSES, chunk_empty);
+       }
+       enumerator->destroy(enumerator);
+ }
+ /**
+  * build a cookie and add it to the message
+  */
+ static void build_cookie(private_ike_mobike_t *this, message_t *message)
+ {
+       rng_t *rng;
+       chunk_free(&this->cookie2);
+       rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
+       if (rng)
+       {
+               rng->allocate_bytes(rng, COOKIE2_SIZE, &this->cookie2);
+               rng->destroy(rng);
+               message->add_notify(message, FALSE, COOKIE2, this->cookie2);
+       }
+ }
+ /**
+  * update addresses of associated CHILD_SAs
+  */
+ static void update_children(private_ike_mobike_t *this)
+ {
+       enumerator_t *enumerator;
+       child_sa_t *child_sa;
+       enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+       while (enumerator->enumerate(enumerator, (void**)&child_sa))
+       {
+               if (child_sa->update(child_sa,
+                               this->ike_sa->get_my_host(this->ike_sa),
+                               this->ike_sa->get_other_host(this->ike_sa),
+                               this->ike_sa->get_virtual_ip(this->ike_sa, TRUE),
+                               this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED)
+               {
+                       this->ike_sa->rekey_child_sa(this->ike_sa,
+                                       child_sa->get_protocol(child_sa),
+                                       child_sa->get_spi(child_sa, TRUE));
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ /**
+  * Apply the port of the old host, if its ip equals the new, use port otherwise.
+  */
+ static void apply_port(host_t *host, host_t *old, u_int16_t port)
+ {
+       if (host->ip_equals(host, old))
+       {
+               port = old->get_port(old);
+       }
+       else if (port == IKEV2_UDP_PORT)
+       {
+               port = IKEV2_NATT_PORT;
+       }
+       host->set_port(host, port);
+ }
+ METHOD(ike_mobike_t, transmit, void,
+          private_ike_mobike_t *this, packet_t *packet)
+ {
+       host_t *me, *other, *me_old, *other_old;
+       enumerator_t *enumerator;
+       ike_cfg_t *ike_cfg;
+       packet_t *copy;
+       if (!this->check)
+       {
+               return;
+       }
+       me_old = this->ike_sa->get_my_host(this->ike_sa);
+       other_old = this->ike_sa->get_other_host(this->ike_sa);
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
++      enumerator = this->ike_sa->create_peer_address_enumerator(this->ike_sa);
+       while (enumerator->enumerate(enumerator, (void**)&other))
+       {
+               me = hydra->kernel_interface->get_source_addr(
+                                                                               hydra->kernel_interface, other, NULL);
+               if (me)
+               {
+                       if (me->get_family(me) != other->get_family(other))
+                       {
+                               me->destroy(me);
+                               continue;
+                       }
+                       /* reuse port for an active address, 4500 otherwise */
+                       apply_port(me, me_old, ike_cfg->get_my_port(ike_cfg));
+                       other = other->clone(other);
+                       apply_port(other, other_old, ike_cfg->get_other_port(ike_cfg));
+                       DBG1(DBG_IKE, "checking path %#H - %#H", me, other);
+                       copy = packet->clone(packet);
+                       copy->set_source(copy, me);
+                       copy->set_destination(copy, other);
+                       charon->sender->send(charon->sender, copy);
+               }
+       }
+       enumerator->destroy(enumerator);
+ }
+ METHOD(task_t, build_i, status_t,
+          private_ike_mobike_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) == IKE_AUTH &&
+               message->get_message_id(message) == 1)
+       {       /* only in first IKE_AUTH */
+               message->add_notify(message, FALSE, MOBIKE_SUPPORTED, chunk_empty);
+               build_address_list(this, message);
+       }
+       else if (message->get_exchange_type(message) == INFORMATIONAL)
+       {
+               host_t *old, *new;
+               /* we check if the existing address is still valid */
+               old = message->get_source(message);
+               new = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
+                                                                               message->get_destination(message), old);
+               if (new)
+               {
+                       if (!new->ip_equals(new, old))
+                       {
+                               new->set_port(new, old->get_port(old));
+                               message->set_source(message, new);
+                       }
+                       else
+                       {
+                               new->destroy(new);
+                       }
+               }
+               if (this->update)
+               {
+                       message->add_notify(message, FALSE, UPDATE_SA_ADDRESSES,
+                                                               chunk_empty);
+                       build_cookie(this, message);
+                       update_children(this);
+               }
+               if (this->address && !this->check)
+               {
+                       build_address_list(this, message);
+               }
+               if (this->natd)
+               {
+                       this->natd->task.build(&this->natd->task, message);
+               }
+       }
+       return NEED_MORE;
+ }
+ METHOD(task_t, process_r, status_t,
+          private_ike_mobike_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) == IKE_AUTH &&
+               message->get_message_id(message) == 1)
+       {       /* only first IKE_AUTH */
+               process_payloads(this, message);
+       }
+       else if (message->get_exchange_type(message) == INFORMATIONAL)
+       {
+               process_payloads(this, message);
+               if (this->update)
+               {
+                       host_t *me, *other;
+                       me = message->get_destination(message);
+                       other = message->get_source(message);
+                       this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
+                       this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+               }
+               if (this->natd)
+               {
+                       this->natd->task.process(&this->natd->task, message);
+               }
+               if (this->addresses_updated && this->ike_sa->has_condition(this->ike_sa,
+                                                                                               COND_ORIGINAL_INITIATOR))
+               {
+                       host_t *other = message->get_source(message);
+                       host_t *other_old = this->ike_sa->get_other_host(this->ike_sa);
+                       if (!other->equals(other, other_old))
+                       {
+                               DBG1(DBG_IKE, "remote address changed from %H to %H", other_old,
+                                        other);
+                               this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+                               this->update = TRUE;
+                       }
+               }
+       }
+       return NEED_MORE;
+ }
+ METHOD(task_t, build_r, status_t,
+          private_ike_mobike_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) == IKE_AUTH &&
+               this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED)
+       {
+               if (this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+               {
+                       message->add_notify(message, FALSE, MOBIKE_SUPPORTED, chunk_empty);
+                       build_address_list(this, message);
+               }
+               return SUCCESS;
+       }
+       else if (message->get_exchange_type(message) == INFORMATIONAL)
+       {
+               if (this->natd)
+               {
+                       this->natd->task.build(&this->natd->task, message);
+               }
+               if (this->cookie2.ptr)
+               {
+                       message->add_notify(message, FALSE, COOKIE2, this->cookie2);
+                       chunk_free(&this->cookie2);
+               }
+               if (this->update)
+               {
+                       update_children(this);
+               }
+               return SUCCESS;
+       }
+       return NEED_MORE;
+ }
+ METHOD(task_t, process_i, status_t,
+          private_ike_mobike_t *this, message_t *message)
+ {
+       if (message->get_exchange_type(message) == IKE_AUTH &&
+               this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED)
+       {
+               process_payloads(this, message);
+               return SUCCESS;
+       }
+       else if (message->get_exchange_type(message) == INFORMATIONAL)
+       {
+               u_int32_t updates = this->ike_sa->get_pending_updates(this->ike_sa) - 1;
+               this->ike_sa->set_pending_updates(this->ike_sa, updates);
+               if (updates > 0)
+               {
+                       /* newer update queued, ignore this one */
+                       return SUCCESS;
+               }
+               if (this->cookie2.ptr)
+               {       /* check cookie if we included one */
+                       chunk_t cookie2;
+                       cookie2 = this->cookie2;
+                       this->cookie2 = chunk_empty;
+                       process_payloads(this, message);
+                       if (!chunk_equals(cookie2, this->cookie2))
+                       {
+                               chunk_free(&cookie2);
+                               DBG1(DBG_IKE, "COOKIE2 mismatch, closing IKE_SA");
+                               return FAILED;
+                       }
+                       chunk_free(&cookie2);
+               }
+               else
+               {
+                       process_payloads(this, message);
+               }
+               if (this->natd)
+               {
+                       this->natd->task.process(&this->natd->task, message);
+                       if (this->natd->has_mapping_changed(this->natd))
+                       {
+                               /* force an update if mappings have changed */
+                               this->update = this->check = TRUE;
+                               DBG1(DBG_IKE, "detected changes in NAT mappings, "
+                                        "initiating MOBIKE update");
+                       }
+               }
+               if (this->update)
+               {
+                       /* update again, as NAT state may have changed */
+                       update_children(this);
+               }
+               if (this->check)
+               {
+                       host_t *me_new, *me_old, *other_new, *other_old;
+                       me_new = message->get_destination(message);
+                       other_new = message->get_source(message);
+                       me_old = this->ike_sa->get_my_host(this->ike_sa);
+                       other_old = this->ike_sa->get_other_host(this->ike_sa);
+                       if (!me_new->equals(me_new, me_old))
+                       {
+                               this->update = TRUE;
+                               this->ike_sa->set_my_host(this->ike_sa, me_new->clone(me_new));
+                       }
+                       if (!other_new->equals(other_new, other_old))
+                       {
+                               this->update = TRUE;
+                               this->ike_sa->set_other_host(this->ike_sa, other_new->clone(other_new));
+                       }
+                       if (this->update)
+                       {
+                               /* use the same task to ... */
+                               if (!this->ike_sa->has_condition(this->ike_sa,
+                                                                                                COND_ORIGINAL_INITIATOR))
+                               {       /*... send an updated list of addresses as responder */
+                                       update_children(this);
+                                       this->update = FALSE;
+                               }
+                               else
+                               {       /* ... send the update as original initiator */
+                                       if (this->natd)
+                                       {
+                                               this->natd->task.destroy(&this->natd->task);
+                                       }
+                                       this->natd = ike_natd_create(this->ike_sa, this->initiator);
+                               }
+                               this->check = FALSE;
+                               this->ike_sa->set_pending_updates(this->ike_sa, 1);
+                               return NEED_MORE;
+                       }
+               }
+               return SUCCESS;
+       }
+       return NEED_MORE;
+ }
+ METHOD(ike_mobike_t, addresses, void,
+          private_ike_mobike_t *this)
+ {
+       this->address = TRUE;
+       this->ike_sa->set_pending_updates(this->ike_sa,
+                                               this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+ }
+ METHOD(ike_mobike_t, roam, void,
+          private_ike_mobike_t *this, bool address)
+ {
+       this->check = TRUE;
+       this->address = address;
+       this->ike_sa->set_pending_updates(this->ike_sa,
+                                               this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+ }
+ METHOD(ike_mobike_t, dpd, void,
+          private_ike_mobike_t *this)
+ {
+       if (!this->natd)
+       {
+               this->natd = ike_natd_create(this->ike_sa, this->initiator);
+       }
+       this->ike_sa->set_pending_updates(this->ike_sa,
+                                               this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+ }
+ METHOD(ike_mobike_t, is_probing, bool,
+          private_ike_mobike_t *this)
+ {
+       return this->check;
+ }
+ METHOD(task_t, get_type, task_type_t,
+          private_ike_mobike_t *this)
+ {
+       return TASK_IKE_MOBIKE;
+ }
+ METHOD(task_t, migrate, void,
+          private_ike_mobike_t *this, ike_sa_t *ike_sa)
+ {
+       chunk_free(&this->cookie2);
+       this->ike_sa = ike_sa;
+       if (this->natd)
+       {
+               this->natd->task.migrate(&this->natd->task, ike_sa);
+       }
+ }
+ METHOD(task_t, destroy, void,
+          private_ike_mobike_t *this)
+ {
+       chunk_free(&this->cookie2);
+       if (this->natd)
+       {
+               this->natd->task.destroy(&this->natd->task);
+       }
+       free(this);
+ }
+ /*
+  * Described in header.
+  */
+ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
+ {
+       private_ike_mobike_t *this;
+       INIT(this,
+               .public = {
+                       .task = {
+                               .get_type = _get_type,
+                               .migrate = _migrate,
+                               .destroy = _destroy,
+                       },
+                       .addresses = _addresses,
+                       .roam = _roam,
+                       .dpd = _dpd,
+                       .transmit = _transmit,
+                       .is_probing = _is_probing,
+               },
+               .ike_sa = ike_sa,
+               .initiator = initiator,
+       );
+       if (initiator)
+       {
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
+       }
+       else
+       {
+               this->public.task.build = _build_r;
+               this->public.task.process = _process_r;
+       }
+       return &this->public;
+ }
@@@ -126,510 -72,28 +72,30 @@@ int keymat_get_keylen_encr(encryption_a
        return 0;
  }
  
- METHOD(keymat_t, create_dh, diffie_hellman_t*,
-       private_keymat_t *this, diffie_hellman_group_t group)
- {
-       return lib->crypto->create_dh(lib->crypto, group);;
- }
- /**
-  * Derive IKE keys for a combined AEAD algorithm
-  */
- static bool derive_ike_aead(private_keymat_t *this, u_int16_t alg,
-                                                       u_int16_t key_size, prf_plus_t *prf_plus)
- {
-       aead_t *aead_i, *aead_r;
-       chunk_t key;
-       /* SK_ei/SK_er used for encryption */
-       aead_i = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
-       aead_r = lib->crypto->create_aead(lib->crypto, alg, key_size / 8);
-       if (aead_i == NULL || aead_r == NULL)
-       {
-               DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
-                        transform_type_names, ENCRYPTION_ALGORITHM,
-                        encryption_algorithm_names, alg, key_size);
-               return FALSE;
-       }
-       key_size = aead_i->get_key_size(aead_i);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_ei secret %B", &key);
-       aead_i->set_key(aead_i, key);
-       chunk_clear(&key);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_er secret %B", &key);
-       aead_r->set_key(aead_r, key);
-       chunk_clear(&key);
-       if (this->initiator)
-       {
-               this->aead_in = aead_r;
-               this->aead_out = aead_i;
-       }
-       else
-       {
-               this->aead_in = aead_i;
-               this->aead_out = aead_r;
-       }
-       return TRUE;
- }
  /**
-  * Derive IKE keys for traditional encryption and MAC algorithms
+  * See header.
   */
- static bool derive_ike_traditional(private_keymat_t *this, u_int16_t enc_alg,
-                                       u_int16_t enc_size, u_int16_t int_alg, prf_plus_t *prf_plus)
- {
-       crypter_t *crypter_i, *crypter_r;
-       signer_t *signer_i, *signer_r;
-       size_t key_size;
-       chunk_t key;
-       /* SK_ai/SK_ar used for integrity protection */
-       signer_i = lib->crypto->create_signer(lib->crypto, int_alg);
-       signer_r = lib->crypto->create_signer(lib->crypto, int_alg);
-       if (signer_i == NULL || signer_r == NULL)
-       {
-               DBG1(DBG_IKE, "%N %N not supported!",
-                        transform_type_names, INTEGRITY_ALGORITHM,
-                        integrity_algorithm_names, int_alg);
-               return FALSE;
-       }
-       key_size = signer_i->get_key_size(signer_i);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_ai secret %B", &key);
-       signer_i->set_key(signer_i, key);
-       chunk_clear(&key);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_ar secret %B", &key);
-       signer_r->set_key(signer_r, key);
-       chunk_clear(&key);
-       /* SK_ei/SK_er used for encryption */
-       crypter_i = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
-       crypter_r = lib->crypto->create_crypter(lib->crypto, enc_alg, enc_size / 8);
-       if (crypter_i == NULL || crypter_r == NULL)
-       {
-               DBG1(DBG_IKE, "%N %N (key size %d) not supported!",
-                        transform_type_names, ENCRYPTION_ALGORITHM,
-                        encryption_algorithm_names, enc_alg, enc_size);
-               signer_i->destroy(signer_i);
-               signer_r->destroy(signer_r);
-               return FALSE;
-       }
-       key_size = crypter_i->get_key_size(crypter_i);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_ei secret %B", &key);
-       crypter_i->set_key(crypter_i, key);
-       chunk_clear(&key);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_er secret %B", &key);
-       crypter_r->set_key(crypter_r, key);
-       chunk_clear(&key);
-       if (this->initiator)
-       {
-               this->aead_in = aead_create(crypter_r, signer_r);
-               this->aead_out = aead_create(crypter_i, signer_i);
-       }
-       else
-       {
-               this->aead_in = aead_create(crypter_i, signer_i);
-               this->aead_out = aead_create(crypter_r, signer_r);
-       }
-       return TRUE;
- }
- METHOD(keymat_t, derive_ike_keys, bool,
-       private_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh,
-       chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id,
-       pseudo_random_function_t rekey_function, chunk_t rekey_skd)
- {
-       chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed;
-       chunk_t spi_i, spi_r;
-       prf_plus_t *prf_plus;
-       u_int16_t alg, key_size, int_alg;
-       prf_t *rekey_prf = NULL;
-       spi_i = chunk_alloca(sizeof(u_int64_t));
-       spi_r = chunk_alloca(sizeof(u_int64_t));
-       if (dh->get_shared_secret(dh, &secret) != SUCCESS)
-       {
-               return FALSE;
-       }
-       /* Create SAs general purpose PRF first, we may use it here */
-       if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL))
-       {
-               DBG1(DBG_IKE, "no %N selected",
-                        transform_type_names, PSEUDO_RANDOM_FUNCTION);
-               return FALSE;
-       }
-       this->prf_alg = alg;
-       this->prf = lib->crypto->create_prf(lib->crypto, alg);
-       if (this->prf == NULL)
-       {
-               DBG1(DBG_IKE, "%N %N not supported!",
-                        transform_type_names, PSEUDO_RANDOM_FUNCTION,
-                        pseudo_random_function_names, alg);
-               return FALSE;
-       }
-       DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
-       /* full nonce is used as seed for PRF+ ... */
-       full_nonce = chunk_cat("cc", nonce_i, nonce_r);
-       /* but the PRF may need a fixed key which only uses the first bytes of
-        * the nonces. */
-       switch (alg)
-       {
-               case PRF_AES128_XCBC:
-                       /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does
-                        * not and therefore fixed key semantics apply to XCBC for key
-                        * derivation. */
-               case PRF_CAMELLIA128_XCBC:
-                       /* draft-kanno-ipsecme-camellia-xcbc refers to rfc 4434, we
-                        * assume fixed key length. */
-                       key_size = this->prf->get_key_size(this->prf)/2;
-                       nonce_i.len = min(nonce_i.len, key_size);
-                       nonce_r.len = min(nonce_r.len, key_size);
-                       break;
-               default:
-                       /* all other algorithms use variable key length, full nonce */
-                       break;
-       }
-       fixed_nonce = chunk_cat("cc", nonce_i, nonce_r);
-       *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
-       *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
-       prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r);
-       /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr)
-        *
-        * if we are rekeying, SKEYSEED is built on another way
-        */
-       if (rekey_function == PRF_UNDEFINED) /* not rekeying */
-       {
-               /* SKEYSEED = prf(Ni | Nr, g^ir) */
-               this->prf->set_key(this->prf, fixed_nonce);
-               this->prf->allocate_bytes(this->prf, secret, &skeyseed);
-               this->prf->set_key(this->prf, skeyseed);
-               prf_plus = prf_plus_create(this->prf, prf_plus_seed);
-       }
-       else
-       {
-               /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr)
-                * use OLD SAs PRF functions for both prf_plus and prf */
-               rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function);
-               if (!rekey_prf)
-               {
-                       DBG1(DBG_IKE, "PRF of old SA %N not supported!",
-                                pseudo_random_function_names, rekey_function);
-                       chunk_free(&full_nonce);
-                       chunk_free(&fixed_nonce);
-                       chunk_clear(&prf_plus_seed);
-                       return FALSE;
-               }
-               secret = chunk_cat("mc", secret, full_nonce);
-               rekey_prf->set_key(rekey_prf, rekey_skd);
-               rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed);
-               rekey_prf->set_key(rekey_prf, skeyseed);
-               prf_plus = prf_plus_create(rekey_prf, prf_plus_seed);
-       }
-       DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed);
-       chunk_clear(&skeyseed);
-       chunk_clear(&secret);
-       chunk_free(&full_nonce);
-       chunk_free(&fixed_nonce);
-       chunk_clear(&prf_plus_seed);
-       /* KEYMAT = SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr */
-       /* SK_d is used for generating CHILD_SA key mat => store for later use */
-       key_size = this->prf->get_key_size(this->prf);
-       prf_plus->allocate_bytes(prf_plus, key_size, &this->skd);
-       DBG4(DBG_IKE, "Sk_d secret %B", &this->skd);
-       if (!proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &key_size))
-       {
-               DBG1(DBG_IKE, "no %N selected",
-                        transform_type_names, ENCRYPTION_ALGORITHM);
-               prf_plus->destroy(prf_plus);
-               DESTROY_IF(rekey_prf);
-               return FALSE;
-       }
-       if (encryption_algorithm_is_aead(alg))
-       {
-               if (!derive_ike_aead(this, alg, key_size, prf_plus))
-               {
-                       prf_plus->destroy(prf_plus);
-                       DESTROY_IF(rekey_prf);
-                       return FALSE;
-               }
-       }
-       else
-       {
-               if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
-                                                                        &int_alg, NULL))
-               {
-                       DBG1(DBG_IKE, "no %N selected",
-                                transform_type_names, INTEGRITY_ALGORITHM);
-                       prf_plus->destroy(prf_plus);
-                       DESTROY_IF(rekey_prf);
-                       return FALSE;
-               }
-               if (!derive_ike_traditional(this, alg, key_size, int_alg, prf_plus))
-               {
-                       prf_plus->destroy(prf_plus);
-                       DESTROY_IF(rekey_prf);
-                       return FALSE;
-               }
-       }
-       /* SK_pi/SK_pr used for authentication => stored for later */
-       key_size = this->prf->get_key_size(this->prf);
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_pi secret %B", &key);
-       if (this->initiator)
-       {
-               this->skp_build = key;
-       }
-       else
-       {
-               this->skp_verify = key;
-       }
-       prf_plus->allocate_bytes(prf_plus, key_size, &key);
-       DBG4(DBG_IKE, "Sk_pr secret %B", &key);
-       if (this->initiator)
-       {
-               this->skp_verify = key;
-       }
-       else
-       {
-               this->skp_build = key;
-       }
-       /* all done, prf_plus not needed anymore */
-       prf_plus->destroy(prf_plus);
-       DESTROY_IF(rekey_prf);
-       return TRUE;
- }
- METHOD(keymat_t, derive_child_keys, bool,
-       private_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh,
-       chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i,
-       chunk_t *encr_r, chunk_t *integ_r)
+ int keymat_get_keylen_integ(integrity_algorithm_t alg)
  {
-       u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
-       chunk_t seed, secret = chunk_empty;
-       prf_plus_t *prf_plus;
-       if (dh)
-       {
-               if (dh->get_shared_secret(dh, &secret) != SUCCESS)
-               {
-                       return FALSE;
-               }
-               DBG4(DBG_CHD, "DH secret %B", &secret);
-       }
-       seed = chunk_cata("mcc", secret, nonce_i, nonce_r);
-       DBG4(DBG_CHD, "seed %B", &seed);
-       if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM,
-                                                               &enc_alg, &enc_size))
-       {
-               DBG2(DBG_CHD, "  using %N for encryption",
-                        encryption_algorithm_names, enc_alg);
-               if (!enc_size)
-               {
-                       enc_size = lookup_keylen(keylen_enc, enc_alg);
-               }
-               if (enc_alg != ENCR_NULL && !enc_size)
-               {
-                       DBG1(DBG_CHD, "no keylength defined for %N",
-                                encryption_algorithm_names, enc_alg);
-                       return FALSE;
-               }
-               /* to bytes */
-               enc_size /= 8;
-               /* CCM/GCM/CTR/GMAC needs additional bytes */
-               switch (enc_alg)
-               {
-                       case ENCR_AES_CCM_ICV8:
-                       case ENCR_AES_CCM_ICV12:
-                       case ENCR_AES_CCM_ICV16:
-                       case ENCR_CAMELLIA_CCM_ICV8:
-                       case ENCR_CAMELLIA_CCM_ICV12:
-                       case ENCR_CAMELLIA_CCM_ICV16:
-                               enc_size += 3;
-                               break;
-                       case ENCR_AES_GCM_ICV8:
-                       case ENCR_AES_GCM_ICV12:
-                       case ENCR_AES_GCM_ICV16:
-                       case ENCR_AES_CTR:
-                       case ENCR_NULL_AUTH_AES_GMAC:
-                               enc_size += 4;
-                               break;
-                       default:
-                               break;
-               }
-       }
-       if (proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM,
-                                                               &int_alg, &int_size))
-       {
-               DBG2(DBG_CHD, "  using %N for integrity",
-                        integrity_algorithm_names, int_alg);
-               if (!int_size)
+       keylen_entry_t map[] = {
+               {AUTH_HMAC_MD5_96,                      128},
++              {AUTH_HMAC_MD5_128,                     128},
+               {AUTH_HMAC_SHA1_96,                     160},
++              {AUTH_HMAC_SHA1_160,            160},
+               {AUTH_HMAC_SHA2_256_96,         256},
+               {AUTH_HMAC_SHA2_256_128,        256},
+               {AUTH_HMAC_SHA2_384_192,        384},
+               {AUTH_HMAC_SHA2_512_256,        512},
+               {AUTH_AES_XCBC_96,                      128},
+       };
+       int i;
+       for (i = 0; i < countof(map); i++)
+       {
+               if (map[i].alg == alg)
                {
-                       int_size = lookup_keylen(keylen_int, int_alg);
+                       return map[i].len;
                }
-               if (!int_size)
-               {
-                       DBG1(DBG_CHD, "no keylength defined for %N",
-                                integrity_algorithm_names, int_alg);
-                       return FALSE;
-               }
-               /* to bytes */
-               int_size /= 8;
-       }
-       this->prf->set_key(this->prf, this->skd);
-       prf_plus = prf_plus_create(this->prf, seed);
-       prf_plus->allocate_bytes(prf_plus, enc_size, encr_i);
-       prf_plus->allocate_bytes(prf_plus, int_size, integ_i);
-       prf_plus->allocate_bytes(prf_plus, enc_size, encr_r);
-       prf_plus->allocate_bytes(prf_plus, int_size, integ_r);
-       prf_plus->destroy(prf_plus);
-       if (enc_size)
-       {
-               DBG4(DBG_CHD, "encryption initiator key %B", encr_i);
-               DBG4(DBG_CHD, "encryption responder key %B", encr_r);
-       }
-       if (int_size)
-       {
-               DBG4(DBG_CHD, "integrity initiator key %B", integ_i);
-               DBG4(DBG_CHD, "integrity responder key %B", integ_r);
        }
-       return TRUE;
- }
- METHOD(keymat_t, get_skd, pseudo_random_function_t,
-       private_keymat_t *this, chunk_t *skd)
- {
-       *skd = this->skd;
-       return this->prf_alg;
- }
- METHOD(keymat_t, get_aead, aead_t*,
-       private_keymat_t *this, bool in)
- {
-       return in ? this->aead_in : this->aead_out;
- }
- METHOD(keymat_t, get_auth_octets, chunk_t,
-       private_keymat_t *this, bool verify, chunk_t ike_sa_init,
-       chunk_t nonce, identification_t *id, char reserved[3])
- {
-       chunk_t chunk, idx, octets;
-       chunk_t skp;
-       skp = verify ? this->skp_verify : this->skp_build;
-       chunk = chunk_alloca(4);
-       chunk.ptr[0] = id->get_type(id);
-       memcpy(chunk.ptr + 1, reserved, 3);
-       idx = chunk_cata("cc", chunk, id->get_encoding(id));
-       DBG3(DBG_IKE, "IDx' %B", &idx);
-       DBG3(DBG_IKE, "SK_p %B", &skp);
-       this->prf->set_key(this->prf, skp);
-       this->prf->allocate_bytes(this->prf, idx, &chunk);
-       octets = chunk_cat("ccm", ike_sa_init, nonce, chunk);
-       DBG3(DBG_IKE, "octets = message + nonce + prf(Sk_px, IDx') %B", &octets);
-       return octets;
- }
- /**
-  * Key pad for the AUTH method SHARED_KEY_MESSAGE_INTEGRITY_CODE.
-  */
- #define IKEV2_KEY_PAD "Key Pad for IKEv2"
- #define IKEV2_KEY_PAD_LENGTH 17
- METHOD(keymat_t, get_psk_sig, chunk_t,
-       private_keymat_t *this, bool verify, chunk_t ike_sa_init,
-       chunk_t nonce, chunk_t secret, identification_t *id, char reserved[3])
- {
-       chunk_t key_pad, key, sig, octets;
-       if (!secret.len)
-       {       /* EAP uses SK_p if no MSK has been established */
-               secret = verify ? this->skp_verify : this->skp_build;
-       }
-       octets = get_auth_octets(this, verify, ike_sa_init, nonce, id, reserved);
-       /* AUTH = prf(prf(Shared Secret,"Key Pad for IKEv2"), <msg octets>) */
-       key_pad = chunk_create(IKEV2_KEY_PAD, IKEV2_KEY_PAD_LENGTH);
-       this->prf->set_key(this->prf, secret);
-       this->prf->allocate_bytes(this->prf, key_pad, &key);
-       this->prf->set_key(this->prf, key);
-       this->prf->allocate_bytes(this->prf, octets, &sig);
-       DBG4(DBG_IKE, "secret %B", &secret);
-       DBG4(DBG_IKE, "prf(secret, keypad) %B", &key);
-       DBG3(DBG_IKE, "AUTH = prf(prf(secret, keypad), octets) %B", &sig);
-       chunk_free(&octets);
-       chunk_free(&key);
-       return sig;
- }
- METHOD(keymat_t, destroy, void,
-       private_keymat_t *this)
- {
-       DESTROY_IF(this->aead_in);
-       DESTROY_IF(this->aead_out);
-       DESTROY_IF(this->prf);
-       chunk_clear(&this->skd);
-       chunk_clear(&this->skp_verify);
-       chunk_clear(&this->skp_build);
-       free(this);
- }
- /**
-  * See header
-  */
- keymat_t *keymat_create(bool initiator)
- {
-       private_keymat_t *this;
-       INIT(this,
-               .public = {
-                       .create_dh = _create_dh,
-                       .derive_ike_keys = _derive_ike_keys,
-                       .derive_child_keys = _derive_child_keys,
-                       .get_skd = _get_skd,
-                       .get_aead = _get_aead,
-                       .get_auth_octets = _get_auth_octets,
-                       .get_psk_sig = _get_psk_sig,
-                       .destroy = _destroy,
-               },
-               .initiator = initiator,
-               .prf_alg = PRF_UNDEFINED,
-       );
-       return &this->public;
+       return 0;
  }
@@@ -266,46 -263,38 +266,49 @@@ METHOD(trap_manager_t, acquire, void
        if (!found)
        {
                DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
 +              this->lock->unlock(this->lock);
 +              return;
        }
 -      else if (found->pending)
 +      if (!cas_bool(&found->pending, FALSE, TRUE))
        {
                DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
 +              this->lock->unlock(this->lock);
 +              return;
        }
 -      else
 +      peer = found->peer_cfg->get_ref(found->peer_cfg);
 +      child = found->child_sa->get_config(found->child_sa);
 +      child = child->get_ref(child);
 +      reqid = found->child_sa->get_reqid(found->child_sa);
 +      /* don't hold the lock while checking out the IKE_SA */
 +      this->lock->unlock(this->lock);
 +
 +      ike_sa = charon->ike_sa_manager->checkout_by_config(
 +                                                                                      charon->ike_sa_manager, peer);
-       if (ike_sa->get_peer_cfg(ike_sa) == NULL)
++      if (ike_sa)
        {
-               ike_sa->set_peer_cfg(ike_sa, peer);
-       }
-       if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
-       {
-               /* make sure the entry is still there */
-               this->lock->read_lock(this->lock);
-               if (this->traps->find_first(this->traps, NULL,
-                                                                       (void**)&found) == SUCCESS)
 -              child = found->child_sa->get_config(found->child_sa);
 -              peer = found->peer_cfg;
 -              ike_sa = charon->ike_sa_manager->checkout_by_config(
 -                                                                                              charon->ike_sa_manager, peer);
 -              if (ike_sa)
++              if (ike_sa->get_peer_cfg(ike_sa) == NULL)
                {
-                       found->ike_sa = ike_sa;
 -                      if (ike_sa->get_peer_cfg(ike_sa) == NULL)
 -                      {
 -                              ike_sa->set_peer_cfg(ike_sa, peer);
 -                      }
 -                      child->get_ref(child);
 -                      reqid = found->child_sa->get_reqid(found->child_sa);
 -                      if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
++                      ike_sa->set_peer_cfg(ike_sa, peer);
++              }
++              if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
++              {
++                      /* make sure the entry is still there */
++                      this->lock->read_lock(this->lock);
++                      if (this->traps->find_first(this->traps, NULL,
++                                                                              (void**)&found) == SUCCESS)
+                       {
 -                              found->pending = ike_sa;
 -                              charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
++                              found->ike_sa = ike_sa;
+                       }
 -                      else
 -                      {
 -                              charon->ike_sa_manager->checkin_and_destroy(
++                      this->lock->unlock(this->lock);
++                      charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
++              }
++              else
++              {
++                      charon->ike_sa_manager->checkin_and_destroy(
+                                                                                               charon->ike_sa_manager, ike_sa);
 -                      }
                }
-               this->lock->unlock(this->lock);
-               charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
-       }
-       else
-       {
-               charon->ike_sa_manager->checkin_and_destroy(
-                                                                                       charon->ike_sa_manager, ike_sa);
        }
 -      this->lock->unlock(this->lock);
 +      peer->destroy(peer);
  }
  
  /**
Simple merge
@@@ -228,12 -228,16 +228,16 @@@ struct credential_manager_t 
         *
         * To add a credential set for the current trustchain verification
         * operation, sets may be added for the calling thread only. This
 -       * does not require a write lock and is therefore a much less expensive
 +       * does not require a write lock and is therefore a much cheaper
         * operation.
+        * The exclusive option allows to disable all other credential sets
+        * until the set is deregistered.
         *
         * @param set           set to register
+        * @param exclusive     TRUE to disable all other sets for this thread
         */
-       void (*add_local_set)(credential_manager_t *this, credential_set_t *set);
+       void (*add_local_set)(credential_manager_t *this, credential_set_t *set,
+                                                 bool exclusive);
  
        /**
         * Unregister a thread local credential set from the manager.
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge