|
@@ -14,7 +14,7 @@
|
14
|
14
|
#include <ipxe/tcpip.h>
|
15
|
15
|
#include <ipxe/dhcp.h>
|
16
|
16
|
#include <ipxe/settings.h>
|
17
|
|
-#include <ipxe/timer.h>
|
|
17
|
+#include <ipxe/fragment.h>
|
18
|
18
|
|
19
|
19
|
/** @file
|
20
|
20
|
*
|
|
@@ -30,12 +30,6 @@ static uint8_t next_ident_high = 0;
|
30
|
30
|
/** List of IPv4 miniroutes */
|
31
|
31
|
struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
|
32
|
32
|
|
33
|
|
-/** List of fragment reassembly buffers */
|
34
|
|
-static LIST_HEAD ( ipv4_fragments );
|
35
|
|
-
|
36
|
|
-/** Fragment reassembly timeout */
|
37
|
|
-#define IP_FRAG_TIMEOUT ( TICKS_PER_SEC / 2 )
|
38
|
|
-
|
39
|
33
|
/**
|
40
|
34
|
* Add IPv4 minirouting table entry
|
41
|
35
|
*
|
|
@@ -133,131 +127,59 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
|
133
|
127
|
}
|
134
|
128
|
|
135
|
129
|
/**
|
136
|
|
- * Expire fragment reassembly buffer
|
|
130
|
+ * Check if IPv4 fragment matches fragment reassembly buffer
|
137
|
131
|
*
|
138
|
|
- * @v timer Retry timer
|
139
|
|
- * @v fail Failure indicator
|
|
132
|
+ * @v fragment Fragment reassembly buffer
|
|
133
|
+ * @v iobuf I/O buffer
|
|
134
|
+ * @v hdrlen Length of non-fragmentable potion of I/O buffer
|
|
135
|
+ * @ret is_fragment Fragment matches this reassembly buffer
|
140
|
136
|
*/
|
141
|
|
-static void ipv4_fragment_expired ( struct retry_timer *timer,
|
142
|
|
- int fail __unused ) {
|
143
|
|
- struct ipv4_fragment *frag =
|
144
|
|
- container_of ( timer, struct ipv4_fragment, timer );
|
145
|
|
- struct iphdr *iphdr = frag->iobuf->data;
|
146
|
|
-
|
147
|
|
- DBGC ( iphdr->src, "IPv4 fragment %04x expired\n",
|
148
|
|
- ntohs ( iphdr->ident ) );
|
149
|
|
- free_iob ( frag->iobuf );
|
150
|
|
- list_del ( &frag->list );
|
151
|
|
- free ( frag );
|
|
137
|
+static int ipv4_is_fragment ( struct fragment *fragment,
|
|
138
|
+ struct io_buffer *iobuf,
|
|
139
|
+ size_t hdrlen __unused ) {
|
|
140
|
+ struct iphdr *frag_iphdr = fragment->iobuf->data;
|
|
141
|
+ struct iphdr *iphdr = iobuf->data;
|
|
142
|
+
|
|
143
|
+ return ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
|
|
144
|
+ ( iphdr->ident == frag_iphdr->ident ) );
|
152
|
145
|
}
|
153
|
146
|
|
154
|
147
|
/**
|
155
|
|
- * Find matching fragment reassembly buffer
|
|
148
|
+ * Get IPv4 fragment offset
|
156
|
149
|
*
|
157
|
|
- * @v iphdr IPv4 header
|
158
|
|
- * @ret frag Fragment reassembly buffer, or NULL
|
|
150
|
+ * @v iobuf I/O buffer
|
|
151
|
+ * @v hdrlen Length of non-fragmentable potion of I/O buffer
|
|
152
|
+ * @ret offset Offset
|
159
|
153
|
*/
|
160
|
|
-static struct ipv4_fragment * ipv4_fragment ( struct iphdr *iphdr ) {
|
161
|
|
- struct ipv4_fragment *frag;
|
162
|
|
- struct iphdr *frag_iphdr;
|
163
|
|
-
|
164
|
|
- list_for_each_entry ( frag, &ipv4_fragments, list ) {
|
165
|
|
- frag_iphdr = frag->iobuf->data;
|
166
|
|
-
|
167
|
|
- if ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
|
168
|
|
- ( iphdr->ident == frag_iphdr->ident ) ) {
|
169
|
|
- return frag;
|
170
|
|
- }
|
171
|
|
- }
|
|
154
|
+static size_t ipv4_fragment_offset ( struct io_buffer *iobuf,
|
|
155
|
+ size_t hdrlen __unused ) {
|
|
156
|
+ struct iphdr *iphdr = iobuf->data;
|
172
|
157
|
|
173
|
|
- return NULL;
|
|
158
|
+ return ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
|
174
|
159
|
}
|
175
|
160
|
|
176
|
161
|
/**
|
177
|
|
- * Fragment reassembler
|
|
162
|
+ * Check if more fragments exist
|
178
|
163
|
*
|
179
|
164
|
* @v iobuf I/O buffer
|
180
|
|
- * @ret iobuf Reassembled packet, or NULL
|
|
165
|
+ * @v hdrlen Length of non-fragmentable potion of I/O buffer
|
|
166
|
+ * @ret more_frags More fragments exist
|
181
|
167
|
*/
|
182
|
|
-static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) {
|
|
168
|
+static int ipv4_more_fragments ( struct io_buffer *iobuf,
|
|
169
|
+ size_t hdrlen __unused ) {
|
183
|
170
|
struct iphdr *iphdr = iobuf->data;
|
184
|
|
- size_t offset = ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
|
185
|
|
- unsigned int more_frags = ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ));
|
186
|
|
- size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
|
187
|
|
- struct ipv4_fragment *frag;
|
188
|
|
- size_t expected_offset;
|
189
|
|
- struct io_buffer *new_iobuf;
|
190
|
|
-
|
191
|
|
- /* Find matching fragment reassembly buffer, if any */
|
192
|
|
- frag = ipv4_fragment ( iphdr );
|
193
|
|
-
|
194
|
|
- /* Drop out-of-order fragments */
|
195
|
|
- expected_offset = ( frag ? frag->offset : 0 );
|
196
|
|
- if ( offset != expected_offset ) {
|
197
|
|
- DBGC ( iphdr->src, "IPv4 dropping out-of-sequence fragment "
|
198
|
|
- "%04x (%zd+%zd, expected %zd)\n",
|
199
|
|
- ntohs ( iphdr->ident ), offset,
|
200
|
|
- ( iob_len ( iobuf ) - hdrlen ), expected_offset );
|
201
|
|
- goto drop;
|
202
|
|
- }
|
203
|
|
-
|
204
|
|
- /* Create or extend fragment reassembly buffer as applicable */
|
205
|
|
- if ( frag == NULL ) {
|
206
|
171
|
|
207
|
|
- /* Create new fragment reassembly buffer */
|
208
|
|
- frag = zalloc ( sizeof ( *frag ) );
|
209
|
|
- if ( ! frag )
|
210
|
|
- goto drop;
|
211
|
|
- list_add ( &frag->list, &ipv4_fragments );
|
212
|
|
- frag->iobuf = iobuf;
|
213
|
|
- frag->offset = ( iob_len ( iobuf ) - hdrlen );
|
214
|
|
- timer_init ( &frag->timer, ipv4_fragment_expired, NULL );
|
215
|
|
-
|
216
|
|
- } else {
|
217
|
|
-
|
218
|
|
- /* Extend reassembly buffer */
|
219
|
|
- iob_pull ( iobuf, hdrlen );
|
220
|
|
- new_iobuf = alloc_iob ( iob_len ( frag->iobuf ) +
|
221
|
|
- iob_len ( iobuf ) );
|
222
|
|
- if ( ! new_iobuf ) {
|
223
|
|
- DBGC ( iphdr->src, "IPv4 could not extend reassembly "
|
224
|
|
- "buffer to %zd bytes\n",
|
225
|
|
- iob_len ( frag->iobuf ) + iob_len ( iobuf ) );
|
226
|
|
- goto drop;
|
227
|
|
- }
|
228
|
|
- memcpy ( iob_put ( new_iobuf, iob_len ( frag->iobuf ) ),
|
229
|
|
- frag->iobuf->data, iob_len ( frag->iobuf ) );
|
230
|
|
- memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
|
231
|
|
- iobuf->data, iob_len ( iobuf ) );
|
232
|
|
- free_iob ( frag->iobuf );
|
233
|
|
- frag->iobuf = new_iobuf;
|
234
|
|
- frag->offset += iob_len ( iobuf );
|
235
|
|
- free_iob ( iobuf );
|
236
|
|
- iphdr = frag->iobuf->data;
|
237
|
|
- iphdr->len = ntohs ( iob_len ( frag->iobuf ) );
|
238
|
|
-
|
239
|
|
- /* Stop fragment reassembly timer */
|
240
|
|
- stop_timer ( &frag->timer );
|
241
|
|
-
|
242
|
|
- /* If this is the final fragment, return it */
|
243
|
|
- if ( ! more_frags ) {
|
244
|
|
- iobuf = frag->iobuf;
|
245
|
|
- list_del ( &frag->list );
|
246
|
|
- free ( frag );
|
247
|
|
- return iobuf;
|
248
|
|
- }
|
249
|
|
- }
|
250
|
|
-
|
251
|
|
- /* (Re)start fragment reassembly timer */
|
252
|
|
- start_timer_fixed ( &frag->timer, IP_FRAG_TIMEOUT );
|
253
|
|
-
|
254
|
|
- return NULL;
|
255
|
|
-
|
256
|
|
- drop:
|
257
|
|
- free_iob ( iobuf );
|
258
|
|
- return NULL;
|
|
172
|
+ return ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) );
|
259
|
173
|
}
|
260
|
174
|
|
|
175
|
+/** IPv4 fragment reassembler */
|
|
176
|
+static struct fragment_reassembler ipv4_reassembler = {
|
|
177
|
+ .list = LIST_HEAD_INIT ( ipv4_reassembler.list ),
|
|
178
|
+ .is_fragment = ipv4_is_fragment,
|
|
179
|
+ .fragment_offset = ipv4_fragment_offset,
|
|
180
|
+ .more_fragments = ipv4_more_fragments,
|
|
181
|
+};
|
|
182
|
+
|
261
|
183
|
/**
|
262
|
184
|
* Add IPv4 pseudo-header checksum to existing checksum
|
263
|
185
|
*
|
|
@@ -526,14 +448,14 @@ static int ipv4_rx ( struct io_buffer *iobuf,
|
526
|
448
|
|
527
|
449
|
/* Perform fragment reassembly if applicable */
|
528
|
450
|
if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) {
|
529
|
|
- /* Pass the fragment to ipv4_reassemble() which returns
|
|
451
|
+ /* Pass the fragment to fragment_reassemble() which returns
|
530
|
452
|
* either a fully reassembled I/O buffer or NULL.
|
531
|
453
|
*/
|
532
|
|
- iobuf = ipv4_reassemble ( iobuf );
|
|
454
|
+ iobuf = fragment_reassemble ( &ipv4_reassembler, iobuf,
|
|
455
|
+ &hdrlen );
|
533
|
456
|
if ( ! iobuf )
|
534
|
457
|
return 0;
|
535
|
458
|
iphdr = iobuf->data;
|
536
|
|
- hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
|
537
|
459
|
}
|
538
|
460
|
|
539
|
461
|
/* Construct socket addresses, calculate pseudo-header
|