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>
22 #include <sys/ioctl.h>
23 #include <sys/socket.h>
26 #include <sys/types.h>
30 #define ETH_MAX_MTU 0xFFFFU
34 #define UDP_SEGMENT 103
37 #ifndef UDP_MAX_SEGMENTS
38 #define UDP_MAX_SEGMENTS (1 << 6UL)
41 #define CONST_MTU_TEST 1500
43 #define CONST_HDRLEN_V4 (sizeof(struct iphdr) + sizeof(struct udphdr))
44 #define CONST_HDRLEN_V6 (sizeof(struct ip6_hdr) + sizeof(struct udphdr))
46 #define CONST_MSS_V4 (CONST_MTU_TEST - CONST_HDRLEN_V4)
47 #define CONST_MSS_V6 (CONST_MTU_TEST - CONST_HDRLEN_V6)
49 #define CONST_MAX_SEGS_V4 (ETH_MAX_MTU / CONST_MSS_V4)
50 #define CONST_MAX_SEGS_V6 (ETH_MAX_MTU / CONST_MSS_V6)
52 static bool cfg_do_ipv4
;
53 static bool cfg_do_ipv6
;
54 static bool cfg_do_connected
;
55 static bool cfg_do_connectionless
;
56 static bool cfg_do_msgmore
;
57 static bool cfg_do_setsockopt
;
58 static int cfg_specific_test_id
= -1;
60 static const char cfg_ifname
[] = "lo";
61 static unsigned short cfg_port
= 9000;
63 static char buf
[ETH_MAX_MTU
];
66 int tlen
; /* send() buffer size, may exceed mss */
67 bool tfail
; /* send() call is expected to fail */
68 int gso_len
; /* mss after applying gso */
69 int r_num_mss
; /* recv(): number of calls of full mss */
70 int r_len_last
; /* recv(): size of last non-mss dgram, if any */
73 const struct in6_addr addr6
= IN6ADDR_LOOPBACK_INIT
;
74 const struct in_addr addr4
= { .s_addr
= __constant_htonl(INADDR_LOOPBACK
+ 2) };
76 struct testcase testcases_v4
[] = {
78 /* no GSO: send a single byte */
83 /* no GSO: send a single MSS */
88 /* no GSO: send a single MSS + 1B: fail */
89 .tlen
= CONST_MSS_V4
+ 1,
93 /* send a single MSS: will fail with GSO, because the segment
94 * logic in udp4_ufo_fragment demands a gso skb to be > MTU
97 .gso_len
= CONST_MSS_V4
,
102 /* send a single MSS + 1B */
103 .tlen
= CONST_MSS_V4
+ 1,
104 .gso_len
= CONST_MSS_V4
,
109 /* send exactly 2 MSS */
110 .tlen
= CONST_MSS_V4
* 2,
111 .gso_len
= CONST_MSS_V4
,
115 /* send 2 MSS + 1B */
116 .tlen
= (CONST_MSS_V4
* 2) + 1,
117 .gso_len
= CONST_MSS_V4
,
123 .tlen
= (ETH_MAX_MTU
/ CONST_MSS_V4
) * CONST_MSS_V4
,
124 .gso_len
= CONST_MSS_V4
,
125 .r_num_mss
= (ETH_MAX_MTU
/ CONST_MSS_V4
),
130 .tlen
= ETH_MAX_MTU
- CONST_HDRLEN_V4
,
131 .gso_len
= CONST_MSS_V4
,
132 .r_num_mss
= CONST_MAX_SEGS_V4
,
133 .r_len_last
= ETH_MAX_MTU
- CONST_HDRLEN_V4
-
134 (CONST_MAX_SEGS_V4
* CONST_MSS_V4
),
137 /* send MAX + 1: fail */
138 .tlen
= ETH_MAX_MTU
- CONST_HDRLEN_V4
+ 1,
139 .gso_len
= CONST_MSS_V4
,
143 /* send a single 1B MSS: will fail, see single MSS above */
150 /* send 2 1B segments */
156 /* send 2B + 2B + 1B segments */
163 /* send max number of min sized segments */
164 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V4
,
166 .r_num_mss
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V4
,
169 /* send max number + 1 of min sized segments: fail */
170 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V4
+ 1,
180 #define IP6_MAX_MTU (ETH_MAX_MTU + sizeof(struct ip6_hdr))
183 struct testcase testcases_v6
[] = {
185 /* no GSO: send a single byte */
190 /* no GSO: send a single MSS */
191 .tlen
= CONST_MSS_V6
,
195 /* no GSO: send a single MSS + 1B: fail */
196 .tlen
= CONST_MSS_V6
+ 1,
200 /* send a single MSS: will fail with GSO, because the segment
201 * logic in udp4_ufo_fragment demands a gso skb to be > MTU
203 .tlen
= CONST_MSS_V6
,
204 .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 fail, see single MSS above */
257 /* send 2 1B segments */
263 /* send 2B + 2B + 1B segments */
270 /* send max number of min sized segments */
271 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V6
,
273 .r_num_mss
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V6
,
276 /* send max number + 1 of min sized segments: fail */
277 .tlen
= UDP_MAX_SEGMENTS
- CONST_HDRLEN_V6
+ 1,
286 static unsigned int get_device_mtu(int fd
, const char *ifname
)
290 memset(&ifr
, 0, sizeof(ifr
));
292 strcpy(ifr
.ifr_name
, ifname
);
294 if (ioctl(fd
, SIOCGIFMTU
, &ifr
))
295 error(1, errno
, "ioctl get mtu");
300 static void __set_device_mtu(int fd
, const char *ifname
, unsigned int mtu
)
304 memset(&ifr
, 0, sizeof(ifr
));
307 strcpy(ifr
.ifr_name
, ifname
);
309 if (ioctl(fd
, SIOCSIFMTU
, &ifr
))
310 error(1, errno
, "ioctl set mtu");
313 static void set_device_mtu(int fd
, int mtu
)
317 val
= get_device_mtu(fd
, cfg_ifname
);
318 fprintf(stderr
, "device mtu (orig): %u\n", val
);
320 __set_device_mtu(fd
, cfg_ifname
, mtu
);
321 val
= get_device_mtu(fd
, cfg_ifname
);
323 error(1, 0, "unable to set device mtu to %u\n", val
);
325 fprintf(stderr
, "device mtu (test): %u\n", val
);
328 static void set_pmtu_discover(int fd
, bool is_ipv4
)
330 int level
, name
, val
;
334 name
= IP_MTU_DISCOVER
;
335 val
= IP_PMTUDISC_DO
;
338 name
= IPV6_MTU_DISCOVER
;
339 val
= IPV6_PMTUDISC_DO
;
342 if (setsockopt(fd
, level
, name
, &val
, sizeof(val
)))
343 error(1, errno
, "setsockopt path mtu");
346 static unsigned int get_path_mtu(int fd
, bool is_ipv4
)
352 vallen
= sizeof(mtu
);
354 ret
= getsockopt(fd
, SOL_IP
, IP_MTU
, &mtu
, &vallen
);
356 ret
= getsockopt(fd
, SOL_IPV6
, IPV6_MTU
, &mtu
, &vallen
);
359 error(1, errno
, "getsockopt mtu");
362 fprintf(stderr
, "path mtu (read): %u\n", mtu
);
366 /* very wordy version of system("ip route add dev lo mtu 1500 127.0.0.3/32") */
367 static void set_route_mtu(int mtu
, bool is_ipv4
)
369 struct sockaddr_nl nladdr
= { .nl_family
= AF_NETLINK
};
373 char data
[NLMSG_ALIGN(sizeof(*nh
)) +
374 NLMSG_ALIGN(sizeof(*rt
)) +
375 NLMSG_ALIGN(RTA_LENGTH(sizeof(addr6
))) +
376 NLMSG_ALIGN(RTA_LENGTH(sizeof(int))) +
377 NLMSG_ALIGN(RTA_LENGTH(0) + RTA_LENGTH(sizeof(int)))];
378 int fd
, ret
, alen
, off
= 0;
380 alen
= is_ipv4
? sizeof(addr4
) : sizeof(addr6
);
382 fd
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
384 error(1, errno
, "socket netlink");
386 memset(data
, 0, sizeof(data
));
389 nh
->nlmsg_type
= RTM_NEWROUTE
;
390 nh
->nlmsg_flags
= NLM_F_REQUEST
| NLM_F_CREATE
;
391 off
+= NLMSG_ALIGN(sizeof(*nh
));
393 rt
= (void *)(data
+ off
);
394 rt
->rtm_family
= is_ipv4
? AF_INET
: AF_INET6
;
395 rt
->rtm_table
= RT_TABLE_MAIN
;
396 rt
->rtm_dst_len
= alen
<< 3;
397 rt
->rtm_protocol
= RTPROT_BOOT
;
398 rt
->rtm_scope
= RT_SCOPE_UNIVERSE
;
399 rt
->rtm_type
= RTN_UNICAST
;
400 off
+= NLMSG_ALIGN(sizeof(*rt
));
402 rta
= (void *)(data
+ off
);
403 rta
->rta_type
= RTA_DST
;
404 rta
->rta_len
= RTA_LENGTH(alen
);
406 memcpy(RTA_DATA(rta
), &addr4
, alen
);
408 memcpy(RTA_DATA(rta
), &addr6
, alen
);
409 off
+= NLMSG_ALIGN(rta
->rta_len
);
411 rta
= (void *)(data
+ off
);
412 rta
->rta_type
= RTA_OIF
;
413 rta
->rta_len
= RTA_LENGTH(sizeof(int));
414 *((int *)(RTA_DATA(rta
))) = 1; //if_nametoindex("lo");
415 off
+= NLMSG_ALIGN(rta
->rta_len
);
417 /* MTU is a subtype in a metrics type */
418 rta
= (void *)(data
+ off
);
419 rta
->rta_type
= RTA_METRICS
;
420 rta
->rta_len
= RTA_LENGTH(0) + RTA_LENGTH(sizeof(int));
421 off
+= NLMSG_ALIGN(rta
->rta_len
);
423 /* now fill MTU subtype. Note that it fits within above rta_len */
424 rta
= (void *)(((char *) rta
) + RTA_LENGTH(0));
425 rta
->rta_type
= RTAX_MTU
;
426 rta
->rta_len
= RTA_LENGTH(sizeof(int));
427 *((int *)(RTA_DATA(rta
))) = mtu
;
431 ret
= sendto(fd
, data
, off
, 0, (void *)&nladdr
, sizeof(nladdr
));
433 error(1, errno
, "send netlink: %uB != %uB\n", ret
, off
);
436 error(1, errno
, "close netlink");
438 fprintf(stderr
, "route mtu (test): %u\n", mtu
);
441 static bool __send_one(int fd
, struct msghdr
*msg
, int flags
)
445 ret
= sendmsg(fd
, msg
, flags
);
447 (errno
== EMSGSIZE
|| errno
== ENOMEM
|| errno
== EINVAL
))
450 error(1, errno
, "sendmsg");
451 if (ret
!= msg
->msg_iov
->iov_len
)
452 error(1, 0, "sendto: %d != %lu", ret
, msg
->msg_iov
->iov_len
);
454 error(1, 0, "sendmsg: return flags 0x%x\n", msg
->msg_flags
);
459 static bool send_one(int fd
, int len
, int gso_len
,
460 struct sockaddr
*addr
, socklen_t alen
)
462 char control
[CMSG_SPACE(sizeof(uint16_t))] = {0};
463 struct msghdr msg
= {0};
464 struct iovec iov
= {0};
474 msg
.msg_namelen
= alen
;
476 if (gso_len
&& !cfg_do_setsockopt
) {
477 msg
.msg_control
= control
;
478 msg
.msg_controllen
= sizeof(control
);
480 cm
= CMSG_FIRSTHDR(&msg
);
481 cm
->cmsg_level
= SOL_UDP
;
482 cm
->cmsg_type
= UDP_SEGMENT
;
483 cm
->cmsg_len
= CMSG_LEN(sizeof(uint16_t));
484 *((uint16_t *) CMSG_DATA(cm
)) = gso_len
;
487 /* If MSG_MORE, send 1 byte followed by remainder */
488 if (cfg_do_msgmore
&& len
> 1) {
490 if (!__send_one(fd
, &msg
, MSG_MORE
))
491 error(1, 0, "send 1B failed");
494 iov
.iov_len
= len
- 1;
497 return __send_one(fd
, &msg
, 0);
500 static int recv_one(int fd
, int flags
)
504 ret
= recv(fd
, buf
, sizeof(buf
), flags
);
505 if (ret
== -1 && errno
== EAGAIN
&& (flags
& MSG_DONTWAIT
))
508 error(1, errno
, "recv");
513 static void run_one(struct testcase
*test
, int fdt
, int fdr
,
514 struct sockaddr
*addr
, socklen_t alen
)
516 int i
, ret
, val
, mss
;
519 fprintf(stderr
, "ipv%d tx:%d gso:%d %s\n",
520 addr
->sa_family
== AF_INET
? 4 : 6,
521 test
->tlen
, test
->gso_len
,
522 test
->tfail
? "(fail)" : "");
525 if (cfg_do_setsockopt
) {
526 if (setsockopt(fdt
, SOL_UDP
, UDP_SEGMENT
, &val
, sizeof(val
)))
527 error(1, errno
, "setsockopt udp segment");
530 sent
= send_one(fdt
, test
->tlen
, test
->gso_len
, addr
, alen
);
531 if (sent
&& test
->tfail
)
532 error(1, 0, "send succeeded while expecting failure");
533 if (!sent
&& !test
->tfail
)
534 error(1, 0, "send failed while expecting success");
541 mss
= addr
->sa_family
== AF_INET
? CONST_MSS_V4
: CONST_MSS_V6
;
544 /* Recv all full MSS datagrams */
545 for (i
= 0; i
< test
->r_num_mss
; i
++) {
546 ret
= recv_one(fdr
, 0);
548 error(1, 0, "recv.%d: %d != %d", i
, ret
, mss
);
551 /* Recv the non-full last datagram, if tlen was not a multiple of mss */
552 if (test
->r_len_last
) {
553 ret
= recv_one(fdr
, 0);
554 if (ret
!= test
->r_len_last
)
555 error(1, 0, "recv.%d: %d != %d (last)",
556 i
, ret
, test
->r_len_last
);
559 /* Verify received all data */
560 ret
= recv_one(fdr
, MSG_DONTWAIT
);
562 error(1, 0, "recv: unexpected datagram");
565 static void run_all(int fdt
, int fdr
, struct sockaddr
*addr
, socklen_t alen
)
567 struct testcase
*tests
, *test
;
569 tests
= addr
->sa_family
== AF_INET
? testcases_v4
: testcases_v6
;
571 for (test
= tests
; test
->tlen
; test
++) {
572 /* if a specific test is given, then skip all others */
573 if (cfg_specific_test_id
== -1 ||
574 cfg_specific_test_id
== test
- tests
)
575 run_one(test
, fdt
, fdr
, addr
, alen
);
579 static void run_test(struct sockaddr
*addr
, socklen_t alen
)
581 struct timeval tv
= { .tv_usec
= 100 * 1000 };
584 fdr
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
586 error(1, errno
, "socket r");
588 if (bind(fdr
, addr
, alen
))
589 error(1, errno
, "bind");
591 /* Have tests fail quickly instead of hang */
592 if (setsockopt(fdr
, SOL_SOCKET
, SO_RCVTIMEO
, &tv
, sizeof(tv
)))
593 error(1, errno
, "setsockopt rcv timeout");
595 fdt
= socket(addr
->sa_family
, SOCK_DGRAM
, 0);
597 error(1, errno
, "socket t");
599 /* Do not fragment these datagrams: only succeed if GSO works */
600 set_pmtu_discover(fdt
, addr
->sa_family
== AF_INET
);
602 if (cfg_do_connectionless
) {
603 set_device_mtu(fdt
, CONST_MTU_TEST
);
604 run_all(fdt
, fdr
, addr
, alen
);
607 if (cfg_do_connected
) {
608 set_device_mtu(fdt
, CONST_MTU_TEST
+ 100);
609 set_route_mtu(CONST_MTU_TEST
, addr
->sa_family
== AF_INET
);
611 if (connect(fdt
, addr
, alen
))
612 error(1, errno
, "connect");
614 val
= get_path_mtu(fdt
, addr
->sa_family
== AF_INET
);
615 if (val
!= CONST_MTU_TEST
)
616 error(1, 0, "bad path mtu %u\n", val
);
618 run_all(fdt
, fdr
, addr
, 0 /* use connected addr */);
622 error(1, errno
, "close t");
624 error(1, errno
, "close r");
627 static void run_test_v4(void)
629 struct sockaddr_in addr
= {0};
631 addr
.sin_family
= AF_INET
;
632 addr
.sin_port
= htons(cfg_port
);
633 addr
.sin_addr
= addr4
;
635 run_test((void *)&addr
, sizeof(addr
));
638 static void run_test_v6(void)
640 struct sockaddr_in6 addr
= {0};
642 addr
.sin6_family
= AF_INET6
;
643 addr
.sin6_port
= htons(cfg_port
);
644 addr
.sin6_addr
= addr6
;
646 run_test((void *)&addr
, sizeof(addr
));
649 static void parse_opts(int argc
, char **argv
)
653 while ((c
= getopt(argc
, argv
, "46cCmst:")) != -1) {
662 cfg_do_connected
= true;
665 cfg_do_connectionless
= true;
668 cfg_do_msgmore
= true;
671 cfg_do_setsockopt
= true;
674 cfg_specific_test_id
= strtoul(optarg
, NULL
, 0);
677 error(1, 0, "%s: parse error", argv
[0]);
682 int main(int argc
, char **argv
)
684 parse_opts(argc
, argv
);
691 fprintf(stderr
, "OK\n");