iPXE
Functions
fragment.c File Reference

Fragment reassembly. More...

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/ipstat.h>
#include <ipxe/fragment.h>

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void fragment_expired (struct retry_timer *timer, int fail __unused)
 Expire fragment reassembly buffer.
static struct fragmentfragment_find (struct fragment_reassembler *fragments, struct io_buffer *iobuf, size_t hdrlen)
 Find fragment reassembly buffer.
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.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void fragment_expired ( struct retry_timer timer,
int fail  __unused 
) [static]

Expire fragment reassembly buffer.

Parameters:
timerRetry timer
failFailure indicator

Definition at line 46 of file fragment.c.

References container_of, DBGC, fragment::fragments, free, free_iob(), fragment::iobuf, fragment::list, list_del, ip_statistics::reasm_fails, and fragment_reassembler::stats.

Referenced by fragment_reassemble().

                                                                              {
        struct fragment *fragment =
                container_of ( timer, struct fragment, timer );

        DBGC ( fragment, "FRAG %p expired\n", fragment );
        free_iob ( fragment->iobuf );
        list_del ( &fragment->list );
        fragment->fragments->stats->reasm_fails++;
        free ( fragment );
}
static struct fragment* fragment_find ( struct fragment_reassembler fragments,
struct io_buffer iobuf,
size_t  hdrlen 
) [static, read]

Find fragment reassembly buffer.

Parameters:
fragmentsFragment reassembler
iobufI/O buffer
hdrlenLength of non-fragmentable potion of I/O buffer
Return values:
fragmentFragment reassembly buffer, or NULL if not found

Definition at line 65 of file fragment.c.

References fragment_reassembler::is_fragment, fragment::list, fragment_reassembler::list, list_for_each_entry, and NULL.

Referenced by fragment_reassemble().

                                                         {
        struct fragment *fragment;

        list_for_each_entry ( fragment, &fragments->list, list ) {
                if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) )
                        return fragment;
        }
        return NULL;
}
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;
}