iPXE
fragment.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <ipxe/retry.h>
00030 #include <ipxe/timer.h>
00031 #include <ipxe/ipstat.h>
00032 #include <ipxe/fragment.h>
00033 
00034 /** @file
00035  *
00036  * Fragment reassembly
00037  *
00038  */
00039 
00040 /**
00041  * Expire fragment reassembly buffer
00042  *
00043  * @v timer             Retry timer
00044  * @v fail              Failure indicator
00045  */
00046 static void fragment_expired ( struct retry_timer *timer, int fail __unused ) {
00047         struct fragment *fragment =
00048                 container_of ( timer, struct fragment, timer );
00049 
00050         DBGC ( fragment, "FRAG %p expired\n", fragment );
00051         free_iob ( fragment->iobuf );
00052         list_del ( &fragment->list );
00053         fragment->fragments->stats->reasm_fails++;
00054         free ( fragment );
00055 }
00056 
00057 /**
00058  * Find fragment reassembly buffer
00059  *
00060  * @v fragments         Fragment reassembler
00061  * @v iobuf             I/O buffer
00062  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00063  * @ret fragment        Fragment reassembly buffer, or NULL if not found
00064  */
00065 static struct fragment * fragment_find ( struct fragment_reassembler *fragments,
00066                                          struct io_buffer *iobuf,
00067                                          size_t hdrlen ) {
00068         struct fragment *fragment;
00069 
00070         list_for_each_entry ( fragment, &fragments->list, list ) {
00071                 if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) )
00072                         return fragment;
00073         }
00074         return NULL;
00075 }
00076 
00077 /**
00078  * Reassemble packet
00079  *
00080  * @v fragments         Fragment reassembler
00081  * @v iobuf             I/O buffer
00082  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
00083  * @ret iobuf           Reassembled packet, or NULL
00084  *
00085  * This function takes ownership of the I/O buffer.  Note that the
00086  * length of the non-fragmentable portion may be modified.
00087  */
00088 struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments,
00089                                          struct io_buffer *iobuf,
00090                                          size_t *hdrlen ) {
00091         struct fragment *fragment;
00092         struct io_buffer *new_iobuf;
00093         size_t new_len;
00094         size_t offset;
00095         size_t expected_offset;
00096         int more_frags;
00097 
00098         /* Update statistics */
00099         fragments->stats->reasm_reqds++;
00100 
00101         /* Find matching fragment reassembly buffer, if any */
00102         fragment = fragment_find ( fragments, iobuf, *hdrlen );
00103 
00104         /* Drop out-of-order fragments */
00105         offset = fragments->fragment_offset ( iobuf, *hdrlen );
00106         expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
00107                                          fragment->hdrlen ) : 0 );
00108         if ( offset != expected_offset ) {
00109                 DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
00110                        "[%zd,%zd), expected [%zd,...)\n", fragment, offset,
00111                        ( offset + iob_len ( iobuf ) - *hdrlen ),
00112                        expected_offset );
00113                 goto drop;
00114         }
00115 
00116         /* Create or extend fragment reassembly buffer as applicable */
00117         if ( ! fragment ) {
00118 
00119                 /* Create new fragment reassembly buffer */
00120                 fragment = zalloc ( sizeof ( *fragment ) );
00121                 if ( ! fragment )
00122                         goto drop;
00123                 list_add ( &fragment->list, &fragments->list );
00124                 fragment->iobuf = iobuf;
00125                 fragment->hdrlen = *hdrlen;
00126                 timer_init ( &fragment->timer, fragment_expired, NULL );
00127                 fragment->fragments = fragments;
00128                 DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
00129                        ( iob_len ( iobuf ) - *hdrlen ) );
00130 
00131         } else {
00132 
00133                 /* Check if this is the final fragment */
00134                 more_frags = fragments->more_fragments ( iobuf, *hdrlen );
00135                 DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
00136                        offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
00137                        ( more_frags ? "" : " complete" ) );
00138 
00139                 /* Extend fragment reassembly buffer.  Preserve I/O
00140                  * buffer headroom to allow for code which modifies
00141                  * and resends the buffer (e.g. ICMP echo responses).
00142                  */
00143                 iob_pull ( iobuf, *hdrlen );
00144                 new_len = ( iob_headroom ( fragment->iobuf ) +
00145                             iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
00146                 new_iobuf = alloc_iob ( new_len );
00147                 if ( ! new_iobuf ) {
00148                         DBGC ( fragment, "FRAG %p could not extend reassembly "
00149                                "buffer to %zd bytes\n", fragment, new_len );
00150                         goto drop;
00151                 }
00152                 iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
00153                 memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
00154                          fragment->iobuf->data, iob_len ( fragment->iobuf ) );
00155                 memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
00156                          iobuf->data, iob_len ( iobuf ) );
00157                 free_iob ( fragment->iobuf );
00158                 fragment->iobuf = new_iobuf;
00159                 free_iob ( iobuf );
00160 
00161                 /* Stop fragment reassembly timer */
00162                 stop_timer ( &fragment->timer );
00163 
00164                 /* If this is the final fragment, return it */
00165                 if ( ! more_frags ) {
00166                         iobuf = fragment->iobuf;
00167                         *hdrlen = fragment->hdrlen;
00168                         list_del ( &fragment->list );
00169                         free ( fragment );
00170                         fragments->stats->reasm_oks++;
00171                         return iobuf;
00172                 }
00173         }
00174 
00175         /* (Re)start fragment reassembly timer */
00176         start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT );
00177 
00178         return NULL;
00179 
00180  drop:
00181         fragments->stats->reasm_fails++;
00182         free_iob ( iobuf );
00183         return NULL;
00184 }