18c2ecf20Sopenharmony_ci/* Evaluate MSG_ZEROCOPY
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * Send traffic between two processes over one of the supported
48c2ecf20Sopenharmony_ci * protocols and modes:
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * PF_INET/PF_INET6
78c2ecf20Sopenharmony_ci * - SOCK_STREAM
88c2ecf20Sopenharmony_ci * - SOCK_DGRAM
98c2ecf20Sopenharmony_ci * - SOCK_DGRAM with UDP_CORK
108c2ecf20Sopenharmony_ci * - SOCK_RAW
118c2ecf20Sopenharmony_ci * - SOCK_RAW with IP_HDRINCL
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * PF_PACKET
148c2ecf20Sopenharmony_ci * - SOCK_DGRAM
158c2ecf20Sopenharmony_ci * - SOCK_RAW
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * PF_RDS
188c2ecf20Sopenharmony_ci * - SOCK_SEQPACKET
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * Start this program on two connected hosts, one in send mode and
218c2ecf20Sopenharmony_ci * the other with option '-r' to put it in receiver mode.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * If zerocopy mode ('-z') is enabled, the sender will verify that
248c2ecf20Sopenharmony_ci * the kernel queues completions on the error queue for all zerocopy
258c2ecf20Sopenharmony_ci * transfers.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define _GNU_SOURCE
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <arpa/inet.h>
318c2ecf20Sopenharmony_ci#include <error.h>
328c2ecf20Sopenharmony_ci#include <errno.h>
338c2ecf20Sopenharmony_ci#include <limits.h>
348c2ecf20Sopenharmony_ci#include <linux/errqueue.h>
358c2ecf20Sopenharmony_ci#include <linux/if_packet.h>
368c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
378c2ecf20Sopenharmony_ci#include <linux/socket.h>
388c2ecf20Sopenharmony_ci#include <linux/sockios.h>
398c2ecf20Sopenharmony_ci#include <net/ethernet.h>
408c2ecf20Sopenharmony_ci#include <net/if.h>
418c2ecf20Sopenharmony_ci#include <netinet/ip.h>
428c2ecf20Sopenharmony_ci#include <netinet/ip6.h>
438c2ecf20Sopenharmony_ci#include <netinet/tcp.h>
448c2ecf20Sopenharmony_ci#include <netinet/udp.h>
458c2ecf20Sopenharmony_ci#include <poll.h>
468c2ecf20Sopenharmony_ci#include <sched.h>
478c2ecf20Sopenharmony_ci#include <stdbool.h>
488c2ecf20Sopenharmony_ci#include <stdio.h>
498c2ecf20Sopenharmony_ci#include <stdint.h>
508c2ecf20Sopenharmony_ci#include <stdlib.h>
518c2ecf20Sopenharmony_ci#include <string.h>
528c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
538c2ecf20Sopenharmony_ci#include <sys/socket.h>
548c2ecf20Sopenharmony_ci#include <sys/stat.h>
558c2ecf20Sopenharmony_ci#include <sys/time.h>
568c2ecf20Sopenharmony_ci#include <sys/types.h>
578c2ecf20Sopenharmony_ci#include <sys/wait.h>
588c2ecf20Sopenharmony_ci#include <unistd.h>
598c2ecf20Sopenharmony_ci#include <linux/rds.h>
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#ifndef SO_EE_ORIGIN_ZEROCOPY
628c2ecf20Sopenharmony_ci#define SO_EE_ORIGIN_ZEROCOPY		5
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#ifndef SO_ZEROCOPY
668c2ecf20Sopenharmony_ci#define SO_ZEROCOPY	60
678c2ecf20Sopenharmony_ci#endif
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#ifndef SO_EE_CODE_ZEROCOPY_COPIED
708c2ecf20Sopenharmony_ci#define SO_EE_CODE_ZEROCOPY_COPIED	1
718c2ecf20Sopenharmony_ci#endif
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#ifndef MSG_ZEROCOPY
748c2ecf20Sopenharmony_ci#define MSG_ZEROCOPY	0x4000000
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int  cfg_cork;
788c2ecf20Sopenharmony_cistatic bool cfg_cork_mixed;
798c2ecf20Sopenharmony_cistatic int  cfg_cpu		= -1;		/* default: pin to last cpu */
808c2ecf20Sopenharmony_cistatic int  cfg_family		= PF_UNSPEC;
818c2ecf20Sopenharmony_cistatic int  cfg_ifindex		= 1;
828c2ecf20Sopenharmony_cistatic int  cfg_payload_len;
838c2ecf20Sopenharmony_cistatic int  cfg_port		= 8000;
848c2ecf20Sopenharmony_cistatic bool cfg_rx;
858c2ecf20Sopenharmony_cistatic int  cfg_runtime_ms	= 4200;
868c2ecf20Sopenharmony_cistatic int  cfg_verbose;
878c2ecf20Sopenharmony_cistatic int  cfg_waittime_ms	= 500;
888c2ecf20Sopenharmony_cistatic bool cfg_zerocopy;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic socklen_t cfg_alen;
918c2ecf20Sopenharmony_cistatic struct sockaddr_storage cfg_dst_addr;
928c2ecf20Sopenharmony_cistatic struct sockaddr_storage cfg_src_addr;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic char payload[IP_MAXPACKET];
958c2ecf20Sopenharmony_cistatic long packets, bytes, completions, expected_completions;
968c2ecf20Sopenharmony_cistatic int  zerocopied = -1;
978c2ecf20Sopenharmony_cistatic uint32_t next_completion;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic unsigned long gettimeofday_ms(void)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct timeval tv;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	gettimeofday(&tv, NULL);
1048c2ecf20Sopenharmony_ci	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic uint16_t get_ip_csum(const uint16_t *start, int num_words)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	unsigned long sum = 0;
1108c2ecf20Sopenharmony_ci	int i;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	for (i = 0; i < num_words; i++)
1138c2ecf20Sopenharmony_ci		sum += start[i];
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	while (sum >> 16)
1168c2ecf20Sopenharmony_ci		sum = (sum & 0xFFFF) + (sum >> 16);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return ~sum;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int do_setcpu(int cpu)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	cpu_set_t mask;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	CPU_ZERO(&mask);
1268c2ecf20Sopenharmony_ci	CPU_SET(cpu, &mask);
1278c2ecf20Sopenharmony_ci	if (sched_setaffinity(0, sizeof(mask), &mask))
1288c2ecf20Sopenharmony_ci		fprintf(stderr, "cpu: unable to pin, may increase variance.\n");
1298c2ecf20Sopenharmony_ci	else if (cfg_verbose)
1308c2ecf20Sopenharmony_ci		fprintf(stderr, "cpu: %u\n", cpu);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void do_setsockopt(int fd, int level, int optname, int val)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	if (setsockopt(fd, level, optname, &val, sizeof(val)))
1388c2ecf20Sopenharmony_ci		error(1, errno, "setsockopt %d.%d: %d", level, optname, val);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int do_poll(int fd, int events)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct pollfd pfd;
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	pfd.events = events;
1478c2ecf20Sopenharmony_ci	pfd.revents = 0;
1488c2ecf20Sopenharmony_ci	pfd.fd = fd;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ret = poll(&pfd, 1, cfg_waittime_ms);
1518c2ecf20Sopenharmony_ci	if (ret == -1)
1528c2ecf20Sopenharmony_ci		error(1, errno, "poll");
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return ret && (pfd.revents & events);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int do_accept(int fd)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	int fda = fd;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	fd = accept(fda, NULL, NULL);
1628c2ecf20Sopenharmony_ci	if (fd == -1)
1638c2ecf20Sopenharmony_ci		error(1, errno, "accept");
1648c2ecf20Sopenharmony_ci	if (close(fda))
1658c2ecf20Sopenharmony_ci		error(1, errno, "close listen sock");
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return fd;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct cmsghdr *cm;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!msg->msg_control)
1758c2ecf20Sopenharmony_ci		error(1, errno, "NULL cookie");
1768c2ecf20Sopenharmony_ci	cm = (void *)msg->msg_control;
1778c2ecf20Sopenharmony_ci	cm->cmsg_len = CMSG_LEN(sizeof(cookie));
1788c2ecf20Sopenharmony_ci	cm->cmsg_level = SOL_RDS;
1798c2ecf20Sopenharmony_ci	cm->cmsg_type = RDS_CMSG_ZCOPY_COOKIE;
1808c2ecf20Sopenharmony_ci	memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie));
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int ret, len, i, flags;
1868c2ecf20Sopenharmony_ci	static uint32_t cookie;
1878c2ecf20Sopenharmony_ci	char ckbuf[CMSG_SPACE(sizeof(cookie))];
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	len = 0;
1908c2ecf20Sopenharmony_ci	for (i = 0; i < msg->msg_iovlen; i++)
1918c2ecf20Sopenharmony_ci		len += msg->msg_iov[i].iov_len;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	flags = MSG_DONTWAIT;
1948c2ecf20Sopenharmony_ci	if (do_zerocopy) {
1958c2ecf20Sopenharmony_ci		flags |= MSG_ZEROCOPY;
1968c2ecf20Sopenharmony_ci		if (domain == PF_RDS) {
1978c2ecf20Sopenharmony_ci			memset(&msg->msg_control, 0, sizeof(msg->msg_control));
1988c2ecf20Sopenharmony_ci			msg->msg_controllen = CMSG_SPACE(sizeof(cookie));
1998c2ecf20Sopenharmony_ci			msg->msg_control = (struct cmsghdr *)ckbuf;
2008c2ecf20Sopenharmony_ci			add_zcopy_cookie(msg, ++cookie);
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	ret = sendmsg(fd, msg, flags);
2058c2ecf20Sopenharmony_ci	if (ret == -1 && errno == EAGAIN)
2068c2ecf20Sopenharmony_ci		return false;
2078c2ecf20Sopenharmony_ci	if (ret == -1)
2088c2ecf20Sopenharmony_ci		error(1, errno, "send");
2098c2ecf20Sopenharmony_ci	if (cfg_verbose && ret != len)
2108c2ecf20Sopenharmony_ci		fprintf(stderr, "send: ret=%u != %u\n", ret, len);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (len) {
2138c2ecf20Sopenharmony_ci		packets++;
2148c2ecf20Sopenharmony_ci		bytes += ret;
2158c2ecf20Sopenharmony_ci		if (do_zerocopy && ret)
2168c2ecf20Sopenharmony_ci			expected_completions++;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci	if (do_zerocopy && domain == PF_RDS) {
2198c2ecf20Sopenharmony_ci		msg->msg_control = NULL;
2208c2ecf20Sopenharmony_ci		msg->msg_controllen = 0;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return true;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void do_sendmsg_corked(int fd, struct msghdr *msg)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	bool do_zerocopy = cfg_zerocopy;
2298c2ecf20Sopenharmony_ci	int i, payload_len, extra_len;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* split up the packet. for non-multiple, make first buffer longer */
2328c2ecf20Sopenharmony_ci	payload_len = cfg_payload_len / cfg_cork;
2338c2ecf20Sopenharmony_ci	extra_len = cfg_payload_len - (cfg_cork * payload_len);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 1);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	for (i = 0; i < cfg_cork; i++) {
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		/* in mixed-frags mode, alternate zerocopy and copy frags
2408c2ecf20Sopenharmony_ci		 * start with non-zerocopy, to ensure attach later works
2418c2ecf20Sopenharmony_ci		 */
2428c2ecf20Sopenharmony_ci		if (cfg_cork_mixed)
2438c2ecf20Sopenharmony_ci			do_zerocopy = (i & 1);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		msg->msg_iov[0].iov_len = payload_len + extra_len;
2468c2ecf20Sopenharmony_ci		extra_len = 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		do_sendmsg(fd, msg, do_zerocopy,
2498c2ecf20Sopenharmony_ci			   (cfg_dst_addr.ss_family == AF_INET ?
2508c2ecf20Sopenharmony_ci			    PF_INET : PF_INET6));
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int setup_iph(struct iphdr *iph, uint16_t payload_len)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct sockaddr_in *daddr = (void *) &cfg_dst_addr;
2598c2ecf20Sopenharmony_ci	struct sockaddr_in *saddr = (void *) &cfg_src_addr;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	memset(iph, 0, sizeof(*iph));
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	iph->version	= 4;
2648c2ecf20Sopenharmony_ci	iph->tos	= 0;
2658c2ecf20Sopenharmony_ci	iph->ihl	= 5;
2668c2ecf20Sopenharmony_ci	iph->ttl	= 2;
2678c2ecf20Sopenharmony_ci	iph->saddr	= saddr->sin_addr.s_addr;
2688c2ecf20Sopenharmony_ci	iph->daddr	= daddr->sin_addr.s_addr;
2698c2ecf20Sopenharmony_ci	iph->protocol	= IPPROTO_EGP;
2708c2ecf20Sopenharmony_ci	iph->tot_len	= htons(sizeof(*iph) + payload_len);
2718c2ecf20Sopenharmony_ci	iph->check	= get_ip_csum((void *) iph, iph->ihl << 1);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return sizeof(*iph);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct sockaddr_in6 *daddr = (void *) &cfg_dst_addr;
2798c2ecf20Sopenharmony_ci	struct sockaddr_in6 *saddr = (void *) &cfg_src_addr;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	memset(ip6h, 0, sizeof(*ip6h));
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	ip6h->version		= 6;
2848c2ecf20Sopenharmony_ci	ip6h->payload_len	= htons(payload_len);
2858c2ecf20Sopenharmony_ci	ip6h->nexthdr		= IPPROTO_EGP;
2868c2ecf20Sopenharmony_ci	ip6h->hop_limit		= 2;
2878c2ecf20Sopenharmony_ci	ip6h->saddr		= saddr->sin6_addr;
2888c2ecf20Sopenharmony_ci	ip6h->daddr		= daddr->sin6_addr;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return sizeof(*ip6h);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic void setup_sockaddr(int domain, const char *str_addr,
2958c2ecf20Sopenharmony_ci			   struct sockaddr_storage *sockaddr)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct sockaddr_in6 *addr6 = (void *) sockaddr;
2988c2ecf20Sopenharmony_ci	struct sockaddr_in *addr4 = (void *) sockaddr;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	switch (domain) {
3018c2ecf20Sopenharmony_ci	case PF_INET:
3028c2ecf20Sopenharmony_ci		memset(addr4, 0, sizeof(*addr4));
3038c2ecf20Sopenharmony_ci		addr4->sin_family = AF_INET;
3048c2ecf20Sopenharmony_ci		addr4->sin_port = htons(cfg_port);
3058c2ecf20Sopenharmony_ci		if (str_addr &&
3068c2ecf20Sopenharmony_ci		    inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
3078c2ecf20Sopenharmony_ci			error(1, 0, "ipv4 parse error: %s", str_addr);
3088c2ecf20Sopenharmony_ci		break;
3098c2ecf20Sopenharmony_ci	case PF_INET6:
3108c2ecf20Sopenharmony_ci		memset(addr6, 0, sizeof(*addr6));
3118c2ecf20Sopenharmony_ci		addr6->sin6_family = AF_INET6;
3128c2ecf20Sopenharmony_ci		addr6->sin6_port = htons(cfg_port);
3138c2ecf20Sopenharmony_ci		if (str_addr &&
3148c2ecf20Sopenharmony_ci		    inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
3158c2ecf20Sopenharmony_ci			error(1, 0, "ipv6 parse error: %s", str_addr);
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	default:
3188c2ecf20Sopenharmony_ci		error(1, 0, "illegal domain");
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int do_setup_tx(int domain, int type, int protocol)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	int fd;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	fd = socket(domain, type, protocol);
3278c2ecf20Sopenharmony_ci	if (fd == -1)
3288c2ecf20Sopenharmony_ci		error(1, errno, "socket t");
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	do_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 1 << 21);
3318c2ecf20Sopenharmony_ci	if (cfg_zerocopy)
3328c2ecf20Sopenharmony_ci		do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (domain != PF_PACKET && domain != PF_RDS)
3358c2ecf20Sopenharmony_ci		if (connect(fd, (void *) &cfg_dst_addr, cfg_alen))
3368c2ecf20Sopenharmony_ci			error(1, errno, "connect");
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (domain == PF_RDS) {
3398c2ecf20Sopenharmony_ci		if (bind(fd, (void *) &cfg_src_addr, cfg_alen))
3408c2ecf20Sopenharmony_ci			error(1, errno, "bind");
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return fd;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic uint32_t do_process_zerocopy_cookies(struct rds_zcopy_cookies *ck)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	int i;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (ck->num > RDS_MAX_ZCOOKIES)
3518c2ecf20Sopenharmony_ci		error(1, 0, "Returned %d cookies, max expected %d\n",
3528c2ecf20Sopenharmony_ci		      ck->num, RDS_MAX_ZCOOKIES);
3538c2ecf20Sopenharmony_ci	for (i = 0; i < ck->num; i++)
3548c2ecf20Sopenharmony_ci		if (cfg_verbose >= 2)
3558c2ecf20Sopenharmony_ci			fprintf(stderr, "%d\n", ck->cookies[i]);
3568c2ecf20Sopenharmony_ci	return ck->num;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic bool do_recvmsg_completion(int fd)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	char cmsgbuf[CMSG_SPACE(sizeof(struct rds_zcopy_cookies))];
3628c2ecf20Sopenharmony_ci	struct rds_zcopy_cookies *ck;
3638c2ecf20Sopenharmony_ci	struct cmsghdr *cmsg;
3648c2ecf20Sopenharmony_ci	struct msghdr msg;
3658c2ecf20Sopenharmony_ci	bool ret = false;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
3688c2ecf20Sopenharmony_ci	msg.msg_control = cmsgbuf;
3698c2ecf20Sopenharmony_ci	msg.msg_controllen = sizeof(cmsgbuf);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (recvmsg(fd, &msg, MSG_DONTWAIT))
3728c2ecf20Sopenharmony_ci		return ret;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (msg.msg_flags & MSG_CTRUNC)
3758c2ecf20Sopenharmony_ci		error(1, errno, "recvmsg notification: truncated");
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
3788c2ecf20Sopenharmony_ci		if (cmsg->cmsg_level == SOL_RDS &&
3798c2ecf20Sopenharmony_ci		    cmsg->cmsg_type == RDS_CMSG_ZCOPY_COMPLETION) {
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci			ck = (struct rds_zcopy_cookies *)CMSG_DATA(cmsg);
3828c2ecf20Sopenharmony_ci			completions += do_process_zerocopy_cookies(ck);
3838c2ecf20Sopenharmony_ci			ret = true;
3848c2ecf20Sopenharmony_ci			break;
3858c2ecf20Sopenharmony_ci		}
3868c2ecf20Sopenharmony_ci		error(0, 0, "ignoring cmsg at level %d type %d\n",
3878c2ecf20Sopenharmony_ci			    cmsg->cmsg_level, cmsg->cmsg_type);
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci	return ret;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic bool do_recv_completion(int fd, int domain)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct sock_extended_err *serr;
3958c2ecf20Sopenharmony_ci	struct msghdr msg = {};
3968c2ecf20Sopenharmony_ci	struct cmsghdr *cm;
3978c2ecf20Sopenharmony_ci	uint32_t hi, lo, range;
3988c2ecf20Sopenharmony_ci	int ret, zerocopy;
3998c2ecf20Sopenharmony_ci	char control[100];
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (domain == PF_RDS)
4028c2ecf20Sopenharmony_ci		return do_recvmsg_completion(fd);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	msg.msg_control = control;
4058c2ecf20Sopenharmony_ci	msg.msg_controllen = sizeof(control);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
4088c2ecf20Sopenharmony_ci	if (ret == -1 && errno == EAGAIN)
4098c2ecf20Sopenharmony_ci		return false;
4108c2ecf20Sopenharmony_ci	if (ret == -1)
4118c2ecf20Sopenharmony_ci		error(1, errno, "recvmsg notification");
4128c2ecf20Sopenharmony_ci	if (msg.msg_flags & MSG_CTRUNC)
4138c2ecf20Sopenharmony_ci		error(1, errno, "recvmsg notification: truncated");
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	cm = CMSG_FIRSTHDR(&msg);
4168c2ecf20Sopenharmony_ci	if (!cm)
4178c2ecf20Sopenharmony_ci		error(1, 0, "cmsg: no cmsg");
4188c2ecf20Sopenharmony_ci	if (!((cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) ||
4198c2ecf20Sopenharmony_ci	      (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_RECVERR) ||
4208c2ecf20Sopenharmony_ci	      (cm->cmsg_level == SOL_PACKET && cm->cmsg_type == PACKET_TX_TIMESTAMP)))
4218c2ecf20Sopenharmony_ci		error(1, 0, "serr: wrong type: %d.%d",
4228c2ecf20Sopenharmony_ci		      cm->cmsg_level, cm->cmsg_type);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	serr = (void *) CMSG_DATA(cm);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
4278c2ecf20Sopenharmony_ci		error(1, 0, "serr: wrong origin: %u", serr->ee_origin);
4288c2ecf20Sopenharmony_ci	if (serr->ee_errno != 0)
4298c2ecf20Sopenharmony_ci		error(1, 0, "serr: wrong error code: %u", serr->ee_errno);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	hi = serr->ee_data;
4328c2ecf20Sopenharmony_ci	lo = serr->ee_info;
4338c2ecf20Sopenharmony_ci	range = hi - lo + 1;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Detect notification gaps. These should not happen often, if at all.
4368c2ecf20Sopenharmony_ci	 * Gaps can occur due to drops, reordering and retransmissions.
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	if (lo != next_completion)
4398c2ecf20Sopenharmony_ci		fprintf(stderr, "gap: %u..%u does not append to %u\n",
4408c2ecf20Sopenharmony_ci			lo, hi, next_completion);
4418c2ecf20Sopenharmony_ci	next_completion = hi + 1;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED);
4448c2ecf20Sopenharmony_ci	if (zerocopied == -1)
4458c2ecf20Sopenharmony_ci		zerocopied = zerocopy;
4468c2ecf20Sopenharmony_ci	else if (zerocopied != zerocopy) {
4478c2ecf20Sopenharmony_ci		fprintf(stderr, "serr: inconsistent\n");
4488c2ecf20Sopenharmony_ci		zerocopied = zerocopy;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (cfg_verbose >= 2)
4528c2ecf20Sopenharmony_ci		fprintf(stderr, "completed: %u (h=%u l=%u)\n",
4538c2ecf20Sopenharmony_ci			range, hi, lo);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	completions += range;
4568c2ecf20Sopenharmony_ci	return true;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/* Read all outstanding messages on the errqueue */
4608c2ecf20Sopenharmony_cistatic void do_recv_completions(int fd, int domain)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	while (do_recv_completion(fd, domain)) {}
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/* Wait for all remaining completions on the errqueue */
4668c2ecf20Sopenharmony_cistatic void do_recv_remaining_completions(int fd, int domain)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	int64_t tstop = gettimeofday_ms() + cfg_waittime_ms;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	while (completions < expected_completions &&
4718c2ecf20Sopenharmony_ci	       gettimeofday_ms() < tstop) {
4728c2ecf20Sopenharmony_ci		if (do_poll(fd, domain == PF_RDS ? POLLIN : POLLERR))
4738c2ecf20Sopenharmony_ci			do_recv_completions(fd, domain);
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (completions < expected_completions)
4778c2ecf20Sopenharmony_ci		fprintf(stderr, "missing notifications: %lu < %lu\n",
4788c2ecf20Sopenharmony_ci			completions, expected_completions);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic void do_tx(int domain, int type, int protocol)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	struct iovec iov[3] = { {0} };
4848c2ecf20Sopenharmony_ci	struct sockaddr_ll laddr;
4858c2ecf20Sopenharmony_ci	struct msghdr msg = {0};
4868c2ecf20Sopenharmony_ci	struct ethhdr eth;
4878c2ecf20Sopenharmony_ci	union {
4888c2ecf20Sopenharmony_ci		struct ipv6hdr ip6h;
4898c2ecf20Sopenharmony_ci		struct iphdr iph;
4908c2ecf20Sopenharmony_ci	} nh;
4918c2ecf20Sopenharmony_ci	uint64_t tstop;
4928c2ecf20Sopenharmony_ci	int fd;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	fd = do_setup_tx(domain, type, protocol);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	if (domain == PF_PACKET) {
4978c2ecf20Sopenharmony_ci		uint16_t proto = cfg_family == PF_INET ? ETH_P_IP : ETH_P_IPV6;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		/* sock_raw passes ll header as data */
5008c2ecf20Sopenharmony_ci		if (type == SOCK_RAW) {
5018c2ecf20Sopenharmony_ci			memset(eth.h_dest, 0x06, ETH_ALEN);
5028c2ecf20Sopenharmony_ci			memset(eth.h_source, 0x02, ETH_ALEN);
5038c2ecf20Sopenharmony_ci			eth.h_proto = htons(proto);
5048c2ecf20Sopenharmony_ci			iov[0].iov_base = &eth;
5058c2ecf20Sopenharmony_ci			iov[0].iov_len = sizeof(eth);
5068c2ecf20Sopenharmony_ci			msg.msg_iovlen++;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		/* both sock_raw and sock_dgram expect name */
5108c2ecf20Sopenharmony_ci		memset(&laddr, 0, sizeof(laddr));
5118c2ecf20Sopenharmony_ci		laddr.sll_family	= AF_PACKET;
5128c2ecf20Sopenharmony_ci		laddr.sll_ifindex	= cfg_ifindex;
5138c2ecf20Sopenharmony_ci		laddr.sll_protocol	= htons(proto);
5148c2ecf20Sopenharmony_ci		laddr.sll_halen		= ETH_ALEN;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci		memset(laddr.sll_addr, 0x06, ETH_ALEN);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		msg.msg_name		= &laddr;
5198c2ecf20Sopenharmony_ci		msg.msg_namelen		= sizeof(laddr);
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/* packet and raw sockets with hdrincl must pass network header */
5238c2ecf20Sopenharmony_ci	if (domain == PF_PACKET || protocol == IPPROTO_RAW) {
5248c2ecf20Sopenharmony_ci		if (cfg_family == PF_INET)
5258c2ecf20Sopenharmony_ci			iov[1].iov_len = setup_iph(&nh.iph, cfg_payload_len);
5268c2ecf20Sopenharmony_ci		else
5278c2ecf20Sopenharmony_ci			iov[1].iov_len = setup_ip6h(&nh.ip6h, cfg_payload_len);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		iov[1].iov_base = (void *) &nh;
5308c2ecf20Sopenharmony_ci		msg.msg_iovlen++;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (domain == PF_RDS) {
5348c2ecf20Sopenharmony_ci		msg.msg_name = &cfg_dst_addr;
5358c2ecf20Sopenharmony_ci		msg.msg_namelen =  (cfg_dst_addr.ss_family == AF_INET ?
5368c2ecf20Sopenharmony_ci				    sizeof(struct sockaddr_in) :
5378c2ecf20Sopenharmony_ci				    sizeof(struct sockaddr_in6));
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	iov[2].iov_base = payload;
5418c2ecf20Sopenharmony_ci	iov[2].iov_len = cfg_payload_len;
5428c2ecf20Sopenharmony_ci	msg.msg_iovlen++;
5438c2ecf20Sopenharmony_ci	msg.msg_iov = &iov[3 - msg.msg_iovlen];
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	tstop = gettimeofday_ms() + cfg_runtime_ms;
5468c2ecf20Sopenharmony_ci	do {
5478c2ecf20Sopenharmony_ci		if (cfg_cork)
5488c2ecf20Sopenharmony_ci			do_sendmsg_corked(fd, &msg);
5498c2ecf20Sopenharmony_ci		else
5508c2ecf20Sopenharmony_ci			do_sendmsg(fd, &msg, cfg_zerocopy, domain);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		while (!do_poll(fd, POLLOUT)) {
5538c2ecf20Sopenharmony_ci			if (cfg_zerocopy)
5548c2ecf20Sopenharmony_ci				do_recv_completions(fd, domain);
5558c2ecf20Sopenharmony_ci		}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	} while (gettimeofday_ms() < tstop);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (cfg_zerocopy)
5608c2ecf20Sopenharmony_ci		do_recv_remaining_completions(fd, domain);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (close(fd))
5638c2ecf20Sopenharmony_ci		error(1, errno, "close");
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n",
5668c2ecf20Sopenharmony_ci		packets, bytes >> 20, completions,
5678c2ecf20Sopenharmony_ci		zerocopied == 1 ? 'y' : 'n');
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic int do_setup_rx(int domain, int type, int protocol)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	int fd;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* If tx over PF_PACKET, rx over PF_INET(6)/SOCK_RAW,
5758c2ecf20Sopenharmony_ci	 * to recv the only copy of the packet, not a clone
5768c2ecf20Sopenharmony_ci	 */
5778c2ecf20Sopenharmony_ci	if (domain == PF_PACKET)
5788c2ecf20Sopenharmony_ci		error(1, 0, "Use PF_INET/SOCK_RAW to read");
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	if (type == SOCK_RAW && protocol == IPPROTO_RAW)
5818c2ecf20Sopenharmony_ci		error(1, 0, "IPPROTO_RAW: not supported on Rx");
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	fd = socket(domain, type, protocol);
5848c2ecf20Sopenharmony_ci	if (fd == -1)
5858c2ecf20Sopenharmony_ci		error(1, errno, "socket r");
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	do_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 1 << 21);
5888c2ecf20Sopenharmony_ci	do_setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, 1 << 16);
5898c2ecf20Sopenharmony_ci	do_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (bind(fd, (void *) &cfg_dst_addr, cfg_alen))
5928c2ecf20Sopenharmony_ci		error(1, errno, "bind");
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (type == SOCK_STREAM) {
5958c2ecf20Sopenharmony_ci		if (listen(fd, 1))
5968c2ecf20Sopenharmony_ci			error(1, errno, "listen");
5978c2ecf20Sopenharmony_ci		fd = do_accept(fd);
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	return fd;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/* Flush all outstanding bytes for the tcp receive queue */
6048c2ecf20Sopenharmony_cistatic void do_flush_tcp(int fd)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	int ret;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* MSG_TRUNC flushes up to len bytes */
6098c2ecf20Sopenharmony_ci	ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
6108c2ecf20Sopenharmony_ci	if (ret == -1 && errno == EAGAIN)
6118c2ecf20Sopenharmony_ci		return;
6128c2ecf20Sopenharmony_ci	if (ret == -1)
6138c2ecf20Sopenharmony_ci		error(1, errno, "flush");
6148c2ecf20Sopenharmony_ci	if (!ret)
6158c2ecf20Sopenharmony_ci		return;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	packets++;
6188c2ecf20Sopenharmony_ci	bytes += ret;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/* Flush all outstanding datagrams. Verify first few bytes of each. */
6228c2ecf20Sopenharmony_cistatic void do_flush_datagram(int fd, int type)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	int ret, off = 0;
6258c2ecf20Sopenharmony_ci	char buf[64];
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* MSG_TRUNC will return full datagram length */
6288c2ecf20Sopenharmony_ci	ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_TRUNC);
6298c2ecf20Sopenharmony_ci	if (ret == -1 && errno == EAGAIN)
6308c2ecf20Sopenharmony_ci		return;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	/* raw ipv4 return with header, raw ipv6 without */
6338c2ecf20Sopenharmony_ci	if (cfg_family == PF_INET && type == SOCK_RAW) {
6348c2ecf20Sopenharmony_ci		off += sizeof(struct iphdr);
6358c2ecf20Sopenharmony_ci		ret -= sizeof(struct iphdr);
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (ret == -1)
6398c2ecf20Sopenharmony_ci		error(1, errno, "recv");
6408c2ecf20Sopenharmony_ci	if (ret != cfg_payload_len)
6418c2ecf20Sopenharmony_ci		error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len);
6428c2ecf20Sopenharmony_ci	if (ret > sizeof(buf) - off)
6438c2ecf20Sopenharmony_ci		ret = sizeof(buf) - off;
6448c2ecf20Sopenharmony_ci	if (memcmp(buf + off, payload, ret))
6458c2ecf20Sopenharmony_ci		error(1, 0, "recv: data mismatch");
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	packets++;
6488c2ecf20Sopenharmony_ci	bytes += cfg_payload_len;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void do_rx(int domain, int type, int protocol)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	const int cfg_receiver_wait_ms = 400;
6548c2ecf20Sopenharmony_ci	uint64_t tstop;
6558c2ecf20Sopenharmony_ci	int fd;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	fd = do_setup_rx(domain, type, protocol);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	tstop = gettimeofday_ms() + cfg_runtime_ms + cfg_receiver_wait_ms;
6608c2ecf20Sopenharmony_ci	do {
6618c2ecf20Sopenharmony_ci		if (type == SOCK_STREAM)
6628c2ecf20Sopenharmony_ci			do_flush_tcp(fd);
6638c2ecf20Sopenharmony_ci		else
6648c2ecf20Sopenharmony_ci			do_flush_datagram(fd, type);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci		do_poll(fd, POLLIN);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	} while (gettimeofday_ms() < tstop);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (close(fd))
6718c2ecf20Sopenharmony_ci		error(1, errno, "close");
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	fprintf(stderr, "rx=%lu (%lu MB)\n", packets, bytes >> 20);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic void do_test(int domain, int type, int protocol)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	int i;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (cfg_cork && (domain == PF_PACKET || type != SOCK_DGRAM))
6818c2ecf20Sopenharmony_ci		error(1, 0, "can only cork udp sockets");
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	do_setcpu(cfg_cpu);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	for (i = 0; i < IP_MAXPACKET; i++)
6868c2ecf20Sopenharmony_ci		payload[i] = 'a' + (i % 26);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (cfg_rx)
6898c2ecf20Sopenharmony_ci		do_rx(domain, type, protocol);
6908c2ecf20Sopenharmony_ci	else
6918c2ecf20Sopenharmony_ci		do_tx(domain, type, protocol);
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic void usage(const char *filepath)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	error(1, 0, "Usage: %s [options] <test>", filepath);
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cistatic void parse_opts(int argc, char **argv)
7008c2ecf20Sopenharmony_ci{
7018c2ecf20Sopenharmony_ci	const int max_payload_len = sizeof(payload) -
7028c2ecf20Sopenharmony_ci				    sizeof(struct ipv6hdr) -
7038c2ecf20Sopenharmony_ci				    sizeof(struct tcphdr) -
7048c2ecf20Sopenharmony_ci				    40 /* max tcp options */;
7058c2ecf20Sopenharmony_ci	int c;
7068c2ecf20Sopenharmony_ci	char *daddr = NULL, *saddr = NULL;
7078c2ecf20Sopenharmony_ci	char *cfg_test;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	cfg_payload_len = max_payload_len;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	while ((c = getopt(argc, argv, "46c:C:D:i:mp:rs:S:t:vz")) != -1) {
7128c2ecf20Sopenharmony_ci		switch (c) {
7138c2ecf20Sopenharmony_ci		case '4':
7148c2ecf20Sopenharmony_ci			if (cfg_family != PF_UNSPEC)
7158c2ecf20Sopenharmony_ci				error(1, 0, "Pass one of -4 or -6");
7168c2ecf20Sopenharmony_ci			cfg_family = PF_INET;
7178c2ecf20Sopenharmony_ci			cfg_alen = sizeof(struct sockaddr_in);
7188c2ecf20Sopenharmony_ci			break;
7198c2ecf20Sopenharmony_ci		case '6':
7208c2ecf20Sopenharmony_ci			if (cfg_family != PF_UNSPEC)
7218c2ecf20Sopenharmony_ci				error(1, 0, "Pass one of -4 or -6");
7228c2ecf20Sopenharmony_ci			cfg_family = PF_INET6;
7238c2ecf20Sopenharmony_ci			cfg_alen = sizeof(struct sockaddr_in6);
7248c2ecf20Sopenharmony_ci			break;
7258c2ecf20Sopenharmony_ci		case 'c':
7268c2ecf20Sopenharmony_ci			cfg_cork = strtol(optarg, NULL, 0);
7278c2ecf20Sopenharmony_ci			break;
7288c2ecf20Sopenharmony_ci		case 'C':
7298c2ecf20Sopenharmony_ci			cfg_cpu = strtol(optarg, NULL, 0);
7308c2ecf20Sopenharmony_ci			break;
7318c2ecf20Sopenharmony_ci		case 'D':
7328c2ecf20Sopenharmony_ci			daddr = optarg;
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci		case 'i':
7358c2ecf20Sopenharmony_ci			cfg_ifindex = if_nametoindex(optarg);
7368c2ecf20Sopenharmony_ci			if (cfg_ifindex == 0)
7378c2ecf20Sopenharmony_ci				error(1, errno, "invalid iface: %s", optarg);
7388c2ecf20Sopenharmony_ci			break;
7398c2ecf20Sopenharmony_ci		case 'm':
7408c2ecf20Sopenharmony_ci			cfg_cork_mixed = true;
7418c2ecf20Sopenharmony_ci			break;
7428c2ecf20Sopenharmony_ci		case 'p':
7438c2ecf20Sopenharmony_ci			cfg_port = strtoul(optarg, NULL, 0);
7448c2ecf20Sopenharmony_ci			break;
7458c2ecf20Sopenharmony_ci		case 'r':
7468c2ecf20Sopenharmony_ci			cfg_rx = true;
7478c2ecf20Sopenharmony_ci			break;
7488c2ecf20Sopenharmony_ci		case 's':
7498c2ecf20Sopenharmony_ci			cfg_payload_len = strtoul(optarg, NULL, 0);
7508c2ecf20Sopenharmony_ci			break;
7518c2ecf20Sopenharmony_ci		case 'S':
7528c2ecf20Sopenharmony_ci			saddr = optarg;
7538c2ecf20Sopenharmony_ci			break;
7548c2ecf20Sopenharmony_ci		case 't':
7558c2ecf20Sopenharmony_ci			cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000;
7568c2ecf20Sopenharmony_ci			break;
7578c2ecf20Sopenharmony_ci		case 'v':
7588c2ecf20Sopenharmony_ci			cfg_verbose++;
7598c2ecf20Sopenharmony_ci			break;
7608c2ecf20Sopenharmony_ci		case 'z':
7618c2ecf20Sopenharmony_ci			cfg_zerocopy = true;
7628c2ecf20Sopenharmony_ci			break;
7638c2ecf20Sopenharmony_ci		}
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	cfg_test = argv[argc - 1];
7678c2ecf20Sopenharmony_ci	if (strcmp(cfg_test, "rds") == 0) {
7688c2ecf20Sopenharmony_ci		if (!daddr)
7698c2ecf20Sopenharmony_ci			error(1, 0, "-D <server addr> required for PF_RDS\n");
7708c2ecf20Sopenharmony_ci		if (!cfg_rx && !saddr)
7718c2ecf20Sopenharmony_ci			error(1, 0, "-S <client addr> required for PF_RDS\n");
7728c2ecf20Sopenharmony_ci	}
7738c2ecf20Sopenharmony_ci	setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
7748c2ecf20Sopenharmony_ci	setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	if (cfg_payload_len > max_payload_len)
7778c2ecf20Sopenharmony_ci		error(1, 0, "-s: payload exceeds max (%d)", max_payload_len);
7788c2ecf20Sopenharmony_ci	if (cfg_cork_mixed && (!cfg_zerocopy || !cfg_cork))
7798c2ecf20Sopenharmony_ci		error(1, 0, "-m: cork_mixed requires corking and zerocopy");
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	if (optind != argc - 1)
7828c2ecf20Sopenharmony_ci		usage(argv[0]);
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ciint main(int argc, char **argv)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	const char *cfg_test;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	parse_opts(argc, argv);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	cfg_test = argv[argc - 1];
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	if (!strcmp(cfg_test, "packet"))
7948c2ecf20Sopenharmony_ci		do_test(PF_PACKET, SOCK_RAW, 0);
7958c2ecf20Sopenharmony_ci	else if (!strcmp(cfg_test, "packet_dgram"))
7968c2ecf20Sopenharmony_ci		do_test(PF_PACKET, SOCK_DGRAM, 0);
7978c2ecf20Sopenharmony_ci	else if (!strcmp(cfg_test, "raw"))
7988c2ecf20Sopenharmony_ci		do_test(cfg_family, SOCK_RAW, IPPROTO_EGP);
7998c2ecf20Sopenharmony_ci	else if (!strcmp(cfg_test, "raw_hdrincl"))
8008c2ecf20Sopenharmony_ci		do_test(cfg_family, SOCK_RAW, IPPROTO_RAW);
8018c2ecf20Sopenharmony_ci	else if (!strcmp(cfg_test, "tcp"))
8028c2ecf20Sopenharmony_ci		do_test(cfg_family, SOCK_STREAM, 0);
8038c2ecf20Sopenharmony_ci	else if (!strcmp(cfg_test, "udp"))
8048c2ecf20Sopenharmony_ci		do_test(cfg_family, SOCK_DGRAM, 0);
8058c2ecf20Sopenharmony_ci	else if (!strcmp(cfg_test, "rds"))
8068c2ecf20Sopenharmony_ci		do_test(PF_RDS, SOCK_SEQPACKET, 0);
8078c2ecf20Sopenharmony_ci	else
8088c2ecf20Sopenharmony_ci		error(1, 0, "unknown cfg_test %s", cfg_test);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	return 0;
8118c2ecf20Sopenharmony_ci}
812