|
@@ -14,6 +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
|
18
|
|
18
|
19
|
/** @file
|
19
|
20
|
*
|
|
@@ -30,7 +31,10 @@ static uint8_t next_ident_high = 0;
|
30
|
31
|
struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
|
31
|
32
|
|
32
|
33
|
/** List of fragment reassembly buffers */
|
33
|
|
-static LIST_HEAD ( frag_buffers );
|
|
34
|
+static LIST_HEAD ( ipv4_fragments );
|
|
35
|
+
|
|
36
|
+/** Fragment reassembly timeout */
|
|
37
|
+#define IP_FRAG_TIMEOUT ( TICKS_PER_SEC / 2 )
|
34
|
38
|
|
35
|
39
|
/**
|
36
|
40
|
* Add IPv4 minirouting table entry
|
|
@@ -128,103 +132,126 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
|
128
|
132
|
}
|
129
|
133
|
|
130
|
134
|
/**
|
131
|
|
- * Fragment reassembly counter timeout
|
|
135
|
+ * Expire fragment reassembly buffer
|
132
|
136
|
*
|
133
|
|
- * @v timer Retry timer
|
134
|
|
- * @v over If asserted, the timer is greater than @c MAX_TIMEOUT
|
|
137
|
+ * @v timer Retry timer
|
|
138
|
+ * @v fail Failure indicator
|
135
|
139
|
*/
|
136
|
|
-static void ipv4_frag_expired ( struct retry_timer *timer __unused,
|
137
|
|
- int over ) {
|
138
|
|
- if ( over ) {
|
139
|
|
- DBG ( "Fragment reassembly timeout" );
|
140
|
|
- /* Free the fragment buffer */
|
141
|
|
- }
|
|
140
|
+static void ipv4_fragment_expired ( struct retry_timer *timer,
|
|
141
|
+ int fail __unused ) {
|
|
142
|
+ struct ipv4_fragment *frag =
|
|
143
|
+ container_of ( timer, struct ipv4_fragment, timer );
|
|
144
|
+ struct iphdr *iphdr = frag->iobuf->data;
|
|
145
|
+
|
|
146
|
+ DBG ( "IPv4 fragment %04x expired\n", ntohs ( iphdr->ident ) );
|
|
147
|
+ free_iob ( frag->iobuf );
|
|
148
|
+ list_del ( &frag->list );
|
|
149
|
+ free ( frag );
|
142
|
150
|
}
|
143
|
151
|
|
144
|
152
|
/**
|
145
|
|
- * Free fragment buffer
|
|
153
|
+ * Find matching fragment reassembly buffer
|
146
|
154
|
*
|
147
|
|
- * @v fragbug Fragment buffer
|
|
155
|
+ * @v iphdr IPv4 header
|
|
156
|
+ * @ret frag Fragment reassembly buffer, or NULL
|
148
|
157
|
*/
|
149
|
|
-static void free_fragbuf ( struct frag_buffer *fragbuf ) {
|
150
|
|
- free ( fragbuf );
|
|
158
|
+static struct ipv4_fragment * ipv4_fragment ( struct iphdr *iphdr ) {
|
|
159
|
+ struct ipv4_fragment *frag;
|
|
160
|
+ struct iphdr *frag_iphdr;
|
|
161
|
+
|
|
162
|
+ list_for_each_entry ( frag, &ipv4_fragments, list ) {
|
|
163
|
+ frag_iphdr = frag->iobuf->data;
|
|
164
|
+
|
|
165
|
+ if ( ( iphdr->src.s_addr == frag_iphdr->src.s_addr ) &&
|
|
166
|
+ ( iphdr->ident == frag_iphdr->ident ) ) {
|
|
167
|
+ return frag;
|
|
168
|
+ }
|
|
169
|
+ }
|
|
170
|
+
|
|
171
|
+ return NULL;
|
151
|
172
|
}
|
152
|
173
|
|
153
|
174
|
/**
|
154
|
175
|
* Fragment reassembler
|
155
|
176
|
*
|
156
|
|
- * @v iobuf I/O buffer, fragment of the datagram
|
157
|
|
- * @ret frag_iob Reassembled packet, or NULL
|
|
177
|
+ * @v iobuf I/O buffer
|
|
178
|
+ * @ret iobuf Reassembled packet, or NULL
|
158
|
179
|
*/
|
159
|
|
-static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) {
|
|
180
|
+static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) {
|
160
|
181
|
struct iphdr *iphdr = iobuf->data;
|
161
|
|
- struct frag_buffer *fragbuf;
|
162
|
|
-
|
163
|
|
- /**
|
164
|
|
- * Check if the fragment belongs to any fragment series
|
165
|
|
- */
|
166
|
|
- list_for_each_entry ( fragbuf, &frag_buffers, list ) {
|
167
|
|
- if ( fragbuf->ident == iphdr->ident &&
|
168
|
|
- fragbuf->src.s_addr == iphdr->src.s_addr ) {
|
169
|
|
- /**
|
170
|
|
- * Check if the packet is the expected fragment
|
171
|
|
- *
|
172
|
|
- * The offset of the new packet must be equal to the
|
173
|
|
- * length of the data accumulated so far (the length of
|
174
|
|
- * the reassembled I/O buffer
|
175
|
|
- */
|
176
|
|
- if ( iob_len ( fragbuf->frag_iob ) ==
|
177
|
|
- ( iphdr->frags & IP_MASK_OFFSET ) ) {
|
178
|
|
- /**
|
179
|
|
- * Append the contents of the fragment to the
|
180
|
|
- * reassembled I/O buffer
|
181
|
|
- */
|
182
|
|
- iob_pull ( iobuf, sizeof ( *iphdr ) );
|
183
|
|
- memcpy ( iob_put ( fragbuf->frag_iob,
|
184
|
|
- iob_len ( iobuf ) ),
|
185
|
|
- iobuf->data, iob_len ( iobuf ) );
|
186
|
|
- free_iob ( iobuf );
|
187
|
|
-
|
188
|
|
- /** Check if the fragment series is over */
|
189
|
|
- if ( ! ( iphdr->frags & IP_MASK_MOREFRAGS ) ) {
|
190
|
|
- iobuf = fragbuf->frag_iob;
|
191
|
|
- free_fragbuf ( fragbuf );
|
192
|
|
- return iobuf;
|
193
|
|
- }
|
194
|
|
-
|
195
|
|
- } else {
|
196
|
|
- /* Discard the fragment series */
|
197
|
|
- free_fragbuf ( fragbuf );
|
198
|
|
- free_iob ( iobuf );
|
199
|
|
- }
|
200
|
|
- return NULL;
|
201
|
|
- }
|
|
182
|
+ size_t offset = ( ( ntohs ( iphdr->frags ) & IP_MASK_OFFSET ) << 3 );
|
|
183
|
+ unsigned int more_frags = ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ));
|
|
184
|
+ size_t hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
|
|
185
|
+ struct ipv4_fragment *frag;
|
|
186
|
+ size_t expected_offset;
|
|
187
|
+ struct io_buffer *new_iobuf;
|
|
188
|
+
|
|
189
|
+ /* Find matching fragment reassembly buffer, if any */
|
|
190
|
+ frag = ipv4_fragment ( iphdr );
|
|
191
|
+
|
|
192
|
+ /* Drop out-of-order fragments */
|
|
193
|
+ expected_offset = ( frag ? frag->offset : 0 );
|
|
194
|
+ if ( offset != expected_offset ) {
|
|
195
|
+ DBG ( "IPv4 dropping out-of-sequence fragment %04x (%zd+%zd, "
|
|
196
|
+ "expected %zd)\n", ntohs ( iphdr->ident ), offset,
|
|
197
|
+ ( iob_len ( iobuf ) - hdrlen ), expected_offset );
|
|
198
|
+ goto drop;
|
202
|
199
|
}
|
203
|
|
-
|
204
|
|
- /** Check if the fragment is the first in the fragment series */
|
205
|
|
- if ( iphdr->frags & IP_MASK_MOREFRAGS &&
|
206
|
|
- ( ( iphdr->frags & IP_MASK_OFFSET ) == 0 ) ) {
|
207
|
|
-
|
208
|
|
- /** Create a new fragment buffer */
|
209
|
|
- fragbuf = ( struct frag_buffer* ) malloc ( sizeof( *fragbuf ) );
|
210
|
|
- fragbuf->ident = iphdr->ident;
|
211
|
|
- fragbuf->src = iphdr->src;
|
212
|
|
-
|
213
|
|
- /* Set up the reassembly I/O buffer */
|
214
|
|
- fragbuf->frag_iob = alloc_iob ( IP_FRAG_IOB_SIZE );
|
215
|
|
- iob_pull ( iobuf, sizeof ( *iphdr ) );
|
216
|
|
- memcpy ( iob_put ( fragbuf->frag_iob, iob_len ( iobuf ) ),
|
|
200
|
+
|
|
201
|
+ /* Create or extend fragment reassembly buffer as applicable */
|
|
202
|
+ if ( frag == NULL ) {
|
|
203
|
+
|
|
204
|
+ /* Create new fragment reassembly buffer */
|
|
205
|
+ frag = zalloc ( sizeof ( *frag ) );
|
|
206
|
+ if ( ! frag )
|
|
207
|
+ goto drop;
|
|
208
|
+ list_add ( &frag->list, &ipv4_fragments );
|
|
209
|
+ frag->iobuf = iobuf;
|
|
210
|
+ frag->offset = ( iob_len ( iobuf ) - hdrlen );
|
|
211
|
+ timer_init ( &frag->timer, ipv4_fragment_expired, NULL );
|
|
212
|
+
|
|
213
|
+ } else {
|
|
214
|
+
|
|
215
|
+ /* Extend reassembly buffer */
|
|
216
|
+ iob_pull ( iobuf, hdrlen );
|
|
217
|
+ new_iobuf = alloc_iob ( iob_len ( frag->iobuf ) +
|
|
218
|
+ iob_len ( iobuf ) );
|
|
219
|
+ if ( ! new_iobuf ) {
|
|
220
|
+ DBG ( "IPv4 could not extend reassembly buffer to "
|
|
221
|
+ "%zd bytes\n",
|
|
222
|
+ ( iob_len ( frag->iobuf ) + iob_len ( iobuf ) ) );
|
|
223
|
+ goto drop;
|
|
224
|
+ }
|
|
225
|
+ memcpy ( iob_put ( new_iobuf, iob_len ( frag->iobuf ) ),
|
|
226
|
+ frag->iobuf->data, iob_len ( frag->iobuf ) );
|
|
227
|
+ memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
|
217
|
228
|
iobuf->data, iob_len ( iobuf ) );
|
|
229
|
+ free_iob ( frag->iobuf );
|
|
230
|
+ frag->iobuf = new_iobuf;
|
|
231
|
+ frag->offset += iob_len ( iobuf );
|
218
|
232
|
free_iob ( iobuf );
|
|
233
|
+ iphdr = frag->iobuf->data;
|
|
234
|
+ iphdr->len = ntohs ( iob_len ( frag->iobuf ) );
|
|
235
|
+
|
|
236
|
+ /* Stop fragment reassembly timer */
|
|
237
|
+ stop_timer ( &frag->timer );
|
|
238
|
+
|
|
239
|
+ /* If this is the final fragment, return it */
|
|
240
|
+ if ( ! more_frags ) {
|
|
241
|
+ iobuf = frag->iobuf;
|
|
242
|
+ list_del ( &frag->list );
|
|
243
|
+ free ( frag );
|
|
244
|
+ return iobuf;
|
|
245
|
+ }
|
|
246
|
+ }
|
219
|
247
|
|
220
|
|
- /* Set the reassembly timer */
|
221
|
|
- timer_init ( &fragbuf->frag_timer, ipv4_frag_expired, NULL );
|
222
|
|
- start_timer_fixed ( &fragbuf->frag_timer, IP_FRAG_TIMEOUT );
|
|
248
|
+ /* (Re)start fragment reassembly timer */
|
|
249
|
+ start_timer_fixed ( &frag->timer, IP_FRAG_TIMEOUT );
|
223
|
250
|
|
224
|
|
- /* Add the fragment buffer to the list of fragment buffers */
|
225
|
|
- list_add ( &fragbuf->list, &frag_buffers );
|
226
|
|
- }
|
227
|
|
-
|
|
251
|
+ return NULL;
|
|
252
|
+
|
|
253
|
+ drop:
|
|
254
|
+ free_iob ( iobuf );
|
228
|
255
|
return NULL;
|
229
|
256
|
}
|
230
|
257
|
|
|
@@ -481,6 +508,9 @@ static int ipv4_rx ( struct io_buffer *iobuf,
|
481
|
508
|
goto err;
|
482
|
509
|
}
|
483
|
510
|
|
|
511
|
+ /* Truncate packet to correct length */
|
|
512
|
+ iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
|
|
513
|
+
|
484
|
514
|
/* Print IPv4 header for debugging */
|
485
|
515
|
DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
|
486
|
516
|
DBG ( "%s len %d proto %d id %04x csum %04x\n",
|
|
@@ -496,31 +526,29 @@ static int ipv4_rx ( struct io_buffer *iobuf,
|
496
|
526
|
goto err;
|
497
|
527
|
}
|
498
|
528
|
|
499
|
|
- /* Truncate packet to correct length, calculate pseudo-header
|
500
|
|
- * checksum and then strip off the IPv4 header.
|
501
|
|
- */
|
502
|
|
- iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
|
503
|
|
- pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
|
504
|
|
- iob_pull ( iobuf, hdrlen );
|
505
|
|
-
|
506
|
|
- /* Fragment reassembly */
|
507
|
|
- if ( ( iphdr->frags & htons ( IP_MASK_MOREFRAGS ) ) ||
|
508
|
|
- ( ( iphdr->frags & htons ( IP_MASK_OFFSET ) ) != 0 ) ) {
|
509
|
|
- /* Pass the fragment to ipv4_reassemble() which either
|
510
|
|
- * returns a fully reassembled I/O buffer or NULL.
|
|
529
|
+ /* Perform fragment reassembly if applicable */
|
|
530
|
+ if ( iphdr->frags & htons ( IP_MASK_OFFSET | IP_MASK_MOREFRAGS ) ) {
|
|
531
|
+ /* Pass the fragment to ipv4_reassemble() which returns
|
|
532
|
+ * either a fully reassembled I/O buffer or NULL.
|
511
|
533
|
*/
|
512
|
534
|
iobuf = ipv4_reassemble ( iobuf );
|
513
|
535
|
if ( ! iobuf )
|
514
|
536
|
return 0;
|
|
537
|
+ iphdr = iobuf->data;
|
|
538
|
+ hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
|
515
|
539
|
}
|
516
|
540
|
|
517
|
|
- /* Construct socket addresses and hand off to transport layer */
|
|
541
|
+ /* Construct socket addresses, calculate pseudo-header
|
|
542
|
+ * checksum, and hand off to transport layer
|
|
543
|
+ */
|
518
|
544
|
memset ( &src, 0, sizeof ( src ) );
|
519
|
545
|
src.sin.sin_family = AF_INET;
|
520
|
546
|
src.sin.sin_addr = iphdr->src;
|
521
|
547
|
memset ( &dest, 0, sizeof ( dest ) );
|
522
|
548
|
dest.sin.sin_family = AF_INET;
|
523
|
549
|
dest.sin.sin_addr = iphdr->dest;
|
|
550
|
+ pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
|
|
551
|
+ iob_pull ( iobuf, hdrlen );
|
524
|
552
|
if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st,
|
525
|
553
|
&dest.st, pshdr_csum ) ) != 0 ) {
|
526
|
554
|
DBG ( "IPv4 received packet rejected by stack: %s\n",
|