brintos

brintos / linux-shallow public Read only

0
0
Text · 21.7 KiB · d626f22 Raw
928 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright 2014 Google Inc.4 * Author: willemb@google.com (Willem de Bruijn)5 *6 * Test software tx timestamping, including7 *8 * - SCHED, SND and ACK timestamps9 * - RAW, UDP and TCP10 * - IPv4 and IPv611 * - various packet sizes (to test GSO and TSO)12 *13 * Consult the command line arguments for help on running14 * the various testcases.15 *16 * This test requires a dummy TCP server.17 * A simple `nc6 [-u] -l -p $DESTPORT` will do18 */19 20#define _GNU_SOURCE21 22#include <arpa/inet.h>23#include <asm/types.h>24#include <error.h>25#include <errno.h>26#include <inttypes.h>27#include <linux/errqueue.h>28#include <linux/if_ether.h>29#include <linux/if_packet.h>30#include <linux/ipv6.h>31#include <linux/net_tstamp.h>32#include <netdb.h>33#include <net/if.h>34#include <netinet/in.h>35#include <netinet/ip.h>36#include <netinet/udp.h>37#include <netinet/tcp.h>38#include <poll.h>39#include <stdarg.h>40#include <stdbool.h>41#include <stdio.h>42#include <stdlib.h>43#include <string.h>44#include <sys/epoll.h>45#include <sys/ioctl.h>46#include <sys/select.h>47#include <sys/socket.h>48#include <sys/time.h>49#include <sys/types.h>50#include <time.h>51#include <unistd.h>52 53#define NSEC_PER_USEC	1000L54#define USEC_PER_SEC	1000000L55#define NSEC_PER_SEC	1000000000LL56 57/* command line parameters */58static int cfg_proto = SOCK_STREAM;59static int cfg_ipproto = IPPROTO_TCP;60static int cfg_num_pkts = 4;61static int do_ipv4 = 1;62static int do_ipv6 = 1;63static int cfg_payload_len = 10;64static int cfg_poll_timeout = 100;65static int cfg_delay_snd;66static int cfg_delay_ack;67static int cfg_delay_tolerance_usec = 500;68static bool cfg_show_payload;69static bool cfg_do_pktinfo;70static bool cfg_busy_poll;71static int cfg_sleep_usec = 50 * 1000;72static bool cfg_loop_nodata;73static bool cfg_use_cmsg;74static bool cfg_use_pf_packet;75static bool cfg_use_epoll;76static bool cfg_epollet;77static bool cfg_do_listen;78static uint16_t dest_port = 9000;79static bool cfg_print_nsec;80 81static struct sockaddr_in daddr;82static struct sockaddr_in6 daddr6;83static struct timespec ts_usr;84 85static int saved_tskey = -1;86static int saved_tskey_type = -1;87 88struct timing_event {89	int64_t min;90	int64_t max;91	int64_t total;92	int count;93};94 95static struct timing_event usr_enq;96static struct timing_event usr_snd;97static struct timing_event usr_ack;98 99static bool test_failed;100 101static int64_t timespec_to_ns64(struct timespec *ts)102{103	return ts->tv_sec * NSEC_PER_SEC + ts->tv_nsec;104}105 106static int64_t timespec_to_us64(struct timespec *ts)107{108	return ts->tv_sec * USEC_PER_SEC + ts->tv_nsec / NSEC_PER_USEC;109}110 111static void init_timing_event(struct timing_event *te)112{113	te->min = INT64_MAX;114	te->max = 0;115	te->total = 0;116	te->count = 0;117}118 119static void add_timing_event(struct timing_event *te,120		struct timespec *t_start, struct timespec *t_end)121{122	int64_t ts_delta = timespec_to_ns64(t_end) - timespec_to_ns64(t_start);123 124	te->count++;125	if (ts_delta < te->min)126		te->min = ts_delta;127	if (ts_delta > te->max)128		te->max = ts_delta;129	te->total += ts_delta;130}131 132static void validate_key(int tskey, int tstype)133{134	int stepsize;135 136	/* compare key for each subsequent request137	 * must only test for one type, the first one requested138	 */139	if (saved_tskey == -1)140		saved_tskey_type = tstype;141	else if (saved_tskey_type != tstype)142		return;143 144	stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1;145	if (tskey != saved_tskey + stepsize) {146		fprintf(stderr, "ERROR: key %d, expected %d\n",147				tskey, saved_tskey + stepsize);148		test_failed = true;149	}150 151	saved_tskey = tskey;152}153 154static void validate_timestamp(struct timespec *cur, int min_delay)155{156	int64_t cur64, start64;157	int max_delay;158 159	cur64 = timespec_to_us64(cur);160	start64 = timespec_to_us64(&ts_usr);161	max_delay = min_delay + cfg_delay_tolerance_usec;162 163	if (cur64 < start64 + min_delay || cur64 > start64 + max_delay) {164		fprintf(stderr, "ERROR: %" PRId64 " us expected between %d and %d\n",165				cur64 - start64, min_delay, max_delay);166		if (!getenv("KSFT_MACHINE_SLOW"))167			test_failed = true;168	}169}170 171static void __print_ts_delta_formatted(int64_t ts_delta)172{173	if (cfg_print_nsec)174		fprintf(stderr, "%" PRId64 " ns", ts_delta);175	else176		fprintf(stderr, "%" PRId64 " us", ts_delta / NSEC_PER_USEC);177}178 179static void __print_timestamp(const char *name, struct timespec *cur,180			      uint32_t key, int payload_len)181{182	int64_t ts_delta;183 184	if (!(cur->tv_sec | cur->tv_nsec))185		return;186 187	if (cfg_print_nsec)188		fprintf(stderr, "  %s: %lu s %lu ns (seq=%u, len=%u)",189				name, cur->tv_sec, cur->tv_nsec,190				key, payload_len);191	else192		fprintf(stderr, "  %s: %lu s %lu us (seq=%u, len=%u)",193				name, cur->tv_sec, cur->tv_nsec / NSEC_PER_USEC,194				key, payload_len);195 196	if (cur != &ts_usr) {197		ts_delta = timespec_to_ns64(cur) - timespec_to_ns64(&ts_usr);198		fprintf(stderr, "  (USR +");199		__print_ts_delta_formatted(ts_delta);200		fprintf(stderr, ")");201	}202 203	fprintf(stderr, "\n");204}205 206static void print_timestamp_usr(void)207{208	if (clock_gettime(CLOCK_REALTIME, &ts_usr))209		error(1, errno, "clock_gettime");210 211	__print_timestamp("  USR", &ts_usr, 0, 0);212}213 214static void print_timestamp(struct scm_timestamping *tss, int tstype,215			    int tskey, int payload_len)216{217	const char *tsname;218 219	validate_key(tskey, tstype);220 221	switch (tstype) {222	case SCM_TSTAMP_SCHED:223		tsname = "  ENQ";224		validate_timestamp(&tss->ts[0], 0);225		add_timing_event(&usr_enq, &ts_usr, &tss->ts[0]);226		break;227	case SCM_TSTAMP_SND:228		tsname = "  SND";229		validate_timestamp(&tss->ts[0], cfg_delay_snd);230		add_timing_event(&usr_snd, &ts_usr, &tss->ts[0]);231		break;232	case SCM_TSTAMP_ACK:233		tsname = "  ACK";234		validate_timestamp(&tss->ts[0], cfg_delay_ack);235		add_timing_event(&usr_ack, &ts_usr, &tss->ts[0]);236		break;237	default:238		error(1, 0, "unknown timestamp type: %u",239		tstype);240	}241	__print_timestamp(tsname, &tss->ts[0], tskey, payload_len);242}243 244static void print_timing_event(char *name, struct timing_event *te)245{246	if (!te->count)247		return;248 249	fprintf(stderr, "    %s: count=%d", name, te->count);250	fprintf(stderr, ", avg=");251	__print_ts_delta_formatted((int64_t)(te->total / te->count));252	fprintf(stderr, ", min=");253	__print_ts_delta_formatted(te->min);254	fprintf(stderr, ", max=");255	__print_ts_delta_formatted(te->max);256	fprintf(stderr, "\n");257}258 259/* TODO: convert to check_and_print payload once API is stable */260static void print_payload(char *data, int len)261{262	int i;263 264	if (!len)265		return;266 267	if (len > 70)268		len = 70;269 270	fprintf(stderr, "payload: ");271	for (i = 0; i < len; i++)272		fprintf(stderr, "%02hhx ", data[i]);273	fprintf(stderr, "\n");274}275 276static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)277{278	char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];279 280	fprintf(stderr, "         pktinfo: ifindex=%u src=%s dst=%s\n",281		ifindex,282		saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",283		daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");284}285 286static void __epoll(int epfd)287{288	struct epoll_event events;289	int ret;290 291	memset(&events, 0, sizeof(events));292	ret = epoll_wait(epfd, &events, 1, cfg_poll_timeout);293	if (ret != 1)294		error(1, errno, "epoll_wait");295}296 297static void __poll(int fd)298{299	struct pollfd pollfd;300	int ret;301 302	memset(&pollfd, 0, sizeof(pollfd));303	pollfd.fd = fd;304	ret = poll(&pollfd, 1, cfg_poll_timeout);305	if (ret != 1)306		error(1, errno, "poll");307}308 309static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)310{311	struct sock_extended_err *serr = NULL;312	struct scm_timestamping *tss = NULL;313	struct cmsghdr *cm;314	int batch = 0;315 316	for (cm = CMSG_FIRSTHDR(msg);317	     cm && cm->cmsg_len;318	     cm = CMSG_NXTHDR(msg, cm)) {319		if (cm->cmsg_level == SOL_SOCKET &&320		    cm->cmsg_type == SCM_TIMESTAMPING) {321			tss = (void *) CMSG_DATA(cm);322		} else if ((cm->cmsg_level == SOL_IP &&323			    cm->cmsg_type == IP_RECVERR) ||324			   (cm->cmsg_level == SOL_IPV6 &&325			    cm->cmsg_type == IPV6_RECVERR) ||326			   (cm->cmsg_level == SOL_PACKET &&327			    cm->cmsg_type == PACKET_TX_TIMESTAMP)) {328			serr = (void *) CMSG_DATA(cm);329			if (serr->ee_errno != ENOMSG ||330			    serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {331				fprintf(stderr, "unknown ip error %d %d\n",332						serr->ee_errno,333						serr->ee_origin);334				serr = NULL;335			}336		} else if (cm->cmsg_level == SOL_IP &&337			   cm->cmsg_type == IP_PKTINFO) {338			struct in_pktinfo *info = (void *) CMSG_DATA(cm);339			print_pktinfo(AF_INET, info->ipi_ifindex,340				      &info->ipi_spec_dst, &info->ipi_addr);341		} else if (cm->cmsg_level == SOL_IPV6 &&342			   cm->cmsg_type == IPV6_PKTINFO) {343			struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);344			print_pktinfo(AF_INET6, info6->ipi6_ifindex,345				      NULL, &info6->ipi6_addr);346		} else347			fprintf(stderr, "unknown cmsg %d,%d\n",348					cm->cmsg_level, cm->cmsg_type);349 350		if (serr && tss) {351			print_timestamp(tss, serr->ee_info, serr->ee_data,352					payload_len);353			serr = NULL;354			tss = NULL;355			batch++;356		}357	}358 359	if (batch > 1) {360		fprintf(stderr, "batched %d timestamps\n", batch);361	} else if (!batch) {362		fprintf(stderr, "Failed to report timestamps\n");363		test_failed = true;364	}365}366 367static int recv_errmsg(int fd)368{369	static char ctrl[1024 /* overprovision*/];370	static struct msghdr msg;371	struct iovec entry;372	static char *data;373	int ret = 0;374 375	data = malloc(cfg_payload_len);376	if (!data)377		error(1, 0, "malloc");378 379	memset(&msg, 0, sizeof(msg));380	memset(&entry, 0, sizeof(entry));381	memset(ctrl, 0, sizeof(ctrl));382 383	entry.iov_base = data;384	entry.iov_len = cfg_payload_len;385	msg.msg_iov = &entry;386	msg.msg_iovlen = 1;387	msg.msg_name = NULL;388	msg.msg_namelen = 0;389	msg.msg_control = ctrl;390	msg.msg_controllen = sizeof(ctrl);391 392	ret = recvmsg(fd, &msg, MSG_ERRQUEUE);393	if (ret == -1 && errno != EAGAIN)394		error(1, errno, "recvmsg");395 396	if (ret >= 0) {397		__recv_errmsg_cmsg(&msg, ret);398		if (cfg_show_payload)399			print_payload(data, cfg_payload_len);400	}401 402	free(data);403	return ret == -1;404}405 406static uint16_t get_ip_csum(const uint16_t *start, int num_words,407			    unsigned long sum)408{409	int i;410 411	for (i = 0; i < num_words; i++)412		sum += start[i];413 414	while (sum >> 16)415		sum = (sum & 0xFFFF) + (sum >> 16);416 417	return ~sum;418}419 420static uint16_t get_udp_csum(const struct udphdr *udph, int alen)421{422	unsigned long pseudo_sum, csum_len;423	const void *csum_start = udph;424 425	pseudo_sum = htons(IPPROTO_UDP);426	pseudo_sum += udph->len;427 428	/* checksum ip(v6) addresses + udp header + payload */429	csum_start -= alen * 2;430	csum_len = ntohs(udph->len) + alen * 2;431 432	return get_ip_csum(csum_start, csum_len >> 1, pseudo_sum);433}434 435static int fill_header_ipv4(void *p)436{437	struct iphdr *iph = p;438 439	memset(iph, 0, sizeof(*iph));440 441	iph->ihl	= 5;442	iph->version	= 4;443	iph->ttl	= 2;444	iph->saddr	= daddr.sin_addr.s_addr;	/* set for udp csum calc */445	iph->daddr	= daddr.sin_addr.s_addr;446	iph->protocol	= IPPROTO_UDP;447 448	/* kernel writes saddr, csum, len */449 450	return sizeof(*iph);451}452 453static int fill_header_ipv6(void *p)454{455	struct ipv6hdr *ip6h = p;456 457	memset(ip6h, 0, sizeof(*ip6h));458 459	ip6h->version		= 6;460	ip6h->payload_len	= htons(sizeof(struct udphdr) + cfg_payload_len);461	ip6h->nexthdr		= IPPROTO_UDP;462	ip6h->hop_limit		= 64;463 464	ip6h->saddr             = daddr6.sin6_addr;465	ip6h->daddr		= daddr6.sin6_addr;466 467	/* kernel does not write saddr in case of ipv6 */468 469	return sizeof(*ip6h);470}471 472static void fill_header_udp(void *p, bool is_ipv4)473{474	struct udphdr *udph = p;475 476	udph->source = ntohs(dest_port + 1);	/* spoof */477	udph->dest   = ntohs(dest_port);478	udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);479	udph->check  = 0;480 481	udph->check  = get_udp_csum(udph, is_ipv4 ? sizeof(struct in_addr) :482						    sizeof(struct in6_addr));483}484 485static void do_test(int family, unsigned int report_opt)486{487	char control[CMSG_SPACE(sizeof(uint32_t))];488	struct sockaddr_ll laddr;489	unsigned int sock_opt;490	struct cmsghdr *cmsg;491	struct msghdr msg;492	struct iovec iov;493	char *buf;494	int fd, i, val = 1, total_len, epfd = 0;495 496	init_timing_event(&usr_enq);497	init_timing_event(&usr_snd);498	init_timing_event(&usr_ack);499 500	total_len = cfg_payload_len;501	if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {502		total_len += sizeof(struct udphdr);503		if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) {504			if (family == PF_INET)505				total_len += sizeof(struct iphdr);506			else507				total_len += sizeof(struct ipv6hdr);508		}509		/* special case, only rawv6_sendmsg:510		 * pass proto in sin6_port if not connected511		 * also see ANK comment in net/ipv4/raw.c512		 */513		daddr6.sin6_port = htons(cfg_ipproto);514	}515 516	buf = malloc(total_len);517	if (!buf)518		error(1, 0, "malloc");519 520	fd = socket(cfg_use_pf_packet ? PF_PACKET : family,521		    cfg_proto, cfg_ipproto);522	if (fd < 0)523		error(1, errno, "socket");524 525	if (cfg_use_epoll) {526		struct epoll_event ev;527 528		memset(&ev, 0, sizeof(ev));529		ev.data.fd = fd;530		if (cfg_epollet)531			ev.events |= EPOLLET;532		epfd = epoll_create(1);533		if (epfd <= 0)534			error(1, errno, "epoll_create");535		if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev))536			error(1, errno, "epoll_ctl");537	}538 539	/* reset expected key on each new socket */540	saved_tskey = -1;541 542	if (cfg_proto == SOCK_STREAM) {543		if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,544			       (char*) &val, sizeof(val)))545			error(1, 0, "setsockopt no nagle");546 547		if (family == PF_INET) {548			if (connect(fd, (void *) &daddr, sizeof(daddr)))549				error(1, errno, "connect ipv4");550		} else {551			if (connect(fd, (void *) &daddr6, sizeof(daddr6)))552				error(1, errno, "connect ipv6");553		}554	}555 556	if (cfg_do_pktinfo) {557		if (family == AF_INET6) {558			if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,559				       &val, sizeof(val)))560				error(1, errno, "setsockopt pktinfo ipv6");561		} else {562			if (setsockopt(fd, SOL_IP, IP_PKTINFO,563				       &val, sizeof(val)))564				error(1, errno, "setsockopt pktinfo ipv4");565		}566	}567 568	sock_opt = SOF_TIMESTAMPING_SOFTWARE |569		   SOF_TIMESTAMPING_OPT_CMSG |570		   SOF_TIMESTAMPING_OPT_ID;571 572	if (!cfg_use_cmsg)573		sock_opt |= report_opt;574 575	if (cfg_loop_nodata)576		sock_opt |= SOF_TIMESTAMPING_OPT_TSONLY;577 578	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,579		       (char *) &sock_opt, sizeof(sock_opt)))580		error(1, 0, "setsockopt timestamping");581 582	for (i = 0; i < cfg_num_pkts; i++) {583		memset(&msg, 0, sizeof(msg));584		memset(buf, 'a' + i, total_len);585 586		if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) {587			int off = 0;588 589			if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) {590				if (family == PF_INET)591					off = fill_header_ipv4(buf);592				else593					off = fill_header_ipv6(buf);594			}595 596			fill_header_udp(buf + off, family == PF_INET);597		}598 599		print_timestamp_usr();600 601		iov.iov_base = buf;602		iov.iov_len = total_len;603 604		if (cfg_proto != SOCK_STREAM) {605			if (cfg_use_pf_packet) {606				memset(&laddr, 0, sizeof(laddr));607 608				laddr.sll_family	= AF_PACKET;609				laddr.sll_ifindex	= 1;610				laddr.sll_protocol	= htons(family == AF_INET ? ETH_P_IP : ETH_P_IPV6);611				laddr.sll_halen		= ETH_ALEN;612 613				msg.msg_name = (void *)&laddr;614				msg.msg_namelen = sizeof(laddr);615			} else if (family == PF_INET) {616				msg.msg_name = (void *)&daddr;617				msg.msg_namelen = sizeof(daddr);618			} else {619				msg.msg_name = (void *)&daddr6;620				msg.msg_namelen = sizeof(daddr6);621			}622		}623 624		msg.msg_iov = &iov;625		msg.msg_iovlen = 1;626 627		if (cfg_use_cmsg) {628			memset(control, 0, sizeof(control));629 630			msg.msg_control = control;631			msg.msg_controllen = sizeof(control);632 633			cmsg = CMSG_FIRSTHDR(&msg);634			cmsg->cmsg_level = SOL_SOCKET;635			cmsg->cmsg_type = SO_TIMESTAMPING;636			cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));637 638			*((uint32_t *) CMSG_DATA(cmsg)) = report_opt;639		}640 641		val = sendmsg(fd, &msg, 0);642		if (val != total_len)643			error(1, errno, "send");644 645		/* wait for all errors to be queued, else ACKs arrive OOO */646		if (cfg_sleep_usec)647			usleep(cfg_sleep_usec);648 649		if (!cfg_busy_poll) {650			if (cfg_use_epoll)651				__epoll(epfd);652			else653				__poll(fd);654		}655 656		while (!recv_errmsg(fd)) {}657	}658 659	print_timing_event("USR-ENQ", &usr_enq);660	print_timing_event("USR-SND", &usr_snd);661	print_timing_event("USR-ACK", &usr_ack);662 663	if (close(fd))664		error(1, errno, "close");665 666	free(buf);667	usleep(100 * NSEC_PER_USEC);668}669 670static void __attribute__((noreturn)) usage(const char *filepath)671{672	fprintf(stderr, "\nUsage: %s [options] hostname\n"673			"\nwhere options are:\n"674			"  -4:   only IPv4\n"675			"  -6:   only IPv6\n"676			"  -h:   show this message\n"677			"  -b:   busy poll to read from error queue\n"678			"  -c N: number of packets for each test\n"679			"  -C:   use cmsg to set tstamp recording options\n"680			"  -e:   use level-triggered epoll() instead of poll()\n"681			"  -E:   use event-triggered epoll() instead of poll()\n"682			"  -F:   poll()/epoll() waits forever for an event\n"683			"  -I:   request PKTINFO\n"684			"  -l N: send N bytes at a time\n"685			"  -L    listen on hostname and port\n"686			"  -n:   set no-payload option\n"687			"  -N:   print timestamps and durations in nsec (instead of usec)\n"688			"  -p N: connect to port N\n"689			"  -P:   use PF_PACKET\n"690			"  -r:   use raw\n"691			"  -R:   use raw (IP_HDRINCL)\n"692			"  -S N: usec to sleep before reading error queue\n"693			"  -t N: tolerance (usec) for timestamp validation\n"694			"  -u:   use udp\n"695			"  -v:   validate SND delay (usec)\n"696			"  -V:   validate ACK delay (usec)\n"697			"  -x:   show payload (up to 70 bytes)\n",698			filepath);699	exit(1);700}701 702static void parse_opt(int argc, char **argv)703{704	int proto_count = 0;705	int c;706 707	while ((c = getopt(argc, argv,708				"46bc:CeEFhIl:LnNp:PrRS:t:uv:V:x")) != -1) {709		switch (c) {710		case '4':711			do_ipv6 = 0;712			break;713		case '6':714			do_ipv4 = 0;715			break;716		case 'b':717			cfg_busy_poll = true;718			break;719		case 'c':720			cfg_num_pkts = strtoul(optarg, NULL, 10);721			break;722		case 'C':723			cfg_use_cmsg = true;724			break;725		case 'e':726			cfg_use_epoll = true;727			break;728		case 'E':729			cfg_use_epoll = true;730			cfg_epollet = true;731		case 'F':732			cfg_poll_timeout = -1;733			break;734		case 'I':735			cfg_do_pktinfo = true;736			break;737		case 'l':738			cfg_payload_len = strtoul(optarg, NULL, 10);739			break;740		case 'L':741			cfg_do_listen = true;742			break;743		case 'n':744			cfg_loop_nodata = true;745			break;746		case 'N':747			cfg_print_nsec = true;748			break;749		case 'p':750			dest_port = strtoul(optarg, NULL, 10);751			break;752		case 'P':753			proto_count++;754			cfg_use_pf_packet = true;755			cfg_proto = SOCK_DGRAM;756			cfg_ipproto = 0;757			break;758		case 'r':759			proto_count++;760			cfg_proto = SOCK_RAW;761			cfg_ipproto = IPPROTO_UDP;762			break;763		case 'R':764			proto_count++;765			cfg_proto = SOCK_RAW;766			cfg_ipproto = IPPROTO_RAW;767			break;768		case 'S':769			cfg_sleep_usec = strtoul(optarg, NULL, 10);770			break;771		case 't':772			cfg_delay_tolerance_usec = strtoul(optarg, NULL, 10);773			break;774		case 'u':775			proto_count++;776			cfg_proto = SOCK_DGRAM;777			cfg_ipproto = IPPROTO_UDP;778			break;779		case 'v':780			cfg_delay_snd = strtoul(optarg, NULL, 10);781			break;782		case 'V':783			cfg_delay_ack = strtoul(optarg, NULL, 10);784			break;785		case 'x':786			cfg_show_payload = true;787			break;788		case 'h':789		default:790			usage(argv[0]);791		}792	}793 794	if (!cfg_payload_len)795		error(1, 0, "payload may not be nonzero");796	if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)797		error(1, 0, "udp packet might exceed expected MTU");798	if (!do_ipv4 && !do_ipv6)799		error(1, 0, "pass -4 or -6, not both");800	if (proto_count > 1)801		error(1, 0, "pass -P, -r, -R or -u, not multiple");802	if (cfg_do_pktinfo && cfg_use_pf_packet)803		error(1, 0, "cannot ask for pktinfo over pf_packet");804	if (cfg_busy_poll && cfg_use_epoll)805		error(1, 0, "pass epoll or busy_poll, not both");806 807	if (optind != argc - 1)808		error(1, 0, "missing required hostname argument");809}810 811static void resolve_hostname(const char *hostname)812{813	struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 };814	struct addrinfo *addrs, *cur;815	int have_ipv4 = 0, have_ipv6 = 0;816 817retry:818	if (getaddrinfo(hostname, NULL, &hints, &addrs))819		error(1, errno, "getaddrinfo");820 821	cur = addrs;822	while (cur && !have_ipv4 && !have_ipv6) {823		if (!have_ipv4 && cur->ai_family == AF_INET) {824			memcpy(&daddr, cur->ai_addr, sizeof(daddr));825			daddr.sin_port = htons(dest_port);826			have_ipv4 = 1;827		}828		else if (!have_ipv6 && cur->ai_family == AF_INET6) {829			memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));830			daddr6.sin6_port = htons(dest_port);831			have_ipv6 = 1;832		}833		cur = cur->ai_next;834	}835	if (addrs)836		freeaddrinfo(addrs);837 838	if (do_ipv6 && hints.ai_family != AF_INET6) {839		hints.ai_family = AF_INET6;840		goto retry;841	}842 843	do_ipv4 &= have_ipv4;844	do_ipv6 &= have_ipv6;845}846 847static void do_listen(int family, void *addr, int alen)848{849	int fd, type;850 851	type = cfg_proto == SOCK_RAW ? SOCK_DGRAM : cfg_proto;852 853	fd = socket(family, type, 0);854	if (fd == -1)855		error(1, errno, "socket rx");856 857	if (bind(fd, addr, alen))858		error(1, errno, "bind rx");859 860	if (type == SOCK_STREAM && listen(fd, 10))861		error(1, errno, "listen rx");862 863	/* leave fd open, will be closed on process exit.864	 * this enables connect() to succeed and avoids icmp replies865	 */866}867 868static void do_main(int family)869{870	fprintf(stderr, "family:       %s %s\n",871			family == PF_INET ? "INET" : "INET6",872			cfg_use_pf_packet ? "(PF_PACKET)" : "");873 874	fprintf(stderr, "test SND\n");875	do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);876 877	fprintf(stderr, "test ENQ\n");878	do_test(family, SOF_TIMESTAMPING_TX_SCHED);879 880	fprintf(stderr, "test ENQ + SND\n");881	do_test(family, SOF_TIMESTAMPING_TX_SCHED |882			SOF_TIMESTAMPING_TX_SOFTWARE);883 884	if (cfg_proto == SOCK_STREAM) {885		fprintf(stderr, "\ntest ACK\n");886		do_test(family, SOF_TIMESTAMPING_TX_ACK);887 888		fprintf(stderr, "\ntest SND + ACK\n");889		do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |890				SOF_TIMESTAMPING_TX_ACK);891 892		fprintf(stderr, "\ntest ENQ + SND + ACK\n");893		do_test(family, SOF_TIMESTAMPING_TX_SCHED |894				SOF_TIMESTAMPING_TX_SOFTWARE |895				SOF_TIMESTAMPING_TX_ACK);896	}897}898 899const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };900 901int main(int argc, char **argv)902{903	if (argc == 1)904		usage(argv[0]);905 906	parse_opt(argc, argv);907	resolve_hostname(argv[argc - 1]);908 909	fprintf(stderr, "protocol:     %s\n", sock_names[cfg_proto]);910	fprintf(stderr, "payload:      %u\n", cfg_payload_len);911	fprintf(stderr, "server port:  %u\n", dest_port);912	fprintf(stderr, "\n");913 914	if (do_ipv4) {915		if (cfg_do_listen)916			do_listen(PF_INET, &daddr, sizeof(daddr));917		do_main(PF_INET);918	}919 920	if (do_ipv6) {921		if (cfg_do_listen)922			do_listen(PF_INET6, &daddr6, sizeof(daddr6));923		do_main(PF_INET6);924	}925 926	return test_failed;927}928