From 8f0ab6dd366ff4d43f5372113035de46d376f784 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Dec 2012 18:16:58 +0100 Subject: [PATCH 01/20] Payload added to handle IKE fragments --- src/libcharon/Android.mk | 1 + src/libcharon/Makefile.am | 1 + .../encoding/payloads/fragment_payload.c | 208 ++++++++++++++++++ .../encoding/payloads/fragment_payload.h | 81 +++++++ src/libcharon/encoding/payloads/payload.c | 29 ++- src/libcharon/encoding/payloads/payload.h | 5 + 6 files changed, 314 insertions(+), 11 deletions(-) create mode 100644 src/libcharon/encoding/payloads/fragment_payload.c create mode 100644 src/libcharon/encoding/payloads/fragment_payload.h diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 9eb864f50..b2d6c3128 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -42,6 +42,7 @@ encoding/payloads/ts_payload.c encoding/payloads/ts_payload.h \ encoding/payloads/unknown_payload.c encoding/payloads/unknown_payload.h \ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \ encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \ +encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \ kernel/kernel_handler.c kernel/kernel_handler.h \ network/receiver.c network/receiver.h network/sender.c network/sender.h \ network/socket.c network/socket.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 1ca4ddee7..5203890ff 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -40,6 +40,7 @@ encoding/payloads/ts_payload.c encoding/payloads/ts_payload.h \ encoding/payloads/unknown_payload.c encoding/payloads/unknown_payload.h \ encoding/payloads/vendor_id_payload.c encoding/payloads/vendor_id_payload.h \ encoding/payloads/hash_payload.c encoding/payloads/hash_payload.h \ +encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \ kernel/kernel_handler.c kernel/kernel_handler.h \ network/receiver.c network/receiver.h network/sender.c network/sender.h \ network/socket.c network/socket.h \ diff --git a/src/libcharon/encoding/payloads/fragment_payload.c b/src/libcharon/encoding/payloads/fragment_payload.c new file mode 100644 index 000000000..2e7e06198 --- /dev/null +++ b/src/libcharon/encoding/payloads/fragment_payload.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "fragment_payload.h" + +#include + +/** Flag that is set in case the given fragment is the last for the message */ +#define LAST_FRAGMENT 0x01 + +typedef struct private_fragment_payload_t private_fragment_payload_t; + +/** + * Private data of an fragment_payload_t object. + */ +struct private_fragment_payload_t { + + /** + * Public fragment_payload_t interface. + */ + fragment_payload_t public; + + /** + * Next payload type. + */ + u_int8_t next_payload; + + /** + * Reserved byte + */ + u_int8_t reserved; + + /** + * Length of this payload. + */ + u_int16_t payload_length; + + /** + * Fragment ID. + */ + u_int16_t fragment_id; + + /** + * Fragment number. + */ + u_int8_t fragment_number; + + /** + * Flags + */ + u_int8_t flags; + + /** + * The contained fragment data. + */ + chunk_t data; +}; + +/** + * Encoding rules for an IKEv1 fragment payload + */ +static encoding_rule_t encodings[] = { + /* 1 Byte next payload type, stored in the field next_payload */ + { U_INT_8, offsetof(private_fragment_payload_t, next_payload) }, + { RESERVED_BYTE, offsetof(private_fragment_payload_t, reserved) }, + /* Length of the whole payload*/ + { PAYLOAD_LENGTH, offsetof(private_fragment_payload_t, payload_length) }, + { U_INT_16, offsetof(private_fragment_payload_t, fragment_id) }, + { U_INT_8, offsetof(private_fragment_payload_t, fragment_number) }, + { U_INT_8, offsetof(private_fragment_payload_t, flags) }, + /* Fragment data is of variable size */ + { CHUNK_DATA, offsetof(private_fragment_payload_t, data) }, +}; + +/* + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Next Payload ! RESERVED ! Payload Length ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! Fragment ID ! Fragment Num ! Flags ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + ! ! + ~ Fragment Data ~ + ! ! + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +METHOD(payload_t, verify, status_t, + private_fragment_payload_t *this) +{ + if (this->fragment_number == 0) + { + return FAILED; + } + return SUCCESS; +} + +METHOD(payload_t, get_encoding_rules, int, + private_fragment_payload_t *this, encoding_rule_t **rules) +{ + *rules = encodings; + return countof(encodings); +} + +METHOD(payload_t, get_header_length, int, + private_fragment_payload_t *this) +{ + return 8; +} + +METHOD(payload_t, get_type, payload_type_t, + private_fragment_payload_t *this) +{ + return FRAGMENT_V1; +} + +METHOD(payload_t, get_next_type, payload_type_t, + private_fragment_payload_t *this) +{ + return this->next_payload; +} + +METHOD(payload_t, set_next_type, void, + private_fragment_payload_t *this, payload_type_t type) +{ + this->next_payload = type; +} + +METHOD(payload_t, get_length, size_t, + private_fragment_payload_t *this) +{ + return this->payload_length; +} + +METHOD(fragment_payload_t, get_id, u_int16_t, + private_fragment_payload_t *this) +{ + return this->fragment_id; +} + +METHOD(fragment_payload_t, get_number, u_int8_t, + private_fragment_payload_t *this) +{ + return this->fragment_number; +} + +METHOD(fragment_payload_t, is_last, bool, + private_fragment_payload_t *this) +{ + return (this->flags & LAST_FRAGMENT) == LAST_FRAGMENT; +} + +METHOD(fragment_payload_t, get_data, chunk_t, + private_fragment_payload_t *this) +{ + return this->data; +} + +METHOD2(payload_t, fragment_payload_t, destroy, void, + private_fragment_payload_t *this) +{ + free(this->data.ptr); + free(this); +} + +/* + * Described in header + */ +fragment_payload_t *fragment_payload_create() +{ + private_fragment_payload_t *this; + + INIT(this, + .public = { + .payload_interface = { + .verify = _verify, + .get_encoding_rules = _get_encoding_rules, + .get_header_length = _get_header_length, + .get_length = _get_length, + .get_next_type = _get_next_type, + .set_next_type = _set_next_type, + .get_type = _get_type, + .destroy = _destroy, + }, + .get_id = _get_id, + .get_number = _get_number, + .is_last = _is_last, + .get_data = _get_data, + .destroy = _destroy, + }, + .next_payload = NO_PAYLOAD, + ); + this->payload_length = get_header_length(this); + return &this->public; +} diff --git a/src/libcharon/encoding/payloads/fragment_payload.h b/src/libcharon/encoding/payloads/fragment_payload.h new file mode 100644 index 000000000..60358abb9 --- /dev/null +++ b/src/libcharon/encoding/payloads/fragment_payload.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup fragment_payload fragment_payload + * @{ @ingroup payloads + */ + +#ifndef FRAGMENT_PAYLOAD_H_ +#define FRAGMENT_PAYLOAD_H_ + +typedef struct fragment_payload_t fragment_payload_t; + +#include +#include + +/** + * Object representing an IKEv1 fragment payload. + */ +struct fragment_payload_t { + + /** + * The payload_t interface. + */ + payload_t payload_interface; + + /** + * Get the fragment ID. Identifies the fragments for a particular IKE + * message. + * + * @return fragment ID + */ + u_int16_t (*get_id)(fragment_payload_t *this); + + /** + * Get the fragment number. Defines the order of the fragments. + * + * @return fragment number + */ + u_int8_t (*get_number)(fragment_payload_t *this); + + /** + * Check if this is the last fragment. + * + * @return TRUE if this is the last fragment + */ + bool (*is_last)(fragment_payload_t *this); + + /** + * Get the fragment data. + * + * @return chunkt to internal fragment data + */ + chunk_t (*get_data)(fragment_payload_t *this); + + /** + * Destroys an fragment_payload_t object. + */ + void (*destroy)(fragment_payload_t *this); +}; + +/** + * Creates an empty fragment_payload_t object. + * + * @return fragment_payload_t object + */ +fragment_payload_t *fragment_payload_create(); + +#endif /** FRAGMENT_PAYLOAD_H_ @}*/ diff --git a/src/libcharon/encoding/payloads/payload.c b/src/libcharon/encoding/payloads/payload.c index 7ddd8e72d..f9dd33edb 100644 --- a/src/libcharon/encoding/payloads/payload.c +++ b/src/libcharon/encoding/payloads/payload.c @@ -36,6 +36,7 @@ #include #include #include +#include #include ENUM_BEGIN(payload_type_names, NO_PAYLOAD, NO_PAYLOAD, @@ -79,15 +80,17 @@ ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWORD_METH #ifdef ME ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD, "ID_PEER"); -ENUM_NEXT(payload_type_names, NAT_D_DRAFT_00_03_V1, NAT_OA_DRAFT_00_03_V1, ID_PEER, +ENUM_NEXT(payload_type_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, ID_PEER, "NAT_D_DRAFT_V1", - "NAT_OA_DRAFT_V1"); + "NAT_OA_DRAFT_V1", + "FRAGMENT"); #else -ENUM_NEXT(payload_type_names, NAT_D_DRAFT_00_03_V1, NAT_OA_DRAFT_00_03_V1, GENERIC_SECURE_PASSWORD_METHOD, +ENUM_NEXT(payload_type_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, GENERIC_SECURE_PASSWORD_METHOD, "NAT_D_DRAFT_V1", - "NAT_OA_DRAFT_V1"); + "NAT_OA_DRAFT_V1", + "FRAGMENT"); #endif /* ME */ -ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, NAT_OA_DRAFT_00_03_V1, +ENUM_NEXT(payload_type_names, HEADER, ENCRYPTED_V1, FRAGMENT_V1, "HEADER", "PROPOSAL_SUBSTRUCTURE", "PROPOSAL_SUBSTRUCTURE_V1", @@ -143,15 +146,17 @@ ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, GENERIC_SECURE_PASSWOR #ifdef ME ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, GENERIC_SECURE_PASSWORD_METHOD, "IDp"); -ENUM_NEXT(payload_type_short_names, NAT_D_DRAFT_00_03_V1, NAT_OA_DRAFT_00_03_V1, ID_PEER, +ENUM_NEXT(payload_type_short_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, ID_PEER, "NAT-D", - "NAT-OA"); + "NAT-OA", + "FRAG"); #else -ENUM_NEXT(payload_type_short_names, NAT_D_DRAFT_00_03_V1, NAT_OA_DRAFT_00_03_V1, GENERIC_SECURE_PASSWORD_METHOD, +ENUM_NEXT(payload_type_short_names, NAT_D_DRAFT_00_03_V1, FRAGMENT_V1, GENERIC_SECURE_PASSWORD_METHOD, "NAT-D", - "NAT-OA"); + "NAT-OA", + "FRAG"); #endif /* ME */ -ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, NAT_OA_DRAFT_00_03_V1, +ENUM_NEXT(payload_type_short_names, HEADER, ENCRYPTED_V1, FRAGMENT_V1, "HDR", "PROP", "PROP", @@ -240,6 +245,8 @@ payload_t *payload_create(payload_type_t type) case ENCRYPTED: case ENCRYPTED_V1: return (payload_t*)encryption_payload_create(type); + case FRAGMENT_V1: + return (payload_t*)fragment_payload_create(); default: return (payload_t*)unknown_payload_create(type); } @@ -272,7 +279,7 @@ bool payload_is_known(payload_type_t type) return TRUE; } #endif - if (type >= NAT_D_DRAFT_00_03_V1 && type <= NAT_OA_DRAFT_00_03_V1) + if (type >= NAT_D_DRAFT_00_03_V1 && type <= FRAGMENT_V1) { return TRUE; } diff --git a/src/libcharon/encoding/payloads/payload.h b/src/libcharon/encoding/payloads/payload.h index d18bbea2d..0e8a9267b 100644 --- a/src/libcharon/encoding/payloads/payload.h +++ b/src/libcharon/encoding/payloads/payload.h @@ -230,6 +230,11 @@ enum payload_type_t { */ NAT_OA_DRAFT_00_03_V1 = 131, + /** + * IKE fragment (proprietary IKEv1 extension) + */ + FRAGMENT_V1 = 132, + /** * Header has a value of PRIVATE USE space. * From ddfad8b67e9b673941558a4709c93379eafc7313 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Dec 2012 18:18:37 +0100 Subject: [PATCH 02/20] Reset the encrypted flag when handling IKE messages that contain a fragment Racoon sets the encrypted bit for messages containing a fragment, but these messages are not really encrypted (the fragmented message is though). --- src/libcharon/encoding/message.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index 55e9f33fc..c0f468663 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -1693,6 +1693,12 @@ METHOD(message_t, parse_header, status_t, } this->first_payload = ike_header->payload_interface.get_next_type( &ike_header->payload_interface); + if (this->first_payload == FRAGMENT_V1 && this->is_encrypted) + { /* racoon sets the encryted bit when sending a fragment, but these + * messages are really not encrypted */ + this->is_encrypted = FALSE; + } + for (i = 0; i < countof(this->reserved); i++) { reserved = payload_get_field(&ike_header->payload_interface, From 9b1659f52c9dba7f5c4c827a7cda474bb83d992f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Dec 2012 16:40:58 +0100 Subject: [PATCH 03/20] Add message rules to properly handle IKE fragments These are sent in unencrypted messages and are the only payload contained in such messages. --- src/libcharon/encoding/message.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c index c0f468663..28fdda735 100644 --- a/src/libcharon/encoding/message.c +++ b/src/libcharon/encoding/message.c @@ -442,6 +442,7 @@ static payload_rule_t id_prot_i_rules[] = { {CERTIFICATE_V1, 0, 2, TRUE, FALSE}, {SIGNATURE_V1, 0, 1, TRUE, FALSE}, {HASH_V1, 0, 1, TRUE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -461,6 +462,7 @@ static payload_order_t id_prot_i_order[] = { {VENDOR_ID_V1, 0}, {NAT_D_V1, 0}, {NAT_D_DRAFT_00_03_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -480,6 +482,7 @@ static payload_rule_t id_prot_r_rules[] = { {CERTIFICATE_V1, 0, 2, TRUE, FALSE}, {SIGNATURE_V1, 0, 1, TRUE, FALSE}, {HASH_V1, 0, 1, TRUE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -499,6 +502,7 @@ static payload_order_t id_prot_r_order[] = { {VENDOR_ID_V1, 0}, {NAT_D_V1, 0}, {NAT_D_DRAFT_00_03_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -518,6 +522,7 @@ static payload_rule_t aggressive_i_rules[] = { {CERTIFICATE_V1, 0, 1, TRUE, FALSE}, {SIGNATURE_V1, 0, 1, TRUE, FALSE}, {HASH_V1, 0, 1, TRUE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -537,6 +542,7 @@ static payload_order_t aggressive_i_order[] = { {CERTIFICATE_REQUEST_V1, 0}, {NOTIFY_V1, 0}, {VENDOR_ID_V1, 0}, + {FRAGMENT_V1, 0}, }; /** @@ -556,6 +562,7 @@ static payload_rule_t aggressive_r_rules[] = { {CERTIFICATE_V1, 0, 1, FALSE, FALSE}, {SIGNATURE_V1, 0, 1, FALSE, FALSE}, {HASH_V1, 0, 1, FALSE, FALSE}, + {FRAGMENT_V1, 0, 1, FALSE, TRUE}, }; /** @@ -575,6 +582,7 @@ static payload_order_t aggressive_r_order[] = { {CERTIFICATE_REQUEST_V1, 0}, {NOTIFY_V1, 0}, {VENDOR_ID_V1, 0}, + {FRAGMENT_V1, 0}, }; /** From 30216000d3752026127c2f91470ce165ab3d3926 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Dec 2012 18:22:32 +0100 Subject: [PATCH 04/20] Store inbound IKE fragments and reassemble the message when all fragments are received --- src/libcharon/sa/ikev1/task_manager_v1.c | 169 ++++++++++++++++++++++- 1 file changed, 166 insertions(+), 3 deletions(-) diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 1ee833cce..5ba12e7ec 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-2011 Tobias Brunner + * Copyright (C) 2007-2012 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -36,6 +36,9 @@ #include #include #include +#include + +#include /** * Number of old messages hashes we keep for retransmission. @@ -160,19 +163,43 @@ struct private_task_manager_t { packet_t *packet; /** - * type of the initated exchange + * type of the initiated exchange */ exchange_type_t type; } initiating; + /** + * Data used to reassemble a fragmented message + */ + 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; + + } frag; + /** * List of queued tasks not yet in action */ linked_list_t *queued_tasks; /** - * List of active tasks, initiated by ourselve + * List of active tasks, initiated by ourselves */ linked_list_t *active_tasks; @@ -212,6 +239,33 @@ struct private_task_manager_t { u_int32_t dpd_recv; }; +/** + * A single fragment within a fragmented message + */ +typedef struct { + + /** fragment number */ + u_int8_t num; + + /** fragment data */ + chunk_t data; + +} fragment_t; + +static void fragment_destroy(fragment_t *this) +{ + chunk_free(&this->data); + free(this); +} + +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.id = id; +} + METHOD(task_manager_t, flush_queue, void, private_task_manager_t *this, task_queue_t queue) { @@ -1038,6 +1092,100 @@ static status_t process_response(private_task_manager_t *this, return initiate(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; + u_int8_t num; + + payload = (fragment_payload_t*)msg->get_payload(msg, FRAGMENT_V1); + if (!payload) + { + return FAILED; + } + + if (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; + } + + enumerator = this->frag.list->create_enumerator(this->frag.list); + while (enumerator->enumerate(enumerator, &fragment)) + { + 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) + { + break; + } + } + + INIT(fragment, + .num = num, + .data = chunk_clone(payload->get_data(payload)), + ); + + 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) + { + message_t *message; + packet_t *pkt; + chunk_t data = chunk_empty; + host_t *src, *dst; + + 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)) + { + data = chunk_cat("mc", data, 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), data); + + 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); + status = NEED_MORE; + } + return status; +} + /** * Parse the given message and verify that it is valid. */ @@ -1085,6 +1233,11 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) return DESTROY_ME; } } + + if (msg->get_first_payload_type(msg) == FRAGMENT_V1) + { + return handle_fragment(this, msg); + } return status; } @@ -1129,6 +1282,10 @@ METHOD(task_manager_t, process_message, status_t, msg->set_request(msg, FALSE); charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); + if (status == NEED_MORE) + { + return SUCCESS; + } if (status != SUCCESS) { return status; @@ -1196,6 +1353,10 @@ METHOD(task_manager_t, process_message, status_t, msg->set_request(msg, TRUE); charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); + if (status == NEED_MORE) + { + return SUCCESS; + } if (status != SUCCESS) { return status; @@ -1591,6 +1752,7 @@ METHOD(task_manager_t, reset, void, this->initiating.seqnr = 0; this->initiating.retransmitted = 0; this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + clear_fragments(this, 0); if (initiate != UINT_MAX) { this->dpd_send = initiate; @@ -1641,6 +1803,7 @@ 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->queued); DESTROY_IF(this->responding.packet); From 7d85f94165c73031e2567da762183fc28c3bbcce Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Dec 2012 18:23:11 +0100 Subject: [PATCH 05/20] Don't update an IKE_SA-entry's cached message ID when handling fragments --- src/libcharon/sa/ike_sa_manager.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 031c632eb..962787647 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -1280,7 +1280,10 @@ 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; - entry->message_id = message->get_message_id(message); + if (message->get_first_payload_type(message) != FRAGMENT_V1) + { + entry->message_id = message->get_message_id(message); + } if (ike_id->get_responder_spi(ike_id) == 0) { ike_id->set_responder_spi(ike_id, id->get_responder_spi(id)); From 8fc8383439675456e83e9aae5350124ab5758ea1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 12 Dec 2012 18:29:31 +0100 Subject: [PATCH 06/20] Don't handle fragmented messages larger than charon.max_packet --- src/libcharon/sa/ikev1/task_manager_v1.c | 43 +++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 5ba12e7ec..80d2b6d80 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -39,6 +39,7 @@ #include #include +#include /** * Number of old messages hashes we keep for retransmission. @@ -49,6 +50,11 @@ */ #define MAX_OLD_HASHES 2 +/** + * Maximum packet size for fragmented packets (same as in sockets) + */ +#define MAX_PACKET 10000 + /** * First sequence number of responding packets. * @@ -191,6 +197,16 @@ struct private_task_manager_t { */ linked_list_t *list; + /** + * Length of all currently received fragments + */ + size_t len; + + /** + * Maximum length of a fragmented packet + */ + size_t max_packet; + } frag; /** @@ -263,6 +279,7 @@ 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; } @@ -1098,6 +1115,7 @@ static status_t handle_fragment(private_task_manager_t *this, message_t *msg) 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, FRAGMENT_V1); @@ -1133,9 +1151,19 @@ static status_t handle_fragment(private_task_manager_t *this, message_t *msg) } } + 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(payload->get_data(payload)), + .data = chunk_clone(data), ); this->frag.list->insert_before(this->frag.list, enumerator, fragment); @@ -1145,21 +1173,24 @@ static status_t handle_fragment(private_task_manager_t *this, message_t *msg) { message_t *message; packet_t *pkt; - chunk_t data = chunk_empty; 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)) { - data = chunk_cat("mc", data, fragment->data); + 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), data); + 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) @@ -1850,6 +1881,10 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .responding = { .seqnr = RESPONDING_SEQ, }, + .frag = { + .max_packet = lib->settings->get_int(lib->settings, + "%s.max_packet", MAX_PACKET, charon->name), + }, .ike_sa = ike_sa, .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), .queued_tasks = linked_list_create(), From b8160377398e3a2e2e040824cedbf863518e98ec Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Dec 2012 11:55:33 +0100 Subject: [PATCH 07/20] Allow ID_PROT/AGGRESSIVE messages for established IKE_SAs if they contain fragments Other implementations send fragments always in an initial message type even for transaction or quick mode exchanges. --- src/libcharon/sa/ike_sa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index de050cd3c..4029db11d 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1232,7 +1232,8 @@ METHOD(ike_sa_t, process_message, status_t, case IKE_SA_INIT: case IKE_AUTH: if (this->state != IKE_CREATED && - this->state != IKE_CONNECTING) + this->state != IKE_CONNECTING && + message->get_first_payload_type(message) != FRAGMENT_V1) { DBG1(DBG_IKE, "ignoring %N in established IKE_SA state", exchange_type_names, message->get_exchange_type(message)); From fe26ddada93a5f55971caa6c1e63e9503f414172 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Dec 2012 12:14:25 +0100 Subject: [PATCH 08/20] Map fragmented initial initial Main or Aggressive Mode messages to the same IKE_SA --- src/libcharon/sa/ike_sa_manager.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 962787647..260da8be1 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -968,14 +968,30 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this) static bool get_init_hash(private_ike_sa_manager_t *this, 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) == FRAGMENT_V1) + { /* only hash the source IP and SPI for fragmented init messages */ + u_int64_t spi; + + src = message->get_source(message); + if (!this->hasher->allocate_hash(this->hasher, + src->get_address(src), NULL)) + { + return FALSE; + } + spi = message->get_initiator_spi(message); + return this->hasher->allocate_hash(this->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 */ - host_t *src = message->get_source(message); + src = message->get_source(message); if (!this->hasher->allocate_hash(this->hasher, src->get_address(src), NULL)) From 667720c8010214c46f0b84d2d197662667fcdbf1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Dec 2012 12:16:08 +0100 Subject: [PATCH 09/20] Detect a peer's support for IKE fragmentation Fragments are accepted even if this vendor ID is not seen. --- src/libcharon/sa/ike_sa.h | 5 +++++ src/libcharon/sa/ikev1/tasks/isakmp_vendor.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 7c6a9a5d9..37a20e7f3 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -126,6 +126,11 @@ enum ike_extension_t { * draft-ietf-ipsec-nat-t-ike-02 .. -03 */ EXT_NATT_DRAFT_02_03 = (1<<10), + + /** + * peer support proprietary IKE fragmentation + */ + EXT_IKE_FRAGMENTATION = (1<<11), }; /** diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 2fd43ba8c..c62c277c8 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -99,6 +99,10 @@ static struct { { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16, "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"}, + /* Proprietary IKE fragmentation extension (0x800000 is added by racoon) */ + { "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"}, + }, vendor_natt_ids[] = { /* NAT-Traversal VIDs ordered by preference */ From c4daac2c0b99ac9851310a196305ce18a97fcc51 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 19 Dec 2012 14:07:06 +0100 Subject: [PATCH 10/20] Log added NAT-T vendor IDs --- src/libcharon/sa/ikev1/tasks/isakmp_vendor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index c62c277c8..bf822b548 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -179,6 +179,7 @@ METHOD(task_t, build, status_t, if ((this->initiator && vendor_natt_ids[i].send) || this->best_natt_ext == i) { + DBG2(DBG_IKE, "sending %s vendor ID", vendor_natt_ids[i].desc); vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1, chunk_clone(chunk_create(vendor_natt_ids[i].id, vendor_natt_ids[i].len))); From 07df944c9c21539f686831fd27585caa12effe02 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 20 Dec 2012 16:04:38 +0100 Subject: [PATCH 11/20] Add support to create IKE fragments All fragments currently use the same fragment ID (1) as that's what other implementations are doing. --- .../encoding/payloads/fragment_payload.c | 17 +++++++++++++++++ .../encoding/payloads/fragment_payload.h | 13 +++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/libcharon/encoding/payloads/fragment_payload.c b/src/libcharon/encoding/payloads/fragment_payload.c index 2e7e06198..1a6b3234b 100644 --- a/src/libcharon/encoding/payloads/fragment_payload.c +++ b/src/libcharon/encoding/payloads/fragment_payload.c @@ -206,3 +206,20 @@ fragment_payload_t *fragment_payload_create() this->payload_length = get_header_length(this); return &this->public; } + +/* + * Described in header + */ +fragment_payload_t *fragment_payload_create_from_data(u_int8_t num, bool last, + chunk_t data) +{ + private_fragment_payload_t *this; + + this = (private_fragment_payload_t*)fragment_payload_create(); + this->fragment_id = 1; + this->fragment_number = num; + this->flags |= (last ? LAST_FRAGMENT : 0); + this->data = chunk_clone(data); + this->payload_length = get_header_length(this) + data.len; + return &this->public; +} \ No newline at end of file diff --git a/src/libcharon/encoding/payloads/fragment_payload.h b/src/libcharon/encoding/payloads/fragment_payload.h index 60358abb9..a49cf32dd 100644 --- a/src/libcharon/encoding/payloads/fragment_payload.h +++ b/src/libcharon/encoding/payloads/fragment_payload.h @@ -78,4 +78,17 @@ struct fragment_payload_t { */ fragment_payload_t *fragment_payload_create(); +/** + * Creates a fragment payload from the given data. All fragments currently + * have the same fragment ID (1), which seems what other implementations are + * doing. + * + * @param num fragment number (first one should be 1) + * @param last TRUE to indicate that this is the last fragment + * @param data fragment data (gets cloned) + * @return fragment_payload_t object + */ +fragment_payload_t *fragment_payload_create_from_data(u_int8_t num, bool last, + chunk_t data); + #endif /** FRAGMENT_PAYLOAD_H_ @}*/ From c849305ad7027ae9f04f1996195a8e7f204de2b6 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Dec 2012 17:49:45 +0100 Subject: [PATCH 12/20] Log message size for in- and outbound IKE messages --- src/libcharon/network/sender.c | 6 ++++-- src/libcharon/processing/jobs/process_message_job.c | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libcharon/network/sender.c b/src/libcharon/network/sender.c index 059f24b39..dd8efc1ec 100644 --- a/src/libcharon/network/sender.c +++ b/src/libcharon/network/sender.c @@ -94,10 +94,11 @@ METHOD(sender_t, send_, void, { host_t *src, *dst; - /* if neither source nor destination port is 500 we add a Non-ESP marker */ src = packet->get_source(packet); dst = packet->get_destination(packet); - DBG1(DBG_NET, "sending packet: from %#H to %#H", src, dst); + + DBG1(DBG_NET, "sending packet: from %#H to %#H (%zu bytes)", src, dst, + packet->get_data(packet).len); if (this->send_delay) { @@ -120,6 +121,7 @@ METHOD(sender_t, send_, void, message->destroy(message); } + /* if neither source nor destination port is 500 we add a Non-ESP marker */ if (dst->get_port(dst) != IKEV2_UDP_PORT && src->get_port(src) != IKEV2_UDP_PORT) { diff --git a/src/libcharon/processing/jobs/process_message_job.c b/src/libcharon/processing/jobs/process_message_job.c index 71a2cb45d..606135b0b 100644 --- a/src/libcharon/processing/jobs/process_message_job.c +++ b/src/libcharon/processing/jobs/process_message_job.c @@ -67,9 +67,10 @@ METHOD(job_t, execute, job_requeue_t, this->message); if (ike_sa) { - DBG1(DBG_NET, "received packet: from %#H to %#H", + DBG1(DBG_NET, "received packet: from %#H to %#H (%zu bytes)", this->message->get_source(this->message), - this->message->get_destination(this->message)); + this->message->get_destination(this->message), + this->message->get_packet_data(this->message).len); if (ike_sa->process_message(ike_sa, this->message) == DESTROY_ME) { charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, From 5cafefcd5d863ddc54321ad383cb67bf068b74b8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Dec 2012 18:25:06 +0100 Subject: [PATCH 13/20] Split larger messages into fragments if IKE fragmentation is supported by peer --- src/libcharon/sa/ikev1/task_manager_v1.c | 128 ++++++++++++++++++++--- 1 file changed, 114 insertions(+), 14 deletions(-) diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 80d2b6d80..e56f4be96 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -55,6 +55,15 @@ */ #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. * @@ -207,6 +216,13 @@ struct private_task_manager_t { */ size_t max_packet; + /** + * 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; /** @@ -353,11 +369,89 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) return found; } +/** + * Send a single fragment with the given data + */ +static bool send_fragment(private_task_manager_t *this, bool request, + host_t *src, host_t *dst, fragment_payload_t *fragment) +{ + message_t *message; + 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) + { + DBG1(DBG_IKE, "failed to generate IKE fragment"); + message->destroy(message); + return FALSE; + } + charon->sender->send(charon->sender, packet); + message->destroy(message); + return TRUE; +} + +/** + * Send a packet, if supported and required do so in fragments + */ +static bool send_packet(private_task_manager_t *this, bool request, + packet_t *packet) +{ + host_t *src, *dst; + chunk_t data; + + data = packet->get_data(packet); + if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && + data.len > MAX_FRAGMENT_SIZE) + { + fragment_payload_t *fragment; + u_int8_t num, count; + size_t len, frag_size; + bool nat; + + /* reduce size due to non-ESP marker */ + nat = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); + frag_size = MAX_FRAGMENT_SIZE - (nat ? 4 : 0); + + src = packet->get_source(packet); + dst = packet->get_destination(packet); + count = (data.len / (frag_size + 1)) + 1; + + 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; + } + charon->sender->send(charon->sender, packet); + return TRUE; +} + /** * Retransmit a packet, either as initiator or as responder */ -static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, - u_int mid, u_int retransmitted, packet_t *packet) +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) { u_int32_t t; @@ -376,7 +470,10 @@ static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ); charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet); } - charon->sender->send(charon->sender, packet->clone(packet)); + if (!send_packet(this, request, packet->clone(packet))) + { + return DESTROY_ME; + } 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; @@ -389,7 +486,7 @@ METHOD(task_manager_t, retransmit, status_t, if (seqnr == this->initiating.seqnr && this->initiating.packet) { - status = retransmit_packet(this, seqnr, this->initiating.mid, + status = retransmit_packet(this, TRUE, seqnr, this->initiating.mid, this->initiating.retransmitted, this->initiating.packet); if (status == NEED_MORE) { @@ -399,7 +496,7 @@ METHOD(task_manager_t, retransmit, status_t, } if (seqnr == this->responding.seqnr && this->responding.packet) { - status = retransmit_packet(this, seqnr, this->responding.mid, + status = retransmit_packet(this, FALSE, seqnr, this->responding.mid, this->responding.retransmitted, this->responding.packet); if (status == NEED_MORE) { @@ -675,12 +772,12 @@ METHOD(task_manager_t, initiate, status_t, } if (keep) { /* keep the packet for retransmission, the responder might request it */ - charon->sender->send(charon->sender, + send_packet(this, TRUE, this->initiating.packet->clone(this->initiating.packet)); } else { - charon->sender->send(charon->sender, this->initiating.packet); + send_packet(this, TRUE, this->initiating.packet); this->initiating.packet = NULL; } message->destroy(message); @@ -784,8 +881,8 @@ static status_t build_response(private_task_manager_t *this, message_t *request) { return retransmit(this, this->responding.seqnr); } - charon->sender->send(charon->sender, - this->responding.packet->clone(this->responding.packet)); + send_packet(this, FALSE, + this->responding.packet->clone(this->responding.packet)); if (delete) { return DESTROY_ME; @@ -840,7 +937,7 @@ static void send_notify(private_task_manager_t *this, message_t *request, if (this->ike_sa->generate_message(this->ike_sa, response, &packet) == SUCCESS) { - charon->sender->send(charon->sender, packet); + send_packet(this, TRUE, packet); } response->destroy(response); } @@ -939,6 +1036,7 @@ 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) @@ -1295,8 +1393,8 @@ METHOD(task_manager_t, process_message, status_t, { DBG1(DBG_IKE, "received retransmit of response with ID %u, " "resending last request", mid); - charon->sender->send(charon->sender, - this->initiating.packet->clone(this->initiating.packet)); + send_packet(this, TRUE, + this->initiating.packet->clone(this->initiating.packet)); return SUCCESS; } DBG1(DBG_IKE, "received retransmit of response with ID %u, " @@ -1341,7 +1439,7 @@ METHOD(task_manager_t, process_message, status_t, { DBG1(DBG_IKE, "received retransmit of request with ID %u, " "retransmitting response", mid); - charon->sender->send(charon->sender, + send_packet(this, FALSE, this->responding.packet->clone(this->responding.packet)); } else if (this->initiating.packet && @@ -1349,7 +1447,7 @@ METHOD(task_manager_t, process_message, status_t, { DBG1(DBG_IKE, "received retransmit of DPD request, " "retransmitting response"); - charon->sender->send(charon->sender, + send_packet(this, TRUE, this->initiating.packet->clone(this->initiating.packet)); } else @@ -1480,6 +1578,7 @@ METHOD(task_manager_t, queue_ike, void, { queue_task(this, (task_t*)aggressive_mode_create(this->ike_sa, TRUE)); } + this->frag.exchange = AGGRESSIVE; } else { @@ -1882,6 +1981,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .seqnr = RESPONDING_SEQ, }, .frag = { + .exchange = ID_PROT, .max_packet = lib->settings->get_int(lib->settings, "%s.max_packet", MAX_PACKET, charon->name), }, From 2f62bb1549c7c4c35453674b4ad3bd00dfa6b162 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Dec 2012 18:27:02 +0100 Subject: [PATCH 14/20] Add an option to en-/disable IKE fragmentation Fragments are always accepted but will not be sent if disabled. The vendor ID is only sent if the option is enabled. --- man/strongswan.conf.5.in | 5 +++++ src/libcharon/sa/ikev1/task_manager_v1.c | 9 ++++++++- src/libcharon/sa/ikev1/tasks/isakmp_vendor.c | 16 ++++++++++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index 8000951ab..14caccb3a 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -178,6 +178,11 @@ openly transmitted hash of the PSK) .BR charon.ignore_routing_tables A space-separated list of routing tables to be excluded from route lookups .TP +.BR charon.ike_fragmentation " [no]" +Enables IKE fragmentation (proprietary IKEv1 extension). Fragmented messages +are always accepted irrespective of the value of this option. If the peer +supports it larger messages will be sent in fragments. +.TP .BR charon.ikesa_table_segments " [1]" Number of exclusively locked segments in the hash table .TP diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index e56f4be96..606a981d0 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -225,6 +225,11 @@ struct private_task_manager_t { } frag; + /** + * TRUE if fragmentation (as sender) is enabled in config + */ + bool fragmentation; + /** * List of queued tasks not yet in action */ @@ -411,7 +416,7 @@ static bool send_packet(private_task_manager_t *this, bool request, data = packet->get_data(packet); if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && - data.len > MAX_FRAGMENT_SIZE) + this->fragmentation && data.len > MAX_FRAGMENT_SIZE) { fragment_payload_t *fragment; u_int8_t num, count; @@ -1996,6 +2001,8 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, charon->name), .retransmit_base = lib->settings->get_double(lib->settings, "%s.retransmit_base", RETRANSMIT_BASE, charon->name), + .fragmentation = lib->settings->get_bool(lib->settings, + "%s.ike_fragmentation", FALSE, charon->name), ); if (!this->rng) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index bf822b548..f83f114b9 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -155,18 +155,26 @@ METHOD(task_t, build, status_t, private_isakmp_vendor_t *this, message_t *message) { vendor_id_payload_t *vid_payload; - bool strongswan, cisco_unity; + bool strongswan, cisco_unity, fragmentation; int i; strongswan = lib->settings->get_bool(lib->settings, - "%s.send_vendor_id", FALSE, charon->name); + "%s.send_vendor_id", FALSE, charon->name); cisco_unity = lib->settings->get_bool(lib->settings, - "%s.cisco_unity", FALSE, charon->name); + "%s.cisco_unity", FALSE, charon->name); + fragmentation = lib->settings->get_bool(lib->settings, + "%s.ike_fragmentation", FALSE, charon->name); + if (!this->initiator && fragmentation) + { + fragmentation = this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_FRAGMENTATION); + } for (i = 0; i < countof(vendor_ids); i++) { if (vendor_ids[i].send || (vendor_ids[i].extension == EXT_STRONGSWAN && strongswan) || - (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity)) + (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity) || + (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION && fragmentation)) { DBG2(DBG_IKE, "sending %s vendor ID", vendor_ids[i].desc); vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1, From e645c15b9c918303134b1be63d67dacf51e1945b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 21 Dec 2012 18:40:23 +0100 Subject: [PATCH 15/20] Include source port in init hash for fragmented messages --- src/libcharon/sa/ike_sa_manager.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 260da8be1..2ac8c3123 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -975,7 +975,8 @@ static bool get_init_hash(private_ike_sa_manager_t *this, message_t *message, return FALSE; } if (message->get_first_payload_type(message) == FRAGMENT_V1) - { /* only hash the source IP and SPI for fragmented init messages */ + { /* 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); @@ -984,6 +985,12 @@ static bool get_init_hash(private_ike_sa_manager_t *this, message_t *message, { return FALSE; } + port = src->get_port(src); + if (!this->hasher->allocate_hash(this->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); From 97973f860924e4472be9c842c34843cd95680642 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 24 Dec 2012 12:28:01 +0100 Subject: [PATCH 16/20] Use a connection specific option to en-/disable IKEv1 fragmentation --- man/ipsec.conf.5.in | 10 ++++++++++ man/strongswan.conf.5.in | 5 ----- src/charon-nm/nm/nm_service.c | 2 +- src/conftest/config.c | 2 +- .../libandroidbridge/backend/android_service.c | 2 +- src/libcharon/config/ike_cfg.c | 17 ++++++++++++++++- src/libcharon/config/ike_cfg.h | 13 +++++++++++-- src/libcharon/plugins/android/android_service.c | 2 +- src/libcharon/plugins/ha/ha_tunnel.c | 2 +- .../plugins/load_tester/load_tester_config.c | 4 ++-- src/libcharon/plugins/maemo/maemo_service.c | 2 +- src/libcharon/plugins/medcli/medcli_config.c | 4 ++-- src/libcharon/plugins/medsrv/medsrv_config.c | 2 +- src/libcharon/plugins/sql/sql_config.c | 2 +- src/libcharon/plugins/stroke/stroke_config.c | 3 ++- src/libcharon/plugins/uci/uci_config.c | 4 ++-- src/libcharon/sa/ikev1/task_manager_v1.c | 11 +++-------- src/libcharon/sa/ikev1/tasks/isakmp_vendor.c | 6 ++++-- src/starter/args.c | 1 + src/starter/confread.c | 3 +++ src/starter/confread.h | 1 + src/starter/keywords.h | 1 + src/starter/keywords.txt | 1 + src/starter/starterstroke.c | 1 + src/stroke/stroke_msg.h | 1 + 25 files changed, 69 insertions(+), 33 deletions(-) diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in index 303fb78fa..01c7c3848 100644 --- a/man/ipsec.conf.5.in +++ b/man/ipsec.conf.5.in @@ -403,6 +403,16 @@ force UDP encapsulation for ESP packets even if no NAT situation is detected. This may help to surmount restrictive firewalls. In order to force the peer to encapsulate packets, NAT detection payloads are faked. .TP +.BR fragmentation " = yes | " no +whether to use IKE fragmentation (proprietary IKEv1 extension). Acceptable +values are +.B yes +and +.B no +(the default). Fragmented messages sent by a peer are always accepted +irrespective of the value of this option. If enabled, and the peer supports it, +larger IKE messages will be sent in fragments. +.TP .BR ike " = " comma-separated list of IKE/ISAKMP SA encryption/authentication algorithms to be used, e.g. diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index 14caccb3a..8000951ab 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -178,11 +178,6 @@ openly transmitted hash of the PSK) .BR charon.ignore_routing_tables A space-separated list of routing tables to be excluded from route lookups .TP -.BR charon.ike_fragmentation " [no]" -Enables IKE fragmentation (proprietary IKEv1 extension). Fragmented messages -are always accepted irrespective of the value of this option. If the peer -supports it larger messages will be sent in fragments. -.TP .BR charon.ikesa_table_segments " [1]" Number of exclusively locked segments in the hash table .TP diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index 6fa5e51a4..7eb5e8565 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -500,7 +500,7 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, */ ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - (char*)address, FALSE, IKEV2_UDP_PORT); + (char*)address, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(priv->name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */ diff --git a/src/conftest/config.c b/src/conftest/config.c index f896b9537..e2705467f 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -106,7 +106,7 @@ static ike_cfg_t *load_ike_config(private_config_t *this, settings->get_str(settings, "configs.%s.lhost", "%any", config), FALSE, settings->get_int(settings, "configs.%s.lport", 500, config), settings->get_str(settings, "configs.%s.rhost", "%any", config), FALSE, - settings->get_int(settings, "configs.%s.rport", 500, config)); + settings->get_int(settings, "configs.%s.rport", 500, config), FALSE); token = settings->get_str(settings, "configs.%s.proposal", NULL, config); if (token) { diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index 6c23bf268..f7fe29070 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -471,7 +471,7 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - this->gateway, FALSE, IKEV2_UDP_PORT); + this->gateway, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index 1006fc295..e87b47e69 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -89,6 +89,11 @@ struct private_ike_cfg_t { */ bool force_encap; + /** + * use IKEv1 fragmentation + */ + bool fragmentation; + /** * List of proposals to use */ @@ -113,6 +118,12 @@ METHOD(ike_cfg_t, force_encap_, bool, return this->force_encap; } +METHOD(ike_cfg_t, fragmentation, bool, + private_ike_cfg_t *this) +{ + return this->fragmentation; +} + METHOD(ike_cfg_t, get_my_addr, char*, private_ike_cfg_t *this, bool *allow_any) { @@ -268,6 +279,7 @@ METHOD(ike_cfg_t, equals, bool, this->version == other->version && this->certreq == other->certreq && this->force_encap == other->force_encap && + this->fragmentation == other->fragmentation && streq(this->me, other->me) && streq(this->other, other->other) && this->my_port == other->my_port && @@ -299,7 +311,8 @@ METHOD(ike_cfg_t, destroy, void, */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, - char *other, bool other_allow_any, u_int16_t other_port) + char *other, bool other_allow_any, u_int16_t other_port, + bool fragmentation) { private_ike_cfg_t *this; @@ -308,6 +321,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .get_version = _get_version, .send_certreq = _send_certreq, .force_encap = _force_encap_, + .fragmentation = _fragmentation, .get_my_addr = _get_my_addr, .get_other_addr = _get_other_addr, .get_my_port = _get_my_port, @@ -324,6 +338,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, .version = version, .certreq = certreq, .force_encap = force_encap, + .fragmentation = fragmentation, .me = strdup(me), .other = strdup(other), .my_allow_any = my_allow_any, diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index fa2aaa325..0c4484252 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -134,10 +134,17 @@ struct ike_cfg_t { /** * Enforce UDP encapsulation by faking NATD notifies? * - * @return TRUE to enfoce UDP encapsulation + * @return TRUE to enforce UDP encapsulation */ bool (*force_encap) (ike_cfg_t *this); + /** + * Use proprietary IKEv1 fragmentation + * + * @return TRUE to use fragmentation + */ + bool (*fragmentation) (ike_cfg_t *this); + /** * Get the DH group to use for IKE_SA setup. * @@ -183,10 +190,12 @@ struct ike_cfg_t { * @param other address/DNS name of remote peer * @param other_allow_any allow override of remote address by any address * @param other_port IKE port to use as dest, 500 uses IKEv2 port floating + * @param fragmentation use IKEv1 fragmentation * @return ike_cfg_t object. */ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, - char *other, bool other_allow_any, u_int16_t other_port); + char *other, bool other_allow_any, u_int16_t other_port, + bool fragmentation); #endif /** IKE_CFG_H_ @}*/ diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c index d398b136f..0188b0c2b 100644 --- a/src/libcharon/plugins/android/android_service.c +++ b/src/libcharon/plugins/android/android_service.c @@ -266,7 +266,7 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT); + hostname, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index 05f522e8e..e7db1ff0f 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -205,7 +205,7 @@ static void setup_tunnel(private_ha_tunnel_t *this, /* create config and backend */ ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT); + remote, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("ha", ike_cfg, CERT_NEVER_SEND, UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, FALSE, 30, diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index 9e1615686..a9d399b9d 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -490,14 +490,14 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) { ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, this->port + num - 1, - remote, FALSE, IKEV2_NATT_PORT); + remote, FALSE, IKEV2_NATT_PORT, FALSE); } else { ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT); + remote, FALSE, IKEV2_UDP_PORT, FALSE); } ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal)); peer_cfg = peer_cfg_create("load-test", ike_cfg, diff --git a/src/libcharon/plugins/maemo/maemo_service.c b/src/libcharon/plugins/maemo/maemo_service.c index dca01fbaa..759bd9646 100644 --- a/src/libcharon/plugins/maemo/maemo_service.c +++ b/src/libcharon/plugins/maemo/maemo_service.c @@ -325,7 +325,7 @@ static gboolean initiate_connection(private_maemo_service_t *this, ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT); + hostname, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(this->current, ike_cfg, diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 42c158231..12ffc1ae4 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -105,7 +105,7 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - address, FALSE, IKEV2_UDP_PORT); + address, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); med_cfg = peer_cfg_create( "mediation", ike_cfg, @@ -380,7 +380,7 @@ medcli_config_t *medcli_config_create(database_t *db) .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - "0.0.0.0", FALSE, IKEV2_UDP_PORT), + "0.0.0.0", FALSE, IKEV2_UDP_PORT, FALSE), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index a2d7489f4..d16758111 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -142,7 +142,7 @@ medsrv_config_t *medsrv_config_create(database_t *db) .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - "0.0.0.0", FALSE, IKEV2_UDP_PORT), + "0.0.0.0", FALSE, IKEV2_UDP_PORT, FALSE), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index 565c8584b..44a593c7b 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -261,7 +261,7 @@ static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e, ike_cfg = ike_cfg_create(IKEV2, certreq, force_encap, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT); + remote, FALSE, IKEV2_UDP_PORT, FALSE); add_ike_proposals(this, ike_cfg, id); return ike_cfg; } diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index de3f4472e..0ab94f2a7 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -233,7 +233,8 @@ static ike_cfg_t *build_ike_cfg(private_stroke_config_t *this, stroke_msg_t *msg ikeport, msg->add_conn.other.address, msg->add_conn.other.allow_any, - msg->add_conn.other.ikeport); + msg->add_conn.other.ikeport, + msg->add_conn.fragmentation); add_proposals(this, msg->add_conn.algorithms.ike, ike_cfg, NULL); return ike_cfg; } diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 76dce125b..6dae14e3d 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -155,7 +155,7 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote_addr, FALSE, IKEV2_UDP_PORT); + remote_addr, FALSE, IKEV2_UDP_PORT, FALSE); ike_cfg->add_proposal(ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); this->peer_cfg = peer_cfg_create( name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_NO, @@ -253,7 +253,7 @@ METHOD(enumerator_t, ike_enumerator_enumerate, bool, this->ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote_addr, FALSE, IKEV2_UDP_PORT); + remote_addr, FALSE, IKEV2_UDP_PORT, FALSE); this->ike_cfg->add_proposal(this->ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 606a981d0..320aa5e6b 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -225,11 +225,6 @@ struct private_task_manager_t { } frag; - /** - * TRUE if fragmentation (as sender) is enabled in config - */ - bool fragmentation; - /** * List of queued tasks not yet in action */ @@ -411,12 +406,14 @@ static bool send_fragment(private_task_manager_t *this, bool request, static bool send_packet(private_task_manager_t *this, bool request, packet_t *packet) { + ike_cfg_t *ike_cfg; host_t *src, *dst; chunk_t data; + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); data = packet->get_data(packet); if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && - this->fragmentation && data.len > MAX_FRAGMENT_SIZE) + ike_cfg->fragmentation(ike_cfg) && data.len > MAX_FRAGMENT_SIZE) { fragment_payload_t *fragment; u_int8_t num, count; @@ -2001,8 +1998,6 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, charon->name), .retransmit_base = lib->settings->get_double(lib->settings, "%s.retransmit_base", RETRANSMIT_BASE, charon->name), - .fragmentation = lib->settings->get_bool(lib->settings, - "%s.ike_fragmentation", FALSE, charon->name), ); if (!this->rng) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index f83f114b9..32eeee353 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -156,14 +157,15 @@ METHOD(task_t, build, status_t, { vendor_id_payload_t *vid_payload; bool strongswan, cisco_unity, fragmentation; + ike_cfg_t *ike_cfg; int i; strongswan = lib->settings->get_bool(lib->settings, "%s.send_vendor_id", FALSE, charon->name); cisco_unity = lib->settings->get_bool(lib->settings, "%s.cisco_unity", FALSE, charon->name); - fragmentation = lib->settings->get_bool(lib->settings, - "%s.ike_fragmentation", FALSE, charon->name); + ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + fragmentation = ike_cfg->fragmentation(ike_cfg); if (!this->initiator && fragmentation) { fragmentation = this->ike_sa->supports_extension(this->ike_sa, diff --git a/src/starter/args.c b/src/starter/args.c index 1c16e0b9c..ad73a1635 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -138,6 +138,7 @@ static const token_info_t token_info[] = { ARG_STR, offsetof(starter_conn_t, aaa_identity), NULL }, { ARG_MISC, 0, NULL /* KW_MOBIKE */ }, { ARG_MISC, 0, NULL /* KW_FORCEENCAPS */ }, + { ARG_MISC, 0, NULL /* KW_FRAGMENTATION */ }, { ARG_TIME, offsetof(starter_conn_t, sa_ike_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_ipsec_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_rekey_margin), NULL }, diff --git a/src/starter/confread.c b/src/starter/confread.c index fecb998df..dfe7e2c89 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -567,6 +567,9 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg case KW_FORCEENCAPS: KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_FORCE_ENCAP) break; + case KW_FRAGMENTATION: + KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_FRAGMENTATION) + break; case KW_MODECONFIG: KW_SA_OPTION_FLAG("push", "pull", SA_OPTION_MODECFG_PUSH) break; diff --git a/src/starter/confread.h b/src/starter/confread.h index 3f2079883..5e0e0f255 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -81,6 +81,7 @@ typedef enum { SA_OPTION_XAUTH_SERVER = 1 << 5, /* are we an XAUTH server? */ SA_OPTION_MOBIKE = 1 << 6, /* enable MOBIKE for IKEv2 */ SA_OPTION_FORCE_ENCAP = 1 << 7, /* force UDP encapsulation */ + SA_OPTION_FRAGMENTATION = 1 << 8, /* enable IKEv1 fragmentation */ } sa_option_t; typedef struct starter_end starter_end_t; diff --git a/src/starter/keywords.h b/src/starter/keywords.h index 537bceb07..f776f33c9 100644 --- a/src/starter/keywords.h +++ b/src/starter/keywords.h @@ -42,6 +42,7 @@ typedef enum { KW_AAA_IDENTITY, KW_MOBIKE, KW_FORCEENCAPS, + KW_FRAGMENTATION, KW_IKELIFETIME, KW_KEYLIFE, KW_REKEYMARGIN, diff --git a/src/starter/keywords.txt b/src/starter/keywords.txt index 8366f5209..1f1641287 100644 --- a/src/starter/keywords.txt +++ b/src/starter/keywords.txt @@ -40,6 +40,7 @@ eap_identity, KW_EAP_IDENTITY aaa_identity, KW_AAA_IDENTITY mobike, KW_MOBIKE forceencaps, KW_FORCEENCAPS +fragmentation, KW_FRAGMENTATION ikelifetime, KW_IKELIFETIME lifetime, KW_KEYLIFE keylife, KW_KEYLIFE diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 68e6fd5ea..41288531d 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -180,6 +180,7 @@ int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn) } msg.add_conn.mobike = conn->options & SA_OPTION_MOBIKE; msg.add_conn.force_encap = conn->options & SA_OPTION_FORCE_ENCAP; + msg.add_conn.fragmentation = conn->options & SA_OPTION_FRAGMENTATION; msg.add_conn.ipcomp = conn->options & SA_OPTION_COMPRESS; msg.add_conn.install_policy = conn->install_policy; msg.add_conn.aggressive = conn->aggressive; diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 1ce44f9eb..e972a5984 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -254,6 +254,7 @@ struct stroke_msg_t { int mobike; int aggressive; int force_encap; + int fragmentation; int ipcomp; time_t inactivity; int proxy_mode; From 365d9a6f67739bdc4c43130eec362ad97414762c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 24 Dec 2012 12:59:30 +0100 Subject: [PATCH 17/20] Added an option that allows to force IKEv1 fragmentation --- man/ipsec.conf.5.in | 13 +++++++++---- src/charon-nm/nm/nm_service.c | 3 ++- src/conftest/config.c | 3 ++- .../libandroidbridge/backend/android_service.c | 3 ++- src/libcharon/config/ike_cfg.c | 6 +++--- src/libcharon/config/ike_cfg.h | 18 ++++++++++++++++-- .../plugins/android/android_service.c | 2 +- src/libcharon/plugins/ha/ha_tunnel.c | 2 +- .../plugins/load_tester/load_tester_config.c | 6 ++++-- src/libcharon/plugins/maemo/maemo_service.c | 2 +- src/libcharon/plugins/medcli/medcli_config.c | 5 +++-- src/libcharon/plugins/medsrv/medsrv_config.c | 3 ++- src/libcharon/plugins/sql/sql_config.c | 3 ++- src/libcharon/plugins/uci/uci_config.c | 6 ++++-- src/libcharon/sa/ikev1/task_manager_v1.c | 7 +++++-- src/libcharon/sa/ikev1/tasks/isakmp_vendor.c | 2 +- src/starter/args.c | 9 ++++++++- src/starter/confread.c | 3 --- src/starter/confread.h | 11 +++++++++-- src/starter/starterstroke.c | 2 +- 20 files changed, 76 insertions(+), 33 deletions(-) diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in index 01c7c3848..2766cc4ed 100644 --- a/man/ipsec.conf.5.in +++ b/man/ipsec.conf.5.in @@ -403,15 +403,20 @@ force UDP encapsulation for ESP packets even if no NAT situation is detected. This may help to surmount restrictive firewalls. In order to force the peer to encapsulate packets, NAT detection payloads are faked. .TP -.BR fragmentation " = yes | " no +.BR fragmentation " = yes | force | " no whether to use IKE fragmentation (proprietary IKEv1 extension). Acceptable values are -.B yes +.BR yes , +.B force and .B no (the default). Fragmented messages sent by a peer are always accepted -irrespective of the value of this option. If enabled, and the peer supports it, -larger IKE messages will be sent in fragments. +irrespective of the value of this option. If set to +.BR yes , +and the peer supports it, larger IKE messages will be sent in fragments. +If set to +.B force +the initial IKE message will already be fragmented if required. .TP .BR ike " = " comma-separated list of IKE/ISAKMP SA encryption/authentication algorithms diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index 7eb5e8565..eb187496d 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -500,7 +500,8 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, */ ike_cfg = ike_cfg_create(IKEV2, TRUE, encap, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - (char*)address, FALSE, IKEV2_UDP_PORT, FALSE); + (char*)address, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(priv->name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_REPLACE, 1, /* keyingtries */ diff --git a/src/conftest/config.c b/src/conftest/config.c index e2705467f..22c9d23e0 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -106,7 +106,8 @@ static ike_cfg_t *load_ike_config(private_config_t *this, settings->get_str(settings, "configs.%s.lhost", "%any", config), FALSE, settings->get_int(settings, "configs.%s.lport", 500, config), settings->get_str(settings, "configs.%s.rhost", "%any", config), FALSE, - settings->get_int(settings, "configs.%s.rport", 500, config), FALSE); + settings->get_int(settings, "configs.%s.rport", 500, config), + FRAGMENTATION_NO); token = settings->get_str(settings, "configs.%s.proposal", NULL, config); if (token) { diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index f7fe29070..cce5ff0d4 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -471,7 +471,8 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - this->gateway, FALSE, IKEV2_UDP_PORT, FALSE); + this->gateway, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c index e87b47e69..5e5fbba42 100644 --- a/src/libcharon/config/ike_cfg.c +++ b/src/libcharon/config/ike_cfg.c @@ -92,7 +92,7 @@ struct private_ike_cfg_t { /** * use IKEv1 fragmentation */ - bool fragmentation; + fragmentation_t fragmentation; /** * List of proposals to use @@ -118,7 +118,7 @@ METHOD(ike_cfg_t, force_encap_, bool, return this->force_encap; } -METHOD(ike_cfg_t, fragmentation, bool, +METHOD(ike_cfg_t, fragmentation, fragmentation_t, private_ike_cfg_t *this) { return this->fragmentation; @@ -312,7 +312,7 @@ METHOD(ike_cfg_t, destroy, void, ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, char *other, bool other_allow_any, u_int16_t other_port, - bool fragmentation) + fragmentation_t fragmentation) { private_ike_cfg_t *this; diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h index 0c4484252..5a7fae1e9 100644 --- a/src/libcharon/config/ike_cfg.h +++ b/src/libcharon/config/ike_cfg.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2012 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -23,6 +24,7 @@ #define IKE_CFG_H_ typedef enum ike_version_t ike_version_t; +typedef enum fragmentation_t fragmentation_t; typedef struct ike_cfg_t ike_cfg_t; #include @@ -44,6 +46,18 @@ enum ike_version_t { IKEV2 = 2, }; +/** + * Proprietary IKEv1 fragmentation + */ +enum fragmentation_t { + /** disable fragmentation */ + FRAGMENTATION_NO, + /** enable fragmentation if supported by peer */ + FRAGMENTATION_YES, + /** force use of fragmentation (even for the first message) */ + FRAGMENTATION_FORCE, +}; + /** * enum strings fro ike_version_t */ @@ -143,7 +157,7 @@ struct ike_cfg_t { * * @return TRUE to use fragmentation */ - bool (*fragmentation) (ike_cfg_t *this); + fragmentation_t (*fragmentation) (ike_cfg_t *this); /** * Get the DH group to use for IKE_SA setup. @@ -196,6 +210,6 @@ struct ike_cfg_t { ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap, char *me, bool my_allow_any, u_int16_t my_port, char *other, bool other_allow_any, u_int16_t other_port, - bool fragmentation); + fragmentation_t fragmentation); #endif /** IKE_CFG_H_ @}*/ diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c index 0188b0c2b..6af35e5df 100644 --- a/src/libcharon/plugins/android/android_service.c +++ b/src/libcharon/plugins/android/android_service.c @@ -266,7 +266,7 @@ static job_requeue_t initiate(private_android_service_t *this) ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT, FALSE); + hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("android", ike_cfg, CERT_SEND_IF_ASKED, diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index e7db1ff0f..130c86e48 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -205,7 +205,7 @@ static void setup_tunnel(private_ha_tunnel_t *this, /* create config and backend */ ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT, FALSE); + remote, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("ha", ike_cfg, CERT_NEVER_SEND, UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, FALSE, 30, diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index a9d399b9d..c6288c5d9 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -490,14 +490,16 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) { ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, this->port + num - 1, - remote, FALSE, IKEV2_NATT_PORT, FALSE); + remote, FALSE, IKEV2_NATT_PORT, + FRAGMENTATION_NO); } else { ike_cfg = ike_cfg_create(this->version, TRUE, FALSE, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT, FALSE); + remote, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); } ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal)); peer_cfg = peer_cfg_create("load-test", ike_cfg, diff --git a/src/libcharon/plugins/maemo/maemo_service.c b/src/libcharon/plugins/maemo/maemo_service.c index 759bd9646..806e4cd65 100644 --- a/src/libcharon/plugins/maemo/maemo_service.c +++ b/src/libcharon/plugins/maemo/maemo_service.c @@ -325,7 +325,7 @@ static gboolean initiate_connection(private_maemo_service_t *this, ike_cfg = ike_cfg_create(IKEV2, TRUE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - hostname, FALSE, IKEV2_UDP_PORT, FALSE); + hostname, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create(this->current, ike_cfg, diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 12ffc1ae4..4be3dea02 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -105,7 +105,7 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - address, FALSE, IKEV2_UDP_PORT, FALSE); + address, FALSE, IKEV2_UDP_PORT, FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); med_cfg = peer_cfg_create( "mediation", ike_cfg, @@ -380,7 +380,8 @@ medcli_config_t *medcli_config_create(database_t *db) .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - "0.0.0.0", FALSE, IKEV2_UDP_PORT, FALSE), + "0.0.0.0", FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index d16758111..be14380ea 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -142,7 +142,8 @@ medsrv_config_t *medsrv_config_create(database_t *db) .ike = ike_cfg_create(IKEV2, FALSE, FALSE, "0.0.0.0", FALSE, charon->socket->get_port(charon->socket, FALSE), - "0.0.0.0", FALSE, IKEV2_UDP_PORT, FALSE), + "0.0.0.0", FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO), ); this->ike->add_proposal(this->ike, proposal_create_default(PROTO_IKE)); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index 44a593c7b..37bd86671 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -261,7 +261,8 @@ static ike_cfg_t *build_ike_cfg(private_sql_config_t *this, enumerator_t *e, ike_cfg = ike_cfg_create(IKEV2, certreq, force_encap, local, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote, FALSE, IKEV2_UDP_PORT, FALSE); + remote, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); add_ike_proposals(this, ike_cfg, id); return ike_cfg; } diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 6dae14e3d..de0bf91af 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -155,7 +155,8 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote_addr, FALSE, IKEV2_UDP_PORT, FALSE); + remote_addr, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); ike_cfg->add_proposal(ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); this->peer_cfg = peer_cfg_create( name, ike_cfg, CERT_SEND_IF_ASKED, UNIQUE_NO, @@ -253,7 +254,8 @@ METHOD(enumerator_t, ike_enumerator_enumerate, bool, this->ike_cfg = ike_cfg_create(IKEV2, FALSE, FALSE, local_addr, FALSE, charon->socket->get_port(charon->socket, FALSE), - remote_addr, FALSE, IKEV2_UDP_PORT, FALSE); + remote_addr, FALSE, IKEV2_UDP_PORT, + FRAGMENTATION_NO); this->ike_cfg->add_proposal(this->ike_cfg, create_proposal(ike_proposal, PROTO_IKE)); diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 320aa5e6b..60ec23199 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -406,14 +406,17 @@ static bool send_fragment(private_task_manager_t *this, bool request, static bool send_packet(private_task_manager_t *this, bool request, packet_t *packet) { + fragmentation_t fragmentation; ike_cfg_t *ike_cfg; host_t *src, *dst; chunk_t data; ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); + fragmentation = ike_cfg->fragmentation(ike_cfg); data = packet->get_data(packet); - if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && - ike_cfg->fragmentation(ike_cfg) && data.len > MAX_FRAGMENT_SIZE) + if (data.len > MAX_FRAGMENT_SIZE && (fragmentation == FRAGMENTATION_FORCE || + (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && + fragmentation == FRAGMENTATION_YES))) { fragment_payload_t *fragment; u_int8_t num, count; diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 32eeee353..1bf7bf643 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -165,7 +165,7 @@ METHOD(task_t, build, status_t, cisco_unity = lib->settings->get_bool(lib->settings, "%s.cisco_unity", FALSE, charon->name); ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); - fragmentation = ike_cfg->fragmentation(ike_cfg); + fragmentation = ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO; if (!this->initiator && fragmentation) { fragmentation = this->ike_sa->supports_extension(this->ike_sa, diff --git a/src/starter/args.c b/src/starter/args.c index ad73a1635..390062a99 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -108,6 +108,13 @@ static const char *LST_authby[] = { NULL }; +static const char *LST_fragmentation[] = { + "no", + "yes", + "force", + NULL +}; + typedef struct { arg_t type; size_t offset; @@ -138,7 +145,7 @@ static const token_info_t token_info[] = { ARG_STR, offsetof(starter_conn_t, aaa_identity), NULL }, { ARG_MISC, 0, NULL /* KW_MOBIKE */ }, { ARG_MISC, 0, NULL /* KW_FORCEENCAPS */ }, - { ARG_MISC, 0, NULL /* KW_FRAGMENTATION */ }, + { ARG_ENUM, offsetof(starter_conn_t, fragmentation), LST_fragmentation }, { ARG_TIME, offsetof(starter_conn_t, sa_ike_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_ipsec_life_seconds), NULL }, { ARG_TIME, offsetof(starter_conn_t, sa_rekey_margin), NULL }, diff --git a/src/starter/confread.c b/src/starter/confread.c index dfe7e2c89..fecb998df 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -567,9 +567,6 @@ static void load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg case KW_FORCEENCAPS: KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_FORCE_ENCAP) break; - case KW_FRAGMENTATION: - KW_SA_OPTION_FLAG("yes", "no", SA_OPTION_FRAGMENTATION) - break; case KW_MODECONFIG: KW_SA_OPTION_FLAG("push", "pull", SA_OPTION_MODECFG_PUSH) break; diff --git a/src/starter/confread.h b/src/starter/confread.h index 5e0e0f255..a0f6234f9 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -50,7 +50,7 @@ typedef enum { typedef enum { STRICT_NO, STRICT_YES, - STRICT_IFURI + STRICT_IFURI, } strict_t; typedef enum { @@ -69,6 +69,13 @@ typedef enum { DPD_ACTION_UNKNOW, } dpd_action_t; +typedef enum { + /* same as in ike_cfg.h */ + FRAGMENTATION_NO, + FRAGMENTATION_YES, + FRAGMENTATION_FORCE, +} fragmentation_t; + typedef enum { /* IPsec options */ SA_OPTION_AUTHENTICATE = 1 << 0, /* use AH instead of ESP? */ @@ -81,7 +88,6 @@ typedef enum { SA_OPTION_XAUTH_SERVER = 1 << 5, /* are we an XAUTH server? */ SA_OPTION_MOBIKE = 1 << 6, /* enable MOBIKE for IKEv2 */ SA_OPTION_FORCE_ENCAP = 1 << 7, /* force UDP encapsulation */ - SA_OPTION_FRAGMENTATION = 1 << 8, /* enable IKEv1 fragmentation */ } sa_option_t; typedef struct starter_end starter_end_t; @@ -141,6 +147,7 @@ struct starter_conn { char *authby; ipsec_mode_t mode; bool proxy_mode; + fragmentation_t fragmentation; sa_option_t options; time_t sa_ike_life_seconds; time_t sa_ipsec_life_seconds; diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 41288531d..4f9e8fb14 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -180,7 +180,7 @@ int starter_stroke_add_conn(starter_config_t *cfg, starter_conn_t *conn) } msg.add_conn.mobike = conn->options & SA_OPTION_MOBIKE; msg.add_conn.force_encap = conn->options & SA_OPTION_FORCE_ENCAP; - msg.add_conn.fragmentation = conn->options & SA_OPTION_FRAGMENTATION; + msg.add_conn.fragmentation = conn->fragmentation; msg.add_conn.ipcomp = conn->options & SA_OPTION_COMPRESS; msg.add_conn.install_policy = conn->install_policy; msg.add_conn.aggressive = conn->aggressive; From 7ae245f6851f8020da51963d63b6599d118d6134 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Sat, 12 Jan 2013 11:39:03 +0100 Subject: [PATCH 18/20] Properly detect fragmentation capabilities Cisco sends 0xc0000000 so we check that part of the VID separately. --- src/libcharon/sa/ikev1/tasks/isakmp_vendor.c | 30 ++++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 1bf7bf643..2ff2b55e9 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2013 Tobias Brunner * Copyright (C) 2009 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -100,7 +100,8 @@ static struct { { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16, "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"}, - /* Proprietary IKE fragmentation extension (0x800000 is added by racoon) */ + /* Proprietary IKE fragmentation extension. Capabilities are handled + * specially on receipt of this VID. */ { "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"}, @@ -150,8 +151,30 @@ static struct { { "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16, "\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"}, + }; +/** + * According to racoon 0x80000000 seems to indicate support for fragmentation + * of Aggressive and Main mode messages. 0x40000000 seems to indicate support + * for fragmentation of base ISAKMP messages (Cisco adds that and thus sends + * 0xc0000000) + */ +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) +{ + if (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION && + data.len == 20 && memeq(data.ptr, vendor_ids[i].id, 16)) + { + return untoh32(&data.ptr[16]) & fragmentation_ike; + } + return FALSE; +} + METHOD(task_t, build, status_t, private_isakmp_vendor_t *this, message_t *message) { @@ -221,7 +244,8 @@ METHOD(task_t, process, status_t, for (i = 0; i < countof(vendor_ids); i++) { if (chunk_equals(data, chunk_create(vendor_ids[i].id, - vendor_ids[i].len))) + vendor_ids[i].len)) || + fragmentation_supported(data, i)) { DBG1(DBG_IKE, "received %s vendor ID", vendor_ids[i].desc); if (vendor_ids[i].extension) From ee6902ef7f69386cc73303e530736d2ba9423051 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Sat, 12 Jan 2013 11:48:32 +0100 Subject: [PATCH 19/20] Added an option to configure the maximum size of a fragment --- man/strongswan.conf.5.in | 4 ++++ src/libcharon/sa/ikev1/task_manager_v1.c | 13 ++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index 8000951ab..3f4e6389e 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -164,6 +164,10 @@ are released to free memory once an IKE_SA is established. Enabling this might conflict with plugins that later need access to e.g. the used certificates. .TP +.BR charon.fragment_size " [512]" +Maximum size (in bytes) of a sent fragment when using the proprietary IKEv1 +fragmentation extension. +.TP .BR charon.half_open_timeout " [30]" Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING). .TP diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 60ec23199..7d6e7ac91 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-2012 Tobias Brunner + * Copyright (C) 2007-2013 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -216,6 +216,11 @@ struct private_task_manager_t { */ 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 @@ -414,7 +419,7 @@ static bool send_packet(private_task_manager_t *this, bool request, ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); fragmentation = ike_cfg->fragmentation(ike_cfg); data = packet->get_data(packet); - if (data.len > MAX_FRAGMENT_SIZE && (fragmentation == FRAGMENTATION_FORCE || + if (data.len > this->frag.size && (fragmentation == FRAGMENTATION_FORCE || (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) && fragmentation == FRAGMENTATION_YES))) { @@ -425,7 +430,7 @@ static bool send_packet(private_task_manager_t *this, bool request, /* reduce size due to non-ESP marker */ nat = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY); - frag_size = MAX_FRAGMENT_SIZE - (nat ? 4 : 0); + frag_size = this->frag.size - (nat ? 4 : 0); src = packet->get_source(packet); dst = packet->get_destination(packet); @@ -1989,6 +1994,8 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .exchange = ID_PROT, .max_packet = lib->settings->get_int(lib->settings, "%s.max_packet", MAX_PACKET, charon->name), + .size = lib->settings->get_int(lib->settings, + "%s.fragment_size", MAX_FRAGMENT_SIZE, charon->name), }, .ike_sa = ike_sa, .rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK), From f31b418094f2f9deb9a69244bf01e8d5dd4c3337 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Sat, 12 Jan 2013 11:51:35 +0100 Subject: [PATCH 20/20] NEWS for fragmentation extension added Conflicts: NEWS --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 05c5cc1e2..272020d8f 100644 --- a/NEWS +++ b/NEWS @@ -35,6 +35,10 @@ strongswan-5.0.2 In contrast to our own DER parser, OpenSSL can handle BER files, which is required for interoperability of our scepclient with EJBCA. +- Support for the proprietary IKEv1 fragmentation extension has been added. + Fragments are always handled on receipt but only sent if supported by the peer + and if enabled with the new fragmentation ipsec.conf option. + strongswan-5.0.1 ----------------