13a687befSWillem de Bruijn // SPDX-License-Identifier: GPL-2.0
23a687befSWillem de Bruijn
33a687befSWillem de Bruijn #define _GNU_SOURCE
43a687befSWillem de Bruijn
53a687befSWillem de Bruijn #include <arpa/inet.h>
63a687befSWillem de Bruijn #include <error.h>
73a687befSWillem de Bruijn #include <errno.h>
83a687befSWillem de Bruijn #include <limits.h>
93a687befSWillem de Bruijn #include <linux/errqueue.h>
103a687befSWillem de Bruijn #include <linux/if_packet.h>
113a687befSWillem de Bruijn #include <linux/socket.h>
123a687befSWillem de Bruijn #include <linux/sockios.h>
133a687befSWillem de Bruijn #include <net/ethernet.h>
143a687befSWillem de Bruijn #include <net/if.h>
153a687befSWillem de Bruijn #include <netinet/ip.h>
163a687befSWillem de Bruijn #include <netinet/ip6.h>
173a687befSWillem de Bruijn #include <netinet/tcp.h>
183a687befSWillem de Bruijn #include <netinet/udp.h>
193a687befSWillem de Bruijn #include <poll.h>
203a687befSWillem de Bruijn #include <sched.h>
213a687befSWillem de Bruijn #include <stdbool.h>
223a687befSWillem de Bruijn #include <stdio.h>
233a687befSWillem de Bruijn #include <stdint.h>
243a687befSWillem de Bruijn #include <stdlib.h>
253a687befSWillem de Bruijn #include <string.h>
263a687befSWillem de Bruijn #include <sys/ioctl.h>
273a687befSWillem de Bruijn #include <sys/socket.h>
283a687befSWillem de Bruijn #include <sys/stat.h>
293a687befSWillem de Bruijn #include <sys/time.h>
303a687befSWillem de Bruijn #include <sys/types.h>
313a687befSWillem de Bruijn #include <sys/wait.h>
323a687befSWillem de Bruijn #include <unistd.h>
333a687befSWillem de Bruijn
340a9ac2e9SPaolo Abeni #ifndef UDP_GRO
350a9ac2e9SPaolo Abeni #define UDP_GRO 104
360a9ac2e9SPaolo Abeni #endif
370a9ac2e9SPaolo Abeni
383a687befSWillem de Bruijn static int cfg_port = 8000;
393a687befSWillem de Bruijn static bool cfg_tcp;
403a687befSWillem de Bruijn static bool cfg_verify;
410a9ac2e9SPaolo Abeni static bool cfg_read_all;
420a9ac2e9SPaolo Abeni static bool cfg_gro_segment;
433327a9c4SPaolo Abeni static int cfg_family = PF_INET6;
443327a9c4SPaolo Abeni static int cfg_alen = sizeof(struct sockaddr_in6);
453327a9c4SPaolo Abeni static int cfg_expected_pkt_nr;
463327a9c4SPaolo Abeni static int cfg_expected_pkt_len;
473327a9c4SPaolo Abeni static int cfg_expected_gso_size;
48ada641ffSPaolo Abeni static int cfg_connect_timeout_ms;
49ada641ffSPaolo Abeni static int cfg_rcv_timeout_ms;
503327a9c4SPaolo Abeni static struct sockaddr_storage cfg_bind_addr;
513a687befSWillem de Bruijn
523a687befSWillem de Bruijn static bool interrupted;
533a687befSWillem de Bruijn static unsigned long packets, bytes;
543a687befSWillem de Bruijn
sigint_handler(int signum)553a687befSWillem de Bruijn static void sigint_handler(int signum)
563a687befSWillem de Bruijn {
573a687befSWillem de Bruijn if (signum == SIGINT)
583a687befSWillem de Bruijn interrupted = true;
593a687befSWillem de Bruijn }
603a687befSWillem de Bruijn
setup_sockaddr(int domain,const char * str_addr,void * sockaddr)613327a9c4SPaolo Abeni static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr)
623327a9c4SPaolo Abeni {
633327a9c4SPaolo Abeni struct sockaddr_in6 *addr6 = (void *) sockaddr;
643327a9c4SPaolo Abeni struct sockaddr_in *addr4 = (void *) sockaddr;
653327a9c4SPaolo Abeni
663327a9c4SPaolo Abeni switch (domain) {
673327a9c4SPaolo Abeni case PF_INET:
683327a9c4SPaolo Abeni addr4->sin_family = AF_INET;
693327a9c4SPaolo Abeni addr4->sin_port = htons(cfg_port);
703327a9c4SPaolo Abeni if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
713327a9c4SPaolo Abeni error(1, 0, "ipv4 parse error: %s", str_addr);
723327a9c4SPaolo Abeni break;
733327a9c4SPaolo Abeni case PF_INET6:
743327a9c4SPaolo Abeni addr6->sin6_family = AF_INET6;
753327a9c4SPaolo Abeni addr6->sin6_port = htons(cfg_port);
763327a9c4SPaolo Abeni if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
773327a9c4SPaolo Abeni error(1, 0, "ipv6 parse error: %s", str_addr);
783327a9c4SPaolo Abeni break;
793327a9c4SPaolo Abeni default:
803327a9c4SPaolo Abeni error(1, 0, "illegal domain");
813327a9c4SPaolo Abeni }
823327a9c4SPaolo Abeni }
833327a9c4SPaolo Abeni
gettimeofday_ms(void)843a687befSWillem de Bruijn static unsigned long gettimeofday_ms(void)
853a687befSWillem de Bruijn {
863a687befSWillem de Bruijn struct timeval tv;
873a687befSWillem de Bruijn
883a687befSWillem de Bruijn gettimeofday(&tv, NULL);
893a687befSWillem de Bruijn return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
903a687befSWillem de Bruijn }
913a687befSWillem de Bruijn
do_poll(int fd,int timeout_ms)92ada641ffSPaolo Abeni static void do_poll(int fd, int timeout_ms)
933a687befSWillem de Bruijn {
943a687befSWillem de Bruijn struct pollfd pfd;
953a687befSWillem de Bruijn int ret;
963a687befSWillem de Bruijn
973a687befSWillem de Bruijn pfd.events = POLLIN;
983a687befSWillem de Bruijn pfd.revents = 0;
993a687befSWillem de Bruijn pfd.fd = fd;
1003a687befSWillem de Bruijn
1013a687befSWillem de Bruijn do {
1023a687befSWillem de Bruijn ret = poll(&pfd, 1, 10);
1030a9ac2e9SPaolo Abeni if (interrupted)
1040a9ac2e9SPaolo Abeni break;
1053a687befSWillem de Bruijn if (ret == -1)
1063a687befSWillem de Bruijn error(1, errno, "poll");
107ada641ffSPaolo Abeni if (ret == 0) {
108ada641ffSPaolo Abeni if (!timeout_ms)
1093a687befSWillem de Bruijn continue;
110ada641ffSPaolo Abeni
111ada641ffSPaolo Abeni timeout_ms -= 10;
112ada641ffSPaolo Abeni if (timeout_ms <= 0) {
113ada641ffSPaolo Abeni interrupted = true;
114ada641ffSPaolo Abeni break;
115ada641ffSPaolo Abeni }
11638bf8cd8SPaolo Abeni
11738bf8cd8SPaolo Abeni /* no events and more time to wait, do poll again */
11838bf8cd8SPaolo Abeni continue;
119ada641ffSPaolo Abeni }
1203a687befSWillem de Bruijn if (pfd.revents != POLLIN)
1213a687befSWillem de Bruijn error(1, errno, "poll: 0x%x expected 0x%x\n",
1223a687befSWillem de Bruijn pfd.revents, POLLIN);
1230a9ac2e9SPaolo Abeni } while (!ret);
1243a687befSWillem de Bruijn }
1253a687befSWillem de Bruijn
do_socket(bool do_tcp)1263a687befSWillem de Bruijn static int do_socket(bool do_tcp)
1273a687befSWillem de Bruijn {
1283a687befSWillem de Bruijn int fd, val;
1293a687befSWillem de Bruijn
1303327a9c4SPaolo Abeni fd = socket(cfg_family, cfg_tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
1313a687befSWillem de Bruijn if (fd == -1)
1323a687befSWillem de Bruijn error(1, errno, "socket");
1333a687befSWillem de Bruijn
1343a687befSWillem de Bruijn val = 1 << 21;
1353a687befSWillem de Bruijn if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)))
1363a687befSWillem de Bruijn error(1, errno, "setsockopt rcvbuf");
1373a687befSWillem de Bruijn val = 1;
1383a687befSWillem de Bruijn if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)))
1393a687befSWillem de Bruijn error(1, errno, "setsockopt reuseport");
1403a687befSWillem de Bruijn
1413327a9c4SPaolo Abeni if (bind(fd, (void *)&cfg_bind_addr, cfg_alen))
1423a687befSWillem de Bruijn error(1, errno, "bind");
1433a687befSWillem de Bruijn
1443a687befSWillem de Bruijn if (do_tcp) {
1453a687befSWillem de Bruijn int accept_fd = fd;
1463a687befSWillem de Bruijn
1473a687befSWillem de Bruijn if (listen(accept_fd, 1))
1483a687befSWillem de Bruijn error(1, errno, "listen");
1493a687befSWillem de Bruijn
150ada641ffSPaolo Abeni do_poll(accept_fd, cfg_connect_timeout_ms);
1510a9ac2e9SPaolo Abeni if (interrupted)
1520a9ac2e9SPaolo Abeni exit(0);
1533a687befSWillem de Bruijn
1543a687befSWillem de Bruijn fd = accept(accept_fd, NULL, NULL);
1553a687befSWillem de Bruijn if (fd == -1)
1563a687befSWillem de Bruijn error(1, errno, "accept");
1573a687befSWillem de Bruijn if (close(accept_fd))
1583a687befSWillem de Bruijn error(1, errno, "close accept fd");
1593a687befSWillem de Bruijn }
1603a687befSWillem de Bruijn
1613a687befSWillem de Bruijn return fd;
1623a687befSWillem de Bruijn }
1633a687befSWillem de Bruijn
1643a687befSWillem de Bruijn /* Flush all outstanding bytes for the tcp receive queue */
do_flush_tcp(int fd)1653a687befSWillem de Bruijn static void do_flush_tcp(int fd)
1663a687befSWillem de Bruijn {
1673a687befSWillem de Bruijn int ret;
1683a687befSWillem de Bruijn
1693a687befSWillem de Bruijn while (true) {
1703a687befSWillem de Bruijn /* MSG_TRUNC flushes up to len bytes */
1713a687befSWillem de Bruijn ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
1723a687befSWillem de Bruijn if (ret == -1 && errno == EAGAIN)
1733a687befSWillem de Bruijn return;
1743a687befSWillem de Bruijn if (ret == -1)
1753a687befSWillem de Bruijn error(1, errno, "flush");
1763a687befSWillem de Bruijn if (ret == 0) {
1773a687befSWillem de Bruijn /* client detached */
1783a687befSWillem de Bruijn exit(0);
1793a687befSWillem de Bruijn }
1803a687befSWillem de Bruijn
1813a687befSWillem de Bruijn packets++;
1823a687befSWillem de Bruijn bytes += ret;
1833a687befSWillem de Bruijn }
1843a687befSWillem de Bruijn
1853a687befSWillem de Bruijn }
1863a687befSWillem de Bruijn
sanitized_char(char val)1873a687befSWillem de Bruijn static char sanitized_char(char val)
1883a687befSWillem de Bruijn {
1893a687befSWillem de Bruijn return (val >= 'a' && val <= 'z') ? val : '.';
1903a687befSWillem de Bruijn }
1913a687befSWillem de Bruijn
do_verify_udp(const char * data,int len)1923a687befSWillem de Bruijn static void do_verify_udp(const char *data, int len)
1933a687befSWillem de Bruijn {
1943a687befSWillem de Bruijn char cur = data[0];
1953a687befSWillem de Bruijn int i;
1963a687befSWillem de Bruijn
1973a687befSWillem de Bruijn /* verify contents */
1983a687befSWillem de Bruijn if (cur < 'a' || cur > 'z')
1993a687befSWillem de Bruijn error(1, 0, "data initial byte out of range");
2003a687befSWillem de Bruijn
2013a687befSWillem de Bruijn for (i = 1; i < len; i++) {
2023a687befSWillem de Bruijn if (cur == 'z')
2033a687befSWillem de Bruijn cur = 'a';
2043a687befSWillem de Bruijn else
2053a687befSWillem de Bruijn cur++;
2063a687befSWillem de Bruijn
2073a687befSWillem de Bruijn if (data[i] != cur)
2083a687befSWillem de Bruijn error(1, 0, "data[%d]: len %d, %c(%hhu) != %c(%hhu)\n",
2093a687befSWillem de Bruijn i, len,
2103a687befSWillem de Bruijn sanitized_char(data[i]), data[i],
2113a687befSWillem de Bruijn sanitized_char(cur), cur);
2123a687befSWillem de Bruijn }
2133a687befSWillem de Bruijn }
2143a687befSWillem de Bruijn
recv_msg(int fd,char * buf,int len,int * gso_size)2153327a9c4SPaolo Abeni static int recv_msg(int fd, char *buf, int len, int *gso_size)
2163327a9c4SPaolo Abeni {
21743686409SJakub Sitnicki char control[CMSG_SPACE(sizeof(int))] = {0};
2183327a9c4SPaolo Abeni struct msghdr msg = {0};
2193327a9c4SPaolo Abeni struct iovec iov = {0};
2203327a9c4SPaolo Abeni struct cmsghdr *cmsg;
2213327a9c4SPaolo Abeni int ret;
2223327a9c4SPaolo Abeni
2233327a9c4SPaolo Abeni iov.iov_base = buf;
2243327a9c4SPaolo Abeni iov.iov_len = len;
2253327a9c4SPaolo Abeni
2263327a9c4SPaolo Abeni msg.msg_iov = &iov;
2273327a9c4SPaolo Abeni msg.msg_iovlen = 1;
2283327a9c4SPaolo Abeni
2293327a9c4SPaolo Abeni msg.msg_control = control;
2303327a9c4SPaolo Abeni msg.msg_controllen = sizeof(control);
2313327a9c4SPaolo Abeni
2323327a9c4SPaolo Abeni *gso_size = -1;
2333327a9c4SPaolo Abeni ret = recvmsg(fd, &msg, MSG_TRUNC | MSG_DONTWAIT);
2343327a9c4SPaolo Abeni if (ret != -1) {
2353327a9c4SPaolo Abeni for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
2363327a9c4SPaolo Abeni cmsg = CMSG_NXTHDR(&msg, cmsg)) {
2373327a9c4SPaolo Abeni if (cmsg->cmsg_level == SOL_UDP
2383327a9c4SPaolo Abeni && cmsg->cmsg_type == UDP_GRO) {
23943686409SJakub Sitnicki *gso_size = *(int *)CMSG_DATA(cmsg);
2403327a9c4SPaolo Abeni break;
2413327a9c4SPaolo Abeni }
2423327a9c4SPaolo Abeni }
2433327a9c4SPaolo Abeni }
2443327a9c4SPaolo Abeni return ret;
2453327a9c4SPaolo Abeni }
2463327a9c4SPaolo Abeni
2473a687befSWillem de Bruijn /* Flush all outstanding datagrams. Verify first few bytes of each. */
do_flush_udp(int fd)2483a687befSWillem de Bruijn static void do_flush_udp(int fd)
2493a687befSWillem de Bruijn {
2500a9ac2e9SPaolo Abeni static char rbuf[ETH_MAX_MTU];
251c03c80e3SAndrei Gherzan int ret, len, gso_size = 0, budget = 256;
2523a687befSWillem de Bruijn
2530a9ac2e9SPaolo Abeni len = cfg_read_all ? sizeof(rbuf) : 0;
2543a687befSWillem de Bruijn while (budget--) {
2553a687befSWillem de Bruijn /* MSG_TRUNC will make return value full datagram length */
2563327a9c4SPaolo Abeni if (!cfg_expected_gso_size)
2573a687befSWillem de Bruijn ret = recv(fd, rbuf, len, MSG_TRUNC | MSG_DONTWAIT);
2583327a9c4SPaolo Abeni else
2593327a9c4SPaolo Abeni ret = recv_msg(fd, rbuf, len, &gso_size);
2603a687befSWillem de Bruijn if (ret == -1 && errno == EAGAIN)
2613327a9c4SPaolo Abeni break;
2623a687befSWillem de Bruijn if (ret == -1)
2633a687befSWillem de Bruijn error(1, errno, "recv");
2643327a9c4SPaolo Abeni if (cfg_expected_pkt_len && ret != cfg_expected_pkt_len)
2653327a9c4SPaolo Abeni error(1, 0, "recv: bad packet len, got %d,"
2663327a9c4SPaolo Abeni " expected %d\n", ret, cfg_expected_pkt_len);
2670a9ac2e9SPaolo Abeni if (len && cfg_verify) {
2683a687befSWillem de Bruijn if (ret == 0)
2693a687befSWillem de Bruijn error(1, errno, "recv: 0 byte datagram\n");
2703a687befSWillem de Bruijn
2713a687befSWillem de Bruijn do_verify_udp(rbuf, ret);
2723a687befSWillem de Bruijn }
2733327a9c4SPaolo Abeni if (cfg_expected_gso_size && cfg_expected_gso_size != gso_size)
2743327a9c4SPaolo Abeni error(1, 0, "recv: bad gso size, got %d, expected %d "
2753327a9c4SPaolo Abeni "(-1 == no gso cmsg))\n", gso_size,
2763327a9c4SPaolo Abeni cfg_expected_gso_size);
2773a687befSWillem de Bruijn
2783a687befSWillem de Bruijn packets++;
2793a687befSWillem de Bruijn bytes += ret;
2803327a9c4SPaolo Abeni if (cfg_expected_pkt_nr && packets >= cfg_expected_pkt_nr)
2813327a9c4SPaolo Abeni break;
2823a687befSWillem de Bruijn }
2833a687befSWillem de Bruijn }
2843a687befSWillem de Bruijn
usage(const char * filepath)2853a687befSWillem de Bruijn static void usage(const char *filepath)
2863a687befSWillem de Bruijn {
287ada641ffSPaolo Abeni error(1, 0, "Usage: %s [-C connect_timeout] [-Grtv] [-b addr] [-p port]"
288ada641ffSPaolo Abeni " [-l pktlen] [-n packetnr] [-R rcv_timeout] [-S gsosize]",
289ada641ffSPaolo Abeni filepath);
2903a687befSWillem de Bruijn }
2913a687befSWillem de Bruijn
parse_opts(int argc,char ** argv)2923a687befSWillem de Bruijn static void parse_opts(int argc, char **argv)
2933a687befSWillem de Bruijn {
294d336509cSWillem de Bruijn const char *bind_addr = NULL;
2953a687befSWillem de Bruijn int c;
2963a687befSWillem de Bruijn
297ada641ffSPaolo Abeni while ((c = getopt(argc, argv, "4b:C:Gl:n:p:rR:S:tv")) != -1) {
2983a687befSWillem de Bruijn switch (c) {
2993327a9c4SPaolo Abeni case '4':
3003327a9c4SPaolo Abeni cfg_family = PF_INET;
3013327a9c4SPaolo Abeni cfg_alen = sizeof(struct sockaddr_in);
3023327a9c4SPaolo Abeni break;
3033327a9c4SPaolo Abeni case 'b':
304d336509cSWillem de Bruijn bind_addr = optarg;
3053327a9c4SPaolo Abeni break;
306ada641ffSPaolo Abeni case 'C':
307ada641ffSPaolo Abeni cfg_connect_timeout_ms = strtoul(optarg, NULL, 0);
308ada641ffSPaolo Abeni break;
3090a9ac2e9SPaolo Abeni case 'G':
3100a9ac2e9SPaolo Abeni cfg_gro_segment = true;
3110a9ac2e9SPaolo Abeni break;
3123327a9c4SPaolo Abeni case 'l':
3133327a9c4SPaolo Abeni cfg_expected_pkt_len = strtoul(optarg, NULL, 0);
3143327a9c4SPaolo Abeni break;
3153327a9c4SPaolo Abeni case 'n':
3163327a9c4SPaolo Abeni cfg_expected_pkt_nr = strtoul(optarg, NULL, 0);
3173327a9c4SPaolo Abeni break;
3183a687befSWillem de Bruijn case 'p':
3190a9ac2e9SPaolo Abeni cfg_port = strtoul(optarg, NULL, 0);
3200a9ac2e9SPaolo Abeni break;
3210a9ac2e9SPaolo Abeni case 'r':
3220a9ac2e9SPaolo Abeni cfg_read_all = true;
3233a687befSWillem de Bruijn break;
324ada641ffSPaolo Abeni case 'R':
325ada641ffSPaolo Abeni cfg_rcv_timeout_ms = strtoul(optarg, NULL, 0);
326ada641ffSPaolo Abeni break;
3273327a9c4SPaolo Abeni case 'S':
3283327a9c4SPaolo Abeni cfg_expected_gso_size = strtol(optarg, NULL, 0);
3293327a9c4SPaolo Abeni break;
3303a687befSWillem de Bruijn case 't':
3313a687befSWillem de Bruijn cfg_tcp = true;
3323a687befSWillem de Bruijn break;
3333a687befSWillem de Bruijn case 'v':
3343a687befSWillem de Bruijn cfg_verify = true;
3350a9ac2e9SPaolo Abeni cfg_read_all = true;
3363a687befSWillem de Bruijn break;
337db9b47eeSAndrei Gherzan default:
338db9b47eeSAndrei Gherzan exit(1);
3393a687befSWillem de Bruijn }
3403a687befSWillem de Bruijn }
3413a687befSWillem de Bruijn
342d336509cSWillem de Bruijn if (!bind_addr)
343d336509cSWillem de Bruijn bind_addr = cfg_family == PF_INET6 ? "::" : "0.0.0.0";
344d336509cSWillem de Bruijn
345d336509cSWillem de Bruijn setup_sockaddr(cfg_family, bind_addr, &cfg_bind_addr);
346d336509cSWillem de Bruijn
3473a687befSWillem de Bruijn if (optind != argc)
3483a687befSWillem de Bruijn usage(argv[0]);
3493a687befSWillem de Bruijn
3503a687befSWillem de Bruijn if (cfg_tcp && cfg_verify)
3513a687befSWillem de Bruijn error(1, 0, "TODO: implement verify mode for tcp");
3523a687befSWillem de Bruijn }
3533a687befSWillem de Bruijn
do_recv(void)3543a687befSWillem de Bruijn static void do_recv(void)
3553a687befSWillem de Bruijn {
356ada641ffSPaolo Abeni int timeout_ms = cfg_tcp ? cfg_rcv_timeout_ms : cfg_connect_timeout_ms;
3573a687befSWillem de Bruijn unsigned long tnow, treport;
358ada641ffSPaolo Abeni int fd;
3593a687befSWillem de Bruijn
3603a687befSWillem de Bruijn fd = do_socket(cfg_tcp);
3613a687befSWillem de Bruijn
3620a9ac2e9SPaolo Abeni if (cfg_gro_segment && !cfg_tcp) {
3630a9ac2e9SPaolo Abeni int val = 1;
3640a9ac2e9SPaolo Abeni if (setsockopt(fd, IPPROTO_UDP, UDP_GRO, &val, sizeof(val)))
3650a9ac2e9SPaolo Abeni error(1, errno, "setsockopt UDP_GRO");
3660a9ac2e9SPaolo Abeni }
3670a9ac2e9SPaolo Abeni
3683a687befSWillem de Bruijn treport = gettimeofday_ms() + 1000;
3693a687befSWillem de Bruijn do {
370ada641ffSPaolo Abeni do_poll(fd, timeout_ms);
3713a687befSWillem de Bruijn
3723a687befSWillem de Bruijn if (cfg_tcp)
3733a687befSWillem de Bruijn do_flush_tcp(fd);
3743a687befSWillem de Bruijn else
3753a687befSWillem de Bruijn do_flush_udp(fd);
3763a687befSWillem de Bruijn
3773a687befSWillem de Bruijn tnow = gettimeofday_ms();
378*104ab0e8SPaolo Abeni if (!cfg_expected_pkt_nr && tnow > treport) {
3793a687befSWillem de Bruijn if (packets)
3803a687befSWillem de Bruijn fprintf(stderr,
3813a687befSWillem de Bruijn "%s rx: %6lu MB/s %8lu calls/s\n",
3823a687befSWillem de Bruijn cfg_tcp ? "tcp" : "udp",
3833a687befSWillem de Bruijn bytes >> 20, packets);
3843a687befSWillem de Bruijn bytes = packets = 0;
3853a687befSWillem de Bruijn treport = tnow + 1000;
3863a687befSWillem de Bruijn }
3873a687befSWillem de Bruijn
388ada641ffSPaolo Abeni timeout_ms = cfg_rcv_timeout_ms;
389ada641ffSPaolo Abeni
3903a687befSWillem de Bruijn } while (!interrupted);
3913a687befSWillem de Bruijn
3923327a9c4SPaolo Abeni if (cfg_expected_pkt_nr && (packets != cfg_expected_pkt_nr))
3933327a9c4SPaolo Abeni error(1, 0, "wrong packet number! got %ld, expected %d\n",
3943327a9c4SPaolo Abeni packets, cfg_expected_pkt_nr);
3953327a9c4SPaolo Abeni
3963a687befSWillem de Bruijn if (close(fd))
3973a687befSWillem de Bruijn error(1, errno, "close");
3983a687befSWillem de Bruijn }
3993a687befSWillem de Bruijn
main(int argc,char ** argv)4003a687befSWillem de Bruijn int main(int argc, char **argv)
4013a687befSWillem de Bruijn {
4023a687befSWillem de Bruijn parse_opts(argc, argv);
4033a687befSWillem de Bruijn
4043a687befSWillem de Bruijn signal(SIGINT, sigint_handler);
4053a687befSWillem de Bruijn
4063a687befSWillem de Bruijn do_recv();
4073a687befSWillem de Bruijn
4083a687befSWillem de Bruijn return 0;
4093a687befSWillem de Bruijn }
410