aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS4
-rw-r--r--man/ipsec.conf.5.in15
-rw-r--r--man/strongswan.conf.5.in4
-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
37 files changed, 882 insertions, 59 deletions
diff --git a/NEWS b/NEWS
index 5655a3b38..d644904b5 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.
+
- IKEv1 in charon can now parse certificates received in PKCS#7 containers and
supports NAT traversal as used by Windows clients. Patches courtesy of
Volker RĂ¼melin.
diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in
index 303fb78fa..2766cc4ed 100644
--- a/man/ipsec.conf.5.in
+++ b/man/ipsec.conf.5.in
@@ -403,6 +403,21 @@ 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 | force | " no
+whether to use IKE fragmentation (proprietary IKEv1 extension). Acceptable
+values are
+.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 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 " = <cipher suites>"
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 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/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;