summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/ike_sa.c138
-rw-r--r--src/libcharon/sa/ike_sa.h34
-rw-r--r--src/libcharon/sa/ike_sa_manager.c48
-rw-r--r--src/libcharon/sa/ikev1/phase1.c12
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c445
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.c32
-rw-r--r--src/libcharon/sa/ikev1/tasks/informational.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_vendor.c39
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c31
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c26
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.c23
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.h5
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c307
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c23
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.c95
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.h8
16 files changed, 776 insertions, 496 deletions
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index c338cdaef..d92b9df8e 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2013 Tobias Brunner
+ * Copyright (C) 2006-2014 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -16,6 +16,28 @@
* for more details.
*/
+/*
+ * Copyright (c) 2014 Volker Rümelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
@@ -251,6 +273,11 @@ struct private_ike_sa_t {
* Flush auth configs once established?
*/
bool flush_auth_cfg;
+
+ /**
+ * Maximum length of a single fragment, 0 for address-specific defaults
+ */
+ size_t fragment_size;
};
/**
@@ -909,11 +936,14 @@ METHOD(ike_sa_t, update_hosts, void,
update = TRUE;
}
- if (!other->equals(other, this->other_host))
+ if (!other->equals(other, this->other_host) &&
+ (force || has_condition(this, COND_NAT_THERE)))
{
- /* update others address if we are NOT NATed */
- if ((has_condition(this, COND_NAT_THERE) &&
- !has_condition(this, COND_NAT_HERE)) || force )
+ /* only update other's address if we are behind a static NAT,
+ * which we assume is the case if we are not initiator */
+ if (force ||
+ (!has_condition(this, COND_NAT_HERE) ||
+ !has_condition(this, COND_ORIGINAL_INITIATOR)))
{
set_other_host(this, other->clone(other));
update = TRUE;
@@ -994,6 +1024,69 @@ METHOD(ike_sa_t, generate_message, status_t,
return status;
}
+static bool filter_fragments(private_ike_sa_t *this, packet_t **fragment,
+ packet_t **packet)
+{
+ *packet = (*fragment)->clone(*fragment);
+ set_dscp(this, *packet);
+ return TRUE;
+}
+
+METHOD(ike_sa_t, generate_message_fragmented, status_t,
+ private_ike_sa_t *this, message_t *message, enumerator_t **packets)
+{
+ enumerator_t *fragments;
+ packet_t *packet;
+ status_t status;
+ bool use_frags = FALSE;
+
+ if (this->ike_cfg)
+ {
+ switch (this->ike_cfg->fragmentation(this->ike_cfg))
+ {
+ case FRAGMENTATION_FORCE:
+ use_frags = TRUE;
+ break;
+ case FRAGMENTATION_YES:
+ use_frags = supports_extension(this, EXT_IKE_FRAGMENTATION);
+ if (use_frags && this->version == IKEV1 &&
+ supports_extension(this, EXT_MS_WINDOWS))
+ {
+ /* It seems Windows 7 and 8 peers only accept proprietary
+ * fragmented messages if they expect certificates. */
+ use_frags = message->get_payload(message,
+ PLV1_CERTIFICATE) != NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (!use_frags)
+ {
+ status = generate_message(this, message, &packet);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ *packets = enumerator_create_single(packet, NULL);
+ return SUCCESS;
+ }
+
+ this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
+ message->set_ike_sa_id(message, this->ike_sa_id);
+ charon->bus->message(charon->bus, message, FALSE, TRUE);
+ status = message->fragment(message, this->keymat, this->fragment_size,
+ &fragments);
+ if (status == SUCCESS)
+ {
+ charon->bus->message(charon->bus, message, FALSE, FALSE);
+ *packets = enumerator_create_filter(fragments, (void*)filter_fragments,
+ this, NULL);
+ }
+ return status;
+}
+
METHOD(ike_sa_t, set_kmaddress, void,
private_ike_sa_t *this, host_t *local, host_t *remote)
{
@@ -1487,6 +1580,14 @@ METHOD(ike_sa_t, reauth, status_t,
{
return INVALID_STATE;
}
+ if (this->state == IKE_CONNECTING)
+ {
+ DBG0(DBG_IKE, "reinitiating IKE_SA %s[%d]",
+ get_name(this), this->unique_id);
+ reset(this);
+ this->task_manager->queue_ike(this->task_manager);
+ return this->task_manager->initiate(this->task_manager);
+ }
/* we can't reauthenticate as responder when we use EAP or virtual IPs.
* If the peer does not support RFC4478, there is no way to keep the
* IKE_SA up. */
@@ -1650,6 +1751,7 @@ METHOD(ike_sa_t, reestablish, status_t,
new->set_other_host(new, host->clone(host));
host = this->my_host;
new->set_my_host(new, host->clone(host));
+ charon->bus->ike_reestablish_pre(charon->bus, &this->public, new);
/* resolve hosts but use the old addresses above as fallback */
resolve_hosts((private_ike_sa_t*)new);
/* if we already have a virtual IP, we reuse it */
@@ -1734,12 +1836,15 @@ METHOD(ike_sa_t, reestablish, status_t,
if (status == DESTROY_ME)
{
+ charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+ FALSE);
charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
status = FAILED;
}
else
{
- charon->bus->ike_reestablish(charon->bus, &this->public, new);
+ charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+ TRUE);
charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
status = SUCCESS;
}
@@ -1899,11 +2004,29 @@ static bool is_any_path_valid(private_ike_sa_t *this)
bool valid = FALSE;
enumerator_t *enumerator;
host_t *src = NULL, *addr;
+ int family = AF_UNSPEC;
+
+ switch (charon->socket->supported_families(charon->socket))
+ {
+ case SOCKET_FAMILY_IPV4:
+ family = AF_INET;
+ break;
+ case SOCKET_FAMILY_IPV6:
+ family = AF_INET6;
+ break;
+ case SOCKET_FAMILY_BOTH:
+ case SOCKET_FAMILY_NONE:
+ break;
+ }
DBG1(DBG_IKE, "old path is not available anymore, try to find another");
enumerator = create_peer_address_enumerator(this);
while (enumerator->enumerate(enumerator, &addr))
{
+ if (family != AF_UNSPEC && addr->get_family(addr) != family)
+ {
+ continue;
+ }
DBG1(DBG_IKE, "looking for a route to %H ...", addr);
src = hydra->kernel_interface->get_source_addr(
hydra->kernel_interface, addr, NULL);
@@ -2332,6 +2455,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
.inherit_pre = _inherit_pre,
.inherit_post = _inherit_post,
.generate_message = _generate_message,
+ .generate_message_fragmented = _generate_message_fragmented,
.reset = _reset,
.get_unique_id = _get_unique_id,
.add_virtual_ip = _add_virtual_ip,
@@ -2377,6 +2501,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
"%s.retry_initiate_interval", 0, lib->ns),
.flush_auth_cfg = lib->settings->get_bool(lib->settings,
"%s.flush_auth_cfg", FALSE, lib->ns),
+ .fragment_size = lib->settings->get_int(lib->settings,
+ "%s.fragment_size", 0, lib->ns),
);
if (version == IKEV2)
diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h
index 15fb47484..c72d87367 100644
--- a/src/libcharon/sa/ike_sa.h
+++ b/src/libcharon/sa/ike_sa.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2012 Tobias Brunner
+ * Copyright (C) 2006-2014 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -102,7 +102,7 @@ enum ike_extension_t {
EXT_EAP_ONLY_AUTHENTICATION = (1<<5),
/**
- * peer is probably a Windows 7 RAS client
+ * peer is probably a Windows RAS client
*/
EXT_MS_WINDOWS = (1<<6),
@@ -128,7 +128,7 @@ enum ike_extension_t {
EXT_NATT_DRAFT_02_03 = (1<<10),
/**
- * peer support proprietary IKE fragmentation
+ * peer supports proprietary IKEv1 or standardized IKEv2 fragmentation
*/
EXT_IKE_FRAGMENTATION = (1<<11),
};
@@ -756,7 +756,7 @@ struct ike_sa_t {
status_t (*roam)(ike_sa_t *this, bool address);
/**
- * Processes a incoming IKEv2-Message.
+ * Processes an incoming IKE message.
*
* Message processing may fail. If a critical failure occurs,
* process_message() return DESTROY_ME. Then the caller must
@@ -768,10 +768,10 @@ struct ike_sa_t {
* - FAILED
* - DESTROY_ME if this IKE_SA MUST be deleted
*/
- status_t (*process_message) (ike_sa_t *this, message_t *message);
+ status_t (*process_message)(ike_sa_t *this, message_t *message);
/**
- * Generate a IKE message to send it to the peer.
+ * Generate an IKE message to send it to the peer.
*
* This method generates all payloads in the message and encrypts/signs
* the packet.
@@ -783,8 +783,26 @@ struct ike_sa_t {
* - FAILED
* - DESTROY_ME if this IKE_SA MUST be deleted
*/
- status_t (*generate_message) (ike_sa_t *this, message_t *message,
- packet_t **packet);
+ status_t (*generate_message)(ike_sa_t *this, message_t *message,
+ packet_t **packet);
+
+ /**
+ * Generate an IKE message to send it to the peer. If enabled and supported
+ * it will be fragmented.
+ *
+ * This method generates all payloads in the message and encrypts/signs
+ * the packet/fragments.
+ *
+ * @param message message to generate
+ * @param packets enumerator of generated packet_t* (are not destroyed
+ * with the enumerator)
+ * @return
+ * - SUCCESS
+ * - FAILED
+ * - DESTROY_ME if this IKE_SA MUST be deleted
+ */
+ status_t (*generate_message_fragmented)(ike_sa_t *this, message_t *message,
+ enumerator_t **packets);
/**
* Retransmits a request.
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 8e68e7bee..bdabc59b5 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -384,11 +384,6 @@ struct private_ike_sa_manager_t {
rng_t *rng;
/**
- * SHA1 hasher for IKE_SA_INIT retransmit detection
- */
- hasher_t *hasher;
-
- /**
* reuse existing IKE_SAs in checkout_by_config
*/
bool reuse_ikesa;
@@ -962,49 +957,39 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this)
*
* @returns TRUE on success
*/
-static bool get_init_hash(private_ike_sa_manager_t *this, message_t *message,
- chunk_t *hash)
+static bool get_init_hash(hasher_t *hasher, message_t *message, chunk_t *hash)
{
host_t *src;
- if (!this->hasher)
- { /* this might be the case when flush() has been called */
- return FALSE;
- }
if (message->get_first_payload_type(message) == PLV1_FRAGMENT)
{ /* only hash the source IP, port and SPI for fragmented init messages */
u_int16_t port;
u_int64_t spi;
src = message->get_source(message);
- if (!this->hasher->allocate_hash(this->hasher,
- src->get_address(src), NULL))
+ if (!hasher->allocate_hash(hasher, src->get_address(src), NULL))
{
return FALSE;
}
port = src->get_port(src);
- if (!this->hasher->allocate_hash(this->hasher,
- chunk_from_thing(port), NULL))
+ if (!hasher->allocate_hash(hasher, chunk_from_thing(port), NULL))
{
return FALSE;
}
spi = message->get_initiator_spi(message);
- return this->hasher->allocate_hash(this->hasher,
- chunk_from_thing(spi), hash);
+ return hasher->allocate_hash(hasher, chunk_from_thing(spi), hash);
}
if (message->get_exchange_type(message) == ID_PROT)
{ /* include the source for Main Mode as the hash will be the same if
* SPIs are reused by two initiators that use the same proposal */
src = message->get_source(message);
- if (!this->hasher->allocate_hash(this->hasher,
- src->get_address(src), NULL))
+ if (!hasher->allocate_hash(hasher, src->get_address(src), NULL))
{
return FALSE;
}
}
- return this->hasher->allocate_hash(this->hasher,
- message->get_packet_data(message), hash);
+ return hasher->allocate_hash(hasher, message->get_packet_data(message), hash);
}
/**
@@ -1227,15 +1212,19 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
if (is_init)
{
+ hasher_t *hasher;
u_int64_t our_spi;
chunk_t hash;
- if (!get_init_hash(this, message, &hash))
+ hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+ if (!hasher || !get_init_hash(hasher, message, &hash))
{
DBG1(DBG_MGR, "ignoring message, failed to hash message");
+ DESTROY_IF(hasher);
id->destroy(id);
return NULL;
}
+ hasher->destroy(hasher);
/* ensure this is not a retransmit of an already handled init message */
switch (check_and_put_init_hash(this, hash, &our_spi))
@@ -1313,8 +1302,9 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
ike_id = entry->ike_sa->get_id(entry->ike_sa);
entry->checked_out = TRUE;
- if (message->get_first_payload_type(message) != PLV1_FRAGMENT)
- {
+ if (message->get_first_payload_type(message) != PLV1_FRAGMENT &&
+ message->get_first_payload_type(message) != PLV2_FRAGMENT)
+ { /* TODO-FRAG: this fails if there are unencrypted payloads */
entry->processing = get_message_id_or_hash(message);
}
if (ike_id->get_responder_spi(ike_id) == 0)
@@ -2058,8 +2048,6 @@ METHOD(ike_sa_manager_t, flush, void,
this->rng->destroy(this->rng);
this->rng = NULL;
- this->hasher->destroy(this->hasher);
- this->hasher = NULL;
}
METHOD(ike_sa_manager_t, destroy, void,
@@ -2134,18 +2122,10 @@ ike_sa_manager_t *ike_sa_manager_create()
},
);
- this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
- if (this->hasher == NULL)
- {
- DBG1(DBG_MGR, "manager initialization failed, no hasher supported");
- free(this);
- return NULL;
- }
this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (this->rng == NULL)
{
DBG1(DBG_MGR, "manager initialization failed, no RNG supported");
- this->hasher->destroy(this->hasher);
free(this);
return NULL;
}
diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c
index 114b8a3e4..d01a831f8 100644
--- a/src/libcharon/sa/ikev1/phase1.c
+++ b/src/libcharon/sa/ikev1/phase1.c
@@ -536,6 +536,7 @@ METHOD(phase1_t, select_config, peer_cfg_t*,
enumerator_t *enumerator;
peer_cfg_t *current;
host_t *me, *other;
+ int unusable = 0;
if (this->peer_cfg)
{ /* try to find an alternative config */
@@ -571,6 +572,10 @@ METHOD(phase1_t, select_config, peer_cfg_t*,
this->candidates->insert_last(this->candidates, current);
}
}
+ else
+ {
+ unusable++;
+ }
}
enumerator->destroy(enumerator);
@@ -580,6 +585,13 @@ METHOD(phase1_t, select_config, peer_cfg_t*,
this->peer_cfg->get_name(this->peer_cfg));
return this->peer_cfg->get_ref(this->peer_cfg);
}
+ if (unusable)
+ {
+ DBG1(DBG_IKE, "found %d matching config%s, but none allows %N "
+ "authentication using %s Mode", unusable, unusable > 1 ? "s" : "",
+ auth_method_names, method, aggressive ? "Aggressive" : "Main");
+ return NULL;
+ }
DBG1(DBG_IKE, "no peer config found");
return NULL;
}
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 97812a5c5..0f8e8bc6d 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2013 Tobias Brunner
+ * Copyright (C) 2007-2014 Tobias Brunner
* Copyright (C) 2007-2011 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -38,8 +38,7 @@
#include <processing/jobs/dpd_timeout_job.h>
#include <processing/jobs/process_message_job.h>
-#include <encoding/payloads/fragment_payload.h>
-#include <bio/bio_writer.h>
+#include <collections/array.h>
/**
* Number of old messages hashes we keep for retransmission.
@@ -51,20 +50,6 @@
#define MAX_OLD_HASHES 2
/**
- * Maximum packet size for fragmented packets (same as in sockets)
- */
-#define MAX_PACKET 10000
-
-/**
- * Maximum size of fragment data when sending packets (currently the same is
- * used for IPv4 and IPv6, even though the latter has a higher minimum datagram
- * size). 576 (= min. IPv4) - 20 (= IP header) - 8 (= UDP header) -
- * - 28 (= IKE header) - 8 (= fragment header) = 512
- * This is reduced by 4 in case of NAT-T (due to the non-ESP marker).
- */
-#define MAX_FRAGMENT_SIZE 512
-
-/**
* First sequence number of responding packets.
*
* To distinguish retransmission jobs for initiating and responding packets,
@@ -127,9 +112,9 @@ struct private_task_manager_t {
u_int32_t hash;
/**
- * packet for retransmission
+ * packet(s) for retransmission
*/
- packet_t *packet;
+ array_t *packets;
/**
* Sequence number of the last sent message
@@ -173,9 +158,9 @@ struct private_task_manager_t {
u_int retransmitted;
/**
- * packet for retransmission
+ * packet(s) for retransmission
*/
- packet_t *packet;
+ array_t *packets;
/**
* type of the initiated exchange
@@ -185,50 +170,9 @@ struct private_task_manager_t {
} initiating;
/**
- * Data used to reassemble a fragmented message
+ * Message we are currently defragmenting, if any (only one at a time)
*/
- struct {
-
- /**
- * Fragment ID (currently only one is supported at a time)
- */
- u_int16_t id;
-
- /**
- * The number of the last fragment (in case we receive the fragments out
- * of order), since the first starts with 1 this defines the number of
- * fragments we expect
- */
- u_int8_t last;
-
- /**
- * List of fragments (fragment_t*)
- */
- linked_list_t *list;
-
- /**
- * Length of all currently received fragments
- */
- size_t len;
-
- /**
- * Maximum length of a fragmented packet
- */
- size_t max_packet;
-
- /**
- * Maximum length of a single fragment (when sending)
- */
- size_t size;
-
- /**
- * The exchange type we use for fragments. Always the initial type even
- * for fragmented quick mode or transaction messages (i.e. either
- * ID_PROT or AGGRESSIVE)
- */
- exchange_type_t exchange;
-
- } frag;
+ message_t *defrag;
/**
* List of queued tasks not yet in action
@@ -277,31 +221,16 @@ struct private_task_manager_t {
};
/**
- * A single fragment within a fragmented message
+ * Reset retransmission packet list
*/
-typedef struct {
-
- /** fragment number */
- u_int8_t num;
-
- /** fragment data */
- chunk_t data;
-
-} fragment_t;
-
-static void fragment_destroy(fragment_t *this)
+static void clear_packets(array_t *array)
{
- chunk_free(&this->data);
- free(this);
-}
+ packet_t *packet;
-static void clear_fragments(private_task_manager_t *this, u_int16_t id)
-{
- DESTROY_FUNCTION_IF(this->frag.list, (void*)fragment_destroy);
- this->frag.list = NULL;
- this->frag.last = 0;
- this->frag.len = 0;
- this->frag.id = id;
+ while (array_remove(array, ARRAY_TAIL, &packet))
+ {
+ packet->destroy(packet);
+ }
}
METHOD(task_manager_t, flush_queue, void,
@@ -321,8 +250,7 @@ METHOD(task_manager_t, flush_queue, void,
list = this->active_tasks;
/* cancel pending retransmits */
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
- DESTROY_IF(this->initiating.packet);
- this->initiating.packet = NULL;
+ clear_packets(this->initiating.packets);
break;
case TASK_QUEUE_PASSIVE:
list = this->passive_tasks;
@@ -373,110 +301,53 @@ static bool activate_task(private_task_manager_t *this, task_type_t type)
}
/**
- * Send a single fragment with the given data
+ * Send packets in the given array (they get cloned)
*/
-static bool send_fragment(private_task_manager_t *this, bool request,
- host_t *src, host_t *dst, fragment_payload_t *fragment)
+static void send_packets(private_task_manager_t *this, array_t *packets)
{
- message_t *message;
+ enumerator_t *enumerator;
packet_t *packet;
- status_t status;
- message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
- /* other implementations seem to just use 0 as message ID, so here we go */
- message->set_message_id(message, 0);
- message->set_request(message, request);
- message->set_source(message, src->clone(src));
- message->set_destination(message, dst->clone(dst));
- message->set_exchange_type(message, this->frag.exchange);
- message->add_payload(message, (payload_t*)fragment);
-
- status = this->ike_sa->generate_message(this->ike_sa, message, &packet);
- if (status != SUCCESS)
+ enumerator = array_create_enumerator(packets);
+ while (enumerator->enumerate(enumerator, &packet))
{
- DBG1(DBG_IKE, "failed to generate IKE fragment");
- message->destroy(message);
- return FALSE;
+ charon->sender->send(charon->sender, packet->clone(packet));
}
- charon->sender->send(charon->sender, packet);
- message->destroy(message);
- return TRUE;
+ enumerator->destroy(enumerator);
}
/**
- * Send a packet, if supported and required do so in fragments
+ * Generates the given message and stores packet(s) in the given array
*/
-static bool send_packet(private_task_manager_t *this, bool request,
- packet_t *packet)
+static bool generate_message(private_task_manager_t *this, message_t *message,
+ array_t **packets)
{
- bool use_frags = FALSE;
- ike_cfg_t *ike_cfg;
- chunk_t data;
+ enumerator_t *fragments;
+ packet_t *fragment;
- ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
- if (ike_cfg)
+ if (this->ike_sa->generate_message_fragmented(this->ike_sa, message,
+ &fragments) != SUCCESS)
{
- switch (ike_cfg->fragmentation(ike_cfg))
- {
- case FRAGMENTATION_FORCE:
- use_frags = TRUE;
- break;
- case FRAGMENTATION_YES:
- use_frags = this->ike_sa->supports_extension(this->ike_sa,
- EXT_IKE_FRAGMENTATION);
- break;
- default:
- break;
- }
+ return FALSE;
}
- data = packet->get_data(packet);
- if (data.len > this->frag.size && use_frags)
+ while (fragments->enumerate(fragments, &fragment))
{
- fragment_payload_t *fragment;
- u_int8_t num, count;
- size_t len, frag_size;
- host_t *src, *dst;
-
- src = packet->get_source(packet);
- dst = packet->get_destination(packet);
-
- frag_size = this->frag.size;
- if (dst->get_port(dst) != IKEV2_UDP_PORT &&
- src->get_port(src) != IKEV2_UDP_PORT)
- { /* reduce size due to non-ESP marker */
- frag_size -= 4;
- }
- count = data.len / frag_size + (data.len % frag_size ? 1 : 0);
-
- DBG1(DBG_IKE, "sending IKE message with length of %zu bytes in "
- "%hhu fragments", data.len, count);
- for (num = 1; num <= count; num++)
- {
- len = min(data.len, frag_size);
- fragment = fragment_payload_create_from_data(num, num == count,
- chunk_create(data.ptr, len));
- if (!send_fragment(this, request, src, dst, fragment))
- {
- packet->destroy(packet);
- return FALSE;
- }
- data = chunk_skip(data, len);
- }
- packet->destroy(packet);
- return TRUE;
+ array_insert_create(packets, ARRAY_TAIL, fragment);
}
- charon->sender->send(charon->sender, packet);
+ fragments->destroy(fragments);
return TRUE;
}
/**
- * Retransmit a packet, either as initiator or as responder
+ * Retransmit a packet (or its fragments)
*/
-static status_t retransmit_packet(private_task_manager_t *this, bool request,
- u_int32_t seqnr, u_int mid, u_int retransmitted, packet_t *packet)
+static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr,
+ u_int mid, u_int retransmitted, array_t *packets)
{
+ packet_t *packet;
u_int32_t t;
+ array_get(packets, 0, &packet);
if (retransmitted > this->retransmit_tries)
{
DBG1(DBG_IKE, "giving up after %u retransmits", retransmitted - 1);
@@ -492,10 +363,7 @@ static status_t retransmit_packet(private_task_manager_t *this, bool request,
mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ);
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet);
}
- if (!send_packet(this, request, packet->clone(packet)))
- {
- return DESTROY_ME;
- }
+ send_packets(this, packets);
lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
retransmit_job_create(seqnr, this->ike_sa->get_id(this->ike_sa)), t);
return NEED_MORE;
@@ -506,20 +374,22 @@ METHOD(task_manager_t, retransmit, status_t,
{
status_t status = SUCCESS;
- if (seqnr == this->initiating.seqnr && this->initiating.packet)
+ if (seqnr == this->initiating.seqnr &&
+ array_count(this->initiating.packets))
{
- status = retransmit_packet(this, TRUE, seqnr, this->initiating.mid,
- this->initiating.retransmitted, this->initiating.packet);
+ status = retransmit_packet(this, seqnr, this->initiating.mid,
+ this->initiating.retransmitted, this->initiating.packets);
if (status == NEED_MORE)
{
this->initiating.retransmitted++;
status = SUCCESS;
}
}
- if (seqnr == this->responding.seqnr && this->responding.packet)
+ if (seqnr == this->responding.seqnr &&
+ array_count(this->responding.packets))
{
- status = retransmit_packet(this, FALSE, seqnr, this->responding.mid,
- this->responding.retransmitted, this->responding.packet);
+ status = retransmit_packet(this, seqnr, this->responding.mid,
+ this->responding.retransmitted, this->responding.packets);
if (status == NEED_MORE)
{
this->responding.retransmitted++;
@@ -586,7 +456,6 @@ METHOD(task_manager_t, initiate, status_t,
task_t *task;
message_t *message;
host_t *me, *other;
- status_t status;
exchange_type_t exchange = EXCHANGE_TYPE_UNDEFINED;
bool new_mid = FALSE, expect_response = FALSE, cancelled = FALSE, keep = FALSE;
@@ -790,10 +659,8 @@ METHOD(task_manager_t, initiate, status_t,
return initiate(this);
}
- DESTROY_IF(this->initiating.packet);
- status = this->ike_sa->generate_message(this->ike_sa, message,
- &this->initiating.packet);
- if (status != SUCCESS)
+ clear_packets(this->initiating.packets);
+ if (!generate_message(this, message, &this->initiating.packets))
{
/* message generation failed. There is nothing more to do than to
* close the SA */
@@ -811,13 +678,12 @@ METHOD(task_manager_t, initiate, status_t,
}
if (keep)
{ /* keep the packet for retransmission, the responder might request it */
- send_packet(this, TRUE,
- this->initiating.packet->clone(this->initiating.packet));
+ send_packets(this, this->initiating.packets);
}
else
{
- send_packet(this, TRUE, this->initiating.packet);
- this->initiating.packet = NULL;
+ send_packets(this, this->initiating.packets);
+ clear_packets(this->initiating.packets);
}
message->destroy(message);
@@ -848,7 +714,6 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
message_t *message;
host_t *me, *other;
bool delete = FALSE, cancelled = FALSE, expect_request = FALSE;
- status_t status;
me = request->get_destination(request);
other = request->get_source(request);
@@ -900,28 +765,25 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
}
enumerator->destroy(enumerator);
- DESTROY_IF(this->responding.packet);
- this->responding.packet = NULL;
+ clear_packets(this->responding.packets);
if (cancelled)
{
message->destroy(message);
return initiate(this);
}
- status = this->ike_sa->generate_message(this->ike_sa, message,
- &this->responding.packet);
- message->destroy(message);
- if (status != SUCCESS)
+ if (!generate_message(this, message, &this->responding.packets))
{
+ message->destroy(message);
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
return DESTROY_ME;
}
+ message->destroy(message);
if (expect_request && !delete)
{
return retransmit(this, this->responding.seqnr);
}
- send_packet(this, FALSE,
- this->responding.packet->clone(this->responding.packet));
+ send_packets(this, this->responding.packets);
if (delete)
{
return DESTROY_ME;
@@ -937,7 +799,7 @@ static void send_notify(private_task_manager_t *this, message_t *request,
notify_type_t type)
{
message_t *response;
- packet_t *packet;
+ array_t *packets = NULL;
host_t *me, *other;
u_int32_t mid;
@@ -973,11 +835,12 @@ static void send_notify(private_task_manager_t *this, message_t *request,
}
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)
+ if (generate_message(this, response, &packets))
{
- send_packet(this, TRUE, packet);
+ send_packets(this, packets);
}
+ clear_packets(packets);
+ array_destroy(packets);
response->destroy(response);
}
@@ -1075,7 +938,6 @@ static status_t process_request(private_task_manager_t *this,
this->passive_tasks->insert_last(this->passive_tasks, task);
task = (task_t *)isakmp_natd_create(this->ike_sa, FALSE);
this->passive_tasks->insert_last(this->passive_tasks, task);
- this->frag.exchange = AGGRESSIVE;
break;
case QUICK_MODE:
if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
@@ -1164,8 +1026,7 @@ static status_t process_request(private_task_manager_t *this,
else
{ /* We don't send a response, so don't retransmit one if we get
* the same message again. */
- DESTROY_IF(this->responding.packet);
- this->responding.packet = NULL;
+ clear_packets(this->responding.packets);
}
if (this->passive_tasks->get_count(this->passive_tasks) == 0 &&
this->queued_tasks->get_count(this->queued_tasks) > 0)
@@ -1237,8 +1098,7 @@ static status_t process_response(private_task_manager_t *this,
enumerator->destroy(enumerator);
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
- DESTROY_IF(this->initiating.packet);
- this->initiating.packet = NULL;
+ clear_packets(this->initiating.packets);
if (this->queued && this->active_tasks->get_count(this->active_tasks) == 0)
{
@@ -1258,107 +1118,23 @@ static status_t process_response(private_task_manager_t *this,
static status_t handle_fragment(private_task_manager_t *this, message_t *msg)
{
- fragment_payload_t *payload;
- enumerator_t *enumerator;
- fragment_t *fragment;
- status_t status = SUCCESS;
- chunk_t data;
- u_int8_t num;
-
- payload = (fragment_payload_t*)msg->get_payload(msg, PLV1_FRAGMENT);
- if (!payload)
- {
- return FAILED;
- }
-
- if (!this->frag.list || this->frag.id != payload->get_id(payload))
- {
- clear_fragments(this, payload->get_id(payload));
- this->frag.list = linked_list_create();
- }
-
- num = payload->get_number(payload);
- if (!this->frag.last && payload->is_last(payload))
- {
- this->frag.last = num;
- }
+ status_t status;
- enumerator = this->frag.list->create_enumerator(this->frag.list);
- while (enumerator->enumerate(enumerator, &fragment))
+ if (!this->defrag)
{
- if (fragment->num == num)
- { /* ignore a duplicate fragment */
- DBG1(DBG_IKE, "received duplicate fragment #%hhu", num);
- enumerator->destroy(enumerator);
- return NEED_MORE;
- }
- if (fragment->num > num)
+ this->defrag = message_create_defrag(msg);
+ if (!this->defrag)
{
- break;
+ return FAILED;
}
}
-
- data = payload->get_data(payload);
- this->frag.len += data.len;
- if (this->frag.len > this->frag.max_packet)
- {
- DBG1(DBG_IKE, "fragmented IKE message is too large");
- enumerator->destroy(enumerator);
- clear_fragments(this, 0);
- return FAILED;
- }
-
- INIT(fragment,
- .num = num,
- .data = chunk_clone(data),
- );
-
- this->frag.list->insert_before(this->frag.list, enumerator, fragment);
- enumerator->destroy(enumerator);
-
- if (this->frag.list->get_count(this->frag.list) == this->frag.last)
+ status = this->defrag->add_fragment(this->defrag, msg);
+ if (status == SUCCESS)
{
- message_t *message;
- packet_t *pkt;
- host_t *src, *dst;
- bio_writer_t *writer;
-
- writer = bio_writer_create(this->frag.len);
- DBG1(DBG_IKE, "received fragment #%hhu, reassembling fragmented IKE "
- "message", num);
- enumerator = this->frag.list->create_enumerator(this->frag.list);
- while (enumerator->enumerate(enumerator, &fragment))
- {
- writer->write_data(writer, fragment->data);
- }
- enumerator->destroy(enumerator);
-
- src = msg->get_source(msg);
- dst = msg->get_destination(msg);
- pkt = packet_create_from_data(src->clone(src), dst->clone(dst),
- writer->extract_buf(writer));
- writer->destroy(writer);
-
- message = message_create_from_packet(pkt);
- if (message->parse_header(message) != SUCCESS)
- {
- DBG1(DBG_IKE, "failed to parse header of reassembled IKE message");
- message->destroy(message);
- status = FAILED;
- }
- else
- {
- lib->processor->queue_job(lib->processor,
- (job_t*)process_message_job_create(message));
- status = NEED_MORE;
-
- }
- clear_fragments(this, 0);
- }
- else
- { /* there are some fragments missing */
- DBG1(DBG_IKE, "received fragment #%hhu, waiting for complete IKE "
- "message", num);
+ lib->processor->queue_job(lib->processor,
+ (job_t*)process_message_job_create(this->defrag));
+ this->defrag = NULL;
+ /* do not process the last fragment */
status = NEED_MORE;
}
return status;
@@ -1435,15 +1211,14 @@ METHOD(task_manager_t, process_message, status_t,
{
if (this->initiating.old_hashes[i] == hash)
{
- if (this->initiating.packet &&
+ if (array_count(this->initiating.packets) &&
i == (this->initiating.old_hash_pos % MAX_OLD_HASHES) &&
(msg->get_exchange_type(msg) == QUICK_MODE ||
msg->get_exchange_type(msg) == AGGRESSIVE))
{
DBG1(DBG_IKE, "received retransmit of response with ID %u, "
"resending last request", mid);
- send_packet(this, TRUE,
- this->initiating.packet->clone(this->initiating.packet));
+ send_packets(this, this->initiating.packets);
return SUCCESS;
}
DBG1(DBG_IKE, "received retransmit of response with ID %u, "
@@ -1484,20 +1259,18 @@ METHOD(task_manager_t, process_message, status_t,
{
if (hash == this->responding.hash)
{
- if (this->responding.packet)
+ if (array_count(this->responding.packets))
{
DBG1(DBG_IKE, "received retransmit of request with ID %u, "
"retransmitting response", mid);
- send_packet(this, FALSE,
- this->responding.packet->clone(this->responding.packet));
+ send_packets(this, this->responding.packets);
}
- else if (this->initiating.packet &&
+ else if (array_count(this->initiating.packets) &&
this->initiating.type == INFORMATIONAL_V1)
{
DBG1(DBG_IKE, "received retransmit of DPD request, "
"retransmitting response");
- send_packet(this, TRUE,
- this->initiating.packet->clone(this->initiating.packet));
+ send_packets(this, this->initiating.packets);
}
else
{
@@ -1593,13 +1366,6 @@ METHOD(task_manager_t, process_message, status_t,
return SUCCESS;
}
-METHOD(task_manager_t, queue_task, void,
- private_task_manager_t *this, task_t *task)
-{
- 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
*/
@@ -1622,6 +1388,28 @@ static bool has_queued(private_task_manager_t *this, task_type_t type)
return found;
}
+METHOD(task_manager_t, queue_task, void,
+ private_task_manager_t *this, task_t *task)
+{
+ task_type_t type = task->get_type(task);
+
+ switch (type)
+ {
+ case TASK_MODE_CONFIG:
+ case TASK_XAUTH:
+ if (has_queued(this, type))
+ {
+ task->destroy(task);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+ this->queued_tasks->insert_last(this->queued_tasks, task);
+}
+
METHOD(task_manager_t, queue_ike, void,
private_task_manager_t *this)
{
@@ -1642,7 +1430,6 @@ METHOD(task_manager_t, queue_ike, void,
{
queue_task(this, (task_t*)aggressive_mode_create(this->ike_sa, TRUE));
}
- this->frag.exchange = AGGRESSIVE;
}
else
{
@@ -1969,17 +1756,16 @@ METHOD(task_manager_t, reset, void,
task_t *task;
/* reset message counters and retransmit packets */
- DESTROY_IF(this->responding.packet);
- DESTROY_IF(this->initiating.packet);
- this->responding.packet = NULL;
+ clear_packets(this->responding.packets);
+ clear_packets(this->initiating.packets);
this->responding.seqnr = RESPONDING_SEQ;
this->responding.retransmitted = 0;
- this->initiating.packet = NULL;
this->initiating.mid = 0;
this->initiating.seqnr = 0;
this->initiating.retransmitted = 0;
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
- clear_fragments(this, 0);
+ DESTROY_IF(this->defrag);
+ this->defrag = NULL;
if (initiate != UINT_MAX)
{
this->dpd_send = initiate;
@@ -2030,11 +1816,13 @@ METHOD(task_manager_t, destroy, void,
this->active_tasks->destroy(this->active_tasks);
this->queued_tasks->destroy(this->queued_tasks);
this->passive_tasks->destroy(this->passive_tasks);
- clear_fragments(this, 0);
+ DESTROY_IF(this->defrag);
DESTROY_IF(this->queued);
- DESTROY_IF(this->responding.packet);
- DESTROY_IF(this->initiating.packet);
+ clear_packets(this->responding.packets);
+ array_destroy(this->responding.packets);
+ clear_packets(this->initiating.packets);
+ array_destroy(this->initiating.packets);
DESTROY_IF(this->rng);
free(this);
}
@@ -2079,13 +1867,6 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
.responding = {
.seqnr = RESPONDING_SEQ,
},
- .frag = {
- .exchange = ID_PROT,
- .max_packet = lib->settings->get_int(lib->settings,
- "%s.max_packet", MAX_PACKET, lib->ns),
- .size = lib->settings->get_int(lib->settings,
- "%s.fragment_size", MAX_FRAGMENT_SIZE, lib->ns),
- },
.ike_sa = ike_sa,
.rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
.queued_tasks = linked_list_create(),
diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
index 7009ae95d..710bf1cd2 100644
--- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
@@ -475,6 +475,9 @@ METHOD(task_t, process_r, status_t,
}
case AM_AUTH:
{
+ adopt_children_job_t *job = NULL;
+ xauth_t *xauth = NULL;
+
while (TRUE)
{
if (this->ph1->verify_auth(this->ph1, this->method, message,
@@ -504,8 +507,8 @@ METHOD(task_t, process_r, status_t,
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)xauth_create(this->ike_sa, TRUE));
+ xauth = xauth_create(this->ike_sa, TRUE);
+ this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth);
break;
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
@@ -524,9 +527,8 @@ METHOD(task_t, process_r, status_t,
{
return send_delete(this);
}
- lib->processor->queue_job(lib->processor, (job_t*)
- adopt_children_job_create(
- this->ike_sa->get_id(this->ike_sa)));
+ job = adopt_children_job_create(
+ this->ike_sa->get_id(this->ike_sa));
break;
}
/* check for and prepare mode config push/pull */
@@ -542,10 +544,26 @@ METHOD(task_t, process_r, status_t,
{
if (!this->peer_cfg->use_pull_mode(this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ if (job)
+ {
+ job->queue_task(job, (task_t*)
+ mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
+ else if (xauth)
+ {
+ xauth->queue_mode_config_push(xauth);
+ }
+ else
+ {
+ this->ike_sa->queue_task(this->ike_sa, (task_t*)
+ mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
}
+ if (job)
+ {
+ lib->processor->queue_job(lib->processor, (job_t*)job);
+ }
return SUCCESS;
}
default:
diff --git a/src/libcharon/sa/ikev1/tasks/informational.c b/src/libcharon/sa/ikev1/tasks/informational.c
index b742dbef9..2798978b2 100644
--- a/src/libcharon/sa/ikev1/tasks/informational.c
+++ b/src/libcharon/sa/ikev1/tasks/informational.c
@@ -112,16 +112,16 @@ METHOD(task_t, process_r, status_t,
IKEV2_UDP_PORT);
if (redirect)
{ /* treat the redirect as reauthentication */
- DBG1(DBG_IKE, "received %N notify. redirected to %H",
+ DBG1(DBG_IKE, "received %N notify, redirected to %H",
notify_type_names, type, redirect);
/* Cisco boxes reject the first message from 4500 */
me = this->ike_sa->get_my_host(this->ike_sa);
me->set_port(me, charon->socket->get_port(
charon->socket, FALSE));
this->ike_sa->set_other_host(this->ike_sa, redirect);
- this->ike_sa->reauth(this->ike_sa);
+ status = this->ike_sa->reauth(this->ike_sa);
enumerator->destroy(enumerator);
- return DESTROY_ME;
+ return status;
}
else
{
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
index 426c4bd69..0162fd84e 100644
--- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
@@ -15,7 +15,7 @@
*/
/*
- * Copyright (C) 2012 Volker Rümelin
+ * Copyright (C) 2012-2014 Volker Rümelin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -106,10 +106,15 @@ static struct {
"\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"},
/* Proprietary IKE fragmentation extension. Capabilities are handled
- * specially on receipt of this VID. */
+ * specially on receipt of this VID. Windows peers send this VID
+ * without capabilities, but accept it with and without capabilities. */
{ "FRAGMENTATION", EXT_IKE_FRAGMENTATION, FALSE, 20,
"\x40\x48\xb7\xd5\x6e\xbc\xe8\x85\x25\xe7\xde\x7f\x00\xd6\xc2\xd3\x80\x00\x00\x00"},
+ /* Windows peers send this VID and a version number */
+ { "MS NT5 ISAKMPOAKLEY", EXT_MS_WINDOWS, FALSE, 20,
+ "\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00\x00\x00"},
+
}, vendor_natt_ids[] = {
/* NAT-Traversal VIDs ordered by preference */
@@ -167,15 +172,27 @@ static struct {
*/
static const u_int32_t fragmentation_ike = 0x80000000;
-/**
- * Check if the given vendor ID indicate support for fragmentation
- */
-static bool fragmentation_supported(chunk_t data, int i)
+static bool is_known_vid(chunk_t data, int i)
{
- if (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION &&
- data.len == 20 && memeq(data.ptr, vendor_ids[i].id, 16))
+ switch (vendor_ids[i].extension)
{
- return untoh32(&data.ptr[16]) & fragmentation_ike;
+ case EXT_IKE_FRAGMENTATION:
+ if (data.len >= 16 && memeq(data.ptr, vendor_ids[i].id, 16))
+ {
+ switch (data.len)
+ {
+ case 16:
+ return TRUE;
+ case 20:
+ return untoh32(&data.ptr[16]) & fragmentation_ike;
+ }
+ }
+ break;
+ case EXT_MS_WINDOWS:
+ return data.len == 20 && memeq(data.ptr, vendor_ids[i].id, 16);
+ default:
+ return chunk_equals(data, chunk_create(vendor_ids[i].id,
+ vendor_ids[i].len));
}
return FALSE;
}
@@ -251,9 +268,7 @@ static void process(private_isakmp_vendor_t *this, message_t *message)
for (i = 0; i < countof(vendor_ids); i++)
{
- if (chunk_equals(data, chunk_create(vendor_ids[i].id,
- vendor_ids[i].len)) ||
- fragmentation_supported(data, i))
+ if (is_known_vid(data, i))
{
DBG1(DBG_IKE, "received %s vendor ID", vendor_ids[i].desc);
if (vendor_ids[i].extension)
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c
index 8a5d9ae16..2fb4c6935 100644
--- a/src/libcharon/sa/ikev1/tasks/main_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.c
@@ -479,6 +479,8 @@ METHOD(task_t, build_r, status_t,
{
id_payload_t *id_payload;
identification_t *id;
+ adopt_children_job_t *job = NULL;
+ xauth_t *xauth = NULL;
id = this->ph1->get_id(this->ph1, this->peer_cfg, TRUE);
if (!id)
@@ -502,8 +504,8 @@ METHOD(task_t, build_r, status_t,
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)xauth_create(this->ike_sa, TRUE));
+ xauth = xauth_create(this->ike_sa, TRUE);
+ this->ike_sa->queue_task(this->ike_sa, (task_t*)xauth);
break;
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
@@ -522,9 +524,8 @@ METHOD(task_t, build_r, status_t,
{
return send_notify(this, AUTHENTICATION_FAILED);
}
- lib->processor->queue_job(lib->processor, (job_t*)
- adopt_children_job_create(
- this->ike_sa->get_id(this->ike_sa)));
+ job = adopt_children_job_create(
+ this->ike_sa->get_id(this->ike_sa));
break;
}
if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
@@ -539,10 +540,26 @@ METHOD(task_t, build_r, status_t,
{
if (!this->peer_cfg->use_pull_mode(this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ if (job)
+ {
+ job->queue_task(job, (task_t*)
+ mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
+ else if (xauth)
+ {
+ xauth->queue_mode_config_push(xauth);
+ }
+ else
+ {
+ this->ike_sa->queue_task(this->ike_sa, (task_t*)
+ mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
}
+ if (job)
+ {
+ lib->processor->queue_job(lib->processor, (job_t*)job);
+ }
return SUCCESS;
}
default:
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index e6273682d..1133aab65 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -1030,7 +1030,8 @@ METHOD(task_t, process_r, status_t,
}
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
- if (!this->config || !this->tsi || !this->tsr)
+ if (!this->config || !this->tsi || !this->tsr ||
+ this->mode != this->config->get_mode(this->config))
{
DBG1(DBG_IKE, "no matching CHILD_SA config found");
return send_notify(this, INVALID_ID_INFORMATION);
@@ -1117,11 +1118,22 @@ METHOD(task_t, process_r, status_t,
}
case QM_NEGOTIATED:
{
- if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
- has_notify_errors(this, message))
+ if (has_notify_errors(this, message))
{
return SUCCESS;
}
+ if (message->get_exchange_type(message) == INFORMATIONAL_V1)
+ {
+ if (message->get_payload(message, PLV1_DELETE))
+ {
+ /* If the DELETE for a Quick Mode follows immediately
+ * after rekeying, we might receive it before the
+ * third completing Quick Mode message. Ignore it, as
+ * it gets handled by a separately queued delete task. */
+ return NEED_MORE;
+ }
+ return SUCCESS;
+ }
if (!install(this))
{
ike_sa_t *ike_sa = this->ike_sa;
@@ -1198,6 +1210,14 @@ METHOD(task_t, build_r, status_t,
this->state = QM_NEGOTIATED;
return NEED_MORE;
}
+ case QM_NEGOTIATED:
+ if (message->get_exchange_type(message) == INFORMATIONAL_V1)
+ {
+ /* skip INFORMATIONAL response if we received a INFORMATIONAL
+ * delete, see process_r() */
+ return ALREADY_DONE;
+ }
+ /* fall */
default:
return FAILED;
}
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c
index bdc5d67f7..a770e90ff 100644
--- a/src/libcharon/sa/ikev1/tasks/xauth.c
+++ b/src/libcharon/sa/ikev1/tasks/xauth.c
@@ -19,6 +19,7 @@
#include <hydra.h>
#include <encoding/payloads/cp_payload.h>
#include <processing/jobs/adopt_children_job.h>
+#include <sa/ikev1/tasks/mode_config.h>
typedef struct private_xauth_t private_xauth_t;
@@ -74,6 +75,11 @@ struct private_xauth_t {
* status of Xauth exchange
*/
xauth_status_t status;
+
+ /**
+ * Queue a Mode Config Push mode after completing XAuth?
+ */
+ bool mode_config_push;
};
/**
@@ -290,6 +296,7 @@ METHOD(task_t, process_i_status, status_t,
private_xauth_t *this, message_t *message)
{
cp_payload_t *cp;
+ adopt_children_job_t *job;
cp = (cp_payload_t*)message->get_payload(message, PLV1_CONFIGURATION);
if (!cp || cp->get_type(cp) != CFG_ACK)
@@ -307,8 +314,13 @@ METHOD(task_t, process_i_status, status_t,
return FAILED;
}
this->ike_sa->set_condition(this->ike_sa, COND_XAUTH_AUTHENTICATED, TRUE);
- lib->processor->queue_job(lib->processor, (job_t*)
- adopt_children_job_create(this->ike_sa->get_id(this->ike_sa)));
+ job = adopt_children_job_create(this->ike_sa->get_id(this->ike_sa));
+ if (this->mode_config_push)
+ {
+ job->queue_task(job,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
+ lib->processor->queue_job(lib->processor, (job_t*)job);
return SUCCESS;
}
@@ -511,6 +523,12 @@ METHOD(task_t, migrate, void,
}
}
+METHOD(xauth_t, queue_mode_config_push, void,
+ private_xauth_t *this)
+{
+ this->mode_config_push = TRUE;
+}
+
METHOD(task_t, destroy, void,
private_xauth_t *this)
{
@@ -533,6 +551,7 @@ xauth_t *xauth_create(ike_sa_t *ike_sa, bool initiator)
.migrate = _migrate,
.destroy = _destroy,
},
+ .queue_mode_config_push = _queue_mode_config_push,
},
.initiator = initiator,
.ike_sa = ike_sa,
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.h b/src/libcharon/sa/ikev1/tasks/xauth.h
index 303eb31ce..ffaf32a32 100644
--- a/src/libcharon/sa/ikev1/tasks/xauth.h
+++ b/src/libcharon/sa/ikev1/tasks/xauth.h
@@ -36,6 +36,11 @@ struct xauth_t {
* Implements the task_t interface
*/
task_t task;
+
+ /**
+ * Queue a Mode Config in Push mode after completing XAuth.
+ */
+ void (*queue_mode_config_push)(xauth_t *this);
};
/**
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index ada798bdc..eb7df3516 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 Tobias Brunner
+ * Copyright (C) 2007-2014 Tobias Brunner
* Copyright (C) 2007-2010 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -90,9 +90,14 @@ struct private_task_manager_t {
u_int32_t mid;
/**
- * packet for retransmission
+ * packet(s) for retransmission
*/
- packet_t *packet;
+ array_t *packets;
+
+ /**
+ * Helper to defragment the request
+ */
+ message_t *defrag;
} responding;
@@ -111,15 +116,25 @@ struct private_task_manager_t {
u_int retransmitted;
/**
- * packet for retransmission
+ * packet(s) for retransmission
*/
- packet_t *packet;
+ array_t *packets;
/**
* type of the initated exchange
*/
exchange_type_t type;
+ /**
+ * TRUE if exchange was deferred because no path was available
+ */
+ bool deferred;
+
+ /**
+ * Helper to defragment the response
+ */
+ message_t *defrag;
+
} initiating;
/**
@@ -158,6 +173,19 @@ struct private_task_manager_t {
double retransmit_base;
};
+/**
+ * Reset retransmission packet list
+ */
+static void clear_packets(array_t *array)
+{
+ packet_t *packet;
+
+ while (array_remove(array, ARRAY_TAIL, &packet))
+ {
+ packet->destroy(packet);
+ }
+}
+
METHOD(task_manager_t, flush_queue, void,
private_task_manager_t *this, task_queue_t queue)
{
@@ -217,10 +245,60 @@ static bool activate_task(private_task_manager_t *this, task_type_t type)
return found;
}
+/**
+ * Send packets in the given array (they get cloned). Optionally, the
+ * source and destination addresses are changed before sending it.
+ */
+static void send_packets(private_task_manager_t *this, array_t *packets,
+ host_t *src, host_t *dst)
+{
+ packet_t *packet, *clone;
+ int i;
+
+ for (i = 0; i < array_count(packets); i++)
+ {
+ array_get(packets, i, &packet);
+ clone = packet->clone(packet);
+ if (src)
+ {
+ clone->set_source(clone, src->clone(src));
+ }
+ if (dst)
+ {
+ clone->set_destination(clone, dst->clone(dst));
+ }
+ charon->sender->send(charon->sender, clone);
+ }
+}
+
+/**
+ * Generates the given message and stores packet(s) in the given array
+ */
+static bool generate_message(private_task_manager_t *this, message_t *message,
+ array_t **packets)
+{
+ enumerator_t *fragments;
+ packet_t *fragment;
+
+ if (this->ike_sa->generate_message_fragmented(this->ike_sa, message,
+ &fragments) != SUCCESS)
+ {
+ return FALSE;
+ }
+ while (fragments->enumerate(fragments, &fragment))
+ {
+ array_insert_create(packets, ARRAY_TAIL, fragment);
+ }
+ fragments->destroy(fragments);
+ array_compress(*packets);
+ return TRUE;
+}
+
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)
+ if (message_id == this->initiating.mid &&
+ array_count(this->initiating.packets))
{
u_int32_t timeout;
job_t *job;
@@ -229,23 +307,24 @@ METHOD(task_manager_t, retransmit, status_t,
task_t *task;
ike_mobike_t *mobike = NULL;
+ array_get(this->initiating.packets, 0, &packet);
+
/* check if we are retransmitting a MOBIKE routability check */
- enumerator = array_create_enumerator(this->active_tasks);
- while (enumerator->enumerate(enumerator, (void*)&task))
+ if (this->initiating.type == INFORMATIONAL)
{
- if (task->get_type(task) == TASK_IKE_MOBIKE)
+ enumerator = array_create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, (void*)&task))
{
- mobike = (ike_mobike_t*)task;
- if (!mobike->is_probing(mobike))
+ if (task->get_type(task) == TASK_IKE_MOBIKE)
{
- mobike = NULL;
+ mobike = (ike_mobike_t*)task;
+ break;
}
- break;
}
+ enumerator->destroy(enumerator);
}
- enumerator->destroy(enumerator);
- if (mobike == NULL)
+ if (!mobike || !mobike->is_probing(mobike))
{
if (this->initiating.retransmitted <= this->retransmit_tries)
{
@@ -257,7 +336,7 @@ METHOD(task_manager_t, retransmit, status_t,
DBG1(DBG_IKE, "giving up after %d retransmits",
this->initiating.retransmitted - 1);
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT,
- this->initiating.packet);
+ packet);
return DESTROY_ME;
}
@@ -265,11 +344,29 @@ METHOD(task_manager_t, retransmit, status_t,
{
DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
this->initiating.retransmitted, message_id);
- charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND,
- this->initiating.packet);
+ charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet);
+ }
+ if (!mobike)
+ {
+ send_packets(this, this->initiating.packets,
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa));
+ }
+ else
+ {
+ if (!mobike->transmit(mobike, packet))
+ {
+ DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update "
+ "deferred");
+ this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
+ this->initiating.deferred = TRUE;
+ return SUCCESS;
+ }
+ else if (mobike->is_probing(mobike))
+ {
+ timeout = ROUTEABILITY_CHECK_INTERVAL;
+ }
}
- packet = this->initiating.packet->clone(this->initiating.packet);
- charon->sender->send(charon->sender, packet);
}
else
{ /* for routeability checks, we use a more aggressive behavior */
@@ -289,7 +386,16 @@ METHOD(task_manager_t, retransmit, status_t,
DBG1(DBG_IKE, "path probing attempt %d",
this->initiating.retransmitted);
}
- mobike->transmit(mobike, this->initiating.packet);
+ /* TODO-FRAG: presumably these small packets are not fragmented,
+ * we should maybe ensure this is the case when generating them */
+ if (!mobike->transmit(mobike, packet))
+ {
+ DBG1(DBG_IKE, "no route found to reach peer, path probing "
+ "deferred");
+ this->ike_sa->set_condition(this->ike_sa, COND_STALE, TRUE);
+ this->initiating.deferred = TRUE;
+ return SUCCESS;
+ }
}
this->initiating.retransmitted++;
@@ -307,7 +413,6 @@ METHOD(task_manager_t, initiate, status_t,
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)
@@ -315,6 +420,12 @@ METHOD(task_manager_t, initiate, status_t,
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 */
+ if (this->initiating.deferred)
+ { /* re-initiate deferred exchange */
+ this->initiating.deferred = FALSE;
+ this->initiating.retransmitted = 0;
+ return retransmit(this, this->initiating.mid);
+ }
return SUCCESS;
}
@@ -347,39 +458,39 @@ METHOD(task_manager_t, initiate, status_t,
}
break;
case IKE_ESTABLISHED:
- if (activate_task(this, TASK_CHILD_CREATE))
+ if (activate_task(this, TASK_IKE_MOBIKE))
{
- exchange = CREATE_CHILD_SA;
+ exchange = INFORMATIONAL;
break;
}
- if (activate_task(this, TASK_CHILD_DELETE))
+ if (activate_task(this, TASK_IKE_DELETE))
{
exchange = INFORMATIONAL;
break;
}
- if (activate_task(this, TASK_CHILD_REKEY))
+ if (activate_task(this, TASK_CHILD_DELETE))
{
- exchange = CREATE_CHILD_SA;
+ exchange = INFORMATIONAL;
break;
}
- if (activate_task(this, TASK_IKE_DELETE))
+ if (activate_task(this, TASK_IKE_REAUTH))
{
exchange = INFORMATIONAL;
break;
}
- if (activate_task(this, TASK_IKE_REKEY))
+ if (activate_task(this, TASK_CHILD_CREATE))
{
exchange = CREATE_CHILD_SA;
break;
}
- if (activate_task(this, TASK_IKE_REAUTH))
+ if (activate_task(this, TASK_CHILD_REKEY))
{
- exchange = INFORMATIONAL;
+ exchange = CREATE_CHILD_SA;
break;
}
- if (activate_task(this, TASK_IKE_MOBIKE))
+ if (activate_task(this, TASK_IKE_REKEY))
{
- exchange = INFORMATIONAL;
+ exchange = CREATE_CHILD_SA;
break;
}
if (activate_task(this, TASK_IKE_DPD))
@@ -458,6 +569,7 @@ METHOD(task_manager_t, initiate, status_t,
message->set_exchange_type(message, exchange);
this->initiating.type = exchange;
this->initiating.retransmitted = 0;
+ this->initiating.deferred = FALSE;
enumerator = array_create_enumerator(this->active_tasks);
while (enumerator->enumerate(enumerator, &task))
@@ -493,9 +605,7 @@ METHOD(task_manager_t, initiate, status_t,
/* 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)
+ if (!generate_message(this, message, &this->initiating.packets))
{
/* message generation failed. There is nothing more to do than to
* close the SA */
@@ -567,8 +677,7 @@ static status_t process_response(private_task_manager_t *this,
this->initiating.mid++;
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
- this->initiating.packet->destroy(this->initiating.packet);
- this->initiating.packet = NULL;
+ clear_packets(this->initiating.packets);
array_compress(this->active_tasks);
@@ -636,8 +745,8 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
host_t *me, *other;
bool delete = FALSE, hook = FALSE;
ike_sa_id_t *id = NULL;
- u_int64_t responder_spi;
- status_t status;
+ u_int64_t responder_spi = 0;
+ bool result;
me = request->get_destination(request);
other = request->get_source(request);
@@ -699,23 +808,20 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
}
/* 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);
+ clear_packets(this->responding.packets);
+ result = generate_message(this, message, &this->responding.packets);
message->destroy(message);
if (id)
{
id->set_responder_spi(id, responder_spi);
}
- if (status != SUCCESS)
+ if (!result)
{
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));
+ send_packets(this, this->responding.packets, NULL, NULL);
if (delete)
{
if (hook)
@@ -964,6 +1070,48 @@ METHOD(task_manager_t, incr_mid, void,
}
/**
+ * Handle the given IKE fragment, if it is one.
+ *
+ * Returns SUCCESS if the message is not a fragment, and NEED_MORE if it was
+ * handled properly. Error states are returned if the fragment was invalid or
+ * the reassembled message could not have been processed properly.
+ */
+static status_t handle_fragment(private_task_manager_t *this,
+ message_t **defrag, message_t *msg)
+{
+ message_t *reassembled;
+ status_t status;
+
+ if (!msg->get_payload(msg, PLV2_FRAGMENT))
+ {
+ return SUCCESS;
+ }
+ if (!*defrag)
+ {
+ *defrag = message_create_defrag(msg);
+ if (!*defrag)
+ {
+ return FAILED;
+ }
+ }
+ status = (*defrag)->add_fragment(*defrag, msg);
+ if (status == SUCCESS)
+ {
+ /* reinject the reassembled message */
+ reassembled = *defrag;
+ *defrag = NULL;
+ status = this->ike_sa->process_message(this->ike_sa, reassembled);
+ if (status == SUCCESS)
+ {
+ /* avoid processing the last fragment */
+ status = NEED_MORE;
+ }
+ reassembled->destroy(reassembled);
+ }
+ return status;
+}
+
+/**
* Send a notify back to the sender
*/
static void send_notify_response(private_task_manager_t *this,
@@ -1156,6 +1304,11 @@ METHOD(task_manager_t, process_message, status_t,
{ /* with MOBIKE, we do no implicit updates */
this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
}
+ status = handle_fragment(this, &this->responding.defrag, msg);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
charon->bus->message(charon->bus, msg, TRUE, TRUE);
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
@@ -1168,20 +1321,19 @@ METHOD(task_manager_t, process_message, status_t,
}
this->responding.mid++;
}
- else if ((mid == this->responding.mid - 1) && this->responding.packet)
+ else if ((mid == this->responding.mid - 1) &&
+ array_count(this->responding.packets))
{
- packet_t *clone;
- host_t *host;
-
+ status = handle_fragment(this, &this->responding.defrag, msg);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
DBG1(DBG_IKE, "received retransmit of request with ID %d, "
"retransmitting response", mid);
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg);
- 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);
+ send_packets(this, this->responding.packets,
+ msg->get_destination(msg), msg->get_source(msg));
}
else
{
@@ -1209,6 +1361,11 @@ METHOD(task_manager_t, process_message, status_t,
this->ike_sa->update_hosts(this->ike_sa, NULL, other, FALSE);
}
}
+ status = handle_fragment(this, &this->initiating.defrag, msg);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
charon->bus->message(charon->bus, msg, TRUE, TRUE);
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
@@ -1368,7 +1525,25 @@ METHOD(task_manager_t, queue_mobike, void,
mobike = ike_mobike_create(this->ike_sa, TRUE);
if (roam)
{
+ enumerator_t *enumerator;
+ task_t *current;
+
mobike->roam(mobike, address);
+
+ /* enable path probing for a currently active MOBIKE task. This might
+ * not be the case if an address appeared on a new interface while the
+ * current address is not working but has not yet disappeared. */
+ enumerator = array_create_enumerator(this->active_tasks);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (current->get_type(current) == TASK_IKE_MOBIKE)
+ {
+ ike_mobike_t *active = (ike_mobike_t*)current;
+ active->enable_probing(active);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
}
else
{
@@ -1485,10 +1660,12 @@ METHOD(task_manager_t, reset, void,
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;
+ clear_packets(this->responding.packets);
+ clear_packets(this->initiating.packets);
+ DESTROY_IF(this->responding.defrag);
+ DESTROY_IF(this->initiating.defrag);
+ this->responding.defrag = NULL;
+ this->initiating.defrag = NULL;
if (initiate != UINT_MAX)
{
this->initiating.mid = initiate;
@@ -1542,8 +1719,12 @@ METHOD(task_manager_t, destroy, void,
array_destroy(this->queued_tasks);
array_destroy(this->passive_tasks);
- DESTROY_IF(this->responding.packet);
- DESTROY_IF(this->initiating.packet);
+ clear_packets(this->responding.packets);
+ array_destroy(this->responding.packets);
+ clear_packets(this->initiating.packets);
+ array_destroy(this->initiating.packets);
+ DESTROY_IF(this->responding.defrag);
+ DESTROY_IF(this->initiating.defrag);
free(this);
}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c
index e3c18ea0f..71c5f22fa 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_init.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_init.c
@@ -161,6 +161,19 @@ static void build_payloads(private_ike_init_t *this, message_t *message)
message->add_payload(message, (payload_t*)ke_payload);
message->add_payload(message, (payload_t*)nonce_payload);
}
+
+ /* negotiate fragmentation if we are not rekeying */
+ if (!this->old_sa &&
+ this->config->fragmentation(this->config) != FRAGMENTATION_NO)
+ {
+ if (this->initiator ||
+ this->ike_sa->supports_extension(this->ike_sa,
+ EXT_IKE_FRAGMENTATION))
+ {
+ message->add_notify(message, FALSE, FRAGMENTATION_SUPPORTED,
+ chunk_empty);
+ }
+ }
}
/**
@@ -220,6 +233,16 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
this->other_nonce = nonce_payload->get_nonce(nonce_payload);
break;
}
+ case PLV2_NOTIFY:
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+
+ if (notify->get_notify_type(notify) == FRAGMENTATION_SUPPORTED)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ EXT_IKE_FRAGMENTATION);
+ }
+ }
default:
break;
}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
index 00ca615d8..d91fa5862 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2012 Tobias Brunner
+ * Copyright (C) 2010-2014 Tobias Brunner
* Copyright (C) 2007 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -77,6 +77,11 @@ struct private_ike_mobike_t {
* additional addresses got updated
*/
bool addresses_updated;
+
+ /**
+ * whether the pending updates counter was increased
+ */
+ bool pending_update;
};
/**
@@ -301,35 +306,61 @@ static void apply_port(host_t *host, host_t *old, u_int16_t port, bool local)
host->set_port(host, port);
}
-METHOD(ike_mobike_t, transmit, void,
+METHOD(ike_mobike_t, transmit, bool,
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;
+ int family = AF_UNSPEC;
+ bool found = FALSE;
+
+ 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);
if (!this->check)
{
- return;
+ me = hydra->kernel_interface->get_source_addr(hydra->kernel_interface,
+ other_old, me_old);
+ if (me)
+ {
+ if (me->ip_equals(me, me_old))
+ {
+ charon->sender->send(charon->sender, packet->clone(packet));
+ me->destroy(me);
+ return TRUE;
+ }
+ me->destroy(me);
+ }
+ this->check = TRUE;
}
- 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);
+ switch (charon->socket->supported_families(charon->socket))
+ {
+ case SOCKET_FAMILY_IPV4:
+ family = AF_INET;
+ break;
+ case SOCKET_FAMILY_IPV6:
+ family = AF_INET6;
+ break;
+ case SOCKET_FAMILY_BOTH:
+ case SOCKET_FAMILY_NONE:
+ break;
+ }
enumerator = this->ike_sa->create_peer_address_enumerator(this->ike_sa);
while (enumerator->enumerate(enumerator, (void**)&other))
{
+ if (family != AF_UNSPEC && other->get_family(other) != family)
+ {
+ continue;
+ }
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), TRUE);
other = other->clone(other);
@@ -339,9 +370,11 @@ METHOD(ike_mobike_t, transmit, void,
copy->set_source(copy, me);
copy->set_destination(copy, other);
charon->sender->send(charon->sender, copy);
+ found = TRUE;
}
}
enumerator->destroy(enumerator);
+ return found;
}
METHOD(task_t, build_i, status_t,
@@ -481,9 +514,7 @@ METHOD(task_t, process_i, status_t,
}
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)
+ if (this->ike_sa->get_pending_updates(this->ike_sa) > 1)
{
/* newer update queued, ignore this one */
return SUCCESS;
@@ -560,7 +591,6 @@ METHOD(task_t, process_i, status_t,
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;
}
}
@@ -573,8 +603,12 @@ METHOD(ike_mobike_t, addresses, void,
private_ike_mobike_t *this)
{
this->address = TRUE;
- this->ike_sa->set_pending_updates(this->ike_sa,
+ if (!this->pending_update)
+ {
+ this->pending_update = 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,
@@ -582,8 +616,12 @@ METHOD(ike_mobike_t, roam, void,
{
this->check = TRUE;
this->address = address;
- this->ike_sa->set_pending_updates(this->ike_sa,
+ if (!this->pending_update)
+ {
+ this->pending_update = TRUE;
+ 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,
@@ -593,8 +631,12 @@ METHOD(ike_mobike_t, dpd, void,
{
this->natd = ike_natd_create(this->ike_sa, this->initiator);
}
- this->ike_sa->set_pending_updates(this->ike_sa,
+ if (!this->pending_update)
+ {
+ this->pending_update = TRUE;
+ 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,
@@ -603,6 +645,12 @@ METHOD(ike_mobike_t, is_probing, bool,
return this->check;
}
+METHOD(ike_mobike_t, enable_probing, void,
+ private_ike_mobike_t *this)
+{
+ this->check = TRUE;
+}
+
METHOD(task_t, get_type, task_type_t,
private_ike_mobike_t *this)
{
@@ -618,11 +666,21 @@ METHOD(task_t, migrate, void,
{
this->natd->task.migrate(&this->natd->task, ike_sa);
}
+ if (this->pending_update)
+ {
+ this->ike_sa->set_pending_updates(this->ike_sa,
+ this->ike_sa->get_pending_updates(this->ike_sa) + 1);
+ }
}
METHOD(task_t, destroy, void,
private_ike_mobike_t *this)
{
+ if (this->pending_update)
+ {
+ this->ike_sa->set_pending_updates(this->ike_sa,
+ this->ike_sa->get_pending_updates(this->ike_sa) - 1);
+ }
chunk_free(&this->cookie2);
if (this->natd)
{
@@ -650,6 +708,7 @@ ike_mobike_t *ike_mobike_create(ike_sa_t *ike_sa, bool initiator)
.dpd = _dpd,
.transmit = _transmit,
.is_probing = _is_probing,
+ .enable_probing = _enable_probing,
},
.ike_sa = ike_sa,
.initiator = initiator,
diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.h b/src/libcharon/sa/ikev2/tasks/ike_mobike.h
index b145a9a8b..bb2318c9c 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_mobike.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.h
@@ -70,8 +70,9 @@ struct ike_mobike_t {
* probing.
*
* @param packet the packet to transmit
+ * @return TRUE if transmitted, FALSE if no path found
*/
- void (*transmit)(ike_mobike_t *this, packet_t *packet);
+ bool (*transmit)(ike_mobike_t *this, packet_t *packet);
/**
* Check if this task is probing for routability.
@@ -79,6 +80,11 @@ struct ike_mobike_t {
* @return TRUE if task is probing
*/
bool (*is_probing)(ike_mobike_t *this);
+
+ /**
+ * Enable probing for routability.
+ */
+ void (*enable_probing)(ike_mobike_t *this);
};
/**