/*
- * Copyright (C) 2007-2011 Tobias Brunner
+ * Copyright (C) 2007-2014 Tobias Brunner
* Copyright (C) 2007-2010 Martin Willi
* Hochschule fuer Technik Rapperswil
*
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;
u_int retransmitted;
/**
- * packet for retransmission
+ * packet(s) for retransmission
*/
- packet_t *packet;
+ array_t *packets;
/**
* type of the initated exchange
*/
bool deferred;
+ /**
+ * Helper to defragment the response
+ */
+ message_t *defrag;
+
} initiating;
/**
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)
{
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;
task_t *task;
ike_mobike_t *mobike = NULL;
+ array_get(this->initiating.packets, 0, &packet);
+
/* check if we are retransmitting a MOBIKE routability check */
if (this->initiating.type == INFORMATIONAL)
{
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;
}
{
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)
{
- packet = this->initiating.packet->clone(this->initiating.packet);
- charon->sender->send(charon->sender, packet);
+ send_packets(this, this->initiating.packets, NULL, NULL);
}
else
{
- if (!mobike->transmit(mobike, this->initiating.packet))
+ if (!mobike->transmit(mobike, packet))
{
DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update "
"deferred");
DBG1(DBG_IKE, "path probing attempt %d",
this->initiating.retransmitted);
}
- if (!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");
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)
/* 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 */
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);
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);
}
/* 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)
}
}
+/**
+ * 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
*/
{ /* 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 */
}
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
{
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 */
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;
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);
}