1a086ee24SJakub Kicinski // SPDX-License-Identifier: GPL-2.0-or-later
2a086ee24SJakub Kicinski #include <errno.h>
349b78613SJakub Kicinski #include <error.h>
4a086ee24SJakub Kicinski #include <netdb.h>
549b78613SJakub Kicinski #include <stdbool.h>
6a086ee24SJakub Kicinski #include <stdio.h>
7a086ee24SJakub Kicinski #include <stdlib.h>
8a086ee24SJakub Kicinski #include <string.h>
94d397424SJakub Kicinski #include <time.h>
10a086ee24SJakub Kicinski #include <unistd.h>
11eb8f3116SJakub Kicinski #include <linux/errqueue.h>
12de17e305SJakub Kicinski #include <linux/icmp.h>
13de17e305SJakub Kicinski #include <linux/icmpv6.h>
144d397424SJakub Kicinski #include <linux/net_tstamp.h>
15a086ee24SJakub Kicinski #include <linux/types.h>
16de17e305SJakub Kicinski #include <linux/udp.h>
17a086ee24SJakub Kicinski #include <sys/socket.h>
18a086ee24SJakub Kicinski 
191abea24aSGuo Zhengkui #include "../kselftest.h"
201abea24aSGuo Zhengkui 
2149b78613SJakub Kicinski enum {
2249b78613SJakub Kicinski 	ERN_SUCCESS = 0,
2349b78613SJakub Kicinski 	/* Well defined errors, callers may depend on these */
2449b78613SJakub Kicinski 	ERN_SEND = 1,
2549b78613SJakub Kicinski 	/* Informational, can reorder */
2649b78613SJakub Kicinski 	ERN_HELP,
2749b78613SJakub Kicinski 	ERN_SEND_SHORT,
2849b78613SJakub Kicinski 	ERN_SOCK_CREATE,
2949b78613SJakub Kicinski 	ERN_RESOLVE,
3049b78613SJakub Kicinski 	ERN_CMSG_WR,
314d397424SJakub Kicinski 	ERN_SOCKOPT,
324d397424SJakub Kicinski 	ERN_GETTIME,
33eb8f3116SJakub Kicinski 	ERN_RECVERR,
34eb8f3116SJakub Kicinski 	ERN_CMSG_RD,
35eb8f3116SJakub Kicinski 	ERN_CMSG_RCV,
3649b78613SJakub Kicinski };
3749b78613SJakub Kicinski 
386f97c7c6SJakub Kicinski struct option_cmsg_u32 {
396f97c7c6SJakub Kicinski 	bool ena;
406f97c7c6SJakub Kicinski 	unsigned int val;
416f97c7c6SJakub Kicinski };
426f97c7c6SJakub Kicinski 
4349b78613SJakub Kicinski struct options {
4449b78613SJakub Kicinski 	bool silent_send;
4549b78613SJakub Kicinski 	const char *host;
4649b78613SJakub Kicinski 	const char *service;
476f97c7c6SJakub Kicinski 	unsigned int size;
4849b78613SJakub Kicinski 	struct {
499bbfbc92SJakub Kicinski 		unsigned int mark;
506f97c7c6SJakub Kicinski 		unsigned int dontfrag;
519657ad09SJakub Kicinski 		unsigned int tclass;
5205ae83d5SJakub Kicinski 		unsigned int hlimit;
539bbfbc92SJakub Kicinski 	} sockopt;
549bbfbc92SJakub Kicinski 	struct {
55de17e305SJakub Kicinski 		unsigned int family;
5649b78613SJakub Kicinski 		unsigned int type;
57de17e305SJakub Kicinski 		unsigned int proto;
5849b78613SJakub Kicinski 	} sock;
596f97c7c6SJakub Kicinski 	struct option_cmsg_u32 mark;
604d397424SJakub Kicinski 	struct {
614d397424SJakub Kicinski 		bool ena;
624d397424SJakub Kicinski 		unsigned int delay;
634d397424SJakub Kicinski 	} txtime;
64eb8f3116SJakub Kicinski 	struct {
65eb8f3116SJakub Kicinski 		bool ena;
66eb8f3116SJakub Kicinski 	} ts;
676f97c7c6SJakub Kicinski 	struct {
686f97c7c6SJakub Kicinski 		struct option_cmsg_u32 dontfrag;
699657ad09SJakub Kicinski 		struct option_cmsg_u32 tclass;
7005ae83d5SJakub Kicinski 		struct option_cmsg_u32 hlimit;
71a22982c3SJakub Kicinski 		struct option_cmsg_u32 exthdr;
726f97c7c6SJakub Kicinski 	} v6;
7349b78613SJakub Kicinski } opt = {
746f97c7c6SJakub Kicinski 	.size = 13,
7549b78613SJakub Kicinski 	.sock = {
76de17e305SJakub Kicinski 		.family	= AF_UNSPEC,
7749b78613SJakub Kicinski 		.type	= SOCK_DGRAM,
78de17e305SJakub Kicinski 		.proto	= IPPROTO_UDP,
7949b78613SJakub Kicinski 	},
8049b78613SJakub Kicinski };
8149b78613SJakub Kicinski 
82eb8f3116SJakub Kicinski static struct timespec time_start_real;
834d397424SJakub Kicinski static struct timespec time_start_mono;
844d397424SJakub Kicinski 
cs_usage(const char * bin)8549b78613SJakub Kicinski static void __attribute__((noreturn)) cs_usage(const char *bin)
86a086ee24SJakub Kicinski {
8749b78613SJakub Kicinski 	printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin);
8849b78613SJakub Kicinski 	printf("Options:\n"
8949b78613SJakub Kicinski 	       "\t\t-s      Silent send() failures\n"
906f97c7c6SJakub Kicinski 	       "\t\t-S      send() size\n"
91de17e305SJakub Kicinski 	       "\t\t-4/-6   Force IPv4 / IPv6 only\n"
92de17e305SJakub Kicinski 	       "\t\t-p prot Socket protocol\n"
93de17e305SJakub Kicinski 	       "\t\t        (u = UDP (default); i = ICMP; r = RAW)\n"
94de17e305SJakub Kicinski 	       "\n"
9549b78613SJakub Kicinski 	       "\t\t-m val  Set SO_MARK with given value\n"
969bbfbc92SJakub Kicinski 	       "\t\t-M val  Set SO_MARK via setsockopt\n"
974d397424SJakub Kicinski 	       "\t\t-d val  Set SO_TXTIME with given delay (usec)\n"
98eb8f3116SJakub Kicinski 	       "\t\t-t      Enable time stamp reporting\n"
996f97c7c6SJakub Kicinski 	       "\t\t-f val  Set don't fragment via cmsg\n"
1006f97c7c6SJakub Kicinski 	       "\t\t-F val  Set don't fragment via setsockopt\n"
1019657ad09SJakub Kicinski 	       "\t\t-c val  Set TCLASS via cmsg\n"
1029657ad09SJakub Kicinski 	       "\t\t-C val  Set TCLASS via setsockopt\n"
10305ae83d5SJakub Kicinski 	       "\t\t-l val  Set HOPLIMIT via cmsg\n"
10405ae83d5SJakub Kicinski 	       "\t\t-L val  Set HOPLIMIT via setsockopt\n"
105a22982c3SJakub Kicinski 	       "\t\t-H type Add an IPv6 header option\n"
106a22982c3SJakub Kicinski 	       "\t\t        (h = HOP; d = DST; r = RTDST)"
10749b78613SJakub Kicinski 	       "");
10849b78613SJakub Kicinski 	exit(ERN_HELP);
10949b78613SJakub Kicinski }
11049b78613SJakub Kicinski 
cs_parse_args(int argc,char * argv[])11149b78613SJakub Kicinski static void cs_parse_args(int argc, char *argv[])
11249b78613SJakub Kicinski {
1131573c688SPo-Hsu Lin 	int o;
11449b78613SJakub Kicinski 
115a22982c3SJakub Kicinski 	while ((o = getopt(argc, argv, "46sS:p:m:M:d:tf:F:c:C:l:L:H:")) != -1) {
11649b78613SJakub Kicinski 		switch (o) {
11749b78613SJakub Kicinski 		case 's':
11849b78613SJakub Kicinski 			opt.silent_send = true;
11949b78613SJakub Kicinski 			break;
1206f97c7c6SJakub Kicinski 		case 'S':
1216f97c7c6SJakub Kicinski 			opt.size = atoi(optarg);
1226f97c7c6SJakub Kicinski 			break;
123de17e305SJakub Kicinski 		case '4':
124de17e305SJakub Kicinski 			opt.sock.family = AF_INET;
125de17e305SJakub Kicinski 			break;
126de17e305SJakub Kicinski 		case '6':
127de17e305SJakub Kicinski 			opt.sock.family = AF_INET6;
128de17e305SJakub Kicinski 			break;
129de17e305SJakub Kicinski 		case 'p':
130de17e305SJakub Kicinski 			if (*optarg == 'u' || *optarg == 'U') {
131de17e305SJakub Kicinski 				opt.sock.proto = IPPROTO_UDP;
132de17e305SJakub Kicinski 			} else if (*optarg == 'i' || *optarg == 'I') {
133de17e305SJakub Kicinski 				opt.sock.proto = IPPROTO_ICMP;
134de17e305SJakub Kicinski 			} else if (*optarg == 'r') {
135de17e305SJakub Kicinski 				opt.sock.type = SOCK_RAW;
136de17e305SJakub Kicinski 			} else {
137de17e305SJakub Kicinski 				printf("Error: unknown protocol: %s\n", optarg);
138de17e305SJakub Kicinski 				cs_usage(argv[0]);
139de17e305SJakub Kicinski 			}
140de17e305SJakub Kicinski 			break;
1414d397424SJakub Kicinski 
14249b78613SJakub Kicinski 		case 'm':
14349b78613SJakub Kicinski 			opt.mark.ena = true;
14449b78613SJakub Kicinski 			opt.mark.val = atoi(optarg);
14549b78613SJakub Kicinski 			break;
1469bbfbc92SJakub Kicinski 		case 'M':
1479bbfbc92SJakub Kicinski 			opt.sockopt.mark = atoi(optarg);
1489bbfbc92SJakub Kicinski 			break;
1494d397424SJakub Kicinski 		case 'd':
1504d397424SJakub Kicinski 			opt.txtime.ena = true;
1514d397424SJakub Kicinski 			opt.txtime.delay = atoi(optarg);
1524d397424SJakub Kicinski 			break;
153eb8f3116SJakub Kicinski 		case 't':
154eb8f3116SJakub Kicinski 			opt.ts.ena = true;
155eb8f3116SJakub Kicinski 			break;
1566f97c7c6SJakub Kicinski 		case 'f':
1576f97c7c6SJakub Kicinski 			opt.v6.dontfrag.ena = true;
1586f97c7c6SJakub Kicinski 			opt.v6.dontfrag.val = atoi(optarg);
1596f97c7c6SJakub Kicinski 			break;
1606f97c7c6SJakub Kicinski 		case 'F':
1616f97c7c6SJakub Kicinski 			opt.sockopt.dontfrag = atoi(optarg);
1626f97c7c6SJakub Kicinski 			break;
1639657ad09SJakub Kicinski 		case 'c':
1649657ad09SJakub Kicinski 			opt.v6.tclass.ena = true;
1659657ad09SJakub Kicinski 			opt.v6.tclass.val = atoi(optarg);
1669657ad09SJakub Kicinski 			break;
1679657ad09SJakub Kicinski 		case 'C':
1689657ad09SJakub Kicinski 			opt.sockopt.tclass = atoi(optarg);
1699657ad09SJakub Kicinski 			break;
17005ae83d5SJakub Kicinski 		case 'l':
17105ae83d5SJakub Kicinski 			opt.v6.hlimit.ena = true;
17205ae83d5SJakub Kicinski 			opt.v6.hlimit.val = atoi(optarg);
17305ae83d5SJakub Kicinski 			break;
17405ae83d5SJakub Kicinski 		case 'L':
17505ae83d5SJakub Kicinski 			opt.sockopt.hlimit = atoi(optarg);
17605ae83d5SJakub Kicinski 			break;
177a22982c3SJakub Kicinski 		case 'H':
178a22982c3SJakub Kicinski 			opt.v6.exthdr.ena = true;
179a22982c3SJakub Kicinski 			switch (optarg[0]) {
180a22982c3SJakub Kicinski 			case 'h':
181a22982c3SJakub Kicinski 				opt.v6.exthdr.val = IPV6_HOPOPTS;
182a22982c3SJakub Kicinski 				break;
183a22982c3SJakub Kicinski 			case 'd':
184a22982c3SJakub Kicinski 				opt.v6.exthdr.val = IPV6_DSTOPTS;
185a22982c3SJakub Kicinski 				break;
186a22982c3SJakub Kicinski 			case 'r':
187a22982c3SJakub Kicinski 				opt.v6.exthdr.val = IPV6_RTHDRDSTOPTS;
188a22982c3SJakub Kicinski 				break;
189a22982c3SJakub Kicinski 			default:
190a22982c3SJakub Kicinski 				printf("Error: hdr type: %s\n", optarg);
191a22982c3SJakub Kicinski 				break;
192a22982c3SJakub Kicinski 			}
193a22982c3SJakub Kicinski 			break;
19449b78613SJakub Kicinski 		}
19549b78613SJakub Kicinski 	}
19649b78613SJakub Kicinski 
19749b78613SJakub Kicinski 	if (optind != argc - 2)
19849b78613SJakub Kicinski 		cs_usage(argv[0]);
19949b78613SJakub Kicinski 
20049b78613SJakub Kicinski 	opt.host = argv[optind];
20149b78613SJakub Kicinski 	opt.service = argv[optind + 1];
20249b78613SJakub Kicinski }
20349b78613SJakub Kicinski 
memrnd(void * s,size_t n)2046f97c7c6SJakub Kicinski static void memrnd(void *s, size_t n)
2056f97c7c6SJakub Kicinski {
2066f97c7c6SJakub Kicinski 	int *dword = s;
2076f97c7c6SJakub Kicinski 	char *byte;
2086f97c7c6SJakub Kicinski 
2096f97c7c6SJakub Kicinski 	for (; n >= 4; n -= 4)
2106f97c7c6SJakub Kicinski 		*dword++ = rand();
2116f97c7c6SJakub Kicinski 	byte = (void *)dword;
2126f97c7c6SJakub Kicinski 	while (n--)
2136f97c7c6SJakub Kicinski 		*byte++ = rand();
2146f97c7c6SJakub Kicinski }
2156f97c7c6SJakub Kicinski 
2166f97c7c6SJakub Kicinski static void
ca_write_cmsg_u32(char * cbuf,size_t cbuf_sz,size_t * cmsg_len,int level,int optname,struct option_cmsg_u32 * uopt)2176f97c7c6SJakub Kicinski ca_write_cmsg_u32(char *cbuf, size_t cbuf_sz, size_t *cmsg_len,
2186f97c7c6SJakub Kicinski 		  int level, int optname, struct option_cmsg_u32 *uopt)
2196f97c7c6SJakub Kicinski {
2206f97c7c6SJakub Kicinski 	struct cmsghdr *cmsg;
2216f97c7c6SJakub Kicinski 
2226f97c7c6SJakub Kicinski 	if (!uopt->ena)
2236f97c7c6SJakub Kicinski 		return;
2246f97c7c6SJakub Kicinski 
2256f97c7c6SJakub Kicinski 	cmsg = (struct cmsghdr *)(cbuf + *cmsg_len);
2266f97c7c6SJakub Kicinski 	*cmsg_len += CMSG_SPACE(sizeof(__u32));
2276f97c7c6SJakub Kicinski 	if (cbuf_sz < *cmsg_len)
2286f97c7c6SJakub Kicinski 		error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
2296f97c7c6SJakub Kicinski 
2306f97c7c6SJakub Kicinski 	cmsg->cmsg_level = level;
2316f97c7c6SJakub Kicinski 	cmsg->cmsg_type = optname;
2326f97c7c6SJakub Kicinski 	cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
2336f97c7c6SJakub Kicinski 	*(__u32 *)CMSG_DATA(cmsg) = uopt->val;
2346f97c7c6SJakub Kicinski }
2356f97c7c6SJakub Kicinski 
23649b78613SJakub Kicinski static void
cs_write_cmsg(int fd,struct msghdr * msg,char * cbuf,size_t cbuf_sz)2374d397424SJakub Kicinski cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
23849b78613SJakub Kicinski {
239a086ee24SJakub Kicinski 	struct cmsghdr *cmsg;
24049b78613SJakub Kicinski 	size_t cmsg_len;
24149b78613SJakub Kicinski 
24249b78613SJakub Kicinski 	msg->msg_control = cbuf;
24349b78613SJakub Kicinski 	cmsg_len = 0;
24449b78613SJakub Kicinski 
2456f97c7c6SJakub Kicinski 	ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
2466f97c7c6SJakub Kicinski 			  SOL_SOCKET, SO_MARK, &opt.mark);
2476f97c7c6SJakub Kicinski 	ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
2486f97c7c6SJakub Kicinski 			  SOL_IPV6, IPV6_DONTFRAG, &opt.v6.dontfrag);
2499657ad09SJakub Kicinski 	ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
2509657ad09SJakub Kicinski 			  SOL_IPV6, IPV6_TCLASS, &opt.v6.tclass);
25105ae83d5SJakub Kicinski 	ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len,
25205ae83d5SJakub Kicinski 			  SOL_IPV6, IPV6_HOPLIMIT, &opt.v6.hlimit);
25349b78613SJakub Kicinski 
2544d397424SJakub Kicinski 	if (opt.txtime.ena) {
2554d397424SJakub Kicinski 		struct sock_txtime so_txtime = {
2564d397424SJakub Kicinski 			.clockid = CLOCK_MONOTONIC,
2574d397424SJakub Kicinski 		};
2584d397424SJakub Kicinski 		__u64 txtime;
2594d397424SJakub Kicinski 
2604d397424SJakub Kicinski 		if (setsockopt(fd, SOL_SOCKET, SO_TXTIME,
2614d397424SJakub Kicinski 			       &so_txtime, sizeof(so_txtime)))
2624d397424SJakub Kicinski 			error(ERN_SOCKOPT, errno, "setsockopt TXTIME");
2634d397424SJakub Kicinski 
2644d397424SJakub Kicinski 		txtime = time_start_mono.tv_sec * (1000ULL * 1000 * 1000) +
2654d397424SJakub Kicinski 			 time_start_mono.tv_nsec +
2664d397424SJakub Kicinski 			 opt.txtime.delay * 1000;
2674d397424SJakub Kicinski 
2684d397424SJakub Kicinski 		cmsg = (struct cmsghdr *)(cbuf + cmsg_len);
2694d397424SJakub Kicinski 		cmsg_len += CMSG_SPACE(sizeof(txtime));
2704d397424SJakub Kicinski 		if (cbuf_sz < cmsg_len)
2714d397424SJakub Kicinski 			error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
2724d397424SJakub Kicinski 
2734d397424SJakub Kicinski 		cmsg->cmsg_level = SOL_SOCKET;
2744d397424SJakub Kicinski 		cmsg->cmsg_type = SCM_TXTIME;
2754d397424SJakub Kicinski 		cmsg->cmsg_len = CMSG_LEN(sizeof(txtime));
2764d397424SJakub Kicinski 		memcpy(CMSG_DATA(cmsg), &txtime, sizeof(txtime));
2774d397424SJakub Kicinski 	}
278eb8f3116SJakub Kicinski 	if (opt.ts.ena) {
279eb8f3116SJakub Kicinski 		__u32 val = SOF_TIMESTAMPING_SOFTWARE |
280eb8f3116SJakub Kicinski 			    SOF_TIMESTAMPING_OPT_TSONLY;
281eb8f3116SJakub Kicinski 
282eb8f3116SJakub Kicinski 		if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
283eb8f3116SJakub Kicinski 			       &val, sizeof(val)))
284eb8f3116SJakub Kicinski 			error(ERN_SOCKOPT, errno, "setsockopt TIMESTAMPING");
285eb8f3116SJakub Kicinski 
286eb8f3116SJakub Kicinski 		cmsg = (struct cmsghdr *)(cbuf + cmsg_len);
287eb8f3116SJakub Kicinski 		cmsg_len += CMSG_SPACE(sizeof(__u32));
288eb8f3116SJakub Kicinski 		if (cbuf_sz < cmsg_len)
289eb8f3116SJakub Kicinski 			error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
290eb8f3116SJakub Kicinski 
291eb8f3116SJakub Kicinski 		cmsg->cmsg_level = SOL_SOCKET;
292eb8f3116SJakub Kicinski 		cmsg->cmsg_type = SO_TIMESTAMPING;
293eb8f3116SJakub Kicinski 		cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
294eb8f3116SJakub Kicinski 		*(__u32 *)CMSG_DATA(cmsg) = SOF_TIMESTAMPING_TX_SCHED |
295eb8f3116SJakub Kicinski 					    SOF_TIMESTAMPING_TX_SOFTWARE;
296eb8f3116SJakub Kicinski 	}
297a22982c3SJakub Kicinski 	if (opt.v6.exthdr.ena) {
298a22982c3SJakub Kicinski 		cmsg = (struct cmsghdr *)(cbuf + cmsg_len);
299a22982c3SJakub Kicinski 		cmsg_len += CMSG_SPACE(8);
300a22982c3SJakub Kicinski 		if (cbuf_sz < cmsg_len)
301a22982c3SJakub Kicinski 			error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small");
302a22982c3SJakub Kicinski 
303a22982c3SJakub Kicinski 		cmsg->cmsg_level = SOL_IPV6;
304a22982c3SJakub Kicinski 		cmsg->cmsg_type = opt.v6.exthdr.val;
305a22982c3SJakub Kicinski 		cmsg->cmsg_len = CMSG_LEN(8);
306a22982c3SJakub Kicinski 		*(__u64 *)CMSG_DATA(cmsg) = 0;
307a22982c3SJakub Kicinski 	}
30849b78613SJakub Kicinski 
30949b78613SJakub Kicinski 	if (cmsg_len)
31049b78613SJakub Kicinski 		msg->msg_controllen = cmsg_len;
31149b78613SJakub Kicinski 	else
31249b78613SJakub Kicinski 		msg->msg_control = NULL;
31349b78613SJakub Kicinski }
31449b78613SJakub Kicinski 
cs_ts_info2str(unsigned int info)315eb8f3116SJakub Kicinski static const char *cs_ts_info2str(unsigned int info)
316eb8f3116SJakub Kicinski {
317eb8f3116SJakub Kicinski 	static const char *names[] = {
318eb8f3116SJakub Kicinski 		[SCM_TSTAMP_SND]	= "SND",
319eb8f3116SJakub Kicinski 		[SCM_TSTAMP_SCHED]	= "SCHED",
320eb8f3116SJakub Kicinski 		[SCM_TSTAMP_ACK]	= "ACK",
321eb8f3116SJakub Kicinski 	};
322eb8f3116SJakub Kicinski 
3231abea24aSGuo Zhengkui 	if (info < ARRAY_SIZE(names))
324eb8f3116SJakub Kicinski 		return names[info];
325eb8f3116SJakub Kicinski 	return "unknown";
326eb8f3116SJakub Kicinski }
327eb8f3116SJakub Kicinski 
328eb8f3116SJakub Kicinski static void
cs_read_cmsg(int fd,struct msghdr * msg,char * cbuf,size_t cbuf_sz)329eb8f3116SJakub Kicinski cs_read_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz)
330eb8f3116SJakub Kicinski {
331eb8f3116SJakub Kicinski 	struct sock_extended_err *see;
332eb8f3116SJakub Kicinski 	struct scm_timestamping *ts;
333eb8f3116SJakub Kicinski 	struct cmsghdr *cmsg;
334eb8f3116SJakub Kicinski 	int i, err;
335eb8f3116SJakub Kicinski 
336eb8f3116SJakub Kicinski 	if (!opt.ts.ena)
337eb8f3116SJakub Kicinski 		return;
338eb8f3116SJakub Kicinski 	msg->msg_control = cbuf;
339eb8f3116SJakub Kicinski 	msg->msg_controllen = cbuf_sz;
340eb8f3116SJakub Kicinski 
341eb8f3116SJakub Kicinski 	while (true) {
342eb8f3116SJakub Kicinski 		ts = NULL;
343eb8f3116SJakub Kicinski 		see = NULL;
344eb8f3116SJakub Kicinski 		memset(cbuf, 0, cbuf_sz);
345eb8f3116SJakub Kicinski 
346eb8f3116SJakub Kicinski 		err = recvmsg(fd, msg, MSG_ERRQUEUE);
347eb8f3116SJakub Kicinski 		if (err < 0) {
348eb8f3116SJakub Kicinski 			if (errno == EAGAIN)
349eb8f3116SJakub Kicinski 				break;
350eb8f3116SJakub Kicinski 			error(ERN_RECVERR, errno, "recvmsg ERRQ");
351eb8f3116SJakub Kicinski 		}
352eb8f3116SJakub Kicinski 
353eb8f3116SJakub Kicinski 		for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
354eb8f3116SJakub Kicinski 		     cmsg = CMSG_NXTHDR(msg, cmsg)) {
355eb8f3116SJakub Kicinski 			if (cmsg->cmsg_level == SOL_SOCKET &&
356eb8f3116SJakub Kicinski 			    cmsg->cmsg_type == SO_TIMESTAMPING_OLD) {
357eb8f3116SJakub Kicinski 				if (cmsg->cmsg_len < sizeof(*ts))
358eb8f3116SJakub Kicinski 					error(ERN_CMSG_RD, EINVAL, "TS cmsg");
359eb8f3116SJakub Kicinski 
360eb8f3116SJakub Kicinski 				ts = (void *)CMSG_DATA(cmsg);
361eb8f3116SJakub Kicinski 			}
362eb8f3116SJakub Kicinski 			if ((cmsg->cmsg_level == SOL_IP &&
363eb8f3116SJakub Kicinski 			     cmsg->cmsg_type == IP_RECVERR) ||
364eb8f3116SJakub Kicinski 			    (cmsg->cmsg_level == SOL_IPV6 &&
365eb8f3116SJakub Kicinski 			     cmsg->cmsg_type == IPV6_RECVERR)) {
366eb8f3116SJakub Kicinski 				if (cmsg->cmsg_len < sizeof(*see))
367eb8f3116SJakub Kicinski 					error(ERN_CMSG_RD, EINVAL, "sock_err cmsg");
368eb8f3116SJakub Kicinski 
369eb8f3116SJakub Kicinski 				see = (void *)CMSG_DATA(cmsg);
370eb8f3116SJakub Kicinski 			}
371eb8f3116SJakub Kicinski 		}
372eb8f3116SJakub Kicinski 
373eb8f3116SJakub Kicinski 		if (!ts)
374eb8f3116SJakub Kicinski 			error(ERN_CMSG_RCV, ENOENT, "TS cmsg not found");
375eb8f3116SJakub Kicinski 		if (!see)
376eb8f3116SJakub Kicinski 			error(ERN_CMSG_RCV, ENOENT, "sock_err cmsg not found");
377eb8f3116SJakub Kicinski 
378eb8f3116SJakub Kicinski 		for (i = 0; i < 3; i++) {
379eb8f3116SJakub Kicinski 			unsigned long long rel_time;
380eb8f3116SJakub Kicinski 
381eb8f3116SJakub Kicinski 			if (!ts->ts[i].tv_sec && !ts->ts[i].tv_nsec)
382eb8f3116SJakub Kicinski 				continue;
383eb8f3116SJakub Kicinski 
384eb8f3116SJakub Kicinski 			rel_time = (ts->ts[i].tv_sec - time_start_real.tv_sec) *
385eb8f3116SJakub Kicinski 				(1000ULL * 1000) +
386eb8f3116SJakub Kicinski 				(ts->ts[i].tv_nsec - time_start_real.tv_nsec) /
387eb8f3116SJakub Kicinski 				1000;
388eb8f3116SJakub Kicinski 			printf(" %5s ts%d %lluus\n",
389eb8f3116SJakub Kicinski 			       cs_ts_info2str(see->ee_info),
390eb8f3116SJakub Kicinski 			       i, rel_time);
391eb8f3116SJakub Kicinski 		}
392eb8f3116SJakub Kicinski 	}
393eb8f3116SJakub Kicinski }
394eb8f3116SJakub Kicinski 
ca_set_sockopts(int fd)3956f97c7c6SJakub Kicinski static void ca_set_sockopts(int fd)
3966f97c7c6SJakub Kicinski {
3976f97c7c6SJakub Kicinski 	if (opt.sockopt.mark &&
3986f97c7c6SJakub Kicinski 	    setsockopt(fd, SOL_SOCKET, SO_MARK,
3996f97c7c6SJakub Kicinski 		       &opt.sockopt.mark, sizeof(opt.sockopt.mark)))
4006f97c7c6SJakub Kicinski 		error(ERN_SOCKOPT, errno, "setsockopt SO_MARK");
4016f97c7c6SJakub Kicinski 	if (opt.sockopt.dontfrag &&
4026f97c7c6SJakub Kicinski 	    setsockopt(fd, SOL_IPV6, IPV6_DONTFRAG,
4036f97c7c6SJakub Kicinski 		       &opt.sockopt.dontfrag, sizeof(opt.sockopt.dontfrag)))
4046f97c7c6SJakub Kicinski 		error(ERN_SOCKOPT, errno, "setsockopt IPV6_DONTFRAG");
4059657ad09SJakub Kicinski 	if (opt.sockopt.tclass &&
4069657ad09SJakub Kicinski 	    setsockopt(fd, SOL_IPV6, IPV6_TCLASS,
4079657ad09SJakub Kicinski 		       &opt.sockopt.tclass, sizeof(opt.sockopt.tclass)))
4089657ad09SJakub Kicinski 		error(ERN_SOCKOPT, errno, "setsockopt IPV6_TCLASS");
40905ae83d5SJakub Kicinski 	if (opt.sockopt.hlimit &&
41005ae83d5SJakub Kicinski 	    setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS,
41105ae83d5SJakub Kicinski 		       &opt.sockopt.hlimit, sizeof(opt.sockopt.hlimit)))
41205ae83d5SJakub Kicinski 		error(ERN_SOCKOPT, errno, "setsockopt IPV6_HOPLIMIT");
4136f97c7c6SJakub Kicinski }
4146f97c7c6SJakub Kicinski 
main(int argc,char * argv[])41549b78613SJakub Kicinski int main(int argc, char *argv[])
41649b78613SJakub Kicinski {
41749b78613SJakub Kicinski 	struct addrinfo hints, *ai;
418a086ee24SJakub Kicinski 	struct iovec iov[1];
419*aea066acSWillem de Bruijn 	unsigned char *buf;
420a086ee24SJakub Kicinski 	struct msghdr msg;
42149b78613SJakub Kicinski 	char cbuf[1024];
422a086ee24SJakub Kicinski 	int err;
423a086ee24SJakub Kicinski 	int fd;
424a086ee24SJakub Kicinski 
42549b78613SJakub Kicinski 	cs_parse_args(argc, argv);
426a086ee24SJakub Kicinski 
4276f97c7c6SJakub Kicinski 	buf = malloc(opt.size);
4286f97c7c6SJakub Kicinski 	memrnd(buf, opt.size);
4296f97c7c6SJakub Kicinski 
430a086ee24SJakub Kicinski 	memset(&hints, 0, sizeof(hints));
431de17e305SJakub Kicinski 	hints.ai_family = opt.sock.family;
432a086ee24SJakub Kicinski 
433a086ee24SJakub Kicinski 	ai = NULL;
43449b78613SJakub Kicinski 	err = getaddrinfo(opt.host, opt.service, &hints, &ai);
435a086ee24SJakub Kicinski 	if (err) {
436de17e305SJakub Kicinski 		fprintf(stderr, "Can't resolve address [%s]:%s\n",
437de17e305SJakub Kicinski 			opt.host, opt.service);
43849b78613SJakub Kicinski 		return ERN_SOCK_CREATE;
439a086ee24SJakub Kicinski 	}
440a086ee24SJakub Kicinski 
441de17e305SJakub Kicinski 	if (ai->ai_family == AF_INET6 && opt.sock.proto == IPPROTO_ICMP)
442de17e305SJakub Kicinski 		opt.sock.proto = IPPROTO_ICMPV6;
443de17e305SJakub Kicinski 
444de17e305SJakub Kicinski 	fd = socket(ai->ai_family, opt.sock.type, opt.sock.proto);
445a086ee24SJakub Kicinski 	if (fd < 0) {
446a086ee24SJakub Kicinski 		fprintf(stderr, "Can't open socket: %s\n", strerror(errno));
447a086ee24SJakub Kicinski 		freeaddrinfo(ai);
44849b78613SJakub Kicinski 		return ERN_RESOLVE;
449a086ee24SJakub Kicinski 	}
450a086ee24SJakub Kicinski 
451de17e305SJakub Kicinski 	if (opt.sock.proto == IPPROTO_ICMP) {
452de17e305SJakub Kicinski 		buf[0] = ICMP_ECHO;
453de17e305SJakub Kicinski 		buf[1] = 0;
454de17e305SJakub Kicinski 	} else if (opt.sock.proto == IPPROTO_ICMPV6) {
455de17e305SJakub Kicinski 		buf[0] = ICMPV6_ECHO_REQUEST;
456de17e305SJakub Kicinski 		buf[1] = 0;
457de17e305SJakub Kicinski 	} else if (opt.sock.type == SOCK_RAW) {
4586f97c7c6SJakub Kicinski 		struct udphdr hdr = { 1, 2, htons(opt.size), 0 };
459dbdd9a28SLi kunyu 		struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;
460de17e305SJakub Kicinski 
461de17e305SJakub Kicinski 		memcpy(buf, &hdr, sizeof(hdr));
462de17e305SJakub Kicinski 		sin6->sin6_port = htons(opt.sock.proto);
463de17e305SJakub Kicinski 	}
464de17e305SJakub Kicinski 
4656f97c7c6SJakub Kicinski 	ca_set_sockopts(fd);
4669bbfbc92SJakub Kicinski 
467eb8f3116SJakub Kicinski 	if (clock_gettime(CLOCK_REALTIME, &time_start_real))
468eb8f3116SJakub Kicinski 		error(ERN_GETTIME, errno, "gettime REALTIME");
4694d397424SJakub Kicinski 	if (clock_gettime(CLOCK_MONOTONIC, &time_start_mono))
47012d8c111SColin Ian King 		error(ERN_GETTIME, errno, "gettime MONOTONIC");
4714d397424SJakub Kicinski 
472de17e305SJakub Kicinski 	iov[0].iov_base = buf;
4736f97c7c6SJakub Kicinski 	iov[0].iov_len = opt.size;
474a086ee24SJakub Kicinski 
47549b78613SJakub Kicinski 	memset(&msg, 0, sizeof(msg));
476a086ee24SJakub Kicinski 	msg.msg_name = ai->ai_addr;
477a086ee24SJakub Kicinski 	msg.msg_namelen = ai->ai_addrlen;
478a086ee24SJakub Kicinski 	msg.msg_iov = iov;
479a086ee24SJakub Kicinski 	msg.msg_iovlen = 1;
480a086ee24SJakub Kicinski 
4814d397424SJakub Kicinski 	cs_write_cmsg(fd, &msg, cbuf, sizeof(cbuf));
482a086ee24SJakub Kicinski 
483a086ee24SJakub Kicinski 	err = sendmsg(fd, &msg, 0);
48449b78613SJakub Kicinski 	if (err < 0) {
48549b78613SJakub Kicinski 		if (!opt.silent_send)
48649b78613SJakub Kicinski 			fprintf(stderr, "send failed: %s\n", strerror(errno));
48749b78613SJakub Kicinski 		err = ERN_SEND;
488eb8f3116SJakub Kicinski 		goto err_out;
4896f97c7c6SJakub Kicinski 	} else if (err != (int)opt.size) {
49049b78613SJakub Kicinski 		fprintf(stderr, "short send\n");
49149b78613SJakub Kicinski 		err = ERN_SEND_SHORT;
492eb8f3116SJakub Kicinski 		goto err_out;
49349b78613SJakub Kicinski 	} else {
49449b78613SJakub Kicinski 		err = ERN_SUCCESS;
49549b78613SJakub Kicinski 	}
496a086ee24SJakub Kicinski 
497eb8f3116SJakub Kicinski 	/* Make sure all timestamps have time to loop back */
498eb8f3116SJakub Kicinski 	usleep(opt.txtime.delay);
499eb8f3116SJakub Kicinski 
500eb8f3116SJakub Kicinski 	cs_read_cmsg(fd, &msg, cbuf, sizeof(cbuf));
501eb8f3116SJakub Kicinski 
502eb8f3116SJakub Kicinski err_out:
503a086ee24SJakub Kicinski 	close(fd);
504a086ee24SJakub Kicinski 	freeaddrinfo(ai);
50549b78613SJakub Kicinski 	return err;
506a086ee24SJakub Kicinski }
507