]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Merge branch 'ikev1'
authorMartin Willi <martin@revosec.ch>
Wed, 2 May 2012 09:12:31 +0000 (11:12 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 2 May 2012 09:12:31 +0000 (11:12 +0200)
Conflicts:
configure.in
man/ipsec.conf.5.in
src/libcharon/encoding/generator.c
src/libcharon/encoding/payloads/notify_payload.c
src/libcharon/encoding/payloads/notify_payload.h
src/libcharon/encoding/payloads/payload.c
src/libcharon/network/receiver.c
src/libcharon/sa/authenticator.c
src/libcharon/sa/authenticator.h
src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/sa/task_manager.c
src/libstrongswan/credentials/auth_cfg.c

34 files changed:
1  2 
configure.in
man/ipsec.conf.5.in
src/libcharon/daemon.c
src/libcharon/encoding/generator.c
src/libcharon/encoding/parser.c
src/libcharon/encoding/payloads/certreq_payload.c
src/libcharon/encoding/payloads/notify_payload.c
src/libcharon/encoding/payloads/notify_payload.h
src/libcharon/encoding/payloads/payload.c
src/libcharon/encoding/payloads/payload.h
src/libcharon/network/receiver.c
src/libcharon/plugins/smp/smp.c
src/libcharon/plugins/stroke/stroke_config.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/authenticator.c
src/libcharon/sa/authenticator.h
src/libcharon/sa/ike_sa.c
src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/ikev2/tasks/ike_auth.c
src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/sa/keymat.h
src/libstrongswan/asn1/oid.txt
src/libstrongswan/chunk.h
src/libstrongswan/credentials/auth_cfg.c
src/libstrongswan/credentials/auth_cfg.h
src/libstrongswan/utils.h
src/pki/commands/print.c
src/pluto/keys.c
src/starter/starterstroke.c
src/stroke/stroke.c
src/stroke/stroke_msg.h

diff --cc configure.in
index 7f28762fba272bac4aa17fbe4941a60956333af3,b99487aea44a0705eabb546c444f12044a41756c..6c6a57f6487abf5158b1d1ec88db4b23247be304
mode 100644,100755..100755
index ab255304da92b4c4dca292dc1ba86c0a90f5eda2,642499fc1295ecdcefd95fce2c704448ae4e2a66..92cf0ca8b445da6957e5a32e58170fcab8bfcf91
@@@ -605,14 -509,13 +509,16 @@@ Fo
  .B eap,
  an optional EAP method can be appended. Currently defined methods are
  .BR eap-aka ,
+ .BR eap-sim ,
  .BR eap-gtc ,
  .BR eap-md5 ,
 +.BR eap-mschapv2 ,
 +.BR eap-peap ,
 +.BR eap-sim ,
  .BR eap-tls ,
 -.B eap-mschapv2
++.BR eap-ttls ,
  and
- .BR eap-ttls .
+ .BR eap-radius .
  Alternatively, IANA assigned EAP method numbers are accepted. Vendor specific
  EAP methods are defined in the form
  .B eap-type-vendor
index f2c054a125b98d48829f309553bd555ca2c94a9b,575627206e8bb14cda82d23dc4226396475c2597..ee2db331864c930fcbbc4dda7df6318fbdbb80e7
@@@ -131,8 -128,8 +131,9 @@@ static void destroy(private_daemon_t *t
        DESTROY_IF(this->public.ike_sa_manager);
        DESTROY_IF(this->public.controller);
        DESTROY_IF(this->public.eap);
+       DESTROY_IF(this->public.xauth);
        DESTROY_IF(this->public.backends);
 +      DESTROY_IF(this->public.sender);
        DESTROY_IF(this->public.socket);
  
        /* rehook library logging, shutdown logging */
index 60fa7e0c4da910bb1ac92385e919fa673d5be166,bf44801932720711869e26e6972a7a41e72b25da..4d65bce84cd25fb1947248a560090e16421b2763
@@@ -519,11 -560,14 +560,14 @@@ METHOD(generator_t, generate_payload, v
                                return;
                }
        }
-       DBG2(DBG_ENC, "generating %N payload finished",
-                payload_type_names, payload_type);
-       DBG3(DBG_ENC, "generated data for this payload %b",
-                this->buffer + offset_start,
-                (u_int)(this->out_position - this->buffer - offset_start));
+       if (this->debug)
+       {
+               DBG2(DBG_ENC, "generating %N payload finished",
+                        payload_type_names, payload_type);
+               DBG3(DBG_ENC, "generated data for this payload %b",
+                        this->buffer + offset_start,
 -                       this->out_position - this->buffer - offset_start);
++                       (u_int)(this->out_position - this->buffer - offset_start));
+       }
  }
  
  METHOD(generator_t, destroy, void,
Simple merge
index 02015f273124d417504ad73b35544e9be948a749,26d1f0a4361c78a0c3dafaaa57ad88bfa1ba368c..df5e73b5b50831c095bf719f5753b892e7800d48
@@@ -109,9 -111,11 +111,10 @@@ static encoding_rule_t encodings[] = 
  METHOD(payload_t, verify, status_t,
        private_certreq_payload_t *this)
  {
-       if (this->encoding == ENC_X509_SIGNATURE)
+       if (this->type == CERTIFICATE_REQUEST &&
+               this->encoding == ENC_X509_SIGNATURE)
        {
 -              if (this->data.len < HASH_SIZE_SHA1 ||
 -                      this->data.len % HASH_SIZE_SHA1)
 +              if (this->data.len % HASH_SIZE_SHA1)
                {
                        DBG1(DBG_ENC, "invalid X509 hash length (%d) in certreq",
                                 this->data.len);
index e03d1af674a1840a3ea8488c28daf01024ef3b52,4115344917e3aba3e932e519d9d0b8f5139af89d..d168e1c12f0931170ecf19fbfcaf565552113e8e
mode 100644,100755..100755
@@@ -98,11 -105,15 +105,18 @@@ ENUM_NEXT(notify_type_names, INITIAL_CO
        "IKEV2_MESSAGE_ID_SYNC_SUPPORTED",
        "IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED",
        "IKEV2_MESSAGE_ID_SYNC",
 -      "IPSEC_REPLAY_COUNTER_SYNC");
 -ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
 +      "IPSEC_REPLAY_COUNTER_SYNC",
 +      "SECURE PASSWORD_METHOD",
 +      "PSK_PERSIST",
 +      "PSK_CONFIRM");
- ENUM_NEXT(notify_type_names, USE_BEET_MODE, USE_BEET_MODE, PSK_CONFIRM,
++ENUM_NEXT(notify_type_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, PSK_CONFIRM,
+       "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, RADIUS_ATTRIBUTE, USE_BEET_MODE,
        "ME_MEDIATION",
@@@ -189,11 -207,15 +210,18 @@@ ENUM_NEXT(notify_type_short_names, INIT
        "MSG_ID_SYN_SUP",
        "RPL_CTR_SYN_SUP",
        "MSG_ID_SYN",
 -      "RPL_CTR_SYN");
 -ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, IPSEC_REPLAY_COUNTER_SYNC,
 +      "RPL_CTR_SYN",
 +      "SEC_PASSWD",
 +      "PSK_PST",
 +      "PSK_CFM");
- ENUM_NEXT(notify_type_short_names, USE_BEET_MODE, USE_BEET_MODE, PSK_CONFIRM,
++ENUM_NEXT(notify_type_short_names, INITIAL_CONTACT_IKEV1, INITIAL_CONTACT_IKEV1, PSK_CONFIRM,
+       "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, RADIUS_ATTRIBUTE, USE_BEET_MODE,
        "ME_MED",
index ced28270073c0c16c9734e846b2676fa26f3f659,07fbcb49ba63c28b0909bd854cb1b87a60f7c647..beec1e2332b21f8b7f8224a2c973e91566f90e6a
mode 100644,100755..100755
@@@ -127,11 -138,13 +138,18 @@@ enum notify_type_t 
        IKEV2_REPLAY_COUNTER_SYNC_SUPPORTED = 16421,
        IKEV2_MESSAGE_ID_SYNC = 16422,
        IPSEC_REPLAY_COUNTER_SYNC = 16423,
 +      /* Secure password methods, RFC 6467 */
 +      SECURE_PASSWORD_METHOD = 16424,
 +      /* PACE - draft-kuegler-ipsecme-pace-ikev2 */
 +      PSK_PERSIST = 16425,
 +      PSK_CONFIRM = 16426,
+       /* IKEv1 initial contact */
+       INITIAL_CONTACT_IKEV1 = 24578,
+       /* IKEv1 DPD */
+       DPD_R_U_THERE = 36136,
+       DPD_R_U_THERE_ACK = 36137,
+       /* IKEv1 Cisco High Availability */
+       UNITY_LOAD_BALANCE = 40501,
        /* BEET mode, not even a draft yet. private use */
        USE_BEET_MODE = 40961,
        /* IKE-ME, private use */
index a2c0a43851f8159b18845d79dc8358a564ef9511,257d5385885c38a527276c9504da4b1d8031a26d..dc158476b79f9866b9e15af5cb9ef2d276777516
  #include <encoding/payloads/cp_payload.h>
  #include <encoding/payloads/configuration_attribute.h>
  #include <encoding/payloads/eap_payload.h>
+ #include <encoding/payloads/hash_payload.h>
  #include <encoding/payloads/unknown_payload.h>
  
  ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD,
        "NO_PAYLOAD");
- ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION,
-                                                         GENERIC_SECURE_PASSWORD_METHOD, NO_PAYLOAD,
+ ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION_V1, CONFIGURATION_V1, NO_PAYLOAD,
+       "SECURITY_ASSOCIATION_V1",
+       "PROPOSAL_V1",
+       "TRANSFORM_V1",
+       "KEY_EXCHANGE_V1",
+       "ID_V1",
+       "CERTIFICATE_V1",
+       "CERTIFICATE_REQUEST_V1",
+       "HASH_V1",
+       "SIGNATURE_V1",
+       "NONCE_V1",
+       "NOTIFY_V1",
+       "DELETE_V1",
+       "VENDOR_ID_V1",
+       "CONFIGURATION_V1");
+ ENUM_NEXT(payload_type_names, NAT_D_V1, NAT_OA_V1, CONFIGURATION_V1,
+       "NAT_D_V1",
+       "NAT_OA_V1");
 -ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NAT_OA_V1,
++ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METHOD, NAT_OA_V1,
        "SECURITY_ASSOCIATION",
        "KEY_EXCHANGE",
        "ID_INITIATOR",
        "TRAFFIC_SELECTOR_RESPONDER",
        "ENCRYPTED",
        "CONFIGURATION",
 -      "EXTENSIBLE_AUTHENTICATION");
 +      "EXTENSIBLE_AUTHENTICATION",
 +      "GENERIC_SECURE_PASSWORD_METHOD");
  #ifdef ME
 -ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION,
 +ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD,
        "ID_PEER");
- ENUM_NEXT(payload_type_names, HEADER, CONFIGURATION_ATTRIBUTE, ID_PEER,
+ ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, ID_PEER,
        "HEADER",
        "PROPOSAL_SUBSTRUCTURE",
+       "PROPOSAL_SUBSTRUCTURE_V1",
        "TRANSFORM_SUBSTRUCTURE",
+       "TRANSFORM_SUBSTRUCTURE_V1",
        "TRANSFORM_ATTRIBUTE",
+       "TRANSFORM_ATTRIBUTE_V1",
        "TRAFFIC_SELECTOR_SUBSTRUCTURE",
-       "CONFIGURATION_ATTRIBUTE");
+       "CONFIGURATION_ATTRIBUTE",
+       "CONFIGURATION_ATTRIBUTE_V1",
+       "ENCRYPTED_V1");
  #else
- ENUM_NEXT(payload_type_names, HEADER, CONFIGURATION_ATTRIBUTE,
-                                                         GENERIC_SECURE_PASSWORD_METHOD,
 -ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, EXTENSIBLE_AUTHENTICATION,
++ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD,
        "HEADER",
        "PROPOSAL_SUBSTRUCTURE",
+       "PROPOSAL_SUBSTRUCTURE_V1",
        "TRANSFORM_SUBSTRUCTURE",
+       "TRANSFORM_SUBSTRUCTURE_V1",
        "TRANSFORM_ATTRIBUTE",
+       "TRANSFORM_ATTRIBUTE_V1",
        "TRAFFIC_SELECTOR_SUBSTRUCTURE",
-       "CONFIGURATION_ATTRIBUTE");
+       "CONFIGURATION_ATTRIBUTE",
+       "CONFIGURATION_ATTRIBUTE_V1",
+       "ENCRYPTED_V1");
  #endif /* ME */
- ENUM_END(payload_type_names, CONFIGURATION_ATTRIBUTE);
+ ENUM_END(payload_type_names, ENCRYPTED_V1);
  
  /* short forms of payload names */
  ENUM_BEGIN(payload_type_short_names, NO_PAYLOAD, NO_PAYLOAD,
        "--");
- ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION,
-                                                                       GENERIC_SECURE_PASSWORD_METHOD, NO_PAYLOAD,
+ ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION_V1, CONFIGURATION_V1, NO_PAYLOAD,
+       "SA",
+       "PROP",
+       "TRANS",
+       "KE",
+       "ID",
+       "CERT",
+       "CERTREQ",
+       "HASH",
+       "SIG",
+       "No",
+       "N",
+       "D",
+       "V",
+       "CP");
+ ENUM_NEXT(payload_type_short_names, NAT_D_V1, NAT_OA_V1, CONFIGURATION_V1,
+       "NAT-D",
+       "NAT-OA");
 -ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, NAT_OA_V1,
++ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METHOD, NAT_OA_V1,
        "SA",
        "KE",
        "IDi",
        "TSr",
        "E",
        "CP",
 -      "EAP");
 +      "EAP",
 +      "GSPM");
  #ifdef ME
 -ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION,
 +ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER,
 +                                                                      GENERIC_SECURE_PASSWORD_METHOD,
        "IDp");
- ENUM_NEXT(payload_type_short_names, HEADER, CONFIGURATION_ATTRIBUTE, ID_PEER,
+ ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, ID_PEER,
        "HDR",
        "PROP",
+       "PROP",
+       "TRANS",
        "TRANS",
        "TRANSATTR",
+       "TRANSATTR",
        "TSSUB",
-       "CPATTR");
+       "CATTR",
+       "CATTR",
+       "E");
  #else
- ENUM_NEXT(payload_type_short_names, HEADER, CONFIGURATION_ATTRIBUTE,
-                                                                       GENERIC_SECURE_PASSWORD_METHOD,
 -ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, EXTENSIBLE_AUTHENTICATION,
++ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, GENERIC_SECURE_PASSWORD_METHOD,
        "HDR",
        "PROP",
+       "PROP",
+       "TRANS",
        "TRANS",
        "TRANSATTR",
+       "TRANSATTR",
        "TSSUB",
-       "CPATTR");
+       "CATTR",
+       "CATTR",
+       "E");
  #endif /* ME */
- ENUM_END(payload_type_short_names, CONFIGURATION_ATTRIBUTE);
+ ENUM_END(payload_type_short_names, ENCRYPTED_V1);
  
  /*
   * see header
index cfb1408ef44752d8dd5468675df90f347ff6da68,599249fcb6e4ff57fa47b3ab67771fc745402a67..fcc730439575a46f9658587e620aee7ed27b77d8
@@@ -308,13 -271,15 +310,14 @@@ static bool drop_ike_sa_init(private_re
        half_open = charon->ike_sa_manager->get_half_open_count(
                                                                                charon->ike_sa_manager, NULL);
  
-       /* check for cookies */
-       if (cookie_required(this, half_open, now) && !check_cookie(this, message))
+       /* check for cookies in IKEv2 */
+       if (message->get_major_version(message) == IKEV2_MAJOR_VERSION &&
 -              this->cookie_threshold && half_open >= this->cookie_threshold &&
 -              !check_cookie(this, message))
++              cookie_required(this, half_open, now) && !check_cookie(this, message))
        {
 -              u_int32_t now = time_monotonic(NULL);
 -              chunk_t cookie = cookie_build(this, message, now - this->secret_offset,
 -                                                                        chunk_from_thing(this->secret));
 +              chunk_t cookie;
  
 +              cookie = cookie_build(this, message, now - this->secret_offset,
 +                                                        chunk_from_thing(this->secret));
                DBG2(DBG_NET, "received packet from: %#H to %#H",
                         message->get_source(message),
                         message->get_destination(message));
Simple merge
index 483e3d25342444db45983b0cb83e0ce56012af12,8ca1464fb50f053bcf07009fd3fbb033852e2d12..89e1e0a911f6da5f4399f9c0d28fd3407b6fb311
@@@ -264,10 -263,9 +264,9 @@@ static auth_cfg_t *build_auth_cfg(priva
  {
        identification_t *identity;
        certificate_t *certificate;
 -      char *auth, *id, *cert, *ca;
 +      char *auth, *id, *pubkey, *cert, *ca;
        stroke_end_t *end, *other_end;
        auth_cfg_t *cfg;
-       char eap_buf[32];
  
        /* select strings */
        if (local)
index 9ffe661ccac9dbf47619ed4d7034903d963fc1e7,91bb7715fbfd55df8a59ccad5bb1bb0f5358186f..a32b6ab122ec5ccf8e706baa79ca7bfecfe2a63a
@@@ -28,12 -31,21 +31,22 @@@ ENUM_BEGIN(auth_method_names, AUTH_RSA
        "RSA signature",
        "pre-shared key",
        "DSS signature");
 -ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_ECDSA_521, AUTH_DSS,
 +ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_GSPM, AUTH_DSS,
        "ECDSA-256 signature",
        "ECDSA-384 signature",
 -      "ECDSA-521 signature");
 -ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_ECDSA_521,
 +      "ECDSA-521 signature",
 +      "secure password method");
- ENUM_END(auth_method_names, AUTH_GSPM);
++ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_GSPM,
+       "XAuthInitPSK",
+       "XAuthRespPSK",
+       "XAuthInitRSA",
+       "XauthRespRSA",
+       "HybridInitRSA",
+       "HybridRespRSA",
+ );
+ ENUM_END(auth_method_names, AUTH_HYBRID_RESP_RSA);
+ #ifdef USE_IKEV2
  
  /**
   * Described in header.
index 5042e4a732d5350c6f57367d9b33c6de0014cab1,3af939160fea48ae99e07fe193f80cb4d6a7f08b..86b42da7acae09db579e12e161d4f6cbec674202
@@@ -68,11 -74,35 +74,40 @@@ enum auth_method_t 
         */
        AUTH_ECDSA_521 = 11,
  
 +      /**
 +       * Generic Secure Password Authentication Method as specified in RFC 6467
 +       */
 +      AUTH_GSPM = 12,
 +
+       /**
+        * IKEv1 initiator XAUTH with PSK, outside of IANA range
+        */
+       AUTH_XAUTH_INIT_PSK = 256,
+       /**
+        * IKEv1 responder XAUTH with PSK, outside of IANA range
+        */
+       AUTH_XAUTH_RESP_PSK,
+       /**
+        * IKEv1 initiator XAUTH with RSA, outside of IANA range
+        */
+       AUTH_XAUTH_INIT_RSA,
+       /**
+        * IKEv1 responder XAUTH with RSA, outside of IANA range
+        */
+       AUTH_XAUTH_RESP_RSA,
+       /**
+        * IKEv1 initiator XAUTH, responder RSA, outside of IANA range
+        */
+       AUTH_HYBRID_INIT_RSA,
+       /**
+        * IKEv1 responder XAUTH, initiator RSA, outside of IANA range
+        */
+       AUTH_HYBRID_RESP_RSA,
  };
  
  /**
Simple merge
index 0000000000000000000000000000000000000000,ba7fdd2da6f095593bc1034512bab312f1520a5b..cc6d3784974a76ee4a160a5b982ec67a91bf1450
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1479 +1,1484 @@@
 -      bool delete = FALSE;
+ /*
+  * 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)
+ {
+       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;
 -                      case DESTROY_ME:
++      bool delete = FALSE, hook = 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;
 -              charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
+                       case FAILED:
+                       default:
++                              hook = TRUE;
++                              /* FALL */
++                      case DESTROY_ME:
+                               /* 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)
+       {
++              if (hook)
++              {
++                      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 dd8a4b086cbdac1607b14406667c35e4bf9d8323,de68e86622c706f5fd3664b25b1fbdae736345d5..3fbbcfd2ae453ce9202ca50665fb6a35c06c1980
@@@ -515,13 -517,11 +517,14 @@@ METHOD(task_t, migrate, void
        chunk_free(&this->other_nonce);
  
        this->ike_sa = ike_sa;
-       this->keymat = ike_sa->get_keymat(ike_sa);
+       this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
        this->proposal = NULL;
 -      DESTROY_IF(this->dh);
 -      this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
 -                                                                                        this->dh_group);
 +      if (this->dh && this->dh->get_dh_group(this->dh) != this->dh_group)
 +      {       /* reset DH value only if group changed (INVALID_KE_PAYLOAD) */
 +              this->dh->destroy(this->dh);
-               this->dh = this->keymat->create_dh(this->keymat, this->dh_group);
++              this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
++                                                                                                this->dh_group);
 +      }
  }
  
  METHOD(task_t, destroy, void,
Simple merge
Simple merge
Simple merge
index 12f75b24033a8307e59f06ea28938d17f64e45d6,0646b0e2c9054d223242fbb6a5871fd208c04b8b..fd2cee607ab98fed271e7062a26295c8c65956a3
@@@ -53,40 -56,6 +56,42 @@@ ENUM(auth_rule_names, AUTH_RULE_IDENTIT
        "HELPER_REVOCATION_CERT",
  );
  
 +/**
 + * Check if the given rule is a rule for which there may be multiple values.
 + */
 +static inline bool is_multi_value_rule(auth_rule_t type)
 +{
 +      switch (type)
 +      {
 +              case AUTH_RULE_AUTH_CLASS:
 +              case AUTH_RULE_EAP_TYPE:
 +              case AUTH_RULE_EAP_VENDOR:
 +              case AUTH_RULE_RSA_STRENGTH:
 +              case AUTH_RULE_ECDSA_STRENGTH:
 +              case AUTH_RULE_IDENTITY:
 +              case AUTH_RULE_EAP_IDENTITY:
 +              case AUTH_RULE_AAA_IDENTITY:
++              case AUTH_RULE_XAUTH_IDENTITY:
++              case AUTH_RULE_XAUTH_BACKEND:
 +              case AUTH_RULE_SUBJECT_CERT:
 +              case AUTH_HELPER_SUBJECT_CERT:
 +              case AUTH_HELPER_SUBJECT_HASH_URL:
 +              case AUTH_RULE_MAX:
 +                      return FALSE;
 +              case AUTH_RULE_OCSP_VALIDATION:
 +              case AUTH_RULE_CRL_VALIDATION:
 +              case AUTH_RULE_GROUP:
 +              case AUTH_RULE_CA_CERT:
 +              case AUTH_RULE_IM_CERT:
 +              case AUTH_RULE_CERT_POLICY:
 +              case AUTH_HELPER_IM_CERT:
 +              case AUTH_HELPER_IM_HASH_URL:
 +              case AUTH_HELPER_REVOCATION_CERT:
 +                      return TRUE;
 +      }
 +      return FALSE;
 +}
 +
  typedef struct private_auth_cfg_t private_auth_cfg_t;
  
  /**
@@@ -180,108 -134,6 +185,112 @@@ METHOD(auth_cfg_t, create_enumerator, e
        return &enumerator->public;
  }
  
 +/**
 + * Create an entry from the given arguments.
 + */
 +static entry_t *entry_create(auth_rule_t type, va_list args)
 +{
 +      entry_t *this = malloc_thing(entry_t);
 +
 +      this->type = type;
 +      switch (type)
 +      {
 +              case AUTH_RULE_AUTH_CLASS:
 +              case AUTH_RULE_EAP_TYPE:
 +              case AUTH_RULE_EAP_VENDOR:
 +              case AUTH_RULE_CRL_VALIDATION:
 +              case AUTH_RULE_OCSP_VALIDATION:
 +              case AUTH_RULE_RSA_STRENGTH:
 +              case AUTH_RULE_ECDSA_STRENGTH:
 +                      /* integer type */
 +                      this->value = (void*)(uintptr_t)va_arg(args, u_int);
 +                      break;
 +              case AUTH_RULE_IDENTITY:
 +              case AUTH_RULE_EAP_IDENTITY:
 +              case AUTH_RULE_AAA_IDENTITY:
++              case AUTH_RULE_XAUTH_BACKEND:
++              case AUTH_RULE_XAUTH_IDENTITY:
 +              case AUTH_RULE_GROUP:
 +              case AUTH_RULE_CA_CERT:
 +              case AUTH_RULE_IM_CERT:
 +              case AUTH_RULE_SUBJECT_CERT:
 +              case AUTH_RULE_CERT_POLICY:
 +              case AUTH_HELPER_IM_CERT:
 +              case AUTH_HELPER_SUBJECT_CERT:
 +              case AUTH_HELPER_IM_HASH_URL:
 +              case AUTH_HELPER_SUBJECT_HASH_URL:
 +              case AUTH_HELPER_REVOCATION_CERT:
 +                      /* pointer type */
 +                      this->value = va_arg(args, void*);
 +                      break;
 +              case AUTH_RULE_MAX:
 +                      this->value = NULL;
 +                      break;
 +      }
 +      return this;
 +}
 +
 +/**
 + * Compare two entries for equality.
 + */
 +static bool entry_equals(entry_t *e1, entry_t *e2)
 +{
 +      if (e1->type != e2->type)
 +      {
 +              return FALSE;
 +      }
 +      switch (e1->type)
 +      {
 +              case AUTH_RULE_AUTH_CLASS:
 +              case AUTH_RULE_EAP_TYPE:
 +              case AUTH_RULE_EAP_VENDOR:
 +              case AUTH_RULE_CRL_VALIDATION:
 +              case AUTH_RULE_OCSP_VALIDATION:
 +              case AUTH_RULE_RSA_STRENGTH:
 +              case AUTH_RULE_ECDSA_STRENGTH:
 +              {
 +                      return e1->value == e2->value;
 +              }
 +              case AUTH_RULE_CA_CERT:
 +              case AUTH_RULE_IM_CERT:
 +              case AUTH_RULE_SUBJECT_CERT:
 +              case AUTH_HELPER_IM_CERT:
 +              case AUTH_HELPER_SUBJECT_CERT:
 +              case AUTH_HELPER_REVOCATION_CERT:
 +              {
 +                      certificate_t *c1, *c2;
 +
 +                      c1 = (certificate_t*)e1->value;
 +                      c2 = (certificate_t*)e2->value;
 +
 +                      return c1->equals(c1, c2);
 +              }
 +              case AUTH_RULE_IDENTITY:
 +              case AUTH_RULE_EAP_IDENTITY:
 +              case AUTH_RULE_AAA_IDENTITY:
++              case AUTH_RULE_XAUTH_IDENTITY:
 +              case AUTH_RULE_GROUP:
 +              {
 +                      identification_t *id1, *id2;
 +
 +                      id1 = (identification_t*)e1->value;
 +                      id2 = (identification_t*)e2->value;
 +
 +                      return id1->equals(id1, id2);
 +              }
 +              case AUTH_RULE_CERT_POLICY:
++              case AUTH_RULE_XAUTH_BACKEND:
 +              case AUTH_HELPER_IM_HASH_URL:
 +              case AUTH_HELPER_SUBJECT_HASH_URL:
 +              {
 +                      return streq(e1->value, e2->value);
 +              }
 +              case AUTH_RULE_MAX:
 +                      break;
 +      }
 +      return FALSE;
 +}
 +
  /**
   * Destroy the value associated with an entry
   */
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge