Skip to content

Commit 13186b6

Browse files
committedJul 16, 2011
[ipv4] Fix fragment reassembly
Signed-off-by: Michael Brown <mcb30@ipxe.org>
1 parent 17f09df commit 13186b6

File tree

2 files changed

+128
-107
lines changed

2 files changed

+128
-107
lines changed
 

‎src/include/ipxe/ip.h

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ struct io_buffer;
3131
#define IP_TOS 0
3232
#define IP_TTL 64
3333

34-
#define IP_FRAG_IOB_SIZE 1500
35-
#define IP_FRAG_TIMEOUT 50
36-
3734
/** An IPv4 packet header */
3835
struct iphdr {
3936
uint8_t verhdrlen;
@@ -73,20 +70,16 @@ struct ipv4_miniroute {
7370
struct in_addr gateway;
7471
};
7572

76-
/* Fragment reassembly buffer */
77-
struct frag_buffer {
78-
/* Identification number */
79-
uint16_t ident;
80-
/* Source network address */
81-
struct in_addr src;
82-
/* Destination network address */
83-
struct in_addr dest;
84-
/* Reassembled I/O buffer */
85-
struct io_buffer *frag_iob;
86-
/* Reassembly timer */
87-
struct retry_timer frag_timer;
73+
/* IPv4 fragment reassembly buffer */
74+
struct ipv4_fragment {
8875
/* List of fragment reassembly buffers */
8976
struct list_head list;
77+
/** Reassembled packet */
78+
struct io_buffer *iobuf;
79+
/** Current offset */
80+
size_t offset;
81+
/** Reassembly timer */
82+
struct retry_timer timer;
9083
};
9184

9285
extern struct list_head ipv4_miniroutes;

‎src/net/ipv4.c

Lines changed: 120 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <ipxe/tcpip.h>
1515
#include <ipxe/dhcp.h>
1616
#include <ipxe/settings.h>
17+
#include <ipxe/timer.h>
1718

1819
/** @file
1920
*
@@ -30,7 +31,10 @@ static uint8_t next_ident_high = 0;
3031
struct list_head ipv4_miniroutes = LIST_HEAD_INIT ( ipv4_miniroutes );
3132

3233
/** 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 )
3438

3539
/**
3640
* Add IPv4 minirouting table entry
@@ -128,103 +132,126 @@ static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) {
128132
}
129133

130134
/**
131-
* Fragment reassembly counter timeout
135+
* Expire fragment reassembly buffer
132136
*
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
135139
*/
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 );
142150
}
143151

144152
/**
145-
* Free fragment buffer
153+
* Find matching fragment reassembly buffer
146154
*
147-
* @v fragbug Fragment buffer
155+
* @v iphdr IPv4 header
156+
* @ret frag Fragment reassembly buffer, or NULL
148157
*/
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;
151172
}
152173

153174
/**
154175
* Fragment reassembler
155176
*
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
158179
*/
159-
static struct io_buffer * ipv4_reassemble ( struct io_buffer * iobuf ) {
180+
static struct io_buffer * ipv4_reassemble ( struct io_buffer *iobuf ) {
160181
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;
202199
}
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 ) ),
217228
iobuf->data, iob_len ( iobuf ) );
229+
free_iob ( frag->iobuf );
230+
frag->iobuf = new_iobuf;
231+
frag->offset += iob_len ( iobuf );
218232
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+
}
219247

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 );
223250

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 );
228255
return NULL;
229256
}
230257

@@ -481,6 +508,9 @@ static int ipv4_rx ( struct io_buffer *iobuf,
481508
goto err;
482509
}
483510

511+
/* Truncate packet to correct length */
512+
iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) );
513+
484514
/* Print IPv4 header for debugging */
485515
DBG ( "IPv4 RX %s<-", inet_ntoa ( iphdr->dest ) );
486516
DBG ( "%s len %d proto %d id %04x csum %04x\n",
@@ -496,31 +526,29 @@ static int ipv4_rx ( struct io_buffer *iobuf,
496526
goto err;
497527
}
498528

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.
511533
*/
512534
iobuf = ipv4_reassemble ( iobuf );
513535
if ( ! iobuf )
514536
return 0;
537+
iphdr = iobuf->data;
538+
hdrlen = ( ( iphdr->verhdrlen & IP_MASK_HLEN ) * 4 );
515539
}
516540

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+
*/
518544
memset ( &src, 0, sizeof ( src ) );
519545
src.sin.sin_family = AF_INET;
520546
src.sin.sin_addr = iphdr->src;
521547
memset ( &dest, 0, sizeof ( dest ) );
522548
dest.sin.sin_family = AF_INET;
523549
dest.sin.sin_addr = iphdr->dest;
550+
pshdr_csum = ipv4_pshdr_chksum ( iobuf, TCPIP_EMPTY_CSUM );
551+
iob_pull ( iobuf, hdrlen );
524552
if ( ( rc = tcpip_rx ( iobuf, iphdr->protocol, &src.st,
525553
&dest.st, pshdr_csum ) ) != 0 ) {
526554
DBG ( "IPv4 received packet rejected by stack: %s\n",

0 commit comments

Comments
 (0)
Please sign in to comment.