Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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;       /* send() buffer size, may exceed mss */
0066     bool tfail;     /* send() call is expected to fail */
0067     int gso_len;        /* mss after applying gso */
0068     int r_num_mss;      /* recv(): number of calls of full mss */
0069     int r_len_last;     /* recv(): size of last non-mss dgram, if any */
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         /* no GSO: send a single byte */
0078         .tlen = 1,
0079         .r_len_last = 1,
0080     },
0081     {
0082         /* no GSO: send a single MSS */
0083         .tlen = CONST_MSS_V4,
0084         .r_num_mss = 1,
0085     },
0086     {
0087         /* no GSO: send a single MSS + 1B: fail */
0088         .tlen = CONST_MSS_V4 + 1,
0089         .tfail = true,
0090     },
0091     {
0092         /* send a single MSS: will fall back to no GSO */
0093         .tlen = CONST_MSS_V4,
0094         .gso_len = CONST_MSS_V4,
0095         .r_num_mss = 1,
0096     },
0097     {
0098         /* send a single MSS + 1B */
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         /* send exactly 2 MSS */
0106         .tlen = CONST_MSS_V4 * 2,
0107         .gso_len = CONST_MSS_V4,
0108         .r_num_mss = 2,
0109     },
0110     {
0111         /* send 2 MSS + 1B */
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         /* send MAX segs */
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         /* send MAX bytes */
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         /* send MAX + 1: fail */
0134         .tlen = ETH_MAX_MTU - CONST_HDRLEN_V4 + 1,
0135         .gso_len = CONST_MSS_V4,
0136         .tfail = true,
0137     },
0138     {
0139         /* send a single 1B MSS: will fall back to no GSO */
0140         .tlen = 1,
0141         .gso_len = 1,
0142         .r_num_mss = 1,
0143     },
0144     {
0145         /* send 2 1B segments */
0146         .tlen = 2,
0147         .gso_len = 1,
0148         .r_num_mss = 2,
0149     },
0150     {
0151         /* send 2B + 2B + 1B segments */
0152         .tlen = 5,
0153         .gso_len = 2,
0154         .r_num_mss = 2,
0155         .r_len_last = 1,
0156     },
0157     {
0158         /* send max number of min sized segments */
0159         .tlen = UDP_MAX_SEGMENTS,
0160         .gso_len = 1,
0161         .r_num_mss = UDP_MAX_SEGMENTS,
0162     },
0163     {
0164         /* send max number + 1 of min sized segments: fail */
0165         .tlen = UDP_MAX_SEGMENTS + 1,
0166         .gso_len = 1,
0167         .tfail = true,
0168     },
0169     {
0170         /* EOL */
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         /* no GSO: send a single byte */
0181         .tlen = 1,
0182         .r_len_last = 1,
0183     },
0184     {
0185         /* no GSO: send a single MSS */
0186         .tlen = CONST_MSS_V6,
0187         .r_num_mss = 1,
0188     },
0189     {
0190         /* no GSO: send a single MSS + 1B: fail */
0191         .tlen = CONST_MSS_V6 + 1,
0192         .tfail = true,
0193     },
0194     {
0195         /* send a single MSS: will fall back to no GSO */
0196         .tlen = CONST_MSS_V6,
0197         .gso_len = CONST_MSS_V6,
0198         .r_num_mss = 1,
0199     },
0200     {
0201         /* send a single MSS + 1B */
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         /* send exactly 2 MSS */
0209         .tlen = CONST_MSS_V6 * 2,
0210         .gso_len = CONST_MSS_V6,
0211         .r_num_mss = 2,
0212     },
0213     {
0214         /* send 2 MSS + 1B */
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         /* send MAX segs */
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         /* send MAX bytes */
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         /* send MAX + 1: fail */
0237         .tlen = IP6_MAX_MTU - CONST_HDRLEN_V6 + 1,
0238         .gso_len = CONST_MSS_V6,
0239         .tfail = true,
0240     },
0241     {
0242         /* send a single 1B MSS: will fall back to no GSO */
0243         .tlen = 1,
0244         .gso_len = 1,
0245         .r_num_mss = 1,
0246     },
0247     {
0248         /* send 2 1B segments */
0249         .tlen = 2,
0250         .gso_len = 1,
0251         .r_num_mss = 2,
0252     },
0253     {
0254         /* send 2B + 2B + 1B segments */
0255         .tlen = 5,
0256         .gso_len = 2,
0257         .r_num_mss = 2,
0258         .r_len_last = 1,
0259     },
0260     {
0261         /* send max number of min sized segments */
0262         .tlen = UDP_MAX_SEGMENTS,
0263         .gso_len = 1,
0264         .r_num_mss = UDP_MAX_SEGMENTS,
0265     },
0266     {
0267         /* send max number + 1 of min sized segments: fail */
0268         .tlen = UDP_MAX_SEGMENTS + 1,
0269         .gso_len = 1,
0270         .tfail = true,
0271     },
0272     {
0273         /* EOL */
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 /* very wordy version of system("ip route add dev lo mtu 1500 127.0.0.3/32") */
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; //if_nametoindex("lo");
0406     off += NLMSG_ALIGN(rta->rta_len);
0407 
0408     /* MTU is a subtype in a metrics type */
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     /* now fill MTU subtype. Note that it fits within above rta_len */
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     /* If MSG_MORE, send 1 byte followed by remainder */
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     /* Recv all full MSS datagrams */
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     /* Recv the non-full last datagram, if tlen was not a multiple of mss */
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     /* Verify received all data */
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         /* if a specific test is given, then skip all others */
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     /* Have tests fail quickly instead of hang */
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     /* Do not fragment these datagrams: only succeed if GSO works */
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 /* use connected addr */);
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 }