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