mirror of
https://github.com/strongswan/strongswan.git
synced 2025-10-06 00:00:47 -04:00
ikev2: Send and receive fragmented IKE messages
If a fragmented message is retransmitted only the first packet is passed to the alert() hook.
This commit is contained in:
parent
1446fd8ac9
commit
b678d9e14f
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2007-2011 Tobias Brunner
|
* Copyright (C) 2007-2014 Tobias Brunner
|
||||||
* Copyright (C) 2007-2010 Martin Willi
|
* Copyright (C) 2007-2010 Martin Willi
|
||||||
* Hochschule fuer Technik Rapperswil
|
* Hochschule fuer Technik Rapperswil
|
||||||
*
|
*
|
||||||
@ -90,9 +90,14 @@ struct private_task_manager_t {
|
|||||||
u_int32_t mid;
|
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;
|
} responding;
|
||||||
|
|
||||||
@ -111,9 +116,9 @@ struct private_task_manager_t {
|
|||||||
u_int retransmitted;
|
u_int retransmitted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* packet for retransmission
|
* packet(s) for retransmission
|
||||||
*/
|
*/
|
||||||
packet_t *packet;
|
array_t *packets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* type of the initated exchange
|
* type of the initated exchange
|
||||||
@ -125,6 +130,11 @@ struct private_task_manager_t {
|
|||||||
*/
|
*/
|
||||||
bool deferred;
|
bool deferred;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to defragment the response
|
||||||
|
*/
|
||||||
|
message_t *defrag;
|
||||||
|
|
||||||
} initiating;
|
} initiating;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,6 +173,19 @@ struct private_task_manager_t {
|
|||||||
double retransmit_base;
|
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,
|
METHOD(task_manager_t, flush_queue, void,
|
||||||
private_task_manager_t *this, task_queue_t queue)
|
private_task_manager_t *this, task_queue_t queue)
|
||||||
{
|
{
|
||||||
@ -222,10 +245,60 @@ static bool activate_task(private_task_manager_t *this, task_type_t type)
|
|||||||
return found;
|
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,
|
METHOD(task_manager_t, retransmit, status_t,
|
||||||
private_task_manager_t *this, u_int32_t message_id)
|
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;
|
u_int32_t timeout;
|
||||||
job_t *job;
|
job_t *job;
|
||||||
@ -234,6 +307,8 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
task_t *task;
|
task_t *task;
|
||||||
ike_mobike_t *mobike = NULL;
|
ike_mobike_t *mobike = NULL;
|
||||||
|
|
||||||
|
array_get(this->initiating.packets, 0, &packet);
|
||||||
|
|
||||||
/* check if we are retransmitting a MOBIKE routability check */
|
/* check if we are retransmitting a MOBIKE routability check */
|
||||||
if (this->initiating.type == INFORMATIONAL)
|
if (this->initiating.type == INFORMATIONAL)
|
||||||
{
|
{
|
||||||
@ -261,7 +336,7 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
DBG1(DBG_IKE, "giving up after %d retransmits",
|
DBG1(DBG_IKE, "giving up after %d retransmits",
|
||||||
this->initiating.retransmitted - 1);
|
this->initiating.retransmitted - 1);
|
||||||
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT,
|
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT,
|
||||||
this->initiating.packet);
|
packet);
|
||||||
return DESTROY_ME;
|
return DESTROY_ME;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,17 +344,15 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
|
DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
|
||||||
this->initiating.retransmitted, message_id);
|
this->initiating.retransmitted, message_id);
|
||||||
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND,
|
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet);
|
||||||
this->initiating.packet);
|
|
||||||
}
|
}
|
||||||
if (!mobike)
|
if (!mobike)
|
||||||
{
|
{
|
||||||
packet = this->initiating.packet->clone(this->initiating.packet);
|
send_packets(this, this->initiating.packets, NULL, NULL);
|
||||||
charon->sender->send(charon->sender, packet);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!mobike->transmit(mobike, this->initiating.packet))
|
if (!mobike->transmit(mobike, packet))
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update "
|
DBG1(DBG_IKE, "no route found to reach peer, MOBIKE update "
|
||||||
"deferred");
|
"deferred");
|
||||||
@ -311,7 +384,9 @@ METHOD(task_manager_t, retransmit, status_t,
|
|||||||
DBG1(DBG_IKE, "path probing attempt %d",
|
DBG1(DBG_IKE, "path probing attempt %d",
|
||||||
this->initiating.retransmitted);
|
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 "
|
DBG1(DBG_IKE, "no route found to reach peer, path probing "
|
||||||
"deferred");
|
"deferred");
|
||||||
@ -336,7 +411,6 @@ METHOD(task_manager_t, initiate, status_t,
|
|||||||
task_t *task;
|
task_t *task;
|
||||||
message_t *message;
|
message_t *message;
|
||||||
host_t *me, *other;
|
host_t *me, *other;
|
||||||
status_t status;
|
|
||||||
exchange_type_t exchange = 0;
|
exchange_type_t exchange = 0;
|
||||||
|
|
||||||
if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
|
if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
|
||||||
@ -529,9 +603,7 @@ METHOD(task_manager_t, initiate, status_t,
|
|||||||
/* update exchange type if a task changed it */
|
/* update exchange type if a task changed it */
|
||||||
this->initiating.type = message->get_exchange_type(message);
|
this->initiating.type = message->get_exchange_type(message);
|
||||||
|
|
||||||
status = this->ike_sa->generate_message(this->ike_sa, message,
|
if (!generate_message(this, message, &this->initiating.packets))
|
||||||
&this->initiating.packet);
|
|
||||||
if (status != SUCCESS)
|
|
||||||
{
|
{
|
||||||
/* message generation failed. There is nothing more to do than to
|
/* message generation failed. There is nothing more to do than to
|
||||||
* close the SA */
|
* close the SA */
|
||||||
@ -603,8 +675,7 @@ static status_t process_response(private_task_manager_t *this,
|
|||||||
|
|
||||||
this->initiating.mid++;
|
this->initiating.mid++;
|
||||||
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
|
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
|
||||||
this->initiating.packet->destroy(this->initiating.packet);
|
clear_packets(this->initiating.packets);
|
||||||
this->initiating.packet = NULL;
|
|
||||||
|
|
||||||
array_compress(this->active_tasks);
|
array_compress(this->active_tasks);
|
||||||
|
|
||||||
@ -672,8 +743,8 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
|
|||||||
host_t *me, *other;
|
host_t *me, *other;
|
||||||
bool delete = FALSE, hook = FALSE;
|
bool delete = FALSE, hook = FALSE;
|
||||||
ike_sa_id_t *id = NULL;
|
ike_sa_id_t *id = NULL;
|
||||||
u_int64_t responder_spi;
|
u_int64_t responder_spi = 0;
|
||||||
status_t status;
|
bool result;
|
||||||
|
|
||||||
me = request->get_destination(request);
|
me = request->get_destination(request);
|
||||||
other = request->get_source(request);
|
other = request->get_source(request);
|
||||||
@ -735,23 +806,20 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* message complete, send it */
|
/* message complete, send it */
|
||||||
DESTROY_IF(this->responding.packet);
|
clear_packets(this->responding.packets);
|
||||||
this->responding.packet = NULL;
|
result = generate_message(this, message, &this->responding.packets);
|
||||||
status = this->ike_sa->generate_message(this->ike_sa, message,
|
|
||||||
&this->responding.packet);
|
|
||||||
message->destroy(message);
|
message->destroy(message);
|
||||||
if (id)
|
if (id)
|
||||||
{
|
{
|
||||||
id->set_responder_spi(id, responder_spi);
|
id->set_responder_spi(id, responder_spi);
|
||||||
}
|
}
|
||||||
if (status != SUCCESS)
|
if (!result)
|
||||||
{
|
{
|
||||||
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
|
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
|
||||||
return DESTROY_ME;
|
return DESTROY_ME;
|
||||||
}
|
}
|
||||||
|
|
||||||
charon->sender->send(charon->sender,
|
send_packets(this, this->responding.packets, NULL, NULL);
|
||||||
this->responding.packet->clone(this->responding.packet));
|
|
||||||
if (delete)
|
if (delete)
|
||||||
{
|
{
|
||||||
if (hook)
|
if (hook)
|
||||||
@ -999,6 +1067,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
|
* Send a notify back to the sender
|
||||||
*/
|
*/
|
||||||
@ -1192,6 +1302,11 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
{ /* with MOBIKE, we do no implicit updates */
|
{ /* with MOBIKE, we do no implicit updates */
|
||||||
this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
|
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);
|
charon->bus->message(charon->bus, msg, TRUE, TRUE);
|
||||||
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
|
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
|
||||||
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
|
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
|
||||||
@ -1204,20 +1319,19 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
}
|
}
|
||||||
this->responding.mid++;
|
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;
|
status = handle_fragment(this, &this->responding.defrag, msg);
|
||||||
host_t *host;
|
if (status != SUCCESS)
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
DBG1(DBG_IKE, "received retransmit of request with ID %d, "
|
DBG1(DBG_IKE, "received retransmit of request with ID %d, "
|
||||||
"retransmitting response", mid);
|
"retransmitting response", mid);
|
||||||
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg);
|
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg);
|
||||||
clone = this->responding.packet->clone(this->responding.packet);
|
send_packets(this, this->responding.packets,
|
||||||
host = msg->get_destination(msg);
|
msg->get_destination(msg), msg->get_source(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
|
else
|
||||||
{
|
{
|
||||||
@ -1245,6 +1359,11 @@ METHOD(task_manager_t, process_message, status_t,
|
|||||||
this->ike_sa->update_hosts(this->ike_sa, NULL, other, FALSE);
|
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);
|
charon->bus->message(charon->bus, msg, TRUE, TRUE);
|
||||||
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
|
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
|
||||||
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
|
{ /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
|
||||||
@ -1539,10 +1658,12 @@ METHOD(task_manager_t, reset, void,
|
|||||||
task_t *task;
|
task_t *task;
|
||||||
|
|
||||||
/* reset message counters and retransmit packets */
|
/* reset message counters and retransmit packets */
|
||||||
DESTROY_IF(this->responding.packet);
|
clear_packets(this->responding.packets);
|
||||||
DESTROY_IF(this->initiating.packet);
|
clear_packets(this->initiating.packets);
|
||||||
this->responding.packet = NULL;
|
DESTROY_IF(this->responding.defrag);
|
||||||
this->initiating.packet = NULL;
|
DESTROY_IF(this->initiating.defrag);
|
||||||
|
this->responding.defrag = NULL;
|
||||||
|
this->initiating.defrag = NULL;
|
||||||
if (initiate != UINT_MAX)
|
if (initiate != UINT_MAX)
|
||||||
{
|
{
|
||||||
this->initiating.mid = initiate;
|
this->initiating.mid = initiate;
|
||||||
@ -1596,8 +1717,12 @@ METHOD(task_manager_t, destroy, void,
|
|||||||
array_destroy(this->queued_tasks);
|
array_destroy(this->queued_tasks);
|
||||||
array_destroy(this->passive_tasks);
|
array_destroy(this->passive_tasks);
|
||||||
|
|
||||||
DESTROY_IF(this->responding.packet);
|
clear_packets(this->responding.packets);
|
||||||
DESTROY_IF(this->initiating.packet);
|
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);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user