xref: /linux/tools/testing/selftests/net/msg_zerocopy.c (revision dfb8434b0a94651dac7adf373900561d8f6499e4)
107b65c5bSWillem de Bruijn /* Evaluate MSG_ZEROCOPY
207b65c5bSWillem de Bruijn  *
307b65c5bSWillem de Bruijn  * Send traffic between two processes over one of the supported
407b65c5bSWillem de Bruijn  * protocols and modes:
507b65c5bSWillem de Bruijn  *
607b65c5bSWillem de Bruijn  * PF_INET/PF_INET6
707b65c5bSWillem de Bruijn  * - SOCK_STREAM
807b65c5bSWillem de Bruijn  * - SOCK_DGRAM
907b65c5bSWillem de Bruijn  * - SOCK_DGRAM with UDP_CORK
1007b65c5bSWillem de Bruijn  * - SOCK_RAW
1107b65c5bSWillem de Bruijn  * - SOCK_RAW with IP_HDRINCL
1207b65c5bSWillem de Bruijn  *
1307b65c5bSWillem de Bruijn  * PF_PACKET
1407b65c5bSWillem de Bruijn  * - SOCK_DGRAM
1507b65c5bSWillem de Bruijn  * - SOCK_RAW
1607b65c5bSWillem de Bruijn  *
17b16ac920SSowmini Varadhan  * PF_RDS
18b16ac920SSowmini Varadhan  * - SOCK_SEQPACKET
19b16ac920SSowmini Varadhan  *
2007b65c5bSWillem de Bruijn  * Start this program on two connected hosts, one in send mode and
2107b65c5bSWillem de Bruijn  * the other with option '-r' to put it in receiver mode.
2207b65c5bSWillem de Bruijn  *
2307b65c5bSWillem de Bruijn  * If zerocopy mode ('-z') is enabled, the sender will verify that
2407b65c5bSWillem de Bruijn  * the kernel queues completions on the error queue for all zerocopy
2507b65c5bSWillem de Bruijn  * transfers.
2607b65c5bSWillem de Bruijn  */
2707b65c5bSWillem de Bruijn 
2807b65c5bSWillem de Bruijn #define _GNU_SOURCE
2907b65c5bSWillem de Bruijn 
3007b65c5bSWillem de Bruijn #include <arpa/inet.h>
3107b65c5bSWillem de Bruijn #include <error.h>
3207b65c5bSWillem de Bruijn #include <errno.h>
3307b65c5bSWillem de Bruijn #include <limits.h>
3407b65c5bSWillem de Bruijn #include <linux/errqueue.h>
3507b65c5bSWillem de Bruijn #include <linux/if_packet.h>
3607b65c5bSWillem de Bruijn #include <linux/ipv6.h>
3707b65c5bSWillem de Bruijn #include <linux/socket.h>
3807b65c5bSWillem de Bruijn #include <linux/sockios.h>
3907b65c5bSWillem de Bruijn #include <net/ethernet.h>
4007b65c5bSWillem de Bruijn #include <net/if.h>
4107b65c5bSWillem de Bruijn #include <netinet/ip.h>
4207b65c5bSWillem de Bruijn #include <netinet/ip6.h>
4307b65c5bSWillem de Bruijn #include <netinet/tcp.h>
4407b65c5bSWillem de Bruijn #include <netinet/udp.h>
4507b65c5bSWillem de Bruijn #include <poll.h>
4607b65c5bSWillem de Bruijn #include <sched.h>
4707b65c5bSWillem de Bruijn #include <stdbool.h>
4807b65c5bSWillem de Bruijn #include <stdio.h>
4907b65c5bSWillem de Bruijn #include <stdint.h>
5007b65c5bSWillem de Bruijn #include <stdlib.h>
5107b65c5bSWillem de Bruijn #include <string.h>
5207b65c5bSWillem de Bruijn #include <sys/ioctl.h>
5307b65c5bSWillem de Bruijn #include <sys/socket.h>
5407b65c5bSWillem de Bruijn #include <sys/stat.h>
5507b65c5bSWillem de Bruijn #include <sys/time.h>
5607b65c5bSWillem de Bruijn #include <sys/types.h>
5707b65c5bSWillem de Bruijn #include <sys/wait.h>
5807b65c5bSWillem de Bruijn #include <unistd.h>
59b16ac920SSowmini Varadhan #include <linux/rds.h>
6007b65c5bSWillem de Bruijn 
6107b65c5bSWillem de Bruijn #ifndef SO_EE_ORIGIN_ZEROCOPY
6206e8852cSThomas Meyer #define SO_EE_ORIGIN_ZEROCOPY		5
6307b65c5bSWillem de Bruijn #endif
6407b65c5bSWillem de Bruijn 
6507b65c5bSWillem de Bruijn #ifndef SO_ZEROCOPY
66bbd9644eSWillem de Bruijn #define SO_ZEROCOPY	60
6707b65c5bSWillem de Bruijn #endif
6807b65c5bSWillem de Bruijn 
6907b65c5bSWillem de Bruijn #ifndef SO_EE_CODE_ZEROCOPY_COPIED
7007b65c5bSWillem de Bruijn #define SO_EE_CODE_ZEROCOPY_COPIED	1
7107b65c5bSWillem de Bruijn #endif
7207b65c5bSWillem de Bruijn 
7307b65c5bSWillem de Bruijn #ifndef MSG_ZEROCOPY
7407b65c5bSWillem de Bruijn #define MSG_ZEROCOPY	0x4000000
7507b65c5bSWillem de Bruijn #endif
7607b65c5bSWillem de Bruijn 
7707b65c5bSWillem de Bruijn static int  cfg_cork;
7807b65c5bSWillem de Bruijn static bool cfg_cork_mixed;
7907b65c5bSWillem de Bruijn static int  cfg_cpu		= -1;		/* default: pin to last cpu */
8007b65c5bSWillem de Bruijn static int  cfg_family		= PF_UNSPEC;
8107b65c5bSWillem de Bruijn static int  cfg_ifindex		= 1;
8207b65c5bSWillem de Bruijn static int  cfg_payload_len;
8307b65c5bSWillem de Bruijn static int  cfg_port		= 8000;
8407b65c5bSWillem de Bruijn static bool cfg_rx;
8507b65c5bSWillem de Bruijn static int  cfg_runtime_ms	= 4200;
8607b65c5bSWillem de Bruijn static int  cfg_verbose;
8707b65c5bSWillem de Bruijn static int  cfg_waittime_ms	= 500;
8807b65c5bSWillem de Bruijn static bool cfg_zerocopy;
8907b65c5bSWillem de Bruijn 
9007b65c5bSWillem de Bruijn static socklen_t cfg_alen;
9107b65c5bSWillem de Bruijn static struct sockaddr_storage cfg_dst_addr;
9207b65c5bSWillem de Bruijn static struct sockaddr_storage cfg_src_addr;
9307b65c5bSWillem de Bruijn 
9407b65c5bSWillem de Bruijn static char payload[IP_MAXPACKET];
9507b65c5bSWillem de Bruijn static long packets, bytes, completions, expected_completions;
9607b65c5bSWillem de Bruijn static int  zerocopied = -1;
9707b65c5bSWillem de Bruijn static uint32_t next_completion;
9807b65c5bSWillem de Bruijn 
9907b65c5bSWillem de Bruijn static unsigned long gettimeofday_ms(void)
10007b65c5bSWillem de Bruijn {
10107b65c5bSWillem de Bruijn 	struct timeval tv;
10207b65c5bSWillem de Bruijn 
10307b65c5bSWillem de Bruijn 	gettimeofday(&tv, NULL);
10407b65c5bSWillem de Bruijn 	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
10507b65c5bSWillem de Bruijn }
10607b65c5bSWillem de Bruijn 
10707b65c5bSWillem de Bruijn static uint16_t get_ip_csum(const uint16_t *start, int num_words)
10807b65c5bSWillem de Bruijn {
10907b65c5bSWillem de Bruijn 	unsigned long sum = 0;
11007b65c5bSWillem de Bruijn 	int i;
11107b65c5bSWillem de Bruijn 
11207b65c5bSWillem de Bruijn 	for (i = 0; i < num_words; i++)
11307b65c5bSWillem de Bruijn 		sum += start[i];
11407b65c5bSWillem de Bruijn 
11507b65c5bSWillem de Bruijn 	while (sum >> 16)
11607b65c5bSWillem de Bruijn 		sum = (sum & 0xFFFF) + (sum >> 16);
11707b65c5bSWillem de Bruijn 
11807b65c5bSWillem de Bruijn 	return ~sum;
11907b65c5bSWillem de Bruijn }
12007b65c5bSWillem de Bruijn 
12107b65c5bSWillem de Bruijn static int do_setcpu(int cpu)
12207b65c5bSWillem de Bruijn {
12307b65c5bSWillem de Bruijn 	cpu_set_t mask;
12407b65c5bSWillem de Bruijn 
12507b65c5bSWillem de Bruijn 	CPU_ZERO(&mask);
12607b65c5bSWillem de Bruijn 	CPU_SET(cpu, &mask);
12707b65c5bSWillem de Bruijn 	if (sched_setaffinity(0, sizeof(mask), &mask))
12807b65c5bSWillem de Bruijn 		error(1, 0, "setaffinity %d", cpu);
12907b65c5bSWillem de Bruijn 
13007b65c5bSWillem de Bruijn 	if (cfg_verbose)
13107b65c5bSWillem de Bruijn 		fprintf(stderr, "cpu: %u\n", cpu);
13207b65c5bSWillem de Bruijn 
13307b65c5bSWillem de Bruijn 	return 0;
13407b65c5bSWillem de Bruijn }
13507b65c5bSWillem de Bruijn 
13607b65c5bSWillem de Bruijn static void do_setsockopt(int fd, int level, int optname, int val)
13707b65c5bSWillem de Bruijn {
13807b65c5bSWillem de Bruijn 	if (setsockopt(fd, level, optname, &val, sizeof(val)))
13907b65c5bSWillem de Bruijn 		error(1, errno, "setsockopt %d.%d: %d", level, optname, val);
14007b65c5bSWillem de Bruijn }
14107b65c5bSWillem de Bruijn 
14207b65c5bSWillem de Bruijn static int do_poll(int fd, int events)
14307b65c5bSWillem de Bruijn {
14407b65c5bSWillem de Bruijn 	struct pollfd pfd;
14507b65c5bSWillem de Bruijn 	int ret;
14607b65c5bSWillem de Bruijn 
14707b65c5bSWillem de Bruijn 	pfd.events = events;
14807b65c5bSWillem de Bruijn 	pfd.revents = 0;
14907b65c5bSWillem de Bruijn 	pfd.fd = fd;
15007b65c5bSWillem de Bruijn 
15107b65c5bSWillem de Bruijn 	ret = poll(&pfd, 1, cfg_waittime_ms);
15207b65c5bSWillem de Bruijn 	if (ret == -1)
15307b65c5bSWillem de Bruijn 		error(1, errno, "poll");
15407b65c5bSWillem de Bruijn 
15507b65c5bSWillem de Bruijn 	return ret && (pfd.revents & events);
15607b65c5bSWillem de Bruijn }
15707b65c5bSWillem de Bruijn 
15807b65c5bSWillem de Bruijn static int do_accept(int fd)
15907b65c5bSWillem de Bruijn {
16007b65c5bSWillem de Bruijn 	int fda = fd;
16107b65c5bSWillem de Bruijn 
16207b65c5bSWillem de Bruijn 	fd = accept(fda, NULL, NULL);
16307b65c5bSWillem de Bruijn 	if (fd == -1)
16407b65c5bSWillem de Bruijn 		error(1, errno, "accept");
16507b65c5bSWillem de Bruijn 	if (close(fda))
16607b65c5bSWillem de Bruijn 		error(1, errno, "close listen sock");
16707b65c5bSWillem de Bruijn 
16807b65c5bSWillem de Bruijn 	return fd;
16907b65c5bSWillem de Bruijn }
17007b65c5bSWillem de Bruijn 
171*dfb8434bSSowmini Varadhan static void add_zcopy_cookie(struct msghdr *msg, uint32_t cookie)
172*dfb8434bSSowmini Varadhan {
173*dfb8434bSSowmini Varadhan 	struct cmsghdr *cm;
174*dfb8434bSSowmini Varadhan 
175*dfb8434bSSowmini Varadhan 	if (!msg->msg_control)
176*dfb8434bSSowmini Varadhan 		error(1, errno, "NULL cookie");
177*dfb8434bSSowmini Varadhan 	cm = (void *)msg->msg_control;
178*dfb8434bSSowmini Varadhan 	cm->cmsg_len = CMSG_LEN(sizeof(cookie));
179*dfb8434bSSowmini Varadhan 	cm->cmsg_level = SOL_RDS;
180*dfb8434bSSowmini Varadhan 	cm->cmsg_type = RDS_CMSG_ZCOPY_COOKIE;
181*dfb8434bSSowmini Varadhan 	memcpy(CMSG_DATA(cm), &cookie, sizeof(cookie));
182*dfb8434bSSowmini Varadhan }
183*dfb8434bSSowmini Varadhan 
184*dfb8434bSSowmini Varadhan static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy, int domain)
18507b65c5bSWillem de Bruijn {
18607b65c5bSWillem de Bruijn 	int ret, len, i, flags;
187*dfb8434bSSowmini Varadhan 	static uint32_t cookie;
188*dfb8434bSSowmini Varadhan 	char ckbuf[CMSG_SPACE(sizeof(cookie))];
18907b65c5bSWillem de Bruijn 
19007b65c5bSWillem de Bruijn 	len = 0;
19107b65c5bSWillem de Bruijn 	for (i = 0; i < msg->msg_iovlen; i++)
19207b65c5bSWillem de Bruijn 		len += msg->msg_iov[i].iov_len;
19307b65c5bSWillem de Bruijn 
19407b65c5bSWillem de Bruijn 	flags = MSG_DONTWAIT;
195*dfb8434bSSowmini Varadhan 	if (do_zerocopy) {
19607b65c5bSWillem de Bruijn 		flags |= MSG_ZEROCOPY;
197*dfb8434bSSowmini Varadhan 		if (domain == PF_RDS) {
198*dfb8434bSSowmini Varadhan 			memset(&msg->msg_control, 0, sizeof(msg->msg_control));
199*dfb8434bSSowmini Varadhan 			msg->msg_controllen = CMSG_SPACE(sizeof(cookie));
200*dfb8434bSSowmini Varadhan 			msg->msg_control = (struct cmsghdr *)ckbuf;
201*dfb8434bSSowmini Varadhan 			add_zcopy_cookie(msg, ++cookie);
202*dfb8434bSSowmini Varadhan 		}
203*dfb8434bSSowmini Varadhan 	}
20407b65c5bSWillem de Bruijn 
20507b65c5bSWillem de Bruijn 	ret = sendmsg(fd, msg, flags);
20607b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
20707b65c5bSWillem de Bruijn 		return false;
20807b65c5bSWillem de Bruijn 	if (ret == -1)
20907b65c5bSWillem de Bruijn 		error(1, errno, "send");
21007b65c5bSWillem de Bruijn 	if (cfg_verbose && ret != len)
21107b65c5bSWillem de Bruijn 		fprintf(stderr, "send: ret=%u != %u\n", ret, len);
21207b65c5bSWillem de Bruijn 
21307b65c5bSWillem de Bruijn 	if (len) {
21407b65c5bSWillem de Bruijn 		packets++;
21507b65c5bSWillem de Bruijn 		bytes += ret;
21607b65c5bSWillem de Bruijn 		if (do_zerocopy && ret)
21707b65c5bSWillem de Bruijn 			expected_completions++;
21807b65c5bSWillem de Bruijn 	}
219*dfb8434bSSowmini Varadhan 	if (do_zerocopy && domain == PF_RDS) {
220*dfb8434bSSowmini Varadhan 		msg->msg_control = NULL;
221*dfb8434bSSowmini Varadhan 		msg->msg_controllen = 0;
222*dfb8434bSSowmini Varadhan 	}
22307b65c5bSWillem de Bruijn 
22407b65c5bSWillem de Bruijn 	return true;
22507b65c5bSWillem de Bruijn }
22607b65c5bSWillem de Bruijn 
22707b65c5bSWillem de Bruijn static void do_sendmsg_corked(int fd, struct msghdr *msg)
22807b65c5bSWillem de Bruijn {
22907b65c5bSWillem de Bruijn 	bool do_zerocopy = cfg_zerocopy;
23007b65c5bSWillem de Bruijn 	int i, payload_len, extra_len;
23107b65c5bSWillem de Bruijn 
23207b65c5bSWillem de Bruijn 	/* split up the packet. for non-multiple, make first buffer longer */
23307b65c5bSWillem de Bruijn 	payload_len = cfg_payload_len / cfg_cork;
23407b65c5bSWillem de Bruijn 	extra_len = cfg_payload_len - (cfg_cork * payload_len);
23507b65c5bSWillem de Bruijn 
23607b65c5bSWillem de Bruijn 	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 1);
23707b65c5bSWillem de Bruijn 
23807b65c5bSWillem de Bruijn 	for (i = 0; i < cfg_cork; i++) {
23907b65c5bSWillem de Bruijn 
24007b65c5bSWillem de Bruijn 		/* in mixed-frags mode, alternate zerocopy and copy frags
24107b65c5bSWillem de Bruijn 		 * start with non-zerocopy, to ensure attach later works
24207b65c5bSWillem de Bruijn 		 */
24307b65c5bSWillem de Bruijn 		if (cfg_cork_mixed)
24407b65c5bSWillem de Bruijn 			do_zerocopy = (i & 1);
24507b65c5bSWillem de Bruijn 
24607b65c5bSWillem de Bruijn 		msg->msg_iov[0].iov_len = payload_len + extra_len;
24707b65c5bSWillem de Bruijn 		extra_len = 0;
24807b65c5bSWillem de Bruijn 
249*dfb8434bSSowmini Varadhan 		do_sendmsg(fd, msg, do_zerocopy,
250*dfb8434bSSowmini Varadhan 			   (cfg_dst_addr.ss_family == AF_INET ?
251*dfb8434bSSowmini Varadhan 			    PF_INET : PF_INET6));
25207b65c5bSWillem de Bruijn 	}
25307b65c5bSWillem de Bruijn 
25407b65c5bSWillem de Bruijn 	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0);
25507b65c5bSWillem de Bruijn }
25607b65c5bSWillem de Bruijn 
25707b65c5bSWillem de Bruijn static int setup_iph(struct iphdr *iph, uint16_t payload_len)
25807b65c5bSWillem de Bruijn {
25907b65c5bSWillem de Bruijn 	struct sockaddr_in *daddr = (void *) &cfg_dst_addr;
26007b65c5bSWillem de Bruijn 	struct sockaddr_in *saddr = (void *) &cfg_src_addr;
26107b65c5bSWillem de Bruijn 
26207b65c5bSWillem de Bruijn 	memset(iph, 0, sizeof(*iph));
26307b65c5bSWillem de Bruijn 
26407b65c5bSWillem de Bruijn 	iph->version	= 4;
26507b65c5bSWillem de Bruijn 	iph->tos	= 0;
26607b65c5bSWillem de Bruijn 	iph->ihl	= 5;
26707b65c5bSWillem de Bruijn 	iph->ttl	= 2;
26807b65c5bSWillem de Bruijn 	iph->saddr	= saddr->sin_addr.s_addr;
26907b65c5bSWillem de Bruijn 	iph->daddr	= daddr->sin_addr.s_addr;
27007b65c5bSWillem de Bruijn 	iph->protocol	= IPPROTO_EGP;
27107b65c5bSWillem de Bruijn 	iph->tot_len	= htons(sizeof(*iph) + payload_len);
27207b65c5bSWillem de Bruijn 	iph->check	= get_ip_csum((void *) iph, iph->ihl << 1);
27307b65c5bSWillem de Bruijn 
27407b65c5bSWillem de Bruijn 	return sizeof(*iph);
27507b65c5bSWillem de Bruijn }
27607b65c5bSWillem de Bruijn 
27707b65c5bSWillem de Bruijn static int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len)
27807b65c5bSWillem de Bruijn {
27907b65c5bSWillem de Bruijn 	struct sockaddr_in6 *daddr = (void *) &cfg_dst_addr;
28007b65c5bSWillem de Bruijn 	struct sockaddr_in6 *saddr = (void *) &cfg_src_addr;
28107b65c5bSWillem de Bruijn 
28207b65c5bSWillem de Bruijn 	memset(ip6h, 0, sizeof(*ip6h));
28307b65c5bSWillem de Bruijn 
28407b65c5bSWillem de Bruijn 	ip6h->version		= 6;
28507b65c5bSWillem de Bruijn 	ip6h->payload_len	= htons(payload_len);
28607b65c5bSWillem de Bruijn 	ip6h->nexthdr		= IPPROTO_EGP;
28707b65c5bSWillem de Bruijn 	ip6h->hop_limit		= 2;
28807b65c5bSWillem de Bruijn 	ip6h->saddr		= saddr->sin6_addr;
28907b65c5bSWillem de Bruijn 	ip6h->daddr		= daddr->sin6_addr;
29007b65c5bSWillem de Bruijn 
29107b65c5bSWillem de Bruijn 	return sizeof(*ip6h);
29207b65c5bSWillem de Bruijn }
29307b65c5bSWillem de Bruijn 
294d36f45e5SSowmini Varadhan 
295d36f45e5SSowmini Varadhan static void setup_sockaddr(int domain, const char *str_addr,
296d36f45e5SSowmini Varadhan 			   struct sockaddr_storage *sockaddr)
29707b65c5bSWillem de Bruijn {
29807b65c5bSWillem de Bruijn 	struct sockaddr_in6 *addr6 = (void *) sockaddr;
29907b65c5bSWillem de Bruijn 	struct sockaddr_in *addr4 = (void *) sockaddr;
30007b65c5bSWillem de Bruijn 
30107b65c5bSWillem de Bruijn 	switch (domain) {
30207b65c5bSWillem de Bruijn 	case PF_INET:
303d36f45e5SSowmini Varadhan 		memset(addr4, 0, sizeof(*addr4));
30407b65c5bSWillem de Bruijn 		addr4->sin_family = AF_INET;
30507b65c5bSWillem de Bruijn 		addr4->sin_port = htons(cfg_port);
306d36f45e5SSowmini Varadhan 		if (str_addr &&
307d36f45e5SSowmini Varadhan 		    inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1)
30807b65c5bSWillem de Bruijn 			error(1, 0, "ipv4 parse error: %s", str_addr);
30907b65c5bSWillem de Bruijn 		break;
31007b65c5bSWillem de Bruijn 	case PF_INET6:
311d36f45e5SSowmini Varadhan 		memset(addr6, 0, sizeof(*addr6));
31207b65c5bSWillem de Bruijn 		addr6->sin6_family = AF_INET6;
31307b65c5bSWillem de Bruijn 		addr6->sin6_port = htons(cfg_port);
314d36f45e5SSowmini Varadhan 		if (str_addr &&
315d36f45e5SSowmini Varadhan 		    inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1)
31607b65c5bSWillem de Bruijn 			error(1, 0, "ipv6 parse error: %s", str_addr);
31707b65c5bSWillem de Bruijn 		break;
31807b65c5bSWillem de Bruijn 	default:
31907b65c5bSWillem de Bruijn 		error(1, 0, "illegal domain");
32007b65c5bSWillem de Bruijn 	}
32107b65c5bSWillem de Bruijn }
32207b65c5bSWillem de Bruijn 
32307b65c5bSWillem de Bruijn static int do_setup_tx(int domain, int type, int protocol)
32407b65c5bSWillem de Bruijn {
32507b65c5bSWillem de Bruijn 	int fd;
32607b65c5bSWillem de Bruijn 
32707b65c5bSWillem de Bruijn 	fd = socket(domain, type, protocol);
32807b65c5bSWillem de Bruijn 	if (fd == -1)
32907b65c5bSWillem de Bruijn 		error(1, errno, "socket t");
33007b65c5bSWillem de Bruijn 
33107b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 1 << 21);
33207b65c5bSWillem de Bruijn 	if (cfg_zerocopy)
33307b65c5bSWillem de Bruijn 		do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1);
33407b65c5bSWillem de Bruijn 
335b16ac920SSowmini Varadhan 	if (domain != PF_PACKET && domain != PF_RDS)
33607b65c5bSWillem de Bruijn 		if (connect(fd, (void *) &cfg_dst_addr, cfg_alen))
33707b65c5bSWillem de Bruijn 			error(1, errno, "connect");
33807b65c5bSWillem de Bruijn 
339b16ac920SSowmini Varadhan 	if (domain == PF_RDS) {
340b16ac920SSowmini Varadhan 		if (bind(fd, (void *) &cfg_src_addr, cfg_alen))
341b16ac920SSowmini Varadhan 			error(1, errno, "bind");
342b16ac920SSowmini Varadhan 	}
343b16ac920SSowmini Varadhan 
34407b65c5bSWillem de Bruijn 	return fd;
34507b65c5bSWillem de Bruijn }
34607b65c5bSWillem de Bruijn 
347*dfb8434bSSowmini Varadhan static int do_process_zerocopy_cookies(struct sock_extended_err *serr,
348*dfb8434bSSowmini Varadhan 				       uint32_t *ckbuf, size_t nbytes)
349*dfb8434bSSowmini Varadhan {
350*dfb8434bSSowmini Varadhan 	int ncookies, i;
351*dfb8434bSSowmini Varadhan 
352*dfb8434bSSowmini Varadhan 	if (serr->ee_errno != 0)
353*dfb8434bSSowmini Varadhan 		error(1, 0, "serr: wrong error code: %u", serr->ee_errno);
354*dfb8434bSSowmini Varadhan 	ncookies = serr->ee_data;
355*dfb8434bSSowmini Varadhan 	if (ncookies > SO_EE_ORIGIN_MAX_ZCOOKIES)
356*dfb8434bSSowmini Varadhan 		error(1, 0, "Returned %d cookies, max expected %d\n",
357*dfb8434bSSowmini Varadhan 		      ncookies, SO_EE_ORIGIN_MAX_ZCOOKIES);
358*dfb8434bSSowmini Varadhan 	if (nbytes != ncookies * sizeof(uint32_t))
359*dfb8434bSSowmini Varadhan 		error(1, 0, "Expected %d cookies, got %ld\n",
360*dfb8434bSSowmini Varadhan 		      ncookies, nbytes/sizeof(uint32_t));
361*dfb8434bSSowmini Varadhan 	for (i = 0; i < ncookies; i++)
362*dfb8434bSSowmini Varadhan 		if (cfg_verbose >= 2)
363*dfb8434bSSowmini Varadhan 			fprintf(stderr, "%d\n", ckbuf[i]);
364*dfb8434bSSowmini Varadhan 	return ncookies;
365*dfb8434bSSowmini Varadhan }
366*dfb8434bSSowmini Varadhan 
36707b65c5bSWillem de Bruijn static bool do_recv_completion(int fd)
36807b65c5bSWillem de Bruijn {
36907b65c5bSWillem de Bruijn 	struct sock_extended_err *serr;
37007b65c5bSWillem de Bruijn 	struct msghdr msg = {};
37107b65c5bSWillem de Bruijn 	struct cmsghdr *cm;
37207b65c5bSWillem de Bruijn 	uint32_t hi, lo, range;
37307b65c5bSWillem de Bruijn 	int ret, zerocopy;
37407b65c5bSWillem de Bruijn 	char control[100];
375*dfb8434bSSowmini Varadhan 	uint32_t ckbuf[SO_EE_ORIGIN_MAX_ZCOOKIES];
376*dfb8434bSSowmini Varadhan 	struct iovec iov;
37707b65c5bSWillem de Bruijn 
37807b65c5bSWillem de Bruijn 	msg.msg_control = control;
37907b65c5bSWillem de Bruijn 	msg.msg_controllen = sizeof(control);
38007b65c5bSWillem de Bruijn 
381*dfb8434bSSowmini Varadhan 	iov.iov_base = ckbuf;
382*dfb8434bSSowmini Varadhan 	iov.iov_len = (SO_EE_ORIGIN_MAX_ZCOOKIES * sizeof(ckbuf[0]));
383*dfb8434bSSowmini Varadhan 	msg.msg_iov = &iov;
384*dfb8434bSSowmini Varadhan 	msg.msg_iovlen = 1;
385*dfb8434bSSowmini Varadhan 
38607b65c5bSWillem de Bruijn 	ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
38707b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
38807b65c5bSWillem de Bruijn 		return false;
38907b65c5bSWillem de Bruijn 	if (ret == -1)
39007b65c5bSWillem de Bruijn 		error(1, errno, "recvmsg notification");
39107b65c5bSWillem de Bruijn 	if (msg.msg_flags & MSG_CTRUNC)
39207b65c5bSWillem de Bruijn 		error(1, errno, "recvmsg notification: truncated");
39307b65c5bSWillem de Bruijn 
39407b65c5bSWillem de Bruijn 	cm = CMSG_FIRSTHDR(&msg);
39507b65c5bSWillem de Bruijn 	if (!cm)
39607b65c5bSWillem de Bruijn 		error(1, 0, "cmsg: no cmsg");
39707b65c5bSWillem de Bruijn 	if (!((cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) ||
39807b65c5bSWillem de Bruijn 	      (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_RECVERR) ||
39907b65c5bSWillem de Bruijn 	      (cm->cmsg_level == SOL_PACKET && cm->cmsg_type == PACKET_TX_TIMESTAMP)))
40007b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong type: %d.%d",
40107b65c5bSWillem de Bruijn 		      cm->cmsg_level, cm->cmsg_type);
40207b65c5bSWillem de Bruijn 
40307b65c5bSWillem de Bruijn 	serr = (void *) CMSG_DATA(cm);
404*dfb8434bSSowmini Varadhan 
405*dfb8434bSSowmini Varadhan 	if (serr->ee_origin == SO_EE_ORIGIN_ZCOOKIE) {
406*dfb8434bSSowmini Varadhan 		completions += do_process_zerocopy_cookies(serr, ckbuf, ret);
407*dfb8434bSSowmini Varadhan 		return true;
408*dfb8434bSSowmini Varadhan 	}
40907b65c5bSWillem de Bruijn 	if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
41007b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong origin: %u", serr->ee_origin);
41107b65c5bSWillem de Bruijn 	if (serr->ee_errno != 0)
41207b65c5bSWillem de Bruijn 		error(1, 0, "serr: wrong error code: %u", serr->ee_errno);
41307b65c5bSWillem de Bruijn 
41407b65c5bSWillem de Bruijn 	hi = serr->ee_data;
41507b65c5bSWillem de Bruijn 	lo = serr->ee_info;
41607b65c5bSWillem de Bruijn 	range = hi - lo + 1;
41707b65c5bSWillem de Bruijn 
41807b65c5bSWillem de Bruijn 	/* Detect notification gaps. These should not happen often, if at all.
41907b65c5bSWillem de Bruijn 	 * Gaps can occur due to drops, reordering and retransmissions.
42007b65c5bSWillem de Bruijn 	 */
42107b65c5bSWillem de Bruijn 	if (lo != next_completion)
42207b65c5bSWillem de Bruijn 		fprintf(stderr, "gap: %u..%u does not append to %u\n",
42307b65c5bSWillem de Bruijn 			lo, hi, next_completion);
42407b65c5bSWillem de Bruijn 	next_completion = hi + 1;
42507b65c5bSWillem de Bruijn 
42607b65c5bSWillem de Bruijn 	zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED);
42707b65c5bSWillem de Bruijn 	if (zerocopied == -1)
42807b65c5bSWillem de Bruijn 		zerocopied = zerocopy;
42907b65c5bSWillem de Bruijn 	else if (zerocopied != zerocopy) {
43007b65c5bSWillem de Bruijn 		fprintf(stderr, "serr: inconsistent\n");
43107b65c5bSWillem de Bruijn 		zerocopied = zerocopy;
43207b65c5bSWillem de Bruijn 	}
43307b65c5bSWillem de Bruijn 
43407b65c5bSWillem de Bruijn 	if (cfg_verbose >= 2)
43507b65c5bSWillem de Bruijn 		fprintf(stderr, "completed: %u (h=%u l=%u)\n",
43607b65c5bSWillem de Bruijn 			range, hi, lo);
43707b65c5bSWillem de Bruijn 
43807b65c5bSWillem de Bruijn 	completions += range;
43907b65c5bSWillem de Bruijn 	return true;
44007b65c5bSWillem de Bruijn }
44107b65c5bSWillem de Bruijn 
44207b65c5bSWillem de Bruijn /* Read all outstanding messages on the errqueue */
44307b65c5bSWillem de Bruijn static void do_recv_completions(int fd)
44407b65c5bSWillem de Bruijn {
44507b65c5bSWillem de Bruijn 	while (do_recv_completion(fd)) {}
44607b65c5bSWillem de Bruijn }
44707b65c5bSWillem de Bruijn 
44807b65c5bSWillem de Bruijn /* Wait for all remaining completions on the errqueue */
44907b65c5bSWillem de Bruijn static void do_recv_remaining_completions(int fd)
45007b65c5bSWillem de Bruijn {
45107b65c5bSWillem de Bruijn 	int64_t tstop = gettimeofday_ms() + cfg_waittime_ms;
45207b65c5bSWillem de Bruijn 
45307b65c5bSWillem de Bruijn 	while (completions < expected_completions &&
45407b65c5bSWillem de Bruijn 	       gettimeofday_ms() < tstop) {
45507b65c5bSWillem de Bruijn 		if (do_poll(fd, POLLERR))
45607b65c5bSWillem de Bruijn 			do_recv_completions(fd);
45707b65c5bSWillem de Bruijn 	}
45807b65c5bSWillem de Bruijn 
45907b65c5bSWillem de Bruijn 	if (completions < expected_completions)
460bbd9644eSWillem de Bruijn 		fprintf(stderr, "missing notifications: %lu < %lu\n",
46107b65c5bSWillem de Bruijn 			completions, expected_completions);
46207b65c5bSWillem de Bruijn }
46307b65c5bSWillem de Bruijn 
46407b65c5bSWillem de Bruijn static void do_tx(int domain, int type, int protocol)
46507b65c5bSWillem de Bruijn {
46607b65c5bSWillem de Bruijn 	struct iovec iov[3] = { {0} };
46707b65c5bSWillem de Bruijn 	struct sockaddr_ll laddr;
46807b65c5bSWillem de Bruijn 	struct msghdr msg = {0};
46907b65c5bSWillem de Bruijn 	struct ethhdr eth;
47007b65c5bSWillem de Bruijn 	union {
47107b65c5bSWillem de Bruijn 		struct ipv6hdr ip6h;
47207b65c5bSWillem de Bruijn 		struct iphdr iph;
47307b65c5bSWillem de Bruijn 	} nh;
47407b65c5bSWillem de Bruijn 	uint64_t tstop;
47507b65c5bSWillem de Bruijn 	int fd;
47607b65c5bSWillem de Bruijn 
47707b65c5bSWillem de Bruijn 	fd = do_setup_tx(domain, type, protocol);
47807b65c5bSWillem de Bruijn 
47907b65c5bSWillem de Bruijn 	if (domain == PF_PACKET) {
48007b65c5bSWillem de Bruijn 		uint16_t proto = cfg_family == PF_INET ? ETH_P_IP : ETH_P_IPV6;
48107b65c5bSWillem de Bruijn 
48207b65c5bSWillem de Bruijn 		/* sock_raw passes ll header as data */
48307b65c5bSWillem de Bruijn 		if (type == SOCK_RAW) {
48407b65c5bSWillem de Bruijn 			memset(eth.h_dest, 0x06, ETH_ALEN);
48507b65c5bSWillem de Bruijn 			memset(eth.h_source, 0x02, ETH_ALEN);
48607b65c5bSWillem de Bruijn 			eth.h_proto = htons(proto);
48707b65c5bSWillem de Bruijn 			iov[0].iov_base = &eth;
48807b65c5bSWillem de Bruijn 			iov[0].iov_len = sizeof(eth);
48907b65c5bSWillem de Bruijn 			msg.msg_iovlen++;
49007b65c5bSWillem de Bruijn 		}
49107b65c5bSWillem de Bruijn 
49207b65c5bSWillem de Bruijn 		/* both sock_raw and sock_dgram expect name */
49307b65c5bSWillem de Bruijn 		memset(&laddr, 0, sizeof(laddr));
49407b65c5bSWillem de Bruijn 		laddr.sll_family	= AF_PACKET;
49507b65c5bSWillem de Bruijn 		laddr.sll_ifindex	= cfg_ifindex;
49607b65c5bSWillem de Bruijn 		laddr.sll_protocol	= htons(proto);
49707b65c5bSWillem de Bruijn 		laddr.sll_halen		= ETH_ALEN;
49807b65c5bSWillem de Bruijn 
49907b65c5bSWillem de Bruijn 		memset(laddr.sll_addr, 0x06, ETH_ALEN);
50007b65c5bSWillem de Bruijn 
50107b65c5bSWillem de Bruijn 		msg.msg_name		= &laddr;
50207b65c5bSWillem de Bruijn 		msg.msg_namelen		= sizeof(laddr);
50307b65c5bSWillem de Bruijn 	}
50407b65c5bSWillem de Bruijn 
50507b65c5bSWillem de Bruijn 	/* packet and raw sockets with hdrincl must pass network header */
50607b65c5bSWillem de Bruijn 	if (domain == PF_PACKET || protocol == IPPROTO_RAW) {
50707b65c5bSWillem de Bruijn 		if (cfg_family == PF_INET)
50807b65c5bSWillem de Bruijn 			iov[1].iov_len = setup_iph(&nh.iph, cfg_payload_len);
50907b65c5bSWillem de Bruijn 		else
51007b65c5bSWillem de Bruijn 			iov[1].iov_len = setup_ip6h(&nh.ip6h, cfg_payload_len);
51107b65c5bSWillem de Bruijn 
51207b65c5bSWillem de Bruijn 		iov[1].iov_base = (void *) &nh;
51307b65c5bSWillem de Bruijn 		msg.msg_iovlen++;
51407b65c5bSWillem de Bruijn 	}
51507b65c5bSWillem de Bruijn 
516b16ac920SSowmini Varadhan 	if (domain == PF_RDS) {
517b16ac920SSowmini Varadhan 		msg.msg_name = &cfg_dst_addr;
518b16ac920SSowmini Varadhan 		msg.msg_namelen =  (cfg_dst_addr.ss_family == AF_INET ?
519b16ac920SSowmini Varadhan 				    sizeof(struct sockaddr_in) :
520b16ac920SSowmini Varadhan 				    sizeof(struct sockaddr_in6));
521b16ac920SSowmini Varadhan 	}
522b16ac920SSowmini Varadhan 
52307b65c5bSWillem de Bruijn 	iov[2].iov_base = payload;
52407b65c5bSWillem de Bruijn 	iov[2].iov_len = cfg_payload_len;
52507b65c5bSWillem de Bruijn 	msg.msg_iovlen++;
52607b65c5bSWillem de Bruijn 	msg.msg_iov = &iov[3 - msg.msg_iovlen];
52707b65c5bSWillem de Bruijn 
52807b65c5bSWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_runtime_ms;
52907b65c5bSWillem de Bruijn 	do {
53007b65c5bSWillem de Bruijn 		if (cfg_cork)
53107b65c5bSWillem de Bruijn 			do_sendmsg_corked(fd, &msg);
53207b65c5bSWillem de Bruijn 		else
533*dfb8434bSSowmini Varadhan 			do_sendmsg(fd, &msg, cfg_zerocopy, domain);
53407b65c5bSWillem de Bruijn 
53507b65c5bSWillem de Bruijn 		while (!do_poll(fd, POLLOUT)) {
53607b65c5bSWillem de Bruijn 			if (cfg_zerocopy)
53707b65c5bSWillem de Bruijn 				do_recv_completions(fd);
53807b65c5bSWillem de Bruijn 		}
53907b65c5bSWillem de Bruijn 
54007b65c5bSWillem de Bruijn 	} while (gettimeofday_ms() < tstop);
54107b65c5bSWillem de Bruijn 
54207b65c5bSWillem de Bruijn 	if (cfg_zerocopy)
54307b65c5bSWillem de Bruijn 		do_recv_remaining_completions(fd);
54407b65c5bSWillem de Bruijn 
54507b65c5bSWillem de Bruijn 	if (close(fd))
54607b65c5bSWillem de Bruijn 		error(1, errno, "close");
54707b65c5bSWillem de Bruijn 
54807b65c5bSWillem de Bruijn 	fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n",
54907b65c5bSWillem de Bruijn 		packets, bytes >> 20, completions,
55007b65c5bSWillem de Bruijn 		zerocopied == 1 ? 'y' : 'n');
55107b65c5bSWillem de Bruijn }
55207b65c5bSWillem de Bruijn 
55307b65c5bSWillem de Bruijn static int do_setup_rx(int domain, int type, int protocol)
55407b65c5bSWillem de Bruijn {
55507b65c5bSWillem de Bruijn 	int fd;
55607b65c5bSWillem de Bruijn 
55707b65c5bSWillem de Bruijn 	/* If tx over PF_PACKET, rx over PF_INET(6)/SOCK_RAW,
55807b65c5bSWillem de Bruijn 	 * to recv the only copy of the packet, not a clone
55907b65c5bSWillem de Bruijn 	 */
56007b65c5bSWillem de Bruijn 	if (domain == PF_PACKET)
56107b65c5bSWillem de Bruijn 		error(1, 0, "Use PF_INET/SOCK_RAW to read");
56207b65c5bSWillem de Bruijn 
56307b65c5bSWillem de Bruijn 	if (type == SOCK_RAW && protocol == IPPROTO_RAW)
56407b65c5bSWillem de Bruijn 		error(1, 0, "IPPROTO_RAW: not supported on Rx");
56507b65c5bSWillem de Bruijn 
56607b65c5bSWillem de Bruijn 	fd = socket(domain, type, protocol);
56707b65c5bSWillem de Bruijn 	if (fd == -1)
56807b65c5bSWillem de Bruijn 		error(1, errno, "socket r");
56907b65c5bSWillem de Bruijn 
57007b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 1 << 21);
57107b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, 1 << 16);
57207b65c5bSWillem de Bruijn 	do_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1);
57307b65c5bSWillem de Bruijn 
57407b65c5bSWillem de Bruijn 	if (bind(fd, (void *) &cfg_dst_addr, cfg_alen))
57507b65c5bSWillem de Bruijn 		error(1, errno, "bind");
57607b65c5bSWillem de Bruijn 
57707b65c5bSWillem de Bruijn 	if (type == SOCK_STREAM) {
57807b65c5bSWillem de Bruijn 		if (listen(fd, 1))
57907b65c5bSWillem de Bruijn 			error(1, errno, "listen");
58007b65c5bSWillem de Bruijn 		fd = do_accept(fd);
58107b65c5bSWillem de Bruijn 	}
58207b65c5bSWillem de Bruijn 
58307b65c5bSWillem de Bruijn 	return fd;
58407b65c5bSWillem de Bruijn }
58507b65c5bSWillem de Bruijn 
58607b65c5bSWillem de Bruijn /* Flush all outstanding bytes for the tcp receive queue */
58707b65c5bSWillem de Bruijn static void do_flush_tcp(int fd)
58807b65c5bSWillem de Bruijn {
58907b65c5bSWillem de Bruijn 	int ret;
59007b65c5bSWillem de Bruijn 
59107b65c5bSWillem de Bruijn 	/* MSG_TRUNC flushes up to len bytes */
59207b65c5bSWillem de Bruijn 	ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT);
59307b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
59407b65c5bSWillem de Bruijn 		return;
59507b65c5bSWillem de Bruijn 	if (ret == -1)
59607b65c5bSWillem de Bruijn 		error(1, errno, "flush");
59707b65c5bSWillem de Bruijn 	if (!ret)
59807b65c5bSWillem de Bruijn 		return;
59907b65c5bSWillem de Bruijn 
60007b65c5bSWillem de Bruijn 	packets++;
60107b65c5bSWillem de Bruijn 	bytes += ret;
60207b65c5bSWillem de Bruijn }
60307b65c5bSWillem de Bruijn 
60407b65c5bSWillem de Bruijn /* Flush all outstanding datagrams. Verify first few bytes of each. */
60507b65c5bSWillem de Bruijn static void do_flush_datagram(int fd, int type)
60607b65c5bSWillem de Bruijn {
60707b65c5bSWillem de Bruijn 	int ret, off = 0;
60807b65c5bSWillem de Bruijn 	char buf[64];
60907b65c5bSWillem de Bruijn 
61007b65c5bSWillem de Bruijn 	/* MSG_TRUNC will return full datagram length */
61107b65c5bSWillem de Bruijn 	ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_TRUNC);
61207b65c5bSWillem de Bruijn 	if (ret == -1 && errno == EAGAIN)
61307b65c5bSWillem de Bruijn 		return;
61407b65c5bSWillem de Bruijn 
61507b65c5bSWillem de Bruijn 	/* raw ipv4 return with header, raw ipv6 without */
61607b65c5bSWillem de Bruijn 	if (cfg_family == PF_INET && type == SOCK_RAW) {
61707b65c5bSWillem de Bruijn 		off += sizeof(struct iphdr);
61807b65c5bSWillem de Bruijn 		ret -= sizeof(struct iphdr);
61907b65c5bSWillem de Bruijn 	}
62007b65c5bSWillem de Bruijn 
62107b65c5bSWillem de Bruijn 	if (ret == -1)
62207b65c5bSWillem de Bruijn 		error(1, errno, "recv");
62307b65c5bSWillem de Bruijn 	if (ret != cfg_payload_len)
62407b65c5bSWillem de Bruijn 		error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len);
62507b65c5bSWillem de Bruijn 	if (ret > sizeof(buf) - off)
62607b65c5bSWillem de Bruijn 		ret = sizeof(buf) - off;
62707b65c5bSWillem de Bruijn 	if (memcmp(buf + off, payload, ret))
62807b65c5bSWillem de Bruijn 		error(1, 0, "recv: data mismatch");
62907b65c5bSWillem de Bruijn 
63007b65c5bSWillem de Bruijn 	packets++;
63107b65c5bSWillem de Bruijn 	bytes += cfg_payload_len;
63207b65c5bSWillem de Bruijn }
63307b65c5bSWillem de Bruijn 
634b16ac920SSowmini Varadhan 
635b16ac920SSowmini Varadhan static void do_recvmsg(int fd)
636b16ac920SSowmini Varadhan {
637b16ac920SSowmini Varadhan 	int ret, off = 0;
638b16ac920SSowmini Varadhan 	char *buf;
639b16ac920SSowmini Varadhan 	struct iovec iov;
640b16ac920SSowmini Varadhan 	struct msghdr msg;
641b16ac920SSowmini Varadhan 	struct sockaddr_storage din;
642b16ac920SSowmini Varadhan 
643b16ac920SSowmini Varadhan 	buf = calloc(cfg_payload_len, sizeof(char));
644b16ac920SSowmini Varadhan 	iov.iov_base = buf;
645b16ac920SSowmini Varadhan 	iov.iov_len = cfg_payload_len;
646b16ac920SSowmini Varadhan 
647b16ac920SSowmini Varadhan 	memset(&msg, 0, sizeof(msg));
648b16ac920SSowmini Varadhan 	msg.msg_name = &din;
649b16ac920SSowmini Varadhan 	msg.msg_namelen = sizeof(din);
650b16ac920SSowmini Varadhan 	msg.msg_iov = &iov;
651b16ac920SSowmini Varadhan 	msg.msg_iovlen = 1;
652b16ac920SSowmini Varadhan 
653b16ac920SSowmini Varadhan 	ret = recvmsg(fd, &msg, MSG_TRUNC);
654b16ac920SSowmini Varadhan 
655b16ac920SSowmini Varadhan 	if (ret == -1)
656b16ac920SSowmini Varadhan 		error(1, errno, "recv");
657b16ac920SSowmini Varadhan 	if (ret != cfg_payload_len)
658b16ac920SSowmini Varadhan 		error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len);
659b16ac920SSowmini Varadhan 
660b16ac920SSowmini Varadhan 	if (memcmp(buf + off, payload, ret))
661b16ac920SSowmini Varadhan 		error(1, 0, "recv: data mismatch");
662b16ac920SSowmini Varadhan 
663b16ac920SSowmini Varadhan 	free(buf);
664b16ac920SSowmini Varadhan 	packets++;
665b16ac920SSowmini Varadhan 	bytes += cfg_payload_len;
666b16ac920SSowmini Varadhan }
667b16ac920SSowmini Varadhan 
66807b65c5bSWillem de Bruijn static void do_rx(int domain, int type, int protocol)
66907b65c5bSWillem de Bruijn {
67007b65c5bSWillem de Bruijn 	uint64_t tstop;
67107b65c5bSWillem de Bruijn 	int fd;
67207b65c5bSWillem de Bruijn 
67307b65c5bSWillem de Bruijn 	fd = do_setup_rx(domain, type, protocol);
67407b65c5bSWillem de Bruijn 
67507b65c5bSWillem de Bruijn 	tstop = gettimeofday_ms() + cfg_runtime_ms;
67607b65c5bSWillem de Bruijn 	do {
67707b65c5bSWillem de Bruijn 		if (type == SOCK_STREAM)
67807b65c5bSWillem de Bruijn 			do_flush_tcp(fd);
679b16ac920SSowmini Varadhan 		else if (domain == PF_RDS)
680b16ac920SSowmini Varadhan 			do_recvmsg(fd);
68107b65c5bSWillem de Bruijn 		else
68207b65c5bSWillem de Bruijn 			do_flush_datagram(fd, type);
68307b65c5bSWillem de Bruijn 
68407b65c5bSWillem de Bruijn 		do_poll(fd, POLLIN);
68507b65c5bSWillem de Bruijn 
68607b65c5bSWillem de Bruijn 	} while (gettimeofday_ms() < tstop);
68707b65c5bSWillem de Bruijn 
68807b65c5bSWillem de Bruijn 	if (close(fd))
68907b65c5bSWillem de Bruijn 		error(1, errno, "close");
69007b65c5bSWillem de Bruijn 
69107b65c5bSWillem de Bruijn 	fprintf(stderr, "rx=%lu (%lu MB)\n", packets, bytes >> 20);
69207b65c5bSWillem de Bruijn }
69307b65c5bSWillem de Bruijn 
69407b65c5bSWillem de Bruijn static void do_test(int domain, int type, int protocol)
69507b65c5bSWillem de Bruijn {
69607b65c5bSWillem de Bruijn 	int i;
69707b65c5bSWillem de Bruijn 
69807b65c5bSWillem de Bruijn 	if (cfg_cork && (domain == PF_PACKET || type != SOCK_DGRAM))
69907b65c5bSWillem de Bruijn 		error(1, 0, "can only cork udp sockets");
70007b65c5bSWillem de Bruijn 
70107b65c5bSWillem de Bruijn 	do_setcpu(cfg_cpu);
70207b65c5bSWillem de Bruijn 
70307b65c5bSWillem de Bruijn 	for (i = 0; i < IP_MAXPACKET; i++)
70407b65c5bSWillem de Bruijn 		payload[i] = 'a' + (i % 26);
70507b65c5bSWillem de Bruijn 
70607b65c5bSWillem de Bruijn 	if (cfg_rx)
70707b65c5bSWillem de Bruijn 		do_rx(domain, type, protocol);
70807b65c5bSWillem de Bruijn 	else
70907b65c5bSWillem de Bruijn 		do_tx(domain, type, protocol);
71007b65c5bSWillem de Bruijn }
71107b65c5bSWillem de Bruijn 
71207b65c5bSWillem de Bruijn static void usage(const char *filepath)
71307b65c5bSWillem de Bruijn {
71407b65c5bSWillem de Bruijn 	error(1, 0, "Usage: %s [options] <test>", filepath);
71507b65c5bSWillem de Bruijn }
71607b65c5bSWillem de Bruijn 
71707b65c5bSWillem de Bruijn static void parse_opts(int argc, char **argv)
71807b65c5bSWillem de Bruijn {
71907b65c5bSWillem de Bruijn 	const int max_payload_len = sizeof(payload) -
72007b65c5bSWillem de Bruijn 				    sizeof(struct ipv6hdr) -
72107b65c5bSWillem de Bruijn 				    sizeof(struct tcphdr) -
72207b65c5bSWillem de Bruijn 				    40 /* max tcp options */;
72307b65c5bSWillem de Bruijn 	int c;
724d36f45e5SSowmini Varadhan 	char *daddr = NULL, *saddr = NULL;
725b16ac920SSowmini Varadhan 	char *cfg_test;
72607b65c5bSWillem de Bruijn 
72707b65c5bSWillem de Bruijn 	cfg_payload_len = max_payload_len;
72807b65c5bSWillem de Bruijn 
72907b65c5bSWillem de Bruijn 	while ((c = getopt(argc, argv, "46c:C:D:i:mp:rs:S:t:vz")) != -1) {
73007b65c5bSWillem de Bruijn 		switch (c) {
73107b65c5bSWillem de Bruijn 		case '4':
73207b65c5bSWillem de Bruijn 			if (cfg_family != PF_UNSPEC)
73307b65c5bSWillem de Bruijn 				error(1, 0, "Pass one of -4 or -6");
73407b65c5bSWillem de Bruijn 			cfg_family = PF_INET;
73507b65c5bSWillem de Bruijn 			cfg_alen = sizeof(struct sockaddr_in);
73607b65c5bSWillem de Bruijn 			break;
73707b65c5bSWillem de Bruijn 		case '6':
73807b65c5bSWillem de Bruijn 			if (cfg_family != PF_UNSPEC)
73907b65c5bSWillem de Bruijn 				error(1, 0, "Pass one of -4 or -6");
74007b65c5bSWillem de Bruijn 			cfg_family = PF_INET6;
74107b65c5bSWillem de Bruijn 			cfg_alen = sizeof(struct sockaddr_in6);
74207b65c5bSWillem de Bruijn 			break;
74307b65c5bSWillem de Bruijn 		case 'c':
74407b65c5bSWillem de Bruijn 			cfg_cork = strtol(optarg, NULL, 0);
74507b65c5bSWillem de Bruijn 			break;
74607b65c5bSWillem de Bruijn 		case 'C':
74707b65c5bSWillem de Bruijn 			cfg_cpu = strtol(optarg, NULL, 0);
74807b65c5bSWillem de Bruijn 			break;
74907b65c5bSWillem de Bruijn 		case 'D':
750d36f45e5SSowmini Varadhan 			daddr = optarg;
75107b65c5bSWillem de Bruijn 			break;
75207b65c5bSWillem de Bruijn 		case 'i':
75307b65c5bSWillem de Bruijn 			cfg_ifindex = if_nametoindex(optarg);
75407b65c5bSWillem de Bruijn 			if (cfg_ifindex == 0)
75507b65c5bSWillem de Bruijn 				error(1, errno, "invalid iface: %s", optarg);
75607b65c5bSWillem de Bruijn 			break;
75707b65c5bSWillem de Bruijn 		case 'm':
75807b65c5bSWillem de Bruijn 			cfg_cork_mixed = true;
75907b65c5bSWillem de Bruijn 			break;
76007b65c5bSWillem de Bruijn 		case 'p':
761d36f45e5SSowmini Varadhan 			cfg_port = strtoul(optarg, NULL, 0);
76207b65c5bSWillem de Bruijn 			break;
76307b65c5bSWillem de Bruijn 		case 'r':
76407b65c5bSWillem de Bruijn 			cfg_rx = true;
76507b65c5bSWillem de Bruijn 			break;
76607b65c5bSWillem de Bruijn 		case 's':
76707b65c5bSWillem de Bruijn 			cfg_payload_len = strtoul(optarg, NULL, 0);
76807b65c5bSWillem de Bruijn 			break;
76907b65c5bSWillem de Bruijn 		case 'S':
770d36f45e5SSowmini Varadhan 			saddr = optarg;
77107b65c5bSWillem de Bruijn 			break;
77207b65c5bSWillem de Bruijn 		case 't':
77307b65c5bSWillem de Bruijn 			cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000;
77407b65c5bSWillem de Bruijn 			break;
77507b65c5bSWillem de Bruijn 		case 'v':
77607b65c5bSWillem de Bruijn 			cfg_verbose++;
77707b65c5bSWillem de Bruijn 			break;
77807b65c5bSWillem de Bruijn 		case 'z':
77907b65c5bSWillem de Bruijn 			cfg_zerocopy = true;
78007b65c5bSWillem de Bruijn 			break;
78107b65c5bSWillem de Bruijn 		}
78207b65c5bSWillem de Bruijn 	}
783b16ac920SSowmini Varadhan 
784b16ac920SSowmini Varadhan 	cfg_test = argv[argc - 1];
785b16ac920SSowmini Varadhan 	if (strcmp(cfg_test, "rds") == 0) {
786b16ac920SSowmini Varadhan 		if (!daddr)
787b16ac920SSowmini Varadhan 			error(1, 0, "-D <server addr> required for PF_RDS\n");
788b16ac920SSowmini Varadhan 		if (!cfg_rx && !saddr)
789b16ac920SSowmini Varadhan 			error(1, 0, "-S <client addr> required for PF_RDS\n");
790b16ac920SSowmini Varadhan 	}
791d36f45e5SSowmini Varadhan 	setup_sockaddr(cfg_family, daddr, &cfg_dst_addr);
792d36f45e5SSowmini Varadhan 	setup_sockaddr(cfg_family, saddr, &cfg_src_addr);
79307b65c5bSWillem de Bruijn 
79407b65c5bSWillem de Bruijn 	if (cfg_payload_len > max_payload_len)
79507b65c5bSWillem de Bruijn 		error(1, 0, "-s: payload exceeds max (%d)", max_payload_len);
79607b65c5bSWillem de Bruijn 	if (cfg_cork_mixed && (!cfg_zerocopy || !cfg_cork))
79707b65c5bSWillem de Bruijn 		error(1, 0, "-m: cork_mixed requires corking and zerocopy");
79807b65c5bSWillem de Bruijn 
79907b65c5bSWillem de Bruijn 	if (optind != argc - 1)
80007b65c5bSWillem de Bruijn 		usage(argv[0]);
80107b65c5bSWillem de Bruijn }
80207b65c5bSWillem de Bruijn 
80307b65c5bSWillem de Bruijn int main(int argc, char **argv)
80407b65c5bSWillem de Bruijn {
80507b65c5bSWillem de Bruijn 	const char *cfg_test;
80607b65c5bSWillem de Bruijn 
80707b65c5bSWillem de Bruijn 	parse_opts(argc, argv);
80807b65c5bSWillem de Bruijn 
80907b65c5bSWillem de Bruijn 	cfg_test = argv[argc - 1];
81007b65c5bSWillem de Bruijn 
81107b65c5bSWillem de Bruijn 	if (!strcmp(cfg_test, "packet"))
81207b65c5bSWillem de Bruijn 		do_test(PF_PACKET, SOCK_RAW, 0);
81307b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "packet_dgram"))
81407b65c5bSWillem de Bruijn 		do_test(PF_PACKET, SOCK_DGRAM, 0);
81507b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "raw"))
81607b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_RAW, IPPROTO_EGP);
81707b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "raw_hdrincl"))
81807b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_RAW, IPPROTO_RAW);
81907b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "tcp"))
82007b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_STREAM, 0);
82107b65c5bSWillem de Bruijn 	else if (!strcmp(cfg_test, "udp"))
82207b65c5bSWillem de Bruijn 		do_test(cfg_family, SOCK_DGRAM, 0);
823b16ac920SSowmini Varadhan 	else if (!strcmp(cfg_test, "rds"))
824b16ac920SSowmini Varadhan 		do_test(PF_RDS, SOCK_SEQPACKET, 0);
82507b65c5bSWillem de Bruijn 	else
82607b65c5bSWillem de Bruijn 		error(1, 0, "unknown cfg_test %s", cfg_test);
82707b65c5bSWillem de Bruijn 
82807b65c5bSWillem de Bruijn 	return 0;
82907b65c5bSWillem de Bruijn }
830