aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/charon-nm/nm/nm_service.c3
-rw-r--r--src/conftest/config.c3
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c3
-rw-r--r--src/libcharon/Android.mk1
-rw-r--r--src/libcharon/Makefile.am1
-rw-r--r--src/libcharon/config/ike_cfg.c17
-rw-r--r--src/libcharon/config/ike_cfg.h27
-rw-r--r--src/libcharon/encoding/message.c14
-rw-r--r--src/libcharon/encoding/payloads/fragment_payload.c225
-rw-r--r--src/libcharon/encoding/payloads/fragment_payload.h94
-rw-r--r--src/libcharon/encoding/payloads/payload.c29
-rw-r--r--src/libcharon/encoding/payloads/payload.h5
-rw-r--r--src/libcharon/network/sender.c6
-rw-r--r--src/libcharon/plugins/android/android_service.c2
-rw-r--r--src/libcharon/plugins/ha/ha_tunnel.c2
-rw-r--r--src/libcharon/plugins/load_tester/load_tester_config.c6
-rw-r--r--src/libcharon/plugins/maemo/maemo_service.c2
-rw-r--r--src/libcharon/plugins/medcli/medcli_config.c5
-rw-r--r--src/libcharon/plugins/medsrv/medsrv_config.c3
-rw-r--r--src/libcharon/plugins/sql/sql_config.c3
-rw-r--r--src/libcharon/plugins/stroke/stroke_config.c3
-rw-r--r--src/libcharon/plugins/uci/uci_config.c6
-rw-r--r--src/libcharon/processing/jobs/process_message_job.c5
-rw-r--r--src/libcharon/sa/ike_sa.c3
-rw-r--r--src/libcharon/sa/ike_sa.h5
-rw-r--r--src/libcharon/sa/ike_sa_manager.c30
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c344
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_vendor.c49
-rw-r--r--src/starter/args.c8
-rw-r--r--src/starter/confread.h10
-rw-r--r--src/starter/keywords.h1
-rw-r--r--src/starter/keywords.txt1
-rw-r--r--src/starter/starterstroke.c1
-rw-r--r--src/stroke/stroke_msg.h1
34 files changed, 859 insertions, 59 deletions
diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c
index 6fa5e51a4..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);
+ (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 f896b9537..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));
+ 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 6c23bf268..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);
+ 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/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/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c
index 1006fc295..5e5fbba42 100644
--- a/src/libcharon/config/ike_cfg.c
+++ b/src/libcharon/config/ike_cfg.c
@@ -90,6 +90,11 @@ struct private_ike_cfg_t {
bool force_encap;
/**
+ * use IKEv1 fragmentation
+ */
+ fragmentation_t fragmentation;
+
+ /**
* List of proposals to use
*/
linked_list_t *proposals;
@@ -113,6 +118,12 @@ METHOD(ike_cfg_t, force_encap_, bool,
return this->force_encap;
}
+METHOD(ike_cfg_t, fragmentation, fragmentation_t,
+ 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,
+ fragmentation_t 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..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 <library.h>
@@ -45,6 +47,18 @@ enum ike_version_t {
};
/**
+ * 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
*/
extern enum_name_t *ike_version_names;
@@ -134,11 +148,18 @@ 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
+ */
+ fragmentation_t (*fragmentation) (ike_cfg_t *this);
+
+ /**
* Get the DH group to use for IKE_SA setup.
*
* @return dh group to use for initialization
@@ -183,10 +204,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,
+ fragmentation_t fragmentation);
#endif /** IKE_CFG_H_ @}*/
diff --git a/src/libcharon/encoding/message.c b/src/libcharon/encoding/message.c
index 55e9f33fc..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},
};
/**
@@ -1693,6 +1701,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,
diff --git a/src/libcharon/encoding/payloads/fragment_payload.c b/src/libcharon/encoding/payloads/fragment_payload.c
new file mode 100644
index 000000000..1a6b3234b
--- /dev/null
+++ b/src/libcharon/encoding/payloads/fragment_payload.c
@@ -0,0 +1,225 @@
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "fragment_payload.h"
+
+#include <encoding/payloads/encodings.h>
+
+/** 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;
+}
+
+/*
+ * 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
new file mode 100644
index 000000000..a49cf32dd
--- /dev/null
+++ b/src/libcharon/encoding/payloads/fragment_payload.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup fragment_payload fragment_payload
+ * @{ @ingroup payloads
+ */
+
+#ifndef FRAGMENT_PAYLOAD_H_
+#define FRAGMENT_PAYLOAD_H_
+
+typedef struct fragment_payload_t fragment_payload_t;
+
+#include <library.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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();
+
+/**
+ * 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_ @}*/
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 <encoding/payloads/configuration_attribute.h>
#include <encoding/payloads/eap_payload.h>
#include <encoding/payloads/hash_payload.h>
+#include <encoding/payloads/fragment_payload.h>
#include <encoding/payloads/unknown_payload.h>
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
@@ -231,6 +231,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.
*
* This type and all the following are never sent over wire and are
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/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c
index d398b136f..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);
+ 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 05f522e8e..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);
+ 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 9e1615686..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);
+ 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);
+ 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 dca01fbaa..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);
+ 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 42c158231..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);
+ 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),
+ "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 a2d7489f4..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),
+ "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 565c8584b..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);
+ remote, FALSE, IKEV2_UDP_PORT,
+ FRAGMENTATION_NO);
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 b100e2450..112c32d41 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..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);
+ 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);
+ 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/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,
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));
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/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 031c632eb..2ac8c3123 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -968,14 +968,37 @@ 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, port and SPI for fragmented init messages */
+ u_int16_t port;
+ u_int64_t spi;
+
+ src = message->get_source(message);
+ if (!this->hasher->allocate_hash(this->hasher,
+ src->get_address(src), NULL))
+ {
+ 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);
+ }
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))
@@ -1280,7 +1303,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));
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 1ee833cce..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-2011 Tobias Brunner
+ * Copyright (C) 2007-2013 Tobias Brunner
* Copyright (C) 2007-2011 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -36,6 +36,10 @@
#include <processing/jobs/retransmit_job.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/dpd_timeout_job.h>
+#include <processing/jobs/process_message_job.h>
+
+#include <encoding/payloads/fragment_payload.h>
+#include <bio/bio_writer.h>
/**
* Number of old messages hashes we keep for retransmission.
@@ -47,6 +51,20 @@
#define MAX_OLD_HASHES 2
/**
+ * Maximum packet size for fragmented packets (same as in sockets)
+ */
+#define MAX_PACKET 10000
+
+/**
+ * Maximum size of fragment data when sending packets (currently the same is
+ * used for IPv4 and IPv6, even though the latter has a higher minimum datagram
+ * size). 576 (= min. IPv4) - 20 (= IP header) - 8 (= UDP header) -
+ * - 28 (= IKE header) - 8 (= fragment header) = 512
+ * This is reduced by 4 in case of NAT-T (due to the non-ESP marker).
+ */
+#define MAX_FRAGMENT_SIZE 512
+
+/**
* First sequence number of responding packets.
*
* To distinguish retransmission jobs for initiating and responding packets,
@@ -160,19 +178,65 @@ 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;
+
+ /**
+ * Length of all currently received fragments
+ */
+ size_t len;
+
+ /**
+ * Maximum length of a fragmented packet
+ */
+ size_t max_packet;
+
+ /**
+ * Maximum length of a single fragment (when sending)
+ */
+ size_t size;
+
+ /**
+ * The exchange type we use for fragments. Always the initial type even
+ * for fragmented quick mode or transaction messages (i.e. either
+ * ID_PROT or AGGRESSIVE)
+ */
+ exchange_type_t exchange;
+
+ } frag;
+
+ /**
* 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 +276,34 @@ 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.len = 0;
+ this->frag.id = id;
+}
+
METHOD(task_manager_t, flush_queue, void,
private_task_manager_t *this, task_queue_t queue)
{
@@ -283,10 +375,93 @@ static bool activate_task(private_task_manager_t *this, task_type_t type)
}
/**
+ * 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)
+{
+ 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 (data.len > this->frag.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;
+ 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 = this->frag.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;
@@ -305,7 +480,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;
@@ -318,7 +496,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)
{
@@ -328,7 +506,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)
{
@@ -604,12 +782,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);
@@ -713,8 +891,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;
@@ -769,7 +947,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);
}
@@ -868,6 +1046,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)
@@ -1038,6 +1217,114 @@ 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;
+ chunk_t data;
+ 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;
+ }
+ }
+
+ data = payload->get_data(payload);
+ this->frag.len += data.len;
+ if (this->frag.len > this->frag.max_packet)
+ {
+ DBG1(DBG_IKE, "fragmented IKE message is too large");
+ enumerator->destroy(enumerator);
+ clear_fragments(this, 0);
+ return FAILED;
+ }
+
+ INIT(fragment,
+ .num = num,
+ .data = chunk_clone(data),
+ );
+
+ this->frag.list->insert_before(this->frag.list, enumerator, fragment);
+ enumerator->destroy(enumerator);
+
+ if (this->frag.list->get_count(this->frag.list) == this->frag.last)
+ {
+ message_t *message;
+ packet_t *pkt;
+ host_t *src, *dst;
+ bio_writer_t *writer;
+
+ writer = bio_writer_create(this->frag.len);
+ DBG1(DBG_IKE, "received fragment #%hhu, reassembling fragmented IKE "
+ "message", num);
+ enumerator = this->frag.list->create_enumerator(this->frag.list);
+ while (enumerator->enumerate(enumerator, &fragment))
+ {
+ writer->write_data(writer, fragment->data);
+ }
+ enumerator->destroy(enumerator);
+
+ src = msg->get_source(msg);
+ dst = msg->get_destination(msg);
+ pkt = packet_create_from_data(src->clone(src), dst->clone(dst),
+ writer->extract_buf(writer));
+ writer->destroy(writer);
+
+ message = message_create_from_packet(pkt);
+ if (message->parse_header(message) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "failed to parse header of reassembled IKE message");
+ message->destroy(message);
+ status = FAILED;
+ }
+ else
+ {
+ lib->processor->queue_job(lib->processor,
+ (job_t*)process_message_job_create(message));
+ status = NEED_MORE;
+
+ }
+ clear_fragments(this, 0);
+ }
+ else
+ { /* there are some fragments missing */
+ DBG1(DBG_IKE, "received fragment #%hhu, waiting for complete IKE "
+ "message", num);
+ status = NEED_MORE;
+ }
+ return status;
+}
+
/**
* Parse the given message and verify that it is valid.
*/
@@ -1085,6 +1372,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;
}
@@ -1111,8 +1403,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, "
@@ -1129,6 +1421,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;
@@ -1153,7 +1449,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 &&
@@ -1161,7 +1457,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
@@ -1196,6 +1492,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;
@@ -1288,6 +1588,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
{
@@ -1591,6 +1892,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 +1943,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);
@@ -1687,6 +1990,13 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
.responding = {
.seqnr = RESPONDING_SEQ,
},
+ .frag = {
+ .exchange = ID_PROT,
+ .max_packet = lib->settings->get_int(lib->settings,
+ "%s.max_packet", MAX_PACKET, 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),
.queued_tasks = linked_list_create(),
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
index 2fd43ba8c..2ff2b55e9 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-2013 Tobias Brunner
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -99,6 +100,11 @@ 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. 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"},
+
}, vendor_natt_ids[] = {
/* NAT-Traversal VIDs ordered by preference */
@@ -145,24 +151,55 @@ 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)
{
vendor_id_payload_t *vid_payload;
- bool strongswan, cisco_unity;
+ 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);
+ "%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);
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ fragmentation = ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO;
+ 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,
@@ -175,6 +212,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)));
@@ -206,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)
diff --git a/src/starter/args.c b/src/starter/args.c
index 1c16e0b9c..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,6 +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_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.h b/src/starter/confread.h
index 3f2079883..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 {
@@ -70,6 +70,13 @@ typedef enum {
} 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? */
SA_OPTION_COMPRESS = 1 << 1, /* use IPComp */
@@ -140,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/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..4f9e8fb14 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->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;