Add qemu 2.4.0
[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
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ipxe/retry.h>
26 #include <ipxe/timer.h>
27 #include <ipxe/ipstat.h>
28 #include <ipxe/fragment.h>
29
30 /** @file
31  *
32  * Fragment reassembly
33  *
34  */
35
36 /**
37  * Expire fragment reassembly buffer
38  *
39  * @v timer             Retry timer
40  * @v fail              Failure indicator
41  */
42 static void fragment_expired ( struct retry_timer *timer, int fail __unused ) {
43         struct fragment *fragment =
44                 container_of ( timer, struct fragment, timer );
45
46         DBGC ( fragment, "FRAG %p expired\n", fragment );
47         free_iob ( fragment->iobuf );
48         list_del ( &fragment->list );
49         fragment->fragments->stats->reasm_fails++;
50         free ( fragment );
51 }
52
53 /**
54  * Find fragment reassembly buffer
55  *
56  * @v fragments         Fragment reassembler
57  * @v iobuf             I/O buffer
58  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
59  * @ret fragment        Fragment reassembly buffer, or NULL if not found
60  */
61 static struct fragment * fragment_find ( struct fragment_reassembler *fragments,
62                                          struct io_buffer *iobuf,
63                                          size_t hdrlen ) {
64         struct fragment *fragment;
65
66         list_for_each_entry ( fragment, &fragments->list, list ) {
67                 if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) )
68                         return fragment;
69         }
70         return NULL;
71 }
72
73 /**
74  * Reassemble packet
75  *
76  * @v fragments         Fragment reassembler
77  * @v iobuf             I/O buffer
78  * @v hdrlen            Length of non-fragmentable potion of I/O buffer
79  * @ret iobuf           Reassembled packet, or NULL
80  *
81  * This function takes ownership of the I/O buffer.  Note that the
82  * length of the non-fragmentable portion may be modified.
83  */
84 struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments,
85                                          struct io_buffer *iobuf,
86                                          size_t *hdrlen ) {
87         struct fragment *fragment;
88         struct io_buffer *new_iobuf;
89         size_t new_len;
90         size_t offset;
91         size_t expected_offset;
92         int more_frags;
93
94         /* Update statistics */
95         fragments->stats->reasm_reqds++;
96
97         /* Find matching fragment reassembly buffer, if any */
98         fragment = fragment_find ( fragments, iobuf, *hdrlen );
99
100         /* Drop out-of-order fragments */
101         offset = fragments->fragment_offset ( iobuf, *hdrlen );
102         expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
103                                          fragment->hdrlen ) : 0 );
104         if ( offset != expected_offset ) {
105                 DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
106                        "[%zd,%zd), expected [%zd,...)\n", fragment, offset,
107                        ( offset + iob_len ( iobuf ) - *hdrlen ),
108                        expected_offset );
109                 goto drop;
110         }
111
112         /* Create or extend fragment reassembly buffer as applicable */
113         if ( ! fragment ) {
114
115                 /* Create new fragment reassembly buffer */
116                 fragment = zalloc ( sizeof ( *fragment ) );
117                 if ( ! fragment )
118                         goto drop;
119                 list_add ( &fragment->list, &fragments->list );
120                 fragment->iobuf = iobuf;
121                 fragment->hdrlen = *hdrlen;
122                 timer_init ( &fragment->timer, fragment_expired, NULL );
123                 fragment->fragments = fragments;
124                 DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
125                        ( iob_len ( iobuf ) - *hdrlen ) );
126
127         } else {
128
129                 /* Check if this is the final fragment */
130                 more_frags = fragments->more_fragments ( iobuf, *hdrlen );
131                 DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
132                        offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
133                        ( more_frags ? "" : " complete" ) );
134
135                 /* Extend fragment reassembly buffer.  Preserve I/O
136                  * buffer headroom to allow for code which modifies
137                  * and resends the buffer (e.g. ICMP echo responses).
138                  */
139                 iob_pull ( iobuf, *hdrlen );
140                 new_len = ( iob_headroom ( fragment->iobuf ) +
141                             iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
142                 new_iobuf = alloc_iob ( new_len );
143                 if ( ! new_iobuf ) {
144                         DBGC ( fragment, "FRAG %p could not extend reassembly "
145                                "buffer to %zd bytes\n", fragment, new_len );
146                         goto drop;
147                 }
148                 iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
149                 memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
150                          fragment->iobuf->data, iob_len ( fragment->iobuf ) );
151                 memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
152                          iobuf->data, iob_len ( iobuf ) );
153                 free_iob ( fragment->iobuf );
154                 fragment->iobuf = new_iobuf;
155                 free_iob ( iobuf );
156
157                 /* Stop fragment reassembly timer */
158                 stop_timer ( &fragment->timer );
159
160                 /* If this is the final fragment, return it */
161                 if ( ! more_frags ) {
162                         iobuf = fragment->iobuf;
163                         *hdrlen = fragment->hdrlen;
164                         list_del ( &fragment->list );
165                         free ( fragment );
166                         fragments->stats->reasm_oks++;
167                         return iobuf;
168                 }
169         }
170
171         /* (Re)start fragment reassembly timer */
172         start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT );
173
174         return NULL;
175
176  drop:
177         fragments->stats->reasm_fails++;
178         free_iob ( iobuf );
179         return NULL;
180 }