iPXE
Data Structures | Defines | Functions
fragment.h File Reference

Fragment reassembly. More...

#include <stdint.h>
#include <ipxe/list.h>
#include <ipxe/iobuf.h>
#include <ipxe/retry.h>

Go to the source code of this file.

Data Structures

struct  fragment
 A fragment reassembly buffer. More...
struct  fragment_reassembler
 A fragment reassembler. More...

Defines

#define FRAGMENT_TIMEOUT   ( TICKS_PER_SEC / 2 )
 Fragment reassembly timeout.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
struct io_bufferfragment_reassemble (struct fragment_reassembler *fragments, struct io_buffer *iobuf, size_t *hdrlen)
 Reassemble packet.

Detailed Description

Fragment reassembly.

Definition in file fragment.h.


Define Documentation

#define FRAGMENT_TIMEOUT   ( TICKS_PER_SEC / 2 )

Fragment reassembly timeout.

Definition at line 18 of file fragment.h.

Referenced by fragment_reassemble().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
struct io_buffer* fragment_reassemble ( struct fragment_reassembler fragments,
struct io_buffer iobuf,
size_t hdrlen 
) [read]

Reassemble packet.

Parameters:
fragmentsFragment reassembler
iobufI/O buffer
hdrlenLength of non-fragmentable potion of I/O buffer
Return values:
iobufReassembled packet, or NULL

This function takes ownership of the I/O buffer. Note that the length of the non-fragmentable portion may be modified.

Definition at line 88 of file fragment.c.

References alloc_iob(), io_buffer::data, DBGC, fragment_expired(), fragment_find(), fragment_reassembler::fragment_offset, FRAGMENT_TIMEOUT, fragment::fragments, free, free_iob(), fragment::hdrlen, iob_headroom(), iob_len(), iob_pull, iob_put, iob_reserve, fragment::iobuf, fragment::list, fragment_reassembler::list, list_add, list_del, memcpy(), fragment_reassembler::more_fragments, NULL, offset, ip_statistics::reasm_fails, ip_statistics::reasm_oks, ip_statistics::reasm_reqds, start_timer_fixed(), fragment_reassembler::stats, stop_timer(), fragment::timer, and zalloc().

Referenced by ipv4_rx(), and ipv6_rx().

                                                          {
        struct fragment *fragment;
        struct io_buffer *new_iobuf;
        size_t new_len;
        size_t offset;
        size_t expected_offset;
        int more_frags;

        /* Update statistics */
        fragments->stats->reasm_reqds++;

        /* Find matching fragment reassembly buffer, if any */
        fragment = fragment_find ( fragments, iobuf, *hdrlen );

        /* Drop out-of-order fragments */
        offset = fragments->fragment_offset ( iobuf, *hdrlen );
        expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
                                         fragment->hdrlen ) : 0 );
        if ( offset != expected_offset ) {
                DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
                       "[%zd,%zd), expected [%zd,...)\n", fragment, offset,
                       ( offset + iob_len ( iobuf ) - *hdrlen ),
                       expected_offset );
                goto drop;
        }

        /* Create or extend fragment reassembly buffer as applicable */
        if ( ! fragment ) {

                /* Create new fragment reassembly buffer */
                fragment = zalloc ( sizeof ( *fragment ) );
                if ( ! fragment )
                        goto drop;
                list_add ( &fragment->list, &fragments->list );
                fragment->iobuf = iobuf;
                fragment->hdrlen = *hdrlen;
                timer_init ( &fragment->timer, fragment_expired, NULL );
                fragment->fragments = fragments;
                DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
                       ( iob_len ( iobuf ) - *hdrlen ) );

        } else {

                /* Check if this is the final fragment */
                more_frags = fragments->more_fragments ( iobuf, *hdrlen );
                DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
                       offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
                       ( more_frags ? "" : " complete" ) );

                /* Extend fragment reassembly buffer.  Preserve I/O
                 * buffer headroom to allow for code which modifies
                 * and resends the buffer (e.g. ICMP echo responses).
                 */
                iob_pull ( iobuf, *hdrlen );
                new_len = ( iob_headroom ( fragment->iobuf ) +
                            iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
                new_iobuf = alloc_iob ( new_len );
                if ( ! new_iobuf ) {
                        DBGC ( fragment, "FRAG %p could not extend reassembly "
                               "buffer to %zd bytes\n", fragment, new_len );
                        goto drop;
                }
                iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
                memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
                         fragment->iobuf->data, iob_len ( fragment->iobuf ) );
                memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
                         iobuf->data, iob_len ( iobuf ) );
                free_iob ( fragment->iobuf );
                fragment->iobuf = new_iobuf;
                free_iob ( iobuf );

                /* Stop fragment reassembly timer */
                stop_timer ( &fragment->timer );

                /* If this is the final fragment, return it */
                if ( ! more_frags ) {
                        iobuf = fragment->iobuf;
                        *hdrlen = fragment->hdrlen;
                        list_del ( &fragment->list );
                        free ( fragment );
                        fragments->stats->reasm_oks++;
                        return iobuf;
                }
        }

        /* (Re)start fragment reassembly timer */
        start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT );

        return NULL;

 drop:
        fragments->stats->reasm_fails++;
        free_iob ( iobuf );
        return NULL;
}