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" ,
0 commit comments