@@ -23,21 +23,28 @@ static bool cfg_overlap;
23
23
static unsigned short cfg_port = 9000 ;
24
24
25
25
const struct in_addr addr4 = { .s_addr = __constant_htonl (INADDR_LOOPBACK + 2 ) };
26
+ const struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT ;
26
27
27
28
#define IP4_HLEN (sizeof(struct iphdr))
28
29
#define IP6_HLEN (sizeof(struct ip6_hdr))
29
30
#define UDP_HLEN (sizeof(struct udphdr))
30
31
31
- static int msg_len ;
32
+ /* IPv6 fragment header lenth. */
33
+ #define FRAG_HLEN 8
34
+
35
+ static int payload_len ;
32
36
static int max_frag_len ;
33
37
34
38
#define MSG_LEN_MAX 60000 /* Max UDP payload length. */
35
39
36
40
#define IP4_MF (1u << 13) /* IPv4 MF flag. */
41
+ #define IP6_MF (1) /* IPv6 MF flag. */
42
+
43
+ #define CSUM_MANGLED_0 (0xffff)
37
44
38
45
static uint8_t udp_payload [MSG_LEN_MAX ];
39
46
static uint8_t ip_frame [IP_MAXPACKET ];
40
- static uint16_t ip_id = 0xabcd ;
47
+ static uint32_t ip_id = 0xabcd ;
41
48
static int msg_counter ;
42
49
static int frag_counter ;
43
50
static unsigned int seed ;
@@ -48,25 +55,25 @@ static void recv_validate_udp(int fd_udp)
48
55
ssize_t ret ;
49
56
static uint8_t recv_buff [MSG_LEN_MAX ];
50
57
51
- ret = recv (fd_udp , recv_buff , msg_len , 0 );
58
+ ret = recv (fd_udp , recv_buff , payload_len , 0 );
52
59
msg_counter ++ ;
53
60
54
61
if (cfg_overlap ) {
55
62
if (ret != -1 )
56
- error (1 , 0 , "recv: expected timeout; got %d; seed = %u " ,
57
- (int )ret , seed );
63
+ error (1 , 0 , "recv: expected timeout; got %d" ,
64
+ (int )ret );
58
65
if (errno != ETIMEDOUT && errno != EAGAIN )
59
- error (1 , errno , "recv: expected timeout: %d; seed = %u " ,
60
- errno , seed );
66
+ error (1 , errno , "recv: expected timeout: %d" ,
67
+ errno );
61
68
return ; /* OK */
62
69
}
63
70
64
71
if (ret == -1 )
65
- error (1 , errno , "recv: msg_len = %d max_frag_len = %d" ,
66
- msg_len , max_frag_len );
67
- if (ret != msg_len )
68
- error (1 , 0 , "recv: wrong size: %d vs %d" , (int )ret , msg_len );
69
- if (memcmp (udp_payload , recv_buff , msg_len ))
72
+ error (1 , errno , "recv: payload_len = %d max_frag_len = %d" ,
73
+ payload_len , max_frag_len );
74
+ if (ret != payload_len )
75
+ error (1 , 0 , "recv: wrong size: %d vs %d" , (int )ret , payload_len );
76
+ if (memcmp (udp_payload , recv_buff , payload_len ))
70
77
error (1 , 0 , "recv: wrong data" );
71
78
}
72
79
@@ -92,31 +99,95 @@ static uint32_t raw_checksum(uint8_t *buf, int len, uint32_t sum)
92
99
static uint16_t udp_checksum (struct ip * iphdr , struct udphdr * udphdr )
93
100
{
94
101
uint32_t sum = 0 ;
102
+ uint16_t res ;
95
103
96
104
sum = raw_checksum ((uint8_t * )& iphdr -> ip_src , 2 * sizeof (iphdr -> ip_src ),
97
- IPPROTO_UDP + (uint32_t )(UDP_HLEN + msg_len ));
98
- sum = raw_checksum ((uint8_t * )udp_payload , msg_len , sum );
105
+ IPPROTO_UDP + (uint32_t )(UDP_HLEN + payload_len ));
106
+ sum = raw_checksum ((uint8_t * )udphdr , UDP_HLEN , sum );
107
+ sum = raw_checksum ((uint8_t * )udp_payload , payload_len , sum );
108
+ res = 0xffff & ~sum ;
109
+ if (res )
110
+ return htons (res );
111
+ else
112
+ return CSUM_MANGLED_0 ;
113
+ }
114
+
115
+ static uint16_t udp6_checksum (struct ip6_hdr * iphdr , struct udphdr * udphdr )
116
+ {
117
+ uint32_t sum = 0 ;
118
+ uint16_t res ;
119
+
120
+ sum = raw_checksum ((uint8_t * )& iphdr -> ip6_src , 2 * sizeof (iphdr -> ip6_src ),
121
+ IPPROTO_UDP );
122
+ sum = raw_checksum ((uint8_t * )& udphdr -> len , sizeof (udphdr -> len ), sum );
99
123
sum = raw_checksum ((uint8_t * )udphdr , UDP_HLEN , sum );
100
- return htons (0xffff & ~sum );
124
+ sum = raw_checksum ((uint8_t * )udp_payload , payload_len , sum );
125
+ res = 0xffff & ~sum ;
126
+ if (res )
127
+ return htons (res );
128
+ else
129
+ return CSUM_MANGLED_0 ;
101
130
}
102
131
103
132
static void send_fragment (int fd_raw , struct sockaddr * addr , socklen_t alen ,
104
- struct ip * iphdr , int offset )
133
+ int offset , bool ipv6 )
105
134
{
106
135
int frag_len ;
107
136
int res ;
137
+ int payload_offset = offset > 0 ? offset - UDP_HLEN : 0 ;
138
+ uint8_t * frag_start = ipv6 ? ip_frame + IP6_HLEN + FRAG_HLEN :
139
+ ip_frame + IP4_HLEN ;
140
+
141
+ if (offset == 0 ) {
142
+ struct udphdr udphdr ;
143
+ udphdr .source = htons (cfg_port + 1 );
144
+ udphdr .dest = htons (cfg_port );
145
+ udphdr .len = htons (UDP_HLEN + payload_len );
146
+ udphdr .check = 0 ;
147
+ if (ipv6 )
148
+ udphdr .check = udp6_checksum ((struct ip6_hdr * )ip_frame , & udphdr );
149
+ else
150
+ udphdr .check = udp_checksum ((struct ip * )ip_frame , & udphdr );
151
+ memcpy (frag_start , & udphdr , UDP_HLEN );
152
+ }
108
153
109
- if (msg_len - offset <= max_frag_len ) {
110
- /* This is the last fragment. */
111
- frag_len = IP4_HLEN + msg_len - offset ;
112
- iphdr -> ip_off = htons ((offset + UDP_HLEN ) / 8 );
154
+ if (ipv6 ) {
155
+ struct ip6_hdr * ip6hdr = (struct ip6_hdr * )ip_frame ;
156
+ struct ip6_frag * fraghdr = (struct ip6_frag * )(ip_frame + IP6_HLEN );
157
+ if (payload_len - payload_offset <= max_frag_len && offset > 0 ) {
158
+ /* This is the last fragment. */
159
+ frag_len = FRAG_HLEN + payload_len - payload_offset ;
160
+ fraghdr -> ip6f_offlg = htons (offset );
161
+ } else {
162
+ frag_len = FRAG_HLEN + max_frag_len ;
163
+ fraghdr -> ip6f_offlg = htons (offset | IP6_MF );
164
+ }
165
+ ip6hdr -> ip6_plen = htons (frag_len );
166
+ if (offset == 0 )
167
+ memcpy (frag_start + UDP_HLEN , udp_payload ,
168
+ frag_len - FRAG_HLEN - UDP_HLEN );
169
+ else
170
+ memcpy (frag_start , udp_payload + payload_offset ,
171
+ frag_len - FRAG_HLEN );
172
+ frag_len += IP6_HLEN ;
113
173
} else {
114
- frag_len = IP4_HLEN + max_frag_len ;
115
- iphdr -> ip_off = htons ((offset + UDP_HLEN ) / 8 | IP4_MF );
174
+ struct ip * iphdr = (struct ip * )ip_frame ;
175
+ if (payload_len - payload_offset <= max_frag_len && offset > 0 ) {
176
+ /* This is the last fragment. */
177
+ frag_len = IP4_HLEN + payload_len - payload_offset ;
178
+ iphdr -> ip_off = htons (offset / 8 );
179
+ } else {
180
+ frag_len = IP4_HLEN + max_frag_len ;
181
+ iphdr -> ip_off = htons (offset / 8 | IP4_MF );
182
+ }
183
+ iphdr -> ip_len = htons (frag_len );
184
+ if (offset == 0 )
185
+ memcpy (frag_start + UDP_HLEN , udp_payload ,
186
+ frag_len - IP4_HLEN - UDP_HLEN );
187
+ else
188
+ memcpy (frag_start , udp_payload + payload_offset ,
189
+ frag_len - IP4_HLEN );
116
190
}
117
- iphdr -> ip_len = htons (frag_len );
118
- memcpy (ip_frame + IP4_HLEN , udp_payload + offset ,
119
- frag_len - IP4_HLEN );
120
191
121
192
res = sendto (fd_raw , ip_frame , frag_len , 0 , addr , alen );
122
193
if (res < 0 )
@@ -127,9 +198,11 @@ static void send_fragment(int fd_raw, struct sockaddr *addr, socklen_t alen,
127
198
frag_counter ++ ;
128
199
}
129
200
130
- static void send_udp_frags_v4 (int fd_raw , struct sockaddr * addr , socklen_t alen )
201
+ static void send_udp_frags (int fd_raw , struct sockaddr * addr ,
202
+ socklen_t alen , bool ipv6 )
131
203
{
132
204
struct ip * iphdr = (struct ip * )ip_frame ;
205
+ struct ip6_hdr * ip6hdr = (struct ip6_hdr * )ip_frame ;
133
206
struct udphdr udphdr ;
134
207
int res ;
135
208
int offset ;
@@ -142,31 +215,55 @@ static void send_udp_frags_v4(int fd_raw, struct sockaddr *addr, socklen_t alen)
142
215
* Odd fragments (1st, 3rd, 5th, etc.) are sent out first, then
143
216
* even fragments (0th, 2nd, etc.) are sent out.
144
217
*/
145
- memset (iphdr , 0 , sizeof (* iphdr ));
146
- iphdr -> ip_hl = 5 ;
147
- iphdr -> ip_v = 4 ;
148
- iphdr -> ip_tos = 0 ;
149
- iphdr -> ip_id = htons (ip_id ++ );
150
- iphdr -> ip_ttl = 0x40 ;
151
- iphdr -> ip_p = IPPROTO_UDP ;
152
- iphdr -> ip_src .s_addr = htonl (INADDR_LOOPBACK );
153
- iphdr -> ip_dst = addr4 ;
154
- iphdr -> ip_sum = 0 ;
218
+ if (ipv6 ) {
219
+ struct ip6_frag * fraghdr = (struct ip6_frag * )(ip_frame + IP6_HLEN );
220
+ ((struct sockaddr_in6 * )addr )-> sin6_port = 0 ;
221
+ memset (ip6hdr , 0 , sizeof (* ip6hdr ));
222
+ ip6hdr -> ip6_flow = htonl (6 <<28 ); /* Version. */
223
+ ip6hdr -> ip6_nxt = IPPROTO_FRAGMENT ;
224
+ ip6hdr -> ip6_hops = 255 ;
225
+ ip6hdr -> ip6_src = addr6 ;
226
+ ip6hdr -> ip6_dst = addr6 ;
227
+ fraghdr -> ip6f_nxt = IPPROTO_UDP ;
228
+ fraghdr -> ip6f_reserved = 0 ;
229
+ fraghdr -> ip6f_ident = htonl (ip_id ++ );
230
+ } else {
231
+ memset (iphdr , 0 , sizeof (* iphdr ));
232
+ iphdr -> ip_hl = 5 ;
233
+ iphdr -> ip_v = 4 ;
234
+ iphdr -> ip_tos = 0 ;
235
+ iphdr -> ip_id = htons (ip_id ++ );
236
+ iphdr -> ip_ttl = 0x40 ;
237
+ iphdr -> ip_p = IPPROTO_UDP ;
238
+ iphdr -> ip_src .s_addr = htonl (INADDR_LOOPBACK );
239
+ iphdr -> ip_dst = addr4 ;
240
+ iphdr -> ip_sum = 0 ;
241
+ }
155
242
156
243
/* Odd fragments. */
157
- offset = 0 ;
158
- while (offset < msg_len ) {
159
- send_fragment (fd_raw , addr , alen , iphdr , offset );
244
+ offset = max_frag_len ;
245
+ while (offset < ( UDP_HLEN + payload_len ) ) {
246
+ send_fragment (fd_raw , addr , alen , offset , ipv6 );
160
247
offset += 2 * max_frag_len ;
161
248
}
162
249
163
250
if (cfg_overlap ) {
164
251
/* Send an extra random fragment. */
165
- offset = rand () % (UDP_HLEN + msg_len - 1 );
252
+ offset = rand () % (UDP_HLEN + payload_len - 1 );
166
253
/* sendto() returns EINVAL if offset + frag_len is too small. */
167
- frag_len = IP4_HLEN + UDP_HLEN + rand () % 256 ;
168
- iphdr -> ip_off = htons (offset / 8 | IP4_MF );
169
- iphdr -> ip_len = htons (frag_len );
254
+ if (ipv6 ) {
255
+ struct ip6_frag * fraghdr = (struct ip6_frag * )(ip_frame + IP6_HLEN );
256
+ frag_len = max_frag_len + rand () % 256 ;
257
+ /* In IPv6 if !!(frag_len % 8), the fragment is dropped. */
258
+ frag_len &= ~0x7 ;
259
+ fraghdr -> ip6f_offlg = htons (offset / 8 | IP6_MF );
260
+ ip6hdr -> ip6_plen = htons (frag_len );
261
+ frag_len += IP6_HLEN ;
262
+ } else {
263
+ frag_len = IP4_HLEN + UDP_HLEN + rand () % 256 ;
264
+ iphdr -> ip_off = htons (offset / 8 | IP4_MF );
265
+ iphdr -> ip_len = htons (frag_len );
266
+ }
170
267
res = sendto (fd_raw , ip_frame , frag_len , 0 , addr , alen );
171
268
if (res < 0 )
172
269
error (1 , errno , "sendto overlap" );
@@ -175,48 +272,26 @@ static void send_udp_frags_v4(int fd_raw, struct sockaddr *addr, socklen_t alen)
175
272
frag_counter ++ ;
176
273
}
177
274
178
- /* Zeroth fragment (UDP header). */
179
- frag_len = IP4_HLEN + UDP_HLEN ;
180
- iphdr -> ip_len = htons (frag_len );
181
- iphdr -> ip_off = htons (IP4_MF );
182
-
183
- udphdr .source = htons (cfg_port + 1 );
184
- udphdr .dest = htons (cfg_port );
185
- udphdr .len = htons (UDP_HLEN + msg_len );
186
- udphdr .check = 0 ;
187
- udphdr .check = udp_checksum (iphdr , & udphdr );
188
-
189
- memcpy (ip_frame + IP4_HLEN , & udphdr , UDP_HLEN );
190
- res = sendto (fd_raw , ip_frame , frag_len , 0 , addr , alen );
191
- if (res < 0 )
192
- error (1 , errno , "sendto UDP header" );
193
- if (res != frag_len )
194
- error (1 , 0 , "sendto UDP header: %d vs %d" , (int )res , frag_len );
195
- frag_counter ++ ;
196
-
197
- /* Even fragments. */
198
- offset = max_frag_len ;
199
- while (offset < msg_len ) {
200
- send_fragment (fd_raw , addr , alen , iphdr , offset );
275
+ /* Event fragments. */
276
+ offset = 0 ;
277
+ while (offset < (UDP_HLEN + payload_len )) {
278
+ send_fragment (fd_raw , addr , alen , offset , ipv6 );
201
279
offset += 2 * max_frag_len ;
202
280
}
203
281
}
204
282
205
- static void run_test (struct sockaddr * addr , socklen_t alen )
283
+ static void run_test (struct sockaddr * addr , socklen_t alen , bool ipv6 )
206
284
{
207
- int fd_tx_udp , fd_tx_raw , fd_rx_udp ;
285
+ int fd_tx_raw , fd_rx_udp ;
208
286
struct timeval tv = { .tv_sec = 0 , .tv_usec = 10 * 1000 };
209
287
int idx ;
288
+ int min_frag_len = ipv6 ? 1280 : 8 ;
210
289
211
290
/* Initialize the payload. */
212
291
for (idx = 0 ; idx < MSG_LEN_MAX ; ++ idx )
213
292
udp_payload [idx ] = idx % 256 ;
214
293
215
294
/* Open sockets. */
216
- fd_tx_udp = socket (addr -> sa_family , SOCK_DGRAM , 0 );
217
- if (fd_tx_udp == -1 )
218
- error (1 , errno , "socket tx_udp" );
219
-
220
295
fd_tx_raw = socket (addr -> sa_family , SOCK_RAW , IPPROTO_RAW );
221
296
if (fd_tx_raw == -1 )
222
297
error (1 , errno , "socket tx_raw" );
@@ -230,22 +305,21 @@ static void run_test(struct sockaddr *addr, socklen_t alen)
230
305
if (setsockopt (fd_rx_udp , SOL_SOCKET , SO_RCVTIMEO , & tv , sizeof (tv )))
231
306
error (1 , errno , "setsockopt rcv timeout" );
232
307
233
- for (msg_len = 1 ; msg_len < MSG_LEN_MAX ; msg_len += (rand () % 4096 )) {
308
+ for (payload_len = min_frag_len ; payload_len < MSG_LEN_MAX ;
309
+ payload_len += (rand () % 4096 )) {
234
310
if (cfg_verbose )
235
- printf ("msg_len: %d\n" , msg_len );
236
- max_frag_len = addr -> sa_family == AF_INET ? 8 : 1280 ;
237
- for (; max_frag_len < 1500 && max_frag_len <= msg_len ;
238
- max_frag_len += 8 ) {
239
- send_udp_frags_v4 (fd_tx_raw , addr , alen );
311
+ printf ("payload_len: %d\n" , payload_len );
312
+ max_frag_len = min_frag_len ;
313
+ do {
314
+ send_udp_frags (fd_tx_raw , addr , alen , ipv6 );
240
315
recv_validate_udp (fd_rx_udp );
241
- }
316
+ max_frag_len += 8 * (rand () % 8 );
317
+ } while (max_frag_len < (1500 - FRAG_HLEN ) && max_frag_len <= payload_len );
242
318
}
243
319
244
320
/* Cleanup. */
245
321
if (close (fd_tx_raw ))
246
322
error (1 , errno , "close tx_raw" );
247
- if (close (fd_tx_udp ))
248
- error (1 , errno , "close tx_udp" );
249
323
if (close (fd_rx_udp ))
250
324
error (1 , errno , "close rx_udp" );
251
325
@@ -265,13 +339,18 @@ static void run_test_v4(void)
265
339
addr .sin_port = htons (cfg_port );
266
340
addr .sin_addr = addr4 ;
267
341
268
- run_test ((void * )& addr , sizeof (addr ));
342
+ run_test ((void * )& addr , sizeof (addr ), false /* !ipv6 */ );
269
343
}
270
344
271
345
static void run_test_v6 (void )
272
346
{
273
- fprintf (stderr , "NOT IMPL.\n" );
274
- exit (1 );
347
+ struct sockaddr_in6 addr = {0 };
348
+
349
+ addr .sin6_family = AF_INET6 ;
350
+ addr .sin6_port = htons (cfg_port );
351
+ addr .sin6_addr = addr6 ;
352
+
353
+ run_test ((void * )& addr , sizeof (addr ), true /* ipv6 */ );
275
354
}
276
355
277
356
static void parse_opts (int argc , char * * argv )
@@ -303,6 +382,8 @@ int main(int argc, char **argv)
303
382
parse_opts (argc , argv );
304
383
seed = time (NULL );
305
384
srand (seed );
385
+ /* Print the seed to track/reproduce potential failures. */
386
+ printf ("seed = %d\n" , seed );
306
387
307
388
if (cfg_do_ipv4 )
308
389
run_test_v4 ();
0 commit comments