Browse Source

[ipv4] Generalise fragment reassembly mechanism

Generalise the concept of fragment reassembly to allow for code
sharing between IPv4 and IPv6 protocols.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 10 years ago
parent
commit
22a0c4475c
4 changed files with 279 additions and 129 deletions
  1. 68
    0
      src/include/ipxe/fragment.h
  2. 0
    12
      src/include/ipxe/ip.h
  3. 172
    0
      src/net/fragment.c
  4. 39
    117
      src/net/ipv4.c

+ 68
- 0
src/include/ipxe/fragment.h View File

@@ -0,0 +1,68 @@
1
+#ifndef _IPXE_FRAGMENT_H
2
+#define _IPXE_FRAGMENT_H
3
+
4
+/** @file
5
+ *
6
+ * Fragment reassembly
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/list.h>
14
+#include <ipxe/iobuf.h>
15
+#include <ipxe/retry.h>
16
+
17
+/** Fragment reassembly timeout */
18
+#define FRAGMENT_TIMEOUT ( TICKS_PER_SEC / 2 )
19
+
20
+/** A fragment reassembly buffer */
21
+struct fragment {
22
+	/* List of fragment reassembly buffers */
23
+	struct list_head list;
24
+	/** Reassembled packet */
25
+	struct io_buffer *iobuf;
26
+	/** Length of non-fragmentable portion of reassembled packet */
27
+	size_t hdrlen;
28
+	/** Reassembly timer */
29
+	struct retry_timer timer;
30
+};
31
+
32
+/** A fragment reassembler */
33
+struct fragment_reassembler {
34
+	/** List of fragment reassembly buffers */
35
+	struct list_head list;
36
+	/**
37
+	 * Check if fragment matches fragment reassembly buffer
38
+	 *
39
+	 * @v fragment		Fragment reassembly buffer
40
+	 * @v iobuf		I/O buffer
41
+	 * @v hdrlen		Length of non-fragmentable potion of I/O buffer
42
+	 * @ret is_fragment	Fragment matches this reassembly buffer
43
+	 */
44
+	int ( * is_fragment ) ( struct fragment *fragment,
45
+				struct io_buffer *iobuf, size_t hdrlen );
46
+	/**
47
+	 * Get fragment offset
48
+	 *
49
+	 * @v iobuf		I/O buffer
50
+	 * @v hdrlen		Length of non-fragmentable potion of I/O buffer
51
+	 * @ret offset		Offset
52
+	 */
53
+	size_t ( * fragment_offset ) ( struct io_buffer *iobuf, size_t hdrlen );
54
+	/**
55
+	 * Check if more fragments exist
56
+	 *
57
+	 * @v iobuf		I/O buffer
58
+	 * @v hdrlen		Length of non-fragmentable potion of I/O buffer
59
+	 * @ret more_frags	More fragments exist
60
+	 */
61
+	int ( * more_fragments ) ( struct io_buffer *iobuf, size_t hdrlen );
62
+};
63
+
64
+extern struct io_buffer *
65
+fragment_reassemble ( struct fragment_reassembler *fragments,
66
+		      struct io_buffer *iobuf, size_t *hdrlen );
67
+
68
+#endif /* _IPXE_FRAGMENT_H */

+ 0
- 12
src/include/ipxe/ip.h View File

@@ -70,18 +70,6 @@ struct ipv4_miniroute {
70 70
 	struct in_addr gateway;
71 71
 };
72 72
 
73
-/* IPv4 fragment reassembly buffer */
74
-struct ipv4_fragment {
75
-	/* List of fragment reassembly buffers */
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;
83
-};
84
-
85 73
 extern struct list_head ipv4_miniroutes;
86 74
 
87 75
 extern struct net_protocol ipv4_protocol __net_protocol;

+ 172
- 0
src/net/fragment.c View File

@@ -0,0 +1,172 @@
1
+/*
2
+ * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stdint.h>
23
+#include <stdlib.h>
24
+#include <string.h>
25
+#include <ipxe/retry.h>
26
+#include <ipxe/timer.h>
27
+#include <ipxe/fragment.h>
28
+
29
+/** @file
30
+ *
31
+ * Fragment reassembly
32
+ *
33
+ */
34
+
35
+/**
36
+ * Expire fragment reassembly buffer
37
+ *
38
+ * @v timer		Retry timer
39
+ * @v fail		Failure indicator
40
+ */
41
+static void fragment_expired ( struct retry_timer *timer, int fail __unused ) {
42
+	struct fragment *fragment =
43
+		container_of ( timer, struct fragment, timer );
44
+
45
+	DBGC ( fragment, "FRAG %p expired\n", fragment );
46
+	free_iob ( fragment->iobuf );
47
+	list_del ( &fragment->list );
48
+	free ( fragment );
49
+}
50
+
51
+/**
52
+ * Find fragment reassembly buffer
53
+ *
54
+ * @v fragments		Fragment reassembler
55
+ * @v iobuf		I/O buffer
56
+ * @v hdrlen		Length of non-fragmentable potion of I/O buffer
57
+ * @ret fragment	Fragment reassembly buffer, or NULL if not found
58
+ */
59
+static struct fragment * fragment_find ( struct fragment_reassembler *fragments,
60
+					 struct io_buffer *iobuf,
61
+					 size_t hdrlen ) {
62
+	struct fragment *fragment;
63
+
64
+	list_for_each_entry ( fragment, &fragments->list, list ) {
65
+		if ( fragments->is_fragment ( fragment, iobuf, hdrlen ) )
66
+			return fragment;
67
+	}
68
+	return NULL;
69
+}
70
+
71
+/**
72
+ * Reassemble packet
73
+ *
74
+ * @v fragments		Fragment reassembler
75
+ * @v iobuf		I/O buffer
76
+ * @v hdrlen		Length of non-fragmentable potion of I/O buffer
77
+ * @ret iobuf		Reassembled packet, or NULL
78
+ *
79
+ * This function takes ownership of the I/O buffer.  Note that the
80
+ * length of the non-fragmentable portion may be modified.
81
+ */
82
+struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments,
83
+					 struct io_buffer *iobuf,
84
+					 size_t *hdrlen ) {
85
+	struct fragment *fragment;
86
+	struct io_buffer *new_iobuf;
87
+	size_t new_len;
88
+	size_t offset;
89
+	size_t expected_offset;
90
+	int more_frags;
91
+
92
+	/* Find matching fragment reassembly buffer, if any */
93
+	fragment = fragment_find ( fragments, iobuf, *hdrlen );
94
+
95
+	/* Drop out-of-order fragments */
96
+	offset = fragments->fragment_offset ( iobuf, *hdrlen );
97
+	expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
98
+					 fragment->hdrlen ) : 0 );
99
+	if ( offset != expected_offset ) {
100
+		DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
101
+		       "[%zd,%zd), expected [%zd,...)\n", fragment, offset,
102
+		       ( offset + iob_len ( iobuf ) - *hdrlen ),
103
+		       expected_offset );
104
+		goto drop;
105
+	}
106
+
107
+	/* Create or extend fragment reassembly buffer as applicable */
108
+	if ( ! fragment ) {
109
+
110
+		/* Create new fragment reassembly buffer */
111
+		fragment = zalloc ( sizeof ( *fragment ) );
112
+		if ( ! fragment )
113
+			goto drop;
114
+		list_add ( &fragment->list, &fragments->list );
115
+		fragment->iobuf = iobuf;
116
+		fragment->hdrlen = *hdrlen;
117
+		timer_init ( &fragment->timer, fragment_expired, NULL );
118
+		DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
119
+		       ( iob_len ( iobuf ) - *hdrlen ) );
120
+
121
+	} else {
122
+
123
+		/* Check if this is the final fragment */
124
+		more_frags = fragments->more_fragments ( iobuf, *hdrlen );
125
+		DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
126
+		       offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
127
+		       ( more_frags ? "" : " complete" ) );
128
+
129
+		/* Extend fragment reassembly buffer.  Preserve I/O
130
+		 * buffer headroom to allow for code which modifies
131
+		 * and resends the buffer (e.g. ICMP echo responses).
132
+		 */
133
+		iob_pull ( iobuf, *hdrlen );
134
+		new_len = ( iob_headroom ( fragment->iobuf ) +
135
+			    iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
136
+		new_iobuf = alloc_iob ( new_len );
137
+		if ( ! new_iobuf ) {
138
+			DBGC ( fragment, "FRAG %p could not extend reassembly "
139
+			       "buffer to %zd bytes\n", fragment, new_len );
140
+			goto drop;
141
+		}
142
+		iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
143
+		memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
144
+			 fragment->iobuf->data, iob_len ( fragment->iobuf ) );
145
+		memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
146
+			 iobuf->data, iob_len ( iobuf ) );
147
+		free_iob ( fragment->iobuf );
148
+		fragment->iobuf = new_iobuf;
149
+		free_iob ( iobuf );
150
+
151
+		/* Stop fragment reassembly timer */
152
+		stop_timer ( &fragment->timer );
153
+
154
+		/* If this is the final fragment, return it */
155
+		if ( ! more_frags ) {
156
+			iobuf = fragment->iobuf;
157
+			*hdrlen = fragment->hdrlen;
158
+			list_del ( &fragment->list );
159
+			free ( fragment );
160
+			return iobuf;
161
+		}
162
+	}
163
+
164
+	/* (Re)start fragment reassembly timer */
165
+	start_timer_fixed ( &fragment->timer, FRAGMENT_TIMEOUT );
166
+
167
+	return NULL;
168
+
169
+ drop:
170
+	free_iob ( iobuf );
171
+	return NULL;
172
+}

+ 39
- 117
src/net/ipv4.c View File

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

Loading…
Cancel
Save