These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / fragment.c
1 /*
2  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ipxe/retry.h>
30 #include <ipxe/timer.h>
31 #include <ipxe/ipstat.h>
32 #include <ipxe/fragment.h>
33
34 /** @file
35  *
36  * Fragment reassembly
37  *
38  */
39
40 /**
41  * Expire fragment reassembly buffer
42  *
43  * @v timer             Retry timer
44  * @v fail              Failure indicator
45  */
46 static void fragment_expired ( struct retry_timer *timer, int fail __unused ) {
47         struct fragment *fragment =
48                 container_of ( timer, struct fragment, timer );
49
50         DBGC ( fragment, "FRAG %p expired\n", fragment );
51         free_iob ( fragment->iobuf );
52         list_del ( &fragment->list );
53         fragment->fragments->stats->reasm_fails++;
54         free ( fragment );
55 }
56
57 /**
58  * Find fragment reassembly buffer
59  *
60  * @v fragments         Fragment reassembler
61  * @v iobuf             I/O buffer
62  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
63  * @ret fragment        Fragment reassembly buffer, or NULL if not found
64  */
65 static struct fragment * fragment_find ( struct fragment_reassembler *fragments,
66                                          struct io_buffer *iobuf,
67                                          size_t hdrlen ) {
68         struct fragment *fragment;
69
70         list_for_each_entry ( fragment, &fragments->list, list ) {
71                 if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) )
72                         return fragment;
73         }
74         return NULL;
75 }
76
77 /**
78  * Reassemble packet
79  *
80  * @v fragments         Fragment reassembler
81  * @v iobuf             I/O buffer
82  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
83  * @ret iobuf           Reassembled packet, or NULL
84  *
85  * This function takes ownership of the I/O buffer.  Note that the
86  * length of the non-fragmentable portion may be modified.
87  */
88 struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments,
89                                          struct io_buffer *iobuf,
90                                          size_t *hdrlen ) {
91         struct fragment *fragment;
92         struct io_buffer *new_iobuf;
93         size_t new_len;
94         size_t offset;
95         size_t expected_offset;
96         int more_frags;
97
98         /* Update statistics */
99         fragments->stats->reasm_reqds++;
100
101         /* Find matching fragment reassembly buffer, if any */
102         fragment = fragment_find ( fragments, iobuf, *hdrlen );
103
104         /* Drop out-of-order fragments */
105         offset = fragments->fragment_offset ( iobuf, *hdrlen );
106         expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
107                                          fragment->hdrlen ) : 0 );
108         if ( offset != expected_offset ) {
109                 DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
110                        "[%zd,%zd), expected [%zd,...)\n", fragment, offset,
111                        ( offset + iob_len ( iobuf ) - *hdrlen ),
112                        expected_offset );
113                 goto drop;
114         }
115
116         /* Create or extend fragment reassembly buffer as applicable */
117         if ( ! fragment ) {
118
119                 /* Create new fragment reassembly buffer */
120                 fragment = zalloc ( sizeof ( *fragment ) );
121                 if ( ! fragment )
122                         goto drop;
123                 list_add ( &fragment->list, &fragments->list );
124                 fragment->iobuf = iobuf;
125                 fragment->hdrlen = *hdrlen;
126                 timer_init ( &fragment->timer, fragment_expired, NULL );
127                 fragment->fragments = fragments;
128                 DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
129                        ( iob_len ( iobuf ) - *hdrlen ) );
130
131         } else {
132
133                 /* Check if this is the final fragment */
134                 more_frags = fragments->more_fragments ( iobuf, *hdrlen );
135                 DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
136                        offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
137                        ( more_frags ? "" : " complete" ) );
138
139                 /* Extend fragment reassembly buffer.  Preserve I/O
140                  * buffer headroom to allow for code which modifies
141                  * and resends the buffer (e.g. ICMP echo responses).
142                  */
143                 iob_pull ( iobuf, *hdrlen );
144                 new_len = ( iob_headroom ( fragment->iobuf ) +
145                             iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
146                 new_iobuf = alloc_iob ( new_len );
147                 if ( ! new_iobuf ) {
148                         DBGC ( fragment, "FRAG %p could not extend reassembly "
149                                "buffer to %zd bytes\n", fragment, new_len );
150                         goto drop;
151                 }
152                 iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
153                 memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
154                          fragment->iobuf->data, iob_len ( fragment->iobuf ) );
155                 memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
156                          iobuf->data, iob_len ( iobuf ) );
157                 free_iob ( fragment->iobuf );
158                 fragment->iobuf = new_iobuf;
159                 free_iob ( iobuf );
160
161                 /* Stop fragment reassembly timer */
162                 stop_timer ( &fragment->timer );
163
164                 /* If this is the final fragment, return it */
165                 if ( ! more_frags ) {
166                         iobuf = fragment->iobuf;
167                         *hdrlen = fragment->hdrlen;
168                         list_del ( &fragment->list );
169                         free ( fragment );
170                         fragments->stats->reasm_oks++;
171                         return iobuf;
172                 }
173         }
174
175         /* (Re)start fragment reassembly timer */
176         start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT );
177
178         return NULL;
179
180  drop:
181         fragments->stats->reasm_fails++;
182         free_iob ( iobuf );
183         return NULL;
184 }