Procházet zdrojové kódy

[ipv4] Fix fragment reassembly

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown před 13 roky
rodič
revize
13186b64b6
2 změnil soubory, kde provedl 128 přidání a 107 odebrání
  1. 8
    15
      src/include/ipxe/ip.h
  2. 120
    92
      src/net/ipv4.c

+ 8
- 15
src/include/ipxe/ip.h Zobrazit soubor

@@ -31,9 +31,6 @@ struct io_buffer;
31 31
 #define IP_TOS		0
32 32
 #define IP_TTL		64
33 33
 
34
-#define IP_FRAG_IOB_SIZE	1500
35
-#define IP_FRAG_TIMEOUT		50
36
-
37 34
 /** An IPv4 packet header */
38 35
 struct iphdr {
39 36
 	uint8_t  verhdrlen;
@@ -73,20 +70,16 @@ struct ipv4_miniroute {
73 70
 	struct in_addr gateway;
74 71
 };
75 72
 
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 {
88 75
 	/* List of fragment reassembly buffers */
89 76
 	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;
90 83
 };
91 84
 
92 85
 extern struct list_head ipv4_miniroutes;

+ 120
- 92
src/net/ipv4.c Zobrazit soubor

@@ -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",

Načítá se…
Zrušit
Uložit