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 << 6UL)
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_setsockopt
;
57 static int cfg_specific_test_id
= -1;
59 static const char cfg_ifname
[] = "lo";
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 */
72 const struct in6_addr addr6
= IN6ADDR_LOOPBACK_INIT
;
73 const struct in_addr addr4
= { .s_addr
= __constant_htonl(INADDR_LOOPBACK
+ 2) };
75 struct testcase testcases_v4
[] = {
77 /* no GSO: send a single byte */
82 /* no GSO: send a single MSS */
87 /* no GSO: send a single MSS + 1B: fail */
88 .tlen
= CONST_MSS_V4
+ 1,
92 /* send a single MSS: will fall back to no GSO */
94 .gso_len
= CONST_MSS_V4
,
98 /* send a single MSS + 1B */
99 .tlen
= CONST_MSS_V4
+ 1,
100 .gso_len
= CONST_MSS_V4
,
105 /* send exactly 2 MSS */
106 .tlen
= CONST_MSS_V4
* 2,
107 .gso_len
= CONST_MSS_V4
,
111 /* send 2 MSS + 1B */
112 .tlen
= (CONST_MSS_V4
* 2) + 1,
113 .gso_len
= CONST_MSS_V4
,
119 .tlen
= (ETH_MAX_MTU
/ CONST_MSS_V4
) * CONST_MSS_V4
,
120 .gso_len
= CONST_MSS_V4
,
121 .r_num_mss
= (ETH_MAX_MTU
/ CONST_MSS_V4
),
126 .tlen
= ETH_MAX_MTU
- CONST_HDRLEN_V4
,
127 .gso_len
= CONST_MSS_V4
,
128 .r_num_mss
= CONST_MAX_SEGS_V4
,
129 .r_len_last
= ETH_MAX_MTU
- CONST_HDRLEN_V4
-
130 (CONST_MAX_SEGS_V4
* CONST_MSS_V4
),
133 /* send MAX + 1: fail */
134 .tlen
= ETH_MAX_MTU
- CONST_HDRLEN_V4
+ 1,
135 .gso_len
= CONST_MSS_V4
,
139 /* send a single 1B MSS: will fall back to no GSO */
145 /* send 2 1B segments */
151 /* send 2B + 2B + 1B segments */
158 /* send max number of min sized segments */
159 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V4
,
161 .r_num_mss
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V4
,
164 /* send max number + 1 of min sized segments: fail */
165 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V4
+ 1,
175 #define IP6_MAX_MTU (ETH_MAX_MTU + sizeof(struct ip6_hdr))
178 struct testcase testcases_v6
[] = {
180 /* no GSO: send a single byte */
185 /* no GSO: send a single MSS */
186 .tlen
= CONST_MSS_V6
,
190 /* no GSO: send a single MSS + 1B: fail */
191 .tlen
= CONST_MSS_V6
+ 1,
195 /* send a single MSS: will fall back to no GSO */
196 .tlen
= CONST_MSS_V6
,
197 .gso_len
= CONST_MSS_V6
,
201 /* send a single MSS + 1B */
202 .tlen
= CONST_MSS_V6
+ 1,
203 .gso_len
= CONST_MSS_V6
,
208 /* send exactly 2 MSS */
209 .tlen
= CONST_MSS_V6
* 2,
210 .gso_len
= CONST_MSS_V6
,
214 /* send 2 MSS + 1B */
215 .tlen
= (CONST_MSS_V6
* 2) + 1,
216 .gso_len
= CONST_MSS_V6
,
222 .tlen
= (IP6_MAX_MTU
/ CONST_MSS_V6
) * CONST_MSS_V6
,
223 .gso_len
= CONST_MSS_V6
,
224 .r_num_mss
= (IP6_MAX_MTU
/ CONST_MSS_V6
),
229 .tlen
= IP6_MAX_MTU
- CONST_HDRLEN_V6
,
230 .gso_len
= CONST_MSS_V6
,
231 .r_num_mss
= CONST_MAX_SEGS_V6
,
232 .r_len_last
= IP6_MAX_MTU
- CONST_HDRLEN_V6
-
233 (CONST_MAX_SEGS_V6
* CONST_MSS_V6
),
236 /* send MAX + 1: fail */
237 .tlen
= IP6_MAX_MTU
- CONST_HDRLEN_V6
+ 1,
238 .gso_len
= CONST_MSS_V6
,
242 /* send a single 1B MSS: will fall back to no GSO */
248 /* send 2 1B segments */
254 /* send 2B + 2B + 1B segments */
261 /* send max number of min sized segments */
262 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V6
,
264 .r_num_mss
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V6
,
267 /* send max number + 1 of min sized segments: fail */
268 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V6
+ 1,
277 static unsigned int get_device_mtu(int fd
, const char *ifname
)
281 memset(&ifr
, 0, sizeof(ifr
));
283 strcpy(ifr
.ifr_name
, ifname
);
285 if (ioctl(fd
, SIOCGIFMTU
, &ifr
))
286 error(1, errno
, "ioctl get mtu");
291 static void __set_device_mtu(int fd
, const char *ifname
, unsigned int mtu
)
295 memset(&ifr
, 0, sizeof(ifr
));
298 strcpy(ifr
.ifr_name
, ifname
);
300 if (ioctl(fd
, SIOCSIFMTU
, &ifr
))
301 error(1, errno
, "ioctl set mtu");
304 static void set_device_mtu(int fd
, int mtu
)
308 val
= get_device_mtu(fd
, cfg_ifname
);
309 fprintf(stderr
, "device mtu (orig): %u\n", val
);
311 __set_device_mtu(fd
, cfg_ifname
, mtu
);
312 val
= get_device_mtu(fd
, cfg_ifname
);
314 error(1, 0, "unable to set device mtu to %u\n", val
);
316 fprintf(stderr
, "device mtu (test): %u\n", val
);
319 static void set_pmtu_discover(int fd
, bool is_ipv4
)
321 int level
, name
, val
;
325 name
= IP_MTU_DISCOVER
;
326 val
= IP_PMTUDISC_DO
;
329 name
= IPV6_MTU_DISCOVER
;
330 val
= IPV6_PMTUDISC_DO
;
333 if (setsockopt(fd
, level
, name
, &val
, sizeof(val
)))
334 error(1, errno
, "setsockopt path mtu");
337 static unsigned int get_path_mtu(int fd
, bool is_ipv4
)
343 vallen
= sizeof(mtu
);
345 ret
= getsockopt(fd
, SOL_IP
, IP_MTU
, &mtu
, &vallen
);
347 ret
= getsockopt(fd
, SOL_IPV6
, IPV6_MTU
, &mtu
, &vallen
);
350 error(1, errno
, "getsockopt mtu");
353 fprintf(stderr
, "path mtu (read): %u\n", mtu
);
357 /* very wordy version of system("ip route add dev lo mtu 1500 127.0.0.3/32") */
358 static void set_route_mtu(int mtu
, bool is_ipv4
)
360 struct sockaddr_nl nladdr
= { .nl_family
= AF_NETLINK
};
364 char data
[NLMSG_ALIGN(sizeof(*nh
)) +
365 NLMSG_ALIGN(sizeof(*rt
)) +
366 NLMSG_ALIGN(RTA_LENGTH(sizeof(addr6
))) +
367 NLMSG_ALIGN(RTA_LENGTH(sizeof(int))) +
368 NLMSG_ALIGN(RTA_LENGTH(0) + RTA_LENGTH(sizeof(int)))];
369 int fd
, ret
, alen
, off
= 0;
371 alen
= is_ipv4
? sizeof(addr4
) : sizeof(addr6
);
373 fd
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
375 error(1, errno
, "socket netlink");
377 memset(data
, 0, sizeof(data
));
380 nh
->nlmsg_type
= RTM_NEWROUTE
;
381 nh
->nlmsg_flags
= NLM_F_REQUEST
| NLM_F_CREATE
;
382 off
+= NLMSG_ALIGN(sizeof(*nh
));
384 rt
= (void *)(data
+ off
);
385 rt
->rtm_family
= is_ipv4
? AF_INET
: AF_INET6
;
386 rt
->rtm_table
= RT_TABLE_MAIN
;
387 rt
->rtm_dst_len
= alen
<< 3;
388 rt
->rtm_protocol
= RTPROT_BOOT
;
389 rt
->rtm_scope
= RT_SCOPE_UNIVERSE
;
390 rt
->rtm_type
= RTN_UNICAST
;
391 off
+= NLMSG_ALIGN(sizeof(*rt
));
393 rta
= (void *)(data
+ off
);
394 rta
->rta_type
= RTA_DST
;
395 rta
->rta_len
= RTA_LENGTH(alen
);
397 memcpy(RTA_DATA(rta
), &addr4
, alen
);
399 memcpy(RTA_DATA(rta
), &addr6
, alen
);
400 off
+= NLMSG_ALIGN(rta
->rta_len
);
402 rta
= (void *)(data
+ off
);
403 rta
->rta_type
= RTA_OIF
;
404 rta
->rta_len
= RTA_LENGTH(sizeof(int));
405 *((int *)(RTA_DATA(rta
))) = 1; //if_nametoindex("lo");
406 off
+= NLMSG_ALIGN(rta
->rta_len
);
408 /* MTU is a subtype in a metrics type */
409 rta
= (void *)(data
+ off
);
410 rta
->rta_type
= RTA_METRICS
;
411 rta
->rta_len
= RTA_LENGTH(0) + RTA_LENGTH(sizeof(int));
412 off
+= NLMSG_ALIGN(rta
->rta_len
);
414 /* now fill MTU subtype. Note that it fits within above rta_len */
415 rta
= (void *)(((char *) rta
) + RTA_LENGTH(0));
416 rta
->rta_type
= RTAX_MTU
;
417 rta
->rta_len
= RTA_LENGTH(sizeof(int));
418 *((int *)(RTA_DATA(rta
))) = mtu
;
422 ret
= sendto(fd
, data
, off
, 0, (void *)&nladdr
, sizeof(nladdr
));
424 error(1, errno
, "send netlink: %uB != %uB\n", ret
, off
);
427 error(1, errno
, "close netlink");
429 fprintf(stderr
, "route mtu (test): %u\n", mtu
);
432 static bool __send_one(int fd
, struct msghdr
*msg
, int flags
)
436 ret
= sendmsg(fd
, msg
, flags
);
438 (errno
== EMSGSIZE
|| errno
== ENOMEM
|| errno
== EINVAL
))
441 error(1, errno
, "sendmsg");
442 if (ret
!= msg
->msg_iov
->iov_len
)
443 error(1, 0, "sendto: %d != %llu", ret
,
444 (unsigned long long)msg
->msg_iov
->iov_len
);
446 error(1, 0, "sendmsg: return flags 0x%x\n", msg
->msg_flags
);
451 static bool send_one(int fd
, int len
, int gso_len
,
452 struct sockaddr
*addr
, socklen_t alen
)
454 char control
[CMSG_SPACE(sizeof(uint16_t))] = {0};
455 struct msghdr msg
= {0};
456 struct iovec iov
= {0};
466 msg
.msg_namelen
= alen
;
468 if (gso_len
&& !cfg_do_setsockopt
) {
469 msg
.msg_control
= control
;
470 msg
.msg_controllen
= sizeof(control
);
472 cm
= CMSG_FIRSTHDR(&msg
);
473 cm
->cmsg_level
= SOL_UDP
;
474 cm
->cmsg_type
= UDP_SEGMENT
;
475 cm
->cmsg_len
= CMSG_LEN(sizeof(uint16_t));
476 *((uint16_t *) CMSG_DATA(cm
)) = gso_len
;
479 /* If MSG_MORE, send 1 byte followed by remainder */
480 if (cfg_do_msgmore
&& len
> 1) {
482 if (!__send_one(fd
, &msg
, MSG_MORE
))
483 error(1, 0, "send 1B failed");
486 iov
.iov_len
= len
- 1;
489 return __send_one(fd
, &msg
, 0);
492 static int recv_one(int fd
, int flags
)
496 ret
= recv(fd
, buf
, sizeof(buf
), flags
);
497 if (ret
== -1 && errno
== EAGAIN
&& (flags
& MSG_DONTWAIT
))
500 error(1, errno
, "recv");
505 static void run_one(struct testcase
*test
, int fdt
, int fdr
,
506 struct sockaddr
*addr
, socklen_t alen
)
508 int i
, ret
, val
, mss
;
511 fprintf(stderr
, "ipv%d tx:%d gso:%d %s\n",
512 addr
->sa_family
== AF_INET
? 4 : 6,
513 test
->tlen
, test
->gso_len
,
514 test
->tfail
? "(fail)" : "");
517 if (cfg_do_setsockopt
) {
518 if (setsockopt(fdt
, SOL_UDP
, UDP_SEGMENT
, &val
, sizeof(val
)))
519 error(1, errno
, "setsockopt udp segment");
522 sent
= send_one(fdt
, test
->tlen
, test
->gso_len
, addr
, alen
);
523 if (sent
&& test
->tfail
)
524 error(1, 0, "send succeeded while expecting failure");
525 if (!sent
&& !test
->tfail
)
526 error(1, 0, "send failed while expecting success");
533 mss
= addr
->sa_family
== AF_INET
? CONST_MSS_V4
: CONST_MSS_V6
;
536 /* Recv all full MSS datagrams */
537 for (i
= 0; i
< test
->r_num_mss
; i
++) {
538 ret
= recv_one(fdr
, 0);
540 error(1, 0, "recv.%d: %d != %d", i
, ret
, mss
);
543 /* Recv the non-full last datagram, if tlen was not a multiple of mss */
544 if (test
->r_len_last
) {
545 ret
= recv_one(fdr
, 0);
546 if (ret
!= test
->r_len_last
)
547 error(1, 0, "recv.%d: %d != %d (last)",
548 i
, ret
, test
->r_len_last
);
551 /* Verify received all data */
552 ret
= recv_one(fdr
, MSG_DONTWAIT
);
554 error(1, 0, "recv: unexpected datagram");
557 static void run_all(int fdt
, int fdr
, struct sockaddr
*addr
, socklen_t alen
)
559 struct testcase
*tests
, *test
;
561 tests
= addr
->sa_family
== AF_INET
? testcases_v4
: testcases_v6
;
563 for (test
= tests
; test
->tlen
; test
++) {
564 /* if a specific test is given, then skip all others */
565 if (cfg_specific_test_id
== -1 ||
566 cfg_specific_test_id
== test
- tests
)
567 run_one(test
, fdt
, fdr
, addr
, alen
);
571 static void run_test(struct sockaddr
*addr
, socklen_t alen
)
573 struct timeval tv
= { .tv_usec
= 100 * 1000 };
576 fdr
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
578 error(1, errno
, "socket r");
580 if (bind(fdr
, addr
, alen
))
581 error(1, errno
, "bind");
583 /* Have tests fail quickly instead of hang */
584 if (setsockopt(fdr
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
)))
585 error(1, errno
, "setsockopt rcv timeout");
587 fdt
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
589 error(1, errno
, "socket t");
591 /* Do not fragment these datagrams: only succeed if GSO works */
592 set_pmtu_discover(fdt
, addr
->sa_family
== AF_INET
);
594 if (cfg_do_connectionless
) {
595 set_device_mtu(fdt
, CONST_MTU_TEST
);
596 run_all(fdt
, fdr
, addr
, alen
);
599 if (cfg_do_connected
) {
600 set_device_mtu(fdt
, CONST_MTU_TEST
+ 100);
601 set_route_mtu(CONST_MTU_TEST
, addr
->sa_family
== AF_INET
);
603 if (connect(fdt
, addr
, alen
))
604 error(1, errno
, "connect");
606 val
= get_path_mtu(fdt
, addr
->sa_family
== AF_INET
);
607 if (val
!= CONST_MTU_TEST
)
608 error(1, 0, "bad path mtu %u\n", val
);
610 run_all(fdt
, fdr
, addr
, 0 /* use connected addr */);
614 error(1, errno
, "close t");
616 error(1, errno
, "close r");
619 static void run_test_v4(void)
621 struct sockaddr_in addr
= {0};
623 addr
.sin_family
= AF_INET
;
624 addr
.sin_port
= htons(cfg_port
);
625 addr
.sin_addr
= addr4
;
627 run_test((void *)&addr
, sizeof(addr
));
630 static void run_test_v6(void)
632 struct sockaddr_in6 addr
= {0};
634 addr
.sin6_family
= AF_INET6
;
635 addr
.sin6_port
= htons(cfg_port
);
636 addr
.sin6_addr
= addr6
;
638 run_test((void *)&addr
, sizeof(addr
));
641 static void parse_opts(int argc
, char **argv
)
645 while ((c
= getopt(argc
, argv
, "46cCmst:")) != -1) {
654 cfg_do_connected
= true;
657 cfg_do_connectionless
= true;
660 cfg_do_msgmore
= true;
663 cfg_do_setsockopt
= true;
666 cfg_specific_test_id
= strtoul(optarg
, NULL
, 0);
669 error(1, 0, "%s: parse error", argv
[0]);
674 int main(int argc
, char **argv
)
676 parse_opts(argc
, argv
);
683 fprintf(stderr
, "OK\n");