1 // SPDX-License-Identifier: GPL-2.0
9 #include <netinet/ip.h>
10 #include <netinet/ip6.h>
11 #include <netinet/udp.h>
19 static bool cfg_do_ipv4
;
20 static bool cfg_do_ipv6
;
21 static bool cfg_verbose
;
22 static bool cfg_overlap
;
23 static bool cfg_permissive
;
24 static unsigned short cfg_port
= 9000;
26 const struct in_addr addr4
= { .s_addr
= __constant_htonl(INADDR_LOOPBACK
+ 2) };
27 const struct in6_addr addr6
= IN6ADDR_LOOPBACK_INIT
;
29 #define IP4_HLEN (sizeof(struct iphdr))
30 #define IP6_HLEN (sizeof(struct ip6_hdr))
31 #define UDP_HLEN (sizeof(struct udphdr))
33 /* IPv6 fragment header lenth. */
36 static int payload_len
;
37 static int max_frag_len
;
39 #define MSG_LEN_MAX 10000 /* Max UDP payload length. */
41 #define IP4_MF (1u << 13) /* IPv4 MF flag. */
42 #define IP6_MF (1) /* IPv6 MF flag. */
44 #define CSUM_MANGLED_0 (0xffff)
46 static uint8_t udp_payload
[MSG_LEN_MAX
];
47 static uint8_t ip_frame
[IP_MAXPACKET
];
48 static uint32_t ip_id
= 0xabcd;
49 static int msg_counter
;
50 static int frag_counter
;
51 static unsigned int seed
;
53 /* Receive a UDP packet. Validate it matches udp_payload. */
54 static void recv_validate_udp(int fd_udp
)
57 static uint8_t recv_buff
[MSG_LEN_MAX
];
59 ret
= recv(fd_udp
, recv_buff
, payload_len
, 0);
63 if (ret
== -1 && (errno
== ETIMEDOUT
|| errno
== EAGAIN
))
65 if (!cfg_permissive
) {
67 error(1, 0, "recv: expected timeout; got %d",
69 error(1, errno
, "recv: expected timeout: %d", errno
);
74 error(1, errno
, "recv: payload_len = %d max_frag_len = %d",
75 payload_len
, max_frag_len
);
76 if (ret
!= payload_len
)
77 error(1, 0, "recv: wrong size: %d vs %d", (int)ret
, payload_len
);
78 if (memcmp(udp_payload
, recv_buff
, payload_len
))
79 error(1, 0, "recv: wrong data");
82 static uint32_t raw_checksum(uint8_t *buf
, int len
, uint32_t sum
)
86 for (i
= 0; i
< (len
& ~1U); i
+= 2) {
87 sum
+= (u_int16_t
)ntohs(*((u_int16_t
*)(buf
+ i
)));
101 static uint16_t udp_checksum(struct ip
*iphdr
, struct udphdr
*udphdr
)
106 sum
= raw_checksum((uint8_t *)&iphdr
->ip_src
, 2 * sizeof(iphdr
->ip_src
),
107 IPPROTO_UDP
+ (uint32_t)(UDP_HLEN
+ payload_len
));
108 sum
= raw_checksum((uint8_t *)udphdr
, UDP_HLEN
, sum
);
109 sum
= raw_checksum((uint8_t *)udp_payload
, payload_len
, sum
);
114 return CSUM_MANGLED_0
;
117 static uint16_t udp6_checksum(struct ip6_hdr
*iphdr
, struct udphdr
*udphdr
)
122 sum
= raw_checksum((uint8_t *)&iphdr
->ip6_src
, 2 * sizeof(iphdr
->ip6_src
),
124 sum
= raw_checksum((uint8_t *)&udphdr
->len
, sizeof(udphdr
->len
), sum
);
125 sum
= raw_checksum((uint8_t *)udphdr
, UDP_HLEN
, sum
);
126 sum
= raw_checksum((uint8_t *)udp_payload
, payload_len
, sum
);
131 return CSUM_MANGLED_0
;
134 static void send_fragment(int fd_raw
, struct sockaddr
*addr
, socklen_t alen
,
135 int offset
, bool ipv6
)
139 int payload_offset
= offset
> 0 ? offset
- UDP_HLEN
: 0;
140 uint8_t *frag_start
= ipv6
? ip_frame
+ IP6_HLEN
+ FRAG_HLEN
:
144 struct udphdr udphdr
;
145 udphdr
.source
= htons(cfg_port
+ 1);
146 udphdr
.dest
= htons(cfg_port
);
147 udphdr
.len
= htons(UDP_HLEN
+ payload_len
);
150 udphdr
.check
= udp6_checksum((struct ip6_hdr
*)ip_frame
, &udphdr
);
152 udphdr
.check
= udp_checksum((struct ip
*)ip_frame
, &udphdr
);
153 memcpy(frag_start
, &udphdr
, UDP_HLEN
);
157 struct ip6_hdr
*ip6hdr
= (struct ip6_hdr
*)ip_frame
;
158 struct ip6_frag
*fraghdr
= (struct ip6_frag
*)(ip_frame
+ IP6_HLEN
);
159 if (payload_len
- payload_offset
<= max_frag_len
&& offset
> 0) {
160 /* This is the last fragment. */
161 frag_len
= FRAG_HLEN
+ payload_len
- payload_offset
;
162 fraghdr
->ip6f_offlg
= htons(offset
);
164 frag_len
= FRAG_HLEN
+ max_frag_len
;
165 fraghdr
->ip6f_offlg
= htons(offset
| IP6_MF
);
167 ip6hdr
->ip6_plen
= htons(frag_len
);
169 memcpy(frag_start
+ UDP_HLEN
, udp_payload
,
170 frag_len
- FRAG_HLEN
- UDP_HLEN
);
172 memcpy(frag_start
, udp_payload
+ payload_offset
,
173 frag_len
- FRAG_HLEN
);
174 frag_len
+= IP6_HLEN
;
176 struct ip
*iphdr
= (struct ip
*)ip_frame
;
177 if (payload_len
- payload_offset
<= max_frag_len
&& offset
> 0) {
178 /* This is the last fragment. */
179 frag_len
= IP4_HLEN
+ payload_len
- payload_offset
;
180 iphdr
->ip_off
= htons(offset
/ 8);
182 frag_len
= IP4_HLEN
+ max_frag_len
;
183 iphdr
->ip_off
= htons(offset
/ 8 | IP4_MF
);
185 iphdr
->ip_len
= htons(frag_len
);
187 memcpy(frag_start
+ UDP_HLEN
, udp_payload
,
188 frag_len
- IP4_HLEN
- UDP_HLEN
);
190 memcpy(frag_start
, udp_payload
+ payload_offset
,
191 frag_len
- IP4_HLEN
);
194 res
= sendto(fd_raw
, ip_frame
, frag_len
, 0, addr
, alen
);
195 if (res
< 0 && errno
!= EPERM
)
196 error(1, errno
, "send_fragment");
197 if (res
>= 0 && res
!= frag_len
)
198 error(1, 0, "send_fragment: %d vs %d", res
, frag_len
);
203 static void send_udp_frags(int fd_raw
, struct sockaddr
*addr
,
204 socklen_t alen
, bool ipv6
)
206 struct ip
*iphdr
= (struct ip
*)ip_frame
;
207 struct ip6_hdr
*ip6hdr
= (struct ip6_hdr
*)ip_frame
;
212 /* Send the UDP datagram using raw IP fragments: the 0th fragment
213 * has the UDP header; other fragments are pieces of udp_payload
214 * split in chunks of frag_len size.
216 * Odd fragments (1st, 3rd, 5th, etc.) are sent out first, then
217 * even fragments (0th, 2nd, etc.) are sent out.
220 struct ip6_frag
*fraghdr
= (struct ip6_frag
*)(ip_frame
+ IP6_HLEN
);
221 ((struct sockaddr_in6
*)addr
)->sin6_port
= 0;
222 memset(ip6hdr
, 0, sizeof(*ip6hdr
));
223 ip6hdr
->ip6_flow
= htonl(6<<28); /* Version. */
224 ip6hdr
->ip6_nxt
= IPPROTO_FRAGMENT
;
225 ip6hdr
->ip6_hops
= 255;
226 ip6hdr
->ip6_src
= addr6
;
227 ip6hdr
->ip6_dst
= addr6
;
228 fraghdr
->ip6f_nxt
= IPPROTO_UDP
;
229 fraghdr
->ip6f_reserved
= 0;
230 fraghdr
->ip6f_ident
= htonl(ip_id
++);
232 memset(iphdr
, 0, sizeof(*iphdr
));
236 iphdr
->ip_id
= htons(ip_id
++);
237 iphdr
->ip_ttl
= 0x40;
238 iphdr
->ip_p
= IPPROTO_UDP
;
239 iphdr
->ip_src
.s_addr
= htonl(INADDR_LOOPBACK
);
240 iphdr
->ip_dst
= addr4
;
244 /* Occasionally test in-order fragments. */
245 if (!cfg_overlap
&& (rand() % 100 < 15)) {
247 while (offset
< (UDP_HLEN
+ payload_len
)) {
248 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
249 offset
+= max_frag_len
;
254 /* Occasionally test IPv4 "runs" (see net/ipv4/ip_fragment.c) */
255 if (!cfg_overlap
&& (rand() % 100 < 20) &&
256 (payload_len
> 9 * max_frag_len
)) {
257 offset
= 6 * max_frag_len
;
258 while (offset
< (UDP_HLEN
+ payload_len
)) {
259 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
260 offset
+= max_frag_len
;
262 offset
= 3 * max_frag_len
;
263 while (offset
< 6 * max_frag_len
) {
264 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
265 offset
+= max_frag_len
;
268 while (offset
< 3 * max_frag_len
) {
269 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
270 offset
+= max_frag_len
;
276 offset
= max_frag_len
;
277 while (offset
< (UDP_HLEN
+ payload_len
)) {
278 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
279 /* IPv4 ignores duplicates, so randomly send a duplicate. */
280 if (rand() % 100 == 1)
281 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
282 offset
+= 2 * max_frag_len
;
286 /* Send an extra random fragment.
288 * Duplicates and some fragments completely inside
289 * previously sent fragments are dropped/ignored. So
290 * random offset and frag_len can result in a dropped
291 * fragment instead of a dropped queue/packet. Thus we
292 * hard-code offset and frag_len.
294 if (max_frag_len
* 4 < payload_len
|| max_frag_len
< 16) {
295 /* not enough payload for random offset and frag_len. */
297 frag_len
= UDP_HLEN
+ max_frag_len
;
299 offset
= rand() % (payload_len
/ 2);
300 frag_len
= 2 * max_frag_len
+ 1 + rand() % 256;
303 struct ip6_frag
*fraghdr
= (struct ip6_frag
*)(ip_frame
+ IP6_HLEN
);
304 /* sendto() returns EINVAL if offset + frag_len is too small. */
305 /* In IPv6 if !!(frag_len % 8), the fragment is dropped. */
307 fraghdr
->ip6f_offlg
= htons(offset
/ 8 | IP6_MF
);
308 ip6hdr
->ip6_plen
= htons(frag_len
);
309 frag_len
+= IP6_HLEN
;
311 frag_len
+= IP4_HLEN
;
312 iphdr
->ip_off
= htons(offset
/ 8 | IP4_MF
);
313 iphdr
->ip_len
= htons(frag_len
);
315 res
= sendto(fd_raw
, ip_frame
, frag_len
, 0, addr
, alen
);
316 if (res
< 0 && errno
!= EPERM
)
317 error(1, errno
, "sendto overlap: %d", frag_len
);
318 if (res
>= 0 && res
!= frag_len
)
319 error(1, 0, "sendto overlap: %d vs %d", (int)res
, frag_len
);
323 /* Event fragments. */
325 while (offset
< (UDP_HLEN
+ payload_len
)) {
326 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
327 /* IPv4 ignores duplicates, so randomly send a duplicate. */
328 if (rand() % 100 == 1)
329 send_fragment(fd_raw
, addr
, alen
, offset
, ipv6
);
330 offset
+= 2 * max_frag_len
;
334 static void run_test(struct sockaddr
*addr
, socklen_t alen
, bool ipv6
)
336 int fd_tx_raw
, fd_rx_udp
;
337 /* Frag queue timeout is set to one second in the calling script;
338 * socket timeout should be just a bit longer to avoid tests interfering
341 struct timeval tv
= { .tv_sec
= 1, .tv_usec
= 10 };
343 int min_frag_len
= 8;
345 /* Initialize the payload. */
346 for (idx
= 0; idx
< MSG_LEN_MAX
; ++idx
)
347 udp_payload
[idx
] = idx
% 256;
350 fd_tx_raw
= socket(addr
->sa_family
, SOCK_RAW
, IPPROTO_RAW
);
352 error(1, errno
, "socket tx_raw");
354 fd_rx_udp
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
356 error(1, errno
, "socket rx_udp");
357 if (bind(fd_rx_udp
, addr
, alen
))
358 error(1, errno
, "bind");
360 if (setsockopt(fd_rx_udp
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
)))
361 error(1, errno
, "setsockopt rcv timeout");
363 for (payload_len
= min_frag_len
; payload_len
< MSG_LEN_MAX
;
364 payload_len
+= (rand() % 4096)) {
366 printf("payload_len: %d\n", payload_len
);
369 /* With overlaps, one send/receive pair below takes
370 * at least one second (== timeout) to run, so there
371 * is not enough test time to run a nested loop:
372 * the full overlap test takes 20-30 seconds.
374 max_frag_len
= min_frag_len
+
375 rand() % (1500 - FRAG_HLEN
- min_frag_len
);
376 send_udp_frags(fd_tx_raw
, addr
, alen
, ipv6
);
377 recv_validate_udp(fd_rx_udp
);
379 /* Without overlaps, each packet reassembly (== one
380 * send/receive pair below) takes very little time to
381 * run, so we can easily afford more thourough testing
382 * with a nested loop: the full non-overlap test takes
383 * less than one second).
385 max_frag_len
= min_frag_len
;
387 send_udp_frags(fd_tx_raw
, addr
, alen
, ipv6
);
388 recv_validate_udp(fd_rx_udp
);
389 max_frag_len
+= 8 * (rand() % 8);
390 } while (max_frag_len
< (1500 - FRAG_HLEN
) &&
391 max_frag_len
<= payload_len
);
396 if (close(fd_tx_raw
))
397 error(1, errno
, "close tx_raw");
398 if (close(fd_rx_udp
))
399 error(1, errno
, "close rx_udp");
402 printf("processed %d messages, %d fragments\n",
403 msg_counter
, frag_counter
);
405 fprintf(stderr
, "PASS\n");
409 static void run_test_v4(void)
411 struct sockaddr_in addr
= {0};
413 addr
.sin_family
= AF_INET
;
414 addr
.sin_port
= htons(cfg_port
);
415 addr
.sin_addr
= addr4
;
417 run_test((void *)&addr
, sizeof(addr
), false /* !ipv6 */);
420 static void run_test_v6(void)
422 struct sockaddr_in6 addr
= {0};
424 addr
.sin6_family
= AF_INET6
;
425 addr
.sin6_port
= htons(cfg_port
);
426 addr
.sin6_addr
= addr6
;
428 run_test((void *)&addr
, sizeof(addr
), true /* ipv6 */);
431 static void parse_opts(int argc
, char **argv
)
435 while ((c
= getopt(argc
, argv
, "46opv")) != -1) {
447 cfg_permissive
= true;
453 error(1, 0, "%s: parse error", argv
[0]);
458 int main(int argc
, char **argv
)
460 parse_opts(argc
, argv
);
463 /* Print the seed to track/reproduce potential failures. */
464 printf("seed = %d\n", seed
);