1 // SPDX-License-Identifier: GPL-2.0
11 #include <linux/netlink.h>
12 #include <linux/rtnetlink.h>
13 #include <netinet/if_ether.h>
14 #include <netinet/ip.h>
15 #include <netinet/ip6.h>
16 #include <netinet/udp.h>
21 #include <sys/ioctl.h>
22 #include <sys/socket.h>
25 #include <sys/types.h>
29 #define ETH_MAX_MTU 0xFFFFU
33 #define UDP_SEGMENT 103
36 #ifndef UDP_MAX_SEGMENTS
37 #define UDP_MAX_SEGMENTS (1 << 7UL)
40 #define CONST_MTU_TEST 1500
42 #define CONST_HDRLEN_V4 (sizeof(struct iphdr) + sizeof(struct udphdr))
43 #define CONST_HDRLEN_V6 (sizeof(struct ip6_hdr) + sizeof(struct udphdr))
45 #define CONST_MSS_V4 (CONST_MTU_TEST - CONST_HDRLEN_V4)
46 #define CONST_MSS_V6 (CONST_MTU_TEST - CONST_HDRLEN_V6)
48 #define CONST_MAX_SEGS_V4 (ETH_MAX_MTU / CONST_MSS_V4)
49 #define CONST_MAX_SEGS_V6 (ETH_MAX_MTU / CONST_MSS_V6)
51 static bool cfg_do_ipv4
;
52 static bool cfg_do_ipv6
;
53 static bool cfg_do_connected
;
54 static bool cfg_do_connectionless
;
55 static bool cfg_do_msgmore
;
56 static bool cfg_do_recv
= true;
57 static bool cfg_do_setsockopt
;
58 static int cfg_specific_test_id
= -1;
60 static unsigned short cfg_port
= 9000;
62 static char buf
[ETH_MAX_MTU
];
65 int tlen
; /* send() buffer size, may exceed mss */
66 bool tfail
; /* send() call is expected to fail */
67 int gso_len
; /* mss after applying gso */
68 int r_num_mss
; /* recv(): number of calls of full mss */
69 int r_len_last
; /* recv(): size of last non-mss dgram, if any */
70 bool v6_ext_hdr
; /* send() dgrams with IPv6 extension headers */
73 const struct in6_addr addr6
= {
74 { { 0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, /* fd00::1 */
77 const struct in_addr addr4
= {
78 __constant_htonl(0x0a000001), /* 10.0.0.1 */
81 static const char ipv6_hopopts_pad1
[8] = { 0 };
83 struct testcase testcases_v4
[] = {
85 /* no GSO: send a single byte */
90 /* no GSO: send a single MSS */
95 /* no GSO: send a single MSS + 1B: fail */
96 .tlen
= CONST_MSS_V4
+ 1,
100 /* send a single MSS: will fall back to no GSO */
101 .tlen
= CONST_MSS_V4
,
102 .gso_len
= CONST_MSS_V4
,
106 /* send a single MSS + 1B */
107 .tlen
= CONST_MSS_V4
+ 1,
108 .gso_len
= CONST_MSS_V4
,
113 /* send exactly 2 MSS */
114 .tlen
= CONST_MSS_V4
* 2,
115 .gso_len
= CONST_MSS_V4
,
119 /* send 2 MSS + 1B */
120 .tlen
= (CONST_MSS_V4
* 2) + 1,
121 .gso_len
= CONST_MSS_V4
,
127 .tlen
= (ETH_MAX_MTU
/ CONST_MSS_V4
) * CONST_MSS_V4
,
128 .gso_len
= CONST_MSS_V4
,
129 .r_num_mss
= (ETH_MAX_MTU
/ CONST_MSS_V4
),
134 .tlen
= ETH_MAX_MTU
- CONST_HDRLEN_V4
,
135 .gso_len
= CONST_MSS_V4
,
136 .r_num_mss
= CONST_MAX_SEGS_V4
,
137 .r_len_last
= ETH_MAX_MTU
- CONST_HDRLEN_V4
-
138 (CONST_MAX_SEGS_V4
* CONST_MSS_V4
),
141 /* send MAX + 1: fail */
142 .tlen
= ETH_MAX_MTU
- CONST_HDRLEN_V4
+ 1,
143 .gso_len
= CONST_MSS_V4
,
147 /* send a single 1B MSS: will fall back to no GSO */
153 /* send 2 1B segments */
159 /* send 2B + 2B + 1B segments */
166 /* send max number of min sized segments */
167 .tlen
= UDP_MAX_SEGMENTS
,
169 .r_num_mss
= UDP_MAX_SEGMENTS
,
172 /* send max number + 1 of min sized segments: fail */
173 .tlen
= UDP_MAX_SEGMENTS
+ 1,
183 #define IP6_MAX_MTU (ETH_MAX_MTU + sizeof(struct ip6_hdr))
186 struct testcase testcases_v6
[] = {
188 /* no GSO: send a single byte */
193 /* no GSO: send a single MSS */
194 .tlen
= CONST_MSS_V6
,
198 /* no GSO: send a single MSS + 1B: fail */
199 .tlen
= CONST_MSS_V6
+ 1,
203 /* send a single MSS: will fall back to no GSO */
204 .tlen
= CONST_MSS_V6
,
205 .gso_len
= CONST_MSS_V6
,
209 /* send a single MSS + 1B */
210 .tlen
= CONST_MSS_V6
+ 1,
211 .gso_len
= CONST_MSS_V6
,
216 /* send exactly 2 MSS */
217 .tlen
= CONST_MSS_V6
* 2,
218 .gso_len
= CONST_MSS_V6
,
222 /* send 2 MSS + 1B */
223 .tlen
= (CONST_MSS_V6
* 2) + 1,
224 .gso_len
= CONST_MSS_V6
,
230 .tlen
= (IP6_MAX_MTU
/ CONST_MSS_V6
) * CONST_MSS_V6
,
231 .gso_len
= CONST_MSS_V6
,
232 .r_num_mss
= (IP6_MAX_MTU
/ CONST_MSS_V6
),
237 .tlen
= IP6_MAX_MTU
- CONST_HDRLEN_V6
,
238 .gso_len
= CONST_MSS_V6
,
239 .r_num_mss
= CONST_MAX_SEGS_V6
,
240 .r_len_last
= IP6_MAX_MTU
- CONST_HDRLEN_V6
-
241 (CONST_MAX_SEGS_V6
* CONST_MSS_V6
),
244 /* send MAX + 1: fail */
245 .tlen
= IP6_MAX_MTU
- CONST_HDRLEN_V6
+ 1,
246 .gso_len
= CONST_MSS_V6
,
250 /* send a single 1B MSS: will fall back to no GSO */
256 /* send 2 1B segments */
262 /* send 2 1B segments with extension headers */
269 /* send 2B + 2B + 1B segments */
276 /* send max number of min sized segments */
277 .tlen
= UDP_MAX_SEGMENTS
,
279 .r_num_mss
= UDP_MAX_SEGMENTS
,
282 /* send max number + 1 of min sized segments: fail */
283 .tlen
= UDP_MAX_SEGMENTS
+ 1,
292 static void set_pmtu_discover(int fd
, bool is_ipv4
)
294 int level
, name
, val
;
298 name
= IP_MTU_DISCOVER
;
299 val
= IP_PMTUDISC_DO
;
302 name
= IPV6_MTU_DISCOVER
;
303 val
= IPV6_PMTUDISC_DO
;
306 if (setsockopt(fd
, level
, name
, &val
, sizeof(val
)))
307 error(1, errno
, "setsockopt path mtu");
310 static unsigned int get_path_mtu(int fd
, bool is_ipv4
)
316 vallen
= sizeof(mtu
);
318 ret
= getsockopt(fd
, SOL_IP
, IP_MTU
, &mtu
, &vallen
);
320 ret
= getsockopt(fd
, SOL_IPV6
, IPV6_MTU
, &mtu
, &vallen
);
323 error(1, errno
, "getsockopt mtu");
326 fprintf(stderr
, "path mtu (read): %u\n", mtu
);
330 static bool __send_one(int fd
, struct msghdr
*msg
, int flags
)
334 ret
= sendmsg(fd
, msg
, flags
);
336 (errno
== EMSGSIZE
|| errno
== ENOMEM
|| errno
== EINVAL
))
339 error(1, errno
, "sendmsg");
340 if (ret
!= msg
->msg_iov
->iov_len
)
341 error(1, 0, "sendto: %d != %llu", ret
,
342 (unsigned long long)msg
->msg_iov
->iov_len
);
344 error(1, 0, "sendmsg: return flags 0x%x\n", msg
->msg_flags
);
349 static bool send_one(int fd
, int len
, int gso_len
,
350 struct sockaddr
*addr
, socklen_t alen
)
352 char control
[CMSG_SPACE(sizeof(uint16_t))] = {0};
353 struct msghdr msg
= {0};
354 struct iovec iov
= {0};
364 msg
.msg_namelen
= alen
;
366 if (gso_len
&& !cfg_do_setsockopt
) {
367 msg
.msg_control
= control
;
368 msg
.msg_controllen
= sizeof(control
);
370 cm
= CMSG_FIRSTHDR(&msg
);
371 cm
->cmsg_level
= SOL_UDP
;
372 cm
->cmsg_type
= UDP_SEGMENT
;
373 cm
->cmsg_len
= CMSG_LEN(sizeof(uint16_t));
374 *((uint16_t *) CMSG_DATA(cm
)) = gso_len
;
377 /* If MSG_MORE, send 1 byte followed by remainder */
378 if (cfg_do_msgmore
&& len
> 1) {
380 if (!__send_one(fd
, &msg
, MSG_MORE
))
381 error(1, 0, "send 1B failed");
384 iov
.iov_len
= len
- 1;
387 return __send_one(fd
, &msg
, 0);
390 static int recv_one(int fd
, int flags
)
394 ret
= recv(fd
, buf
, sizeof(buf
), flags
);
395 if (ret
== -1 && errno
== EAGAIN
&& (flags
& MSG_DONTWAIT
))
398 error(1, errno
, "recv");
403 static void run_one(struct testcase
*test
, int fdt
, int fdr
,
404 struct sockaddr
*addr
, socklen_t alen
)
406 int i
, ret
, val
, mss
;
409 fprintf(stderr
, "ipv%d tx:%d gso:%d %s%s\n",
410 addr
->sa_family
== AF_INET
? 4 : 6,
411 test
->tlen
, test
->gso_len
,
412 test
->v6_ext_hdr
? "ext-hdr " : "",
413 test
->tfail
? "(fail)" : "");
415 if (test
->v6_ext_hdr
) {
416 if (setsockopt(fdt
, IPPROTO_IPV6
, IPV6_HOPOPTS
,
417 ipv6_hopopts_pad1
, sizeof(ipv6_hopopts_pad1
)))
418 error(1, errno
, "setsockopt ipv6 hopopts");
422 if (cfg_do_setsockopt
) {
423 if (setsockopt(fdt
, SOL_UDP
, UDP_SEGMENT
, &val
, sizeof(val
)))
424 error(1, errno
, "setsockopt udp segment");
427 sent
= send_one(fdt
, test
->tlen
, test
->gso_len
, addr
, alen
);
428 if (sent
&& test
->tfail
)
429 error(1, 0, "send succeeded while expecting failure");
430 if (!sent
&& !test
->tfail
)
431 error(1, 0, "send failed while expecting success");
433 if (test
->v6_ext_hdr
) {
434 if (setsockopt(fdt
, IPPROTO_IPV6
, IPV6_HOPOPTS
, NULL
, 0))
435 error(1, errno
, "setsockopt ipv6 hopopts clear");
447 mss
= addr
->sa_family
== AF_INET
? CONST_MSS_V4
: CONST_MSS_V6
;
450 /* Recv all full MSS datagrams */
451 for (i
= 0; i
< test
->r_num_mss
; i
++) {
452 ret
= recv_one(fdr
, 0);
454 error(1, 0, "recv.%d: %d != %d", i
, ret
, mss
);
457 /* Recv the non-full last datagram, if tlen was not a multiple of mss */
458 if (test
->r_len_last
) {
459 ret
= recv_one(fdr
, 0);
460 if (ret
!= test
->r_len_last
)
461 error(1, 0, "recv.%d: %d != %d (last)",
462 i
, ret
, test
->r_len_last
);
465 /* Verify received all data */
466 ret
= recv_one(fdr
, MSG_DONTWAIT
);
468 error(1, 0, "recv: unexpected datagram");
471 static void run_all(int fdt
, int fdr
, struct sockaddr
*addr
, socklen_t alen
)
473 struct testcase
*tests
, *test
;
475 tests
= addr
->sa_family
== AF_INET
? testcases_v4
: testcases_v6
;
477 for (test
= tests
; test
->tlen
; test
++) {
478 /* if a specific test is given, then skip all others */
479 if (cfg_specific_test_id
== -1 ||
480 cfg_specific_test_id
== test
- tests
)
481 run_one(test
, fdt
, fdr
, addr
, alen
);
485 static void run_test(struct sockaddr
*addr
, socklen_t alen
)
487 struct timeval tv
= { .tv_usec
= 100 * 1000 };
490 fdr
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
492 error(1, errno
, "socket r");
495 if (bind(fdr
, addr
, alen
))
496 error(1, errno
, "bind");
499 /* Have tests fail quickly instead of hang */
500 if (setsockopt(fdr
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
)))
501 error(1, errno
, "setsockopt rcv timeout");
503 fdt
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
505 error(1, errno
, "socket t");
507 /* Do not fragment these datagrams: only succeed if GSO works */
508 set_pmtu_discover(fdt
, addr
->sa_family
== AF_INET
);
510 if (cfg_do_connectionless
)
511 run_all(fdt
, fdr
, addr
, alen
);
513 if (cfg_do_connected
) {
514 if (connect(fdt
, addr
, alen
))
515 error(1, errno
, "connect");
517 val
= get_path_mtu(fdt
, addr
->sa_family
== AF_INET
);
518 if (val
!= CONST_MTU_TEST
)
519 error(1, 0, "bad path mtu %u\n", val
);
521 run_all(fdt
, fdr
, addr
, 0 /* use connected addr */);
525 error(1, errno
, "close t");
527 error(1, errno
, "close r");
530 static void run_test_v4(void)
532 struct sockaddr_in addr
= {0};
534 addr
.sin_family
= AF_INET
;
535 addr
.sin_port
= htons(cfg_port
);
536 addr
.sin_addr
= addr4
;
538 run_test((void *)&addr
, sizeof(addr
));
541 static void run_test_v6(void)
543 struct sockaddr_in6 addr
= {0};
545 addr
.sin6_family
= AF_INET6
;
546 addr
.sin6_port
= htons(cfg_port
);
547 addr
.sin6_addr
= addr6
;
549 run_test((void *)&addr
, sizeof(addr
));
552 static void parse_opts(int argc
, char **argv
)
556 while ((c
= getopt(argc
, argv
, "46cCmRst:")) != -1) {
565 cfg_do_connected
= true;
568 cfg_do_connectionless
= true;
571 cfg_do_msgmore
= true;
577 cfg_do_setsockopt
= true;
580 cfg_specific_test_id
= strtoul(optarg
, NULL
, 0);
583 error(1, 0, "%s: parse error", argv
[0]);
588 int main(int argc
, char **argv
)
590 parse_opts(argc
, argv
);
597 fprintf(stderr
, "OK\n");