message: fragment() generates message and fragments and caches them
authorTobias Brunner <tobias@strongswan.org>
Thu, 12 Jun 2014 08:14:00 +0000 (10:14 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 10 Oct 2014 07:30:26 +0000 (09:30 +0200)
src/libcharon/encoding/message.c
src/libcharon/encoding/message.h
src/libcharon/sa/ikev1/task_manager_v1.c

index 44221b6..31f8c14 100644 (file)
@@ -878,6 +878,11 @@ struct private_message_t {
        packet_t *packet;
 
        /**
+        * Array of generated fragments (if any), as packet_t*.
+        */
+       array_t *fragments;
+
+       /**
         * Linked List where payload data are stored in.
         */
        linked_list_t *payloads;
@@ -1051,6 +1056,12 @@ METHOD(message_t, is_encoded, bool,
        return this->packet->get_data(this->packet).ptr != NULL;
 }
 
+METHOD(message_t, is_fragmented, bool,
+       private_message_t *this)
+{
+       return array_count(this->fragments) > 0;
+}
+
 METHOD(message_t, add_payload, void,
        private_message_t *this, payload_t *payload)
 {
@@ -1341,6 +1352,8 @@ static void order_payloads(private_message_t *this)
        payload_t *payload;
        int i;
 
+       DBG2(DBG_ENC, "order payloads in message");
+
        /* move to temp list */
        list = linked_list_create();
        while (this->payloads->remove_last(this->payloads,
@@ -1669,6 +1682,7 @@ static message_t *clone_message(private_message_t *this)
        dst = this->packet->get_destination(this->packet);
 
        message = message_create(this->major_version, this->minor_version);
+       message->set_ike_sa_id(message, this->ike_sa_id);
        message->set_message_id(message, this->message_id);
        message->set_request(message, this->is_request);
        message->set_source(message, src->clone(src));
@@ -1685,36 +1699,64 @@ static message_t *create_fragment(private_message_t *this, u_int8_t num,
 {
        fragment_payload_t *fragment;
        message_t *message;
+       peer_cfg_t *peer_cfg;
+       ike_sa_t *ike_sa;
 
        fragment = fragment_payload_create_from_data(num, last, data);
        message = clone_message(this);
+       /* other implementations seem to just use 0 as message ID, so here we go */
+       message->set_message_id(message, 0);
+       /* always use the initial message type for fragments, even for quick mode
+        * or transaction messages. */
+       ike_sa = charon->bus->get_sa(charon->bus);
+       if (ike_sa && (peer_cfg = ike_sa->get_peer_cfg(ike_sa)) &&
+               peer_cfg->use_aggressive(peer_cfg))
+       {
+               message->set_exchange_type(message, AGGRESSIVE);
+       }
+       else
+       {
+               message->set_exchange_type(message, ID_PROT);
+       }
        message->add_payload(message, (payload_t*)fragment);
        return message;
 }
 
 /**
- * Destroy all messages in the given array
+ * Destroy all fragments
  */
-CALLBACK(destroy_fragments, void,
-       array_t *fragments)
+static void clear_fragments(private_message_t *this)
 {
-       array_destroy_offset(fragments, offsetof(message_t, destroy));
+       array_destroy_offset(this->fragments, offsetof(packet_t, destroy));
+       this->fragments = NULL;
 }
 
 METHOD(message_t, fragment, status_t,
-       private_message_t *this, size_t frag_len, enumerator_t **fragments)
+       private_message_t *this, keymat_t *keymat, size_t frag_len,
+       enumerator_t **fragments)
 {
-       array_t *messages;
        message_t *fragment;
+       packet_t *packet;
        u_int8_t num, count;
        host_t *src, *dst;
        chunk_t data;
+       status_t status;
        size_t len;
 
-       if (!is_encoded(this) || this->major_version == IKEV2_MAJOR_VERSION)
+       if (this->major_version == IKEV2_MAJOR_VERSION)
        {
                return INVALID_STATE;
        }
+       clear_fragments(this);
+
+       if (!is_encoded(this))
+       {
+               status = generate(this, keymat, NULL);
+               if (status != SUCCESS)
+               {
+                       return status;
+               }
+       }
 
        src = this->packet->get_source(this->packet);
        dst = this->packet->get_destination(this->packet);
@@ -1736,13 +1778,14 @@ METHOD(message_t, fragment, status_t,
        data = this->packet->get_data(this->packet);
        if (data.len <= frag_len)
        {
-               return ALREADY_DONE;
+               *fragments = enumerator_create_single(this->packet, NULL);
+               return SUCCESS;
        }
        /* overhead for the fragmentation payload header */
        frag_len -= 8;
 
        count = data.len / frag_len + (data.len % frag_len ? 1 : 0);
-       messages = array_create(0, count);
+       this->fragments = array_create(0, count);
        DBG2(DBG_ENC, "splitting IKE message with length of %zu bytes into "
                 "%hhu fragments", data.len, count);
        for (num = 1; num <= count; num++)
@@ -1750,34 +1793,39 @@ METHOD(message_t, fragment, status_t,
                len = min(data.len, frag_len);
                fragment = create_fragment(this, num, num == count,
                                                                   chunk_create(data.ptr, len));
-               array_insert(messages, ARRAY_TAIL, fragment);
+               status = fragment->generate(fragment, keymat, &packet);
+               fragment->destroy(fragment);
+               if (status != SUCCESS)
+               {
+                       DBG1(DBG_ENC, "failed to generate IKE fragment");
+                       clear_fragments(this);
+                       return FAILED;
+               }
+               array_insert(this->fragments, ARRAY_TAIL, packet);
                data = chunk_skip(data, len);
        }
-       *fragments = enumerator_create_cleaner(array_create_enumerator(messages),
-                                                                                  destroy_fragments, messages);
+       *fragments = array_create_enumerator(this->fragments);
        return SUCCESS;
 }
 
 METHOD(message_t, get_packet, packet_t*,
        private_message_t *this)
 {
-       if (this->packet == NULL)
-       {
-               return NULL;
-       }
        return this->packet->clone(this->packet);
 }
 
 METHOD(message_t, get_packet_data, chunk_t,
        private_message_t *this)
 {
-       if (this->packet == NULL)
-       {
-               return chunk_empty;
-       }
        return this->packet->get_data(this->packet);
 }
 
+METHOD(message_t, get_fragments, enumerator_t*,
+       private_message_t *this)
+{
+       return array_create_enumerator(this->fragments);
+}
+
 METHOD(message_t, parse_header, status_t,
        private_message_t *this)
 {
@@ -2264,6 +2312,7 @@ METHOD(message_t, destroy, void,
 {
        DESTROY_IF(this->ike_sa_id);
        this->payloads->destroy_offset(this->payloads, offsetof(payload_t, destroy));
+       array_destroy_offset(this->fragments, offsetof(packet_t, destroy));
        this->packet->destroy(this->packet);
        this->parser->destroy(this->parser);
        free(this);
@@ -2301,6 +2350,7 @@ message_t *message_create_from_packet(packet_t *packet)
                        .disable_sort = _disable_sort,
                        .generate = _generate,
                        .is_encoded = _is_encoded,
+                       .is_fragmented = _is_fragmented,
                        .fragment = _fragment,
                        .set_source = _set_source,
                        .get_source = _get_source,
@@ -2314,6 +2364,7 @@ message_t *message_create_from_packet(packet_t *packet)
                        .parse_body = _parse_body,
                        .get_packet = _get_packet,
                        .get_packet_data = _get_packet_data,
+                       .get_fragments = _get_fragments,
                        .destroy = _destroy,
                },
                .exchange_type = EXCHANGE_TYPE_UNDEFINED,
index bd32965..e8db51c 100644 (file)
@@ -265,25 +265,38 @@ struct message_t {
        bool (*is_encoded)(message_t *this);
 
        /**
-        * Split the (generated) message into fragments of the given size (total IP
+        * Generates the message split into fragments of the given size (total IP
         * datagram length).
         *
         * @note Only supported for IKEv1 at the moment.
         *
+        * @param keymat        keymat to encrypt/sign message(s)
         * @param frag_len      fragment length (maximum total IP datagram length), 0
         *                                      for default value depending on address family
-        * @param fragments     receives an enumerator with message_t* (not generated),
+        * @param fragments     receives an enumerator with generated packet_t*,
         *                                      which are owned by the enumerator
         * @return
         *                                      - SUCCESS if message could be fragmented
-        *                                      - ALREADY_DONE if message does not need to be fragmented
-        *                                      - INVALID_STATE if message was not generated or is IKEv2
+        *                                      - INVALID_STATE if message is IKEv2
         *                                      - FAILED if fragmentation failed
+        *                                      - and the possible return values of generate()
         */
-       status_t (*fragment)(message_t *this, size_t frag_len,
+       status_t (*fragment)(message_t *this, keymat_t *keymat, size_t frag_len,
                                                 enumerator_t **fragments);
 
        /**
+        * Check if the message has been encoded and fragmented using fragment(),
+        * and whether there actually resulted fragments (if not is_encoded() will
+        * be TRUE).
+        *
+        * The packets of individual fragments can be retrieved with
+        * get_fragments().
+        *
+        * @return                      TRUE if message has been encoded and fragmented
+        */
+       bool (*is_fragmented)(message_t *this);
+
+       /**
         * Gets the source host informations.
         *
         * @warning Returned host_t object is not getting cloned,
@@ -356,11 +369,11 @@ struct message_t {
        notify_payload_t* (*get_notify)(message_t *this, notify_type_t type);
 
        /**
-        * Returns a clone of the internal stored packet_t object.
+        * Returns a clone of the internally stored packet_t object.
         *
         * @return                      packet_t object as clone of internal one
         */
-       packet_t * (*get_packet) (message_t *this);
+       packet_t *(*get_packet) (message_t *this);
 
        /**
         * Returns a chunk pointing to internal packet_t data.
@@ -370,6 +383,13 @@ struct message_t {
        chunk_t (*get_packet_data) (message_t *this);
 
        /**
+        * Returns internally stored packet_t* objects for each fragment.
+        *
+        * @return                      enumerator internal packet_t* objects
+        */
+       enumerator_t *(*get_fragments)(message_t *this);
+
+       /**
         * Destroys a message and all including objects.
         */
        void (*destroy) (message_t *this);
index af9db65..4e35118 100644 (file)
@@ -401,16 +401,9 @@ static bool generate_message(private_task_manager_t *this, message_t *message,
        bool use_frags = FALSE, result = TRUE;
        ike_cfg_t *ike_cfg;
        enumerator_t *fragments;
-       message_t *fragment;
        packet_t *packet;
        status_t status;
 
-       if (this->ike_sa->generate_message(this->ike_sa, message,
-                                                                          &packet) != SUCCESS)
-       {
-               return FALSE;
-       }
-
        ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
        if (ike_cfg)
        {
@@ -430,37 +423,24 @@ static bool generate_message(private_task_manager_t *this, message_t *message,
 
        if (!use_frags)
        {
+               if (this->ike_sa->generate_message(this->ike_sa, message,
+                                                                                  &packet) != SUCCESS)
+               {
+                       return FALSE;
+               }
                array_insert_create(packets, ARRAY_TAIL, packet);
                return TRUE;
        }
-       /* other implementations seem to just use 0 as message ID, so here we go */
-       message->set_message_id(message, 0);
-       /* always use the initial message type for fragments */
-       message->set_exchange_type(message, this->frag.exchange);
-       status = message->fragment(message, this->frag.size, &fragments);
-       if (status == ALREADY_DONE)
-       {
-               array_insert_create(packets, ARRAY_TAIL, packet);
-               return TRUE;
-       }
-       else if (status != SUCCESS)
+       message->set_ike_sa_id(message, this->ike_sa->get_id(this->ike_sa));
+       status = message->fragment(message, this->ike_sa->get_keymat(this->ike_sa),
+                                                          this->frag.size, &fragments);
+       if (status != SUCCESS)
        {
-               packet->destroy(packet);
                return FALSE;
        }
-       packet->destroy(packet);
-
-       while (fragments->enumerate(fragments, &fragment))
+       while (fragments->enumerate(fragments, &packet))
        {
-               status = this->ike_sa->generate_message(this->ike_sa, fragment,
-                                                                                               &packet);
-               if (status != SUCCESS)
-               {
-                       DBG1(DBG_IKE, "failed to generate IKE fragment");
-                       result = FALSE;
-                       break;
-               }
-               array_insert_create(packets, ARRAY_TAIL, packet);
+               array_insert_create(packets, ARRAY_TAIL, packet->clone(packet));
        }
        fragments->destroy(fragments);
        return result;