0001
0002
0003 #define _GNU_SOURCE
0004
0005 #include <stddef.h>
0006 #include <arpa/inet.h>
0007 #include <error.h>
0008 #include <errno.h>
0009 #include <net/if.h>
0010 #include <linux/in.h>
0011 #include <linux/netlink.h>
0012 #include <linux/rtnetlink.h>
0013 #include <netinet/if_ether.h>
0014 #include <netinet/ip.h>
0015 #include <netinet/ip6.h>
0016 #include <netinet/udp.h>
0017 #include <stdbool.h>
0018 #include <stdlib.h>
0019 #include <stdio.h>
0020 #include <string.h>
0021 #include <sys/ioctl.h>
0022 #include <sys/socket.h>
0023 #include <sys/stat.h>
0024 #include <sys/time.h>
0025 #include <sys/types.h>
0026 #include <unistd.h>
0027
0028 #ifndef ETH_MAX_MTU
0029 #define ETH_MAX_MTU 0xFFFFU
0030 #endif
0031
0032 #ifndef UDP_SEGMENT
0033 #define UDP_SEGMENT 103
0034 #endif
0035
0036 #ifndef UDP_MAX_SEGMENTS
0037 #define UDP_MAX_SEGMENTS (1 << 6UL)
0038 #endif
0039
0040 #define CONST_MTU_TEST 1500
0041
0042 #define CONST_HDRLEN_V4 (sizeof(struct iphdr) + sizeof(struct udphdr))
0043 #define CONST_HDRLEN_V6 (sizeof(struct ip6_hdr) + sizeof(struct udphdr))
0044
0045 #define CONST_MSS_V4 (CONST_MTU_TEST - CONST_HDRLEN_V4)
0046 #define CONST_MSS_V6 (CONST_MTU_TEST - CONST_HDRLEN_V6)
0047
0048 #define CONST_MAX_SEGS_V4 (ETH_MAX_MTU / CONST_MSS_V4)
0049 #define CONST_MAX_SEGS_V6 (ETH_MAX_MTU / CONST_MSS_V6)
0050
0051 static bool cfg_do_ipv4;
0052 static bool cfg_do_ipv6;
0053 static bool cfg_do_connected;
0054 static bool cfg_do_connectionless;
0055 static bool cfg_do_msgmore;
0056 static bool cfg_do_setsockopt;
0057 static int cfg_specific_test_id = -1;
0058
0059 static const char cfg_ifname[] = "lo";
0060 static unsigned short cfg_port = 9000;
0061
0062 static char buf[ETH_MAX_MTU];
0063
0064 struct testcase {
0065 int tlen;
0066 bool tfail;
0067 int gso_len;
0068 int r_num_mss;
0069 int r_len_last;
0070 };
0071
0072 const struct in6_addr addr6 = IN6ADDR_LOOPBACK_INIT;
0073 const struct in_addr addr4 = { .s_addr = __constant_htonl(INADDR_LOOPBACK + 2) };
0074
0075 struct testcase testcases_v4[] = {
0076 {
0077
0078 .tlen = 1,
0079 .r_len_last = 1,
0080 },
0081 {
0082
0083 .tlen = CONST_MSS_V4,
0084 .r_num_mss = 1,
0085 },
0086 {
0087
0088 .tlen = CONST_MSS_V4 + 1,
0089 .tfail = true,
0090 },
0091 {
0092
0093 .tlen = CONST_MSS_V4,
0094 .gso_len = CONST_MSS_V4,
0095 .r_num_mss = 1,
0096 },
0097 {
0098
0099 .tlen = CONST_MSS_V4 + 1,
0100 .gso_len = CONST_MSS_V4,
0101 .r_num_mss = 1,
0102 .r_len_last = 1,
0103 },
0104 {
0105
0106 .tlen = CONST_MSS_V4 * 2,
0107 .gso_len = CONST_MSS_V4,
0108 .r_num_mss = 2,
0109 },
0110 {
0111
0112 .tlen = (CONST_MSS_V4 * 2) + 1,
0113 .gso_len = CONST_MSS_V4,
0114 .r_num_mss = 2,
0115 .r_len_last = 1,
0116 },
0117 {
0118
0119 .tlen = (ETH_MAX_MTU / CONST_MSS_V4) * CONST_MSS_V4,
0120 .gso_len = CONST_MSS_V4,
0121 .r_num_mss = (ETH_MAX_MTU / CONST_MSS_V4),
0122 },
0123
0124 {
0125
0126 .tlen = ETH_MAX_MTU - CONST_HDRLEN_V4,
0127 .gso_len = CONST_MSS_V4,
0128 .r_num_mss = CONST_MAX_SEGS_V4,
0129 .r_len_last = ETH_MAX_MTU - CONST_HDRLEN_V4 -
0130 (CONST_MAX_SEGS_V4 * CONST_MSS_V4),
0131 },
0132 {
0133
0134 .tlen = ETH_MAX_MTU - CONST_HDRLEN_V4 + 1,
0135 .gso_len = CONST_MSS_V4,
0136 .tfail = true,
0137 },
0138 {
0139
0140 .tlen = 1,
0141 .gso_len = 1,
0142 .r_num_mss = 1,
0143 },
0144 {
0145
0146 .tlen = 2,
0147 .gso_len = 1,
0148 .r_num_mss = 2,
0149 },
0150 {
0151
0152 .tlen = 5,
0153 .gso_len = 2,
0154 .r_num_mss = 2,
0155 .r_len_last = 1,
0156 },
0157 {
0158
0159 .tlen = UDP_MAX_SEGMENTS,
0160 .gso_len = 1,
0161 .r_num_mss = UDP_MAX_SEGMENTS,
0162 },
0163 {
0164
0165 .tlen = UDP_MAX_SEGMENTS + 1,
0166 .gso_len = 1,
0167 .tfail = true,
0168 },
0169 {
0170
0171 }
0172 };
0173
0174 #ifndef IP6_MAX_MTU
0175 #define IP6_MAX_MTU (ETH_MAX_MTU + sizeof(struct ip6_hdr))
0176 #endif
0177
0178 struct testcase testcases_v6[] = {
0179 {
0180
0181 .tlen = 1,
0182 .r_len_last = 1,
0183 },
0184 {
0185
0186 .tlen = CONST_MSS_V6,
0187 .r_num_mss = 1,
0188 },
0189 {
0190
0191 .tlen = CONST_MSS_V6 + 1,
0192 .tfail = true,
0193 },
0194 {
0195
0196 .tlen = CONST_MSS_V6,
0197 .gso_len = CONST_MSS_V6,
0198 .r_num_mss = 1,
0199 },
0200 {
0201
0202 .tlen = CONST_MSS_V6 + 1,
0203 .gso_len = CONST_MSS_V6,
0204 .r_num_mss = 1,
0205 .r_len_last = 1,
0206 },
0207 {
0208
0209 .tlen = CONST_MSS_V6 * 2,
0210 .gso_len = CONST_MSS_V6,
0211 .r_num_mss = 2,
0212 },
0213 {
0214
0215 .tlen = (CONST_MSS_V6 * 2) + 1,
0216 .gso_len = CONST_MSS_V6,
0217 .r_num_mss = 2,
0218 .r_len_last = 1,
0219 },
0220 {
0221
0222 .tlen = (IP6_MAX_MTU / CONST_MSS_V6) * CONST_MSS_V6,
0223 .gso_len = CONST_MSS_V6,
0224 .r_num_mss = (IP6_MAX_MTU / CONST_MSS_V6),
0225 },
0226
0227 {
0228
0229 .tlen = IP6_MAX_MTU - CONST_HDRLEN_V6,
0230 .gso_len = CONST_MSS_V6,
0231 .r_num_mss = CONST_MAX_SEGS_V6,
0232 .r_len_last = IP6_MAX_MTU - CONST_HDRLEN_V6 -
0233 (CONST_MAX_SEGS_V6 * CONST_MSS_V6),
0234 },
0235 {
0236
0237 .tlen = IP6_MAX_MTU - CONST_HDRLEN_V6 + 1,
0238 .gso_len = CONST_MSS_V6,
0239 .tfail = true,
0240 },
0241 {
0242
0243 .tlen = 1,
0244 .gso_len = 1,
0245 .r_num_mss = 1,
0246 },
0247 {
0248
0249 .tlen = 2,
0250 .gso_len = 1,
0251 .r_num_mss = 2,
0252 },
0253 {
0254
0255 .tlen = 5,
0256 .gso_len = 2,
0257 .r_num_mss = 2,
0258 .r_len_last = 1,
0259 },
0260 {
0261
0262 .tlen = UDP_MAX_SEGMENTS,
0263 .gso_len = 1,
0264 .r_num_mss = UDP_MAX_SEGMENTS,
0265 },
0266 {
0267
0268 .tlen = UDP_MAX_SEGMENTS + 1,
0269 .gso_len = 1,
0270 .tfail = true,
0271 },
0272 {
0273
0274 }
0275 };
0276
0277 static unsigned int get_device_mtu(int fd, const char *ifname)
0278 {
0279 struct ifreq ifr;
0280
0281 memset(&ifr, 0, sizeof(ifr));
0282
0283 strcpy(ifr.ifr_name, ifname);
0284
0285 if (ioctl(fd, SIOCGIFMTU, &ifr))
0286 error(1, errno, "ioctl get mtu");
0287
0288 return ifr.ifr_mtu;
0289 }
0290
0291 static void __set_device_mtu(int fd, const char *ifname, unsigned int mtu)
0292 {
0293 struct ifreq ifr;
0294
0295 memset(&ifr, 0, sizeof(ifr));
0296
0297 ifr.ifr_mtu = mtu;
0298 strcpy(ifr.ifr_name, ifname);
0299
0300 if (ioctl(fd, SIOCSIFMTU, &ifr))
0301 error(1, errno, "ioctl set mtu");
0302 }
0303
0304 static void set_device_mtu(int fd, int mtu)
0305 {
0306 int val;
0307
0308 val = get_device_mtu(fd, cfg_ifname);
0309 fprintf(stderr, "device mtu (orig): %u\n", val);
0310
0311 __set_device_mtu(fd, cfg_ifname, mtu);
0312 val = get_device_mtu(fd, cfg_ifname);
0313 if (val != mtu)
0314 error(1, 0, "unable to set device mtu to %u\n", val);
0315
0316 fprintf(stderr, "device mtu (test): %u\n", val);
0317 }
0318
0319 static void set_pmtu_discover(int fd, bool is_ipv4)
0320 {
0321 int level, name, val;
0322
0323 if (is_ipv4) {
0324 level = SOL_IP;
0325 name = IP_MTU_DISCOVER;
0326 val = IP_PMTUDISC_DO;
0327 } else {
0328 level = SOL_IPV6;
0329 name = IPV6_MTU_DISCOVER;
0330 val = IPV6_PMTUDISC_DO;
0331 }
0332
0333 if (setsockopt(fd, level, name, &val, sizeof(val)))
0334 error(1, errno, "setsockopt path mtu");
0335 }
0336
0337 static unsigned int get_path_mtu(int fd, bool is_ipv4)
0338 {
0339 socklen_t vallen;
0340 unsigned int mtu;
0341 int ret;
0342
0343 vallen = sizeof(mtu);
0344 if (is_ipv4)
0345 ret = getsockopt(fd, SOL_IP, IP_MTU, &mtu, &vallen);
0346 else
0347 ret = getsockopt(fd, SOL_IPV6, IPV6_MTU, &mtu, &vallen);
0348
0349 if (ret)
0350 error(1, errno, "getsockopt mtu");
0351
0352
0353 fprintf(stderr, "path mtu (read): %u\n", mtu);
0354 return mtu;
0355 }
0356
0357
0358 static void set_route_mtu(int mtu, bool is_ipv4)
0359 {
0360 struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK };
0361 struct nlmsghdr *nh;
0362 struct rtattr *rta;
0363 struct rtmsg *rt;
0364 char data[NLMSG_ALIGN(sizeof(*nh)) +
0365 NLMSG_ALIGN(sizeof(*rt)) +
0366 NLMSG_ALIGN(RTA_LENGTH(sizeof(addr6))) +
0367 NLMSG_ALIGN(RTA_LENGTH(sizeof(int))) +
0368 NLMSG_ALIGN(RTA_LENGTH(0) + RTA_LENGTH(sizeof(int)))];
0369 int fd, ret, alen, off = 0;
0370
0371 alen = is_ipv4 ? sizeof(addr4) : sizeof(addr6);
0372
0373 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
0374 if (fd == -1)
0375 error(1, errno, "socket netlink");
0376
0377 memset(data, 0, sizeof(data));
0378
0379 nh = (void *)data;
0380 nh->nlmsg_type = RTM_NEWROUTE;
0381 nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
0382 off += NLMSG_ALIGN(sizeof(*nh));
0383
0384 rt = (void *)(data + off);
0385 rt->rtm_family = is_ipv4 ? AF_INET : AF_INET6;
0386 rt->rtm_table = RT_TABLE_MAIN;
0387 rt->rtm_dst_len = alen << 3;
0388 rt->rtm_protocol = RTPROT_BOOT;
0389 rt->rtm_scope = RT_SCOPE_UNIVERSE;
0390 rt->rtm_type = RTN_UNICAST;
0391 off += NLMSG_ALIGN(sizeof(*rt));
0392
0393 rta = (void *)(data + off);
0394 rta->rta_type = RTA_DST;
0395 rta->rta_len = RTA_LENGTH(alen);
0396 if (is_ipv4)
0397 memcpy(RTA_DATA(rta), &addr4, alen);
0398 else
0399 memcpy(RTA_DATA(rta), &addr6, alen);
0400 off += NLMSG_ALIGN(rta->rta_len);
0401
0402 rta = (void *)(data + off);
0403 rta->rta_type = RTA_OIF;
0404 rta->rta_len = RTA_LENGTH(sizeof(int));
0405 *((int *)(RTA_DATA(rta))) = 1;
0406 off += NLMSG_ALIGN(rta->rta_len);
0407
0408
0409 rta = (void *)(data + off);
0410 rta->rta_type = RTA_METRICS;
0411 rta->rta_len = RTA_LENGTH(0) + RTA_LENGTH(sizeof(int));
0412 off += NLMSG_ALIGN(rta->rta_len);
0413
0414
0415 rta = (void *)(((char *) rta) + RTA_LENGTH(0));
0416 rta->rta_type = RTAX_MTU;
0417 rta->rta_len = RTA_LENGTH(sizeof(int));
0418 *((int *)(RTA_DATA(rta))) = mtu;
0419
0420 nh->nlmsg_len = off;
0421
0422 ret = sendto(fd, data, off, 0, (void *)&nladdr, sizeof(nladdr));
0423 if (ret != off)
0424 error(1, errno, "send netlink: %uB != %uB\n", ret, off);
0425
0426 if (close(fd))
0427 error(1, errno, "close netlink");
0428
0429 fprintf(stderr, "route mtu (test): %u\n", mtu);
0430 }
0431
0432 static bool __send_one(int fd, struct msghdr *msg, int flags)
0433 {
0434 int ret;
0435
0436 ret = sendmsg(fd, msg, flags);
0437 if (ret == -1 &&
0438 (errno == EMSGSIZE || errno == ENOMEM || errno == EINVAL))
0439 return false;
0440 if (ret == -1)
0441 error(1, errno, "sendmsg");
0442 if (ret != msg->msg_iov->iov_len)
0443 error(1, 0, "sendto: %d != %llu", ret,
0444 (unsigned long long)msg->msg_iov->iov_len);
0445 if (msg->msg_flags)
0446 error(1, 0, "sendmsg: return flags 0x%x\n", msg->msg_flags);
0447
0448 return true;
0449 }
0450
0451 static bool send_one(int fd, int len, int gso_len,
0452 struct sockaddr *addr, socklen_t alen)
0453 {
0454 char control[CMSG_SPACE(sizeof(uint16_t))] = {0};
0455 struct msghdr msg = {0};
0456 struct iovec iov = {0};
0457 struct cmsghdr *cm;
0458
0459 iov.iov_base = buf;
0460 iov.iov_len = len;
0461
0462 msg.msg_iov = &iov;
0463 msg.msg_iovlen = 1;
0464
0465 msg.msg_name = addr;
0466 msg.msg_namelen = alen;
0467
0468 if (gso_len && !cfg_do_setsockopt) {
0469 msg.msg_control = control;
0470 msg.msg_controllen = sizeof(control);
0471
0472 cm = CMSG_FIRSTHDR(&msg);
0473 cm->cmsg_level = SOL_UDP;
0474 cm->cmsg_type = UDP_SEGMENT;
0475 cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
0476 *((uint16_t *) CMSG_DATA(cm)) = gso_len;
0477 }
0478
0479
0480 if (cfg_do_msgmore && len > 1) {
0481 iov.iov_len = 1;
0482 if (!__send_one(fd, &msg, MSG_MORE))
0483 error(1, 0, "send 1B failed");
0484
0485 iov.iov_base++;
0486 iov.iov_len = len - 1;
0487 }
0488
0489 return __send_one(fd, &msg, 0);
0490 }
0491
0492 static int recv_one(int fd, int flags)
0493 {
0494 int ret;
0495
0496 ret = recv(fd, buf, sizeof(buf), flags);
0497 if (ret == -1 && errno == EAGAIN && (flags & MSG_DONTWAIT))
0498 return 0;
0499 if (ret == -1)
0500 error(1, errno, "recv");
0501
0502 return ret;
0503 }
0504
0505 static void run_one(struct testcase *test, int fdt, int fdr,
0506 struct sockaddr *addr, socklen_t alen)
0507 {
0508 int i, ret, val, mss;
0509 bool sent;
0510
0511 fprintf(stderr, "ipv%d tx:%d gso:%d %s\n",
0512 addr->sa_family == AF_INET ? 4 : 6,
0513 test->tlen, test->gso_len,
0514 test->tfail ? "(fail)" : "");
0515
0516 val = test->gso_len;
0517 if (cfg_do_setsockopt) {
0518 if (setsockopt(fdt, SOL_UDP, UDP_SEGMENT, &val, sizeof(val)))
0519 error(1, errno, "setsockopt udp segment");
0520 }
0521
0522 sent = send_one(fdt, test->tlen, test->gso_len, addr, alen);
0523 if (sent && test->tfail)
0524 error(1, 0, "send succeeded while expecting failure");
0525 if (!sent && !test->tfail)
0526 error(1, 0, "send failed while expecting success");
0527 if (!sent)
0528 return;
0529
0530 if (test->gso_len)
0531 mss = test->gso_len;
0532 else
0533 mss = addr->sa_family == AF_INET ? CONST_MSS_V4 : CONST_MSS_V6;
0534
0535
0536
0537 for (i = 0; i < test->r_num_mss; i++) {
0538 ret = recv_one(fdr, 0);
0539 if (ret != mss)
0540 error(1, 0, "recv.%d: %d != %d", i, ret, mss);
0541 }
0542
0543
0544 if (test->r_len_last) {
0545 ret = recv_one(fdr, 0);
0546 if (ret != test->r_len_last)
0547 error(1, 0, "recv.%d: %d != %d (last)",
0548 i, ret, test->r_len_last);
0549 }
0550
0551
0552 ret = recv_one(fdr, MSG_DONTWAIT);
0553 if (ret)
0554 error(1, 0, "recv: unexpected datagram");
0555 }
0556
0557 static void run_all(int fdt, int fdr, struct sockaddr *addr, socklen_t alen)
0558 {
0559 struct testcase *tests, *test;
0560
0561 tests = addr->sa_family == AF_INET ? testcases_v4 : testcases_v6;
0562
0563 for (test = tests; test->tlen; test++) {
0564
0565 if (cfg_specific_test_id == -1 ||
0566 cfg_specific_test_id == test - tests)
0567 run_one(test, fdt, fdr, addr, alen);
0568 }
0569 }
0570
0571 static void run_test(struct sockaddr *addr, socklen_t alen)
0572 {
0573 struct timeval tv = { .tv_usec = 100 * 1000 };
0574 int fdr, fdt, val;
0575
0576 fdr = socket(addr->sa_family, SOCK_DGRAM, 0);
0577 if (fdr == -1)
0578 error(1, errno, "socket r");
0579
0580 if (bind(fdr, addr, alen))
0581 error(1, errno, "bind");
0582
0583
0584 if (setsockopt(fdr, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
0585 error(1, errno, "setsockopt rcv timeout");
0586
0587 fdt = socket(addr->sa_family, SOCK_DGRAM, 0);
0588 if (fdt == -1)
0589 error(1, errno, "socket t");
0590
0591
0592 set_pmtu_discover(fdt, addr->sa_family == AF_INET);
0593
0594 if (cfg_do_connectionless) {
0595 set_device_mtu(fdt, CONST_MTU_TEST);
0596 run_all(fdt, fdr, addr, alen);
0597 }
0598
0599 if (cfg_do_connected) {
0600 set_device_mtu(fdt, CONST_MTU_TEST + 100);
0601 set_route_mtu(CONST_MTU_TEST, addr->sa_family == AF_INET);
0602
0603 if (connect(fdt, addr, alen))
0604 error(1, errno, "connect");
0605
0606 val = get_path_mtu(fdt, addr->sa_family == AF_INET);
0607 if (val != CONST_MTU_TEST)
0608 error(1, 0, "bad path mtu %u\n", val);
0609
0610 run_all(fdt, fdr, addr, 0 );
0611 }
0612
0613 if (close(fdt))
0614 error(1, errno, "close t");
0615 if (close(fdr))
0616 error(1, errno, "close r");
0617 }
0618
0619 static void run_test_v4(void)
0620 {
0621 struct sockaddr_in addr = {0};
0622
0623 addr.sin_family = AF_INET;
0624 addr.sin_port = htons(cfg_port);
0625 addr.sin_addr = addr4;
0626
0627 run_test((void *)&addr, sizeof(addr));
0628 }
0629
0630 static void run_test_v6(void)
0631 {
0632 struct sockaddr_in6 addr = {0};
0633
0634 addr.sin6_family = AF_INET6;
0635 addr.sin6_port = htons(cfg_port);
0636 addr.sin6_addr = addr6;
0637
0638 run_test((void *)&addr, sizeof(addr));
0639 }
0640
0641 static void parse_opts(int argc, char **argv)
0642 {
0643 int c;
0644
0645 while ((c = getopt(argc, argv, "46cCmst:")) != -1) {
0646 switch (c) {
0647 case '4':
0648 cfg_do_ipv4 = true;
0649 break;
0650 case '6':
0651 cfg_do_ipv6 = true;
0652 break;
0653 case 'c':
0654 cfg_do_connected = true;
0655 break;
0656 case 'C':
0657 cfg_do_connectionless = true;
0658 break;
0659 case 'm':
0660 cfg_do_msgmore = true;
0661 break;
0662 case 's':
0663 cfg_do_setsockopt = true;
0664 break;
0665 case 't':
0666 cfg_specific_test_id = strtoul(optarg, NULL, 0);
0667 break;
0668 default:
0669 error(1, 0, "%s: parse error", argv[0]);
0670 }
0671 }
0672 }
0673
0674 int main(int argc, char **argv)
0675 {
0676 parse_opts(argc, argv);
0677
0678 if (cfg_do_ipv4)
0679 run_test_v4();
0680 if (cfg_do_ipv6)
0681 run_test_v6();
0682
0683 fprintf(stderr, "OK\n");
0684 return 0;
0685 }