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 1949b78613SJakub Kicinski enum { 2049b78613SJakub Kicinski ERN_SUCCESS = 0, 2149b78613SJakub Kicinski /* Well defined errors, callers may depend on these */ 2249b78613SJakub Kicinski ERN_SEND = 1, 2349b78613SJakub Kicinski /* Informational, can reorder */ 2449b78613SJakub Kicinski ERN_HELP, 2549b78613SJakub Kicinski ERN_SEND_SHORT, 2649b78613SJakub Kicinski ERN_SOCK_CREATE, 2749b78613SJakub Kicinski ERN_RESOLVE, 2849b78613SJakub Kicinski ERN_CMSG_WR, 294d397424SJakub Kicinski ERN_SOCKOPT, 304d397424SJakub Kicinski ERN_GETTIME, 31eb8f3116SJakub Kicinski ERN_RECVERR, 32eb8f3116SJakub Kicinski ERN_CMSG_RD, 33eb8f3116SJakub Kicinski ERN_CMSG_RCV, 3449b78613SJakub Kicinski }; 3549b78613SJakub Kicinski 366f97c7c6SJakub Kicinski struct option_cmsg_u32 { 376f97c7c6SJakub Kicinski bool ena; 386f97c7c6SJakub Kicinski unsigned int val; 396f97c7c6SJakub Kicinski }; 406f97c7c6SJakub Kicinski 4149b78613SJakub Kicinski struct options { 4249b78613SJakub Kicinski bool silent_send; 4349b78613SJakub Kicinski const char *host; 4449b78613SJakub Kicinski const char *service; 456f97c7c6SJakub Kicinski unsigned int size; 4649b78613SJakub Kicinski struct { 479bbfbc92SJakub Kicinski unsigned int mark; 486f97c7c6SJakub Kicinski unsigned int dontfrag; 499657ad09SJakub Kicinski unsigned int tclass; 50*05ae83d5SJakub Kicinski unsigned int hlimit; 519bbfbc92SJakub Kicinski } sockopt; 529bbfbc92SJakub Kicinski struct { 53de17e305SJakub Kicinski unsigned int family; 5449b78613SJakub Kicinski unsigned int type; 55de17e305SJakub Kicinski unsigned int proto; 5649b78613SJakub Kicinski } sock; 576f97c7c6SJakub Kicinski struct option_cmsg_u32 mark; 584d397424SJakub Kicinski struct { 594d397424SJakub Kicinski bool ena; 604d397424SJakub Kicinski unsigned int delay; 614d397424SJakub Kicinski } txtime; 62eb8f3116SJakub Kicinski struct { 63eb8f3116SJakub Kicinski bool ena; 64eb8f3116SJakub Kicinski } ts; 656f97c7c6SJakub Kicinski struct { 666f97c7c6SJakub Kicinski struct option_cmsg_u32 dontfrag; 679657ad09SJakub Kicinski struct option_cmsg_u32 tclass; 68*05ae83d5SJakub Kicinski struct option_cmsg_u32 hlimit; 696f97c7c6SJakub Kicinski } v6; 7049b78613SJakub Kicinski } opt = { 716f97c7c6SJakub Kicinski .size = 13, 7249b78613SJakub Kicinski .sock = { 73de17e305SJakub Kicinski .family = AF_UNSPEC, 7449b78613SJakub Kicinski .type = SOCK_DGRAM, 75de17e305SJakub Kicinski .proto = IPPROTO_UDP, 7649b78613SJakub Kicinski }, 7749b78613SJakub Kicinski }; 7849b78613SJakub Kicinski 79eb8f3116SJakub Kicinski static struct timespec time_start_real; 804d397424SJakub Kicinski static struct timespec time_start_mono; 814d397424SJakub Kicinski 8249b78613SJakub Kicinski static void __attribute__((noreturn)) cs_usage(const char *bin) 83a086ee24SJakub Kicinski { 8449b78613SJakub Kicinski printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin); 8549b78613SJakub Kicinski printf("Options:\n" 8649b78613SJakub Kicinski "\t\t-s Silent send() failures\n" 876f97c7c6SJakub Kicinski "\t\t-S send() size\n" 88de17e305SJakub Kicinski "\t\t-4/-6 Force IPv4 / IPv6 only\n" 89de17e305SJakub Kicinski "\t\t-p prot Socket protocol\n" 90de17e305SJakub Kicinski "\t\t (u = UDP (default); i = ICMP; r = RAW)\n" 91de17e305SJakub Kicinski "\n" 9249b78613SJakub Kicinski "\t\t-m val Set SO_MARK with given value\n" 939bbfbc92SJakub Kicinski "\t\t-M val Set SO_MARK via setsockopt\n" 944d397424SJakub Kicinski "\t\t-d val Set SO_TXTIME with given delay (usec)\n" 95eb8f3116SJakub Kicinski "\t\t-t Enable time stamp reporting\n" 966f97c7c6SJakub Kicinski "\t\t-f val Set don't fragment via cmsg\n" 976f97c7c6SJakub Kicinski "\t\t-F val Set don't fragment via setsockopt\n" 989657ad09SJakub Kicinski "\t\t-c val Set TCLASS via cmsg\n" 999657ad09SJakub Kicinski "\t\t-C val Set TCLASS via setsockopt\n" 100*05ae83d5SJakub Kicinski "\t\t-l val Set HOPLIMIT via cmsg\n" 101*05ae83d5SJakub Kicinski "\t\t-L val Set HOPLIMIT via setsockopt\n" 10249b78613SJakub Kicinski ""); 10349b78613SJakub Kicinski exit(ERN_HELP); 10449b78613SJakub Kicinski } 10549b78613SJakub Kicinski 10649b78613SJakub Kicinski static void cs_parse_args(int argc, char *argv[]) 10749b78613SJakub Kicinski { 10849b78613SJakub Kicinski char o; 10949b78613SJakub Kicinski 110*05ae83d5SJakub Kicinski while ((o = getopt(argc, argv, "46sS:p:m:M:d:tf:F:c:C:l:L:")) != -1) { 11149b78613SJakub Kicinski switch (o) { 11249b78613SJakub Kicinski case 's': 11349b78613SJakub Kicinski opt.silent_send = true; 11449b78613SJakub Kicinski break; 1156f97c7c6SJakub Kicinski case 'S': 1166f97c7c6SJakub Kicinski opt.size = atoi(optarg); 1176f97c7c6SJakub Kicinski break; 118de17e305SJakub Kicinski case '4': 119de17e305SJakub Kicinski opt.sock.family = AF_INET; 120de17e305SJakub Kicinski break; 121de17e305SJakub Kicinski case '6': 122de17e305SJakub Kicinski opt.sock.family = AF_INET6; 123de17e305SJakub Kicinski break; 124de17e305SJakub Kicinski case 'p': 125de17e305SJakub Kicinski if (*optarg == 'u' || *optarg == 'U') { 126de17e305SJakub Kicinski opt.sock.proto = IPPROTO_UDP; 127de17e305SJakub Kicinski } else if (*optarg == 'i' || *optarg == 'I') { 128de17e305SJakub Kicinski opt.sock.proto = IPPROTO_ICMP; 129de17e305SJakub Kicinski } else if (*optarg == 'r') { 130de17e305SJakub Kicinski opt.sock.type = SOCK_RAW; 131de17e305SJakub Kicinski } else { 132de17e305SJakub Kicinski printf("Error: unknown protocol: %s\n", optarg); 133de17e305SJakub Kicinski cs_usage(argv[0]); 134de17e305SJakub Kicinski } 135de17e305SJakub Kicinski break; 1364d397424SJakub Kicinski 13749b78613SJakub Kicinski case 'm': 13849b78613SJakub Kicinski opt.mark.ena = true; 13949b78613SJakub Kicinski opt.mark.val = atoi(optarg); 14049b78613SJakub Kicinski break; 1419bbfbc92SJakub Kicinski case 'M': 1429bbfbc92SJakub Kicinski opt.sockopt.mark = atoi(optarg); 1439bbfbc92SJakub Kicinski break; 1444d397424SJakub Kicinski case 'd': 1454d397424SJakub Kicinski opt.txtime.ena = true; 1464d397424SJakub Kicinski opt.txtime.delay = atoi(optarg); 1474d397424SJakub Kicinski break; 148eb8f3116SJakub Kicinski case 't': 149eb8f3116SJakub Kicinski opt.ts.ena = true; 150eb8f3116SJakub Kicinski break; 1516f97c7c6SJakub Kicinski case 'f': 1526f97c7c6SJakub Kicinski opt.v6.dontfrag.ena = true; 1536f97c7c6SJakub Kicinski opt.v6.dontfrag.val = atoi(optarg); 1546f97c7c6SJakub Kicinski break; 1556f97c7c6SJakub Kicinski case 'F': 1566f97c7c6SJakub Kicinski opt.sockopt.dontfrag = atoi(optarg); 1576f97c7c6SJakub Kicinski break; 1589657ad09SJakub Kicinski case 'c': 1599657ad09SJakub Kicinski opt.v6.tclass.ena = true; 1609657ad09SJakub Kicinski opt.v6.tclass.val = atoi(optarg); 1619657ad09SJakub Kicinski break; 1629657ad09SJakub Kicinski case 'C': 1639657ad09SJakub Kicinski opt.sockopt.tclass = atoi(optarg); 1649657ad09SJakub Kicinski break; 165*05ae83d5SJakub Kicinski case 'l': 166*05ae83d5SJakub Kicinski opt.v6.hlimit.ena = true; 167*05ae83d5SJakub Kicinski opt.v6.hlimit.val = atoi(optarg); 168*05ae83d5SJakub Kicinski break; 169*05ae83d5SJakub Kicinski case 'L': 170*05ae83d5SJakub Kicinski opt.sockopt.hlimit = atoi(optarg); 171*05ae83d5SJakub Kicinski break; 17249b78613SJakub Kicinski } 17349b78613SJakub Kicinski } 17449b78613SJakub Kicinski 17549b78613SJakub Kicinski if (optind != argc - 2) 17649b78613SJakub Kicinski cs_usage(argv[0]); 17749b78613SJakub Kicinski 17849b78613SJakub Kicinski opt.host = argv[optind]; 17949b78613SJakub Kicinski opt.service = argv[optind + 1]; 18049b78613SJakub Kicinski } 18149b78613SJakub Kicinski 1826f97c7c6SJakub Kicinski static void memrnd(void *s, size_t n) 1836f97c7c6SJakub Kicinski { 1846f97c7c6SJakub Kicinski int *dword = s; 1856f97c7c6SJakub Kicinski char *byte; 1866f97c7c6SJakub Kicinski 1876f97c7c6SJakub Kicinski for (; n >= 4; n -= 4) 1886f97c7c6SJakub Kicinski *dword++ = rand(); 1896f97c7c6SJakub Kicinski byte = (void *)dword; 1906f97c7c6SJakub Kicinski while (n--) 1916f97c7c6SJakub Kicinski *byte++ = rand(); 1926f97c7c6SJakub Kicinski } 1936f97c7c6SJakub Kicinski 1946f97c7c6SJakub Kicinski static void 1956f97c7c6SJakub Kicinski ca_write_cmsg_u32(char *cbuf, size_t cbuf_sz, size_t *cmsg_len, 1966f97c7c6SJakub Kicinski int level, int optname, struct option_cmsg_u32 *uopt) 1976f97c7c6SJakub Kicinski { 1986f97c7c6SJakub Kicinski struct cmsghdr *cmsg; 1996f97c7c6SJakub Kicinski 2006f97c7c6SJakub Kicinski if (!uopt->ena) 2016f97c7c6SJakub Kicinski return; 2026f97c7c6SJakub Kicinski 2036f97c7c6SJakub Kicinski cmsg = (struct cmsghdr *)(cbuf + *cmsg_len); 2046f97c7c6SJakub Kicinski *cmsg_len += CMSG_SPACE(sizeof(__u32)); 2056f97c7c6SJakub Kicinski if (cbuf_sz < *cmsg_len) 2066f97c7c6SJakub Kicinski error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); 2076f97c7c6SJakub Kicinski 2086f97c7c6SJakub Kicinski cmsg->cmsg_level = level; 2096f97c7c6SJakub Kicinski cmsg->cmsg_type = optname; 2106f97c7c6SJakub Kicinski cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); 2116f97c7c6SJakub Kicinski *(__u32 *)CMSG_DATA(cmsg) = uopt->val; 2126f97c7c6SJakub Kicinski } 2136f97c7c6SJakub Kicinski 21449b78613SJakub Kicinski static void 2154d397424SJakub Kicinski cs_write_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) 21649b78613SJakub Kicinski { 217a086ee24SJakub Kicinski struct cmsghdr *cmsg; 21849b78613SJakub Kicinski size_t cmsg_len; 21949b78613SJakub Kicinski 22049b78613SJakub Kicinski msg->msg_control = cbuf; 22149b78613SJakub Kicinski cmsg_len = 0; 22249b78613SJakub Kicinski 2236f97c7c6SJakub Kicinski ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, 2246f97c7c6SJakub Kicinski SOL_SOCKET, SO_MARK, &opt.mark); 2256f97c7c6SJakub Kicinski ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, 2266f97c7c6SJakub Kicinski SOL_IPV6, IPV6_DONTFRAG, &opt.v6.dontfrag); 2279657ad09SJakub Kicinski ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, 2289657ad09SJakub Kicinski SOL_IPV6, IPV6_TCLASS, &opt.v6.tclass); 229*05ae83d5SJakub Kicinski ca_write_cmsg_u32(cbuf, cbuf_sz, &cmsg_len, 230*05ae83d5SJakub Kicinski SOL_IPV6, IPV6_HOPLIMIT, &opt.v6.hlimit); 23149b78613SJakub Kicinski 2324d397424SJakub Kicinski if (opt.txtime.ena) { 2334d397424SJakub Kicinski struct sock_txtime so_txtime = { 2344d397424SJakub Kicinski .clockid = CLOCK_MONOTONIC, 2354d397424SJakub Kicinski }; 2364d397424SJakub Kicinski __u64 txtime; 2374d397424SJakub Kicinski 2384d397424SJakub Kicinski if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, 2394d397424SJakub Kicinski &so_txtime, sizeof(so_txtime))) 2404d397424SJakub Kicinski error(ERN_SOCKOPT, errno, "setsockopt TXTIME"); 2414d397424SJakub Kicinski 2424d397424SJakub Kicinski txtime = time_start_mono.tv_sec * (1000ULL * 1000 * 1000) + 2434d397424SJakub Kicinski time_start_mono.tv_nsec + 2444d397424SJakub Kicinski opt.txtime.delay * 1000; 2454d397424SJakub Kicinski 2464d397424SJakub Kicinski cmsg = (struct cmsghdr *)(cbuf + cmsg_len); 2474d397424SJakub Kicinski cmsg_len += CMSG_SPACE(sizeof(txtime)); 2484d397424SJakub Kicinski if (cbuf_sz < cmsg_len) 2494d397424SJakub Kicinski error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); 2504d397424SJakub Kicinski 2514d397424SJakub Kicinski cmsg->cmsg_level = SOL_SOCKET; 2524d397424SJakub Kicinski cmsg->cmsg_type = SCM_TXTIME; 2534d397424SJakub Kicinski cmsg->cmsg_len = CMSG_LEN(sizeof(txtime)); 2544d397424SJakub Kicinski memcpy(CMSG_DATA(cmsg), &txtime, sizeof(txtime)); 2554d397424SJakub Kicinski } 256eb8f3116SJakub Kicinski if (opt.ts.ena) { 257eb8f3116SJakub Kicinski __u32 val = SOF_TIMESTAMPING_SOFTWARE | 258eb8f3116SJakub Kicinski SOF_TIMESTAMPING_OPT_TSONLY; 259eb8f3116SJakub Kicinski 260eb8f3116SJakub Kicinski if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, 261eb8f3116SJakub Kicinski &val, sizeof(val))) 262eb8f3116SJakub Kicinski error(ERN_SOCKOPT, errno, "setsockopt TIMESTAMPING"); 263eb8f3116SJakub Kicinski 264eb8f3116SJakub Kicinski cmsg = (struct cmsghdr *)(cbuf + cmsg_len); 265eb8f3116SJakub Kicinski cmsg_len += CMSG_SPACE(sizeof(__u32)); 266eb8f3116SJakub Kicinski if (cbuf_sz < cmsg_len) 267eb8f3116SJakub Kicinski error(ERN_CMSG_WR, EFAULT, "cmsg buffer too small"); 268eb8f3116SJakub Kicinski 269eb8f3116SJakub Kicinski cmsg->cmsg_level = SOL_SOCKET; 270eb8f3116SJakub Kicinski cmsg->cmsg_type = SO_TIMESTAMPING; 271eb8f3116SJakub Kicinski cmsg->cmsg_len = CMSG_LEN(sizeof(__u32)); 272eb8f3116SJakub Kicinski *(__u32 *)CMSG_DATA(cmsg) = SOF_TIMESTAMPING_TX_SCHED | 273eb8f3116SJakub Kicinski SOF_TIMESTAMPING_TX_SOFTWARE; 274eb8f3116SJakub Kicinski } 27549b78613SJakub Kicinski 27649b78613SJakub Kicinski if (cmsg_len) 27749b78613SJakub Kicinski msg->msg_controllen = cmsg_len; 27849b78613SJakub Kicinski else 27949b78613SJakub Kicinski msg->msg_control = NULL; 28049b78613SJakub Kicinski } 28149b78613SJakub Kicinski 282eb8f3116SJakub Kicinski static const char *cs_ts_info2str(unsigned int info) 283eb8f3116SJakub Kicinski { 284eb8f3116SJakub Kicinski static const char *names[] = { 285eb8f3116SJakub Kicinski [SCM_TSTAMP_SND] = "SND", 286eb8f3116SJakub Kicinski [SCM_TSTAMP_SCHED] = "SCHED", 287eb8f3116SJakub Kicinski [SCM_TSTAMP_ACK] = "ACK", 288eb8f3116SJakub Kicinski }; 289eb8f3116SJakub Kicinski 290eb8f3116SJakub Kicinski if (info < sizeof(names) / sizeof(names[0])) 291eb8f3116SJakub Kicinski return names[info]; 292eb8f3116SJakub Kicinski return "unknown"; 293eb8f3116SJakub Kicinski } 294eb8f3116SJakub Kicinski 295eb8f3116SJakub Kicinski static void 296eb8f3116SJakub Kicinski cs_read_cmsg(int fd, struct msghdr *msg, char *cbuf, size_t cbuf_sz) 297eb8f3116SJakub Kicinski { 298eb8f3116SJakub Kicinski struct sock_extended_err *see; 299eb8f3116SJakub Kicinski struct scm_timestamping *ts; 300eb8f3116SJakub Kicinski struct cmsghdr *cmsg; 301eb8f3116SJakub Kicinski int i, err; 302eb8f3116SJakub Kicinski 303eb8f3116SJakub Kicinski if (!opt.ts.ena) 304eb8f3116SJakub Kicinski return; 305eb8f3116SJakub Kicinski msg->msg_control = cbuf; 306eb8f3116SJakub Kicinski msg->msg_controllen = cbuf_sz; 307eb8f3116SJakub Kicinski 308eb8f3116SJakub Kicinski while (true) { 309eb8f3116SJakub Kicinski ts = NULL; 310eb8f3116SJakub Kicinski see = NULL; 311eb8f3116SJakub Kicinski memset(cbuf, 0, cbuf_sz); 312eb8f3116SJakub Kicinski 313eb8f3116SJakub Kicinski err = recvmsg(fd, msg, MSG_ERRQUEUE); 314eb8f3116SJakub Kicinski if (err < 0) { 315eb8f3116SJakub Kicinski if (errno == EAGAIN) 316eb8f3116SJakub Kicinski break; 317eb8f3116SJakub Kicinski error(ERN_RECVERR, errno, "recvmsg ERRQ"); 318eb8f3116SJakub Kicinski } 319eb8f3116SJakub Kicinski 320eb8f3116SJakub Kicinski for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; 321eb8f3116SJakub Kicinski cmsg = CMSG_NXTHDR(msg, cmsg)) { 322eb8f3116SJakub Kicinski if (cmsg->cmsg_level == SOL_SOCKET && 323eb8f3116SJakub Kicinski cmsg->cmsg_type == SO_TIMESTAMPING_OLD) { 324eb8f3116SJakub Kicinski if (cmsg->cmsg_len < sizeof(*ts)) 325eb8f3116SJakub Kicinski error(ERN_CMSG_RD, EINVAL, "TS cmsg"); 326eb8f3116SJakub Kicinski 327eb8f3116SJakub Kicinski ts = (void *)CMSG_DATA(cmsg); 328eb8f3116SJakub Kicinski } 329eb8f3116SJakub Kicinski if ((cmsg->cmsg_level == SOL_IP && 330eb8f3116SJakub Kicinski cmsg->cmsg_type == IP_RECVERR) || 331eb8f3116SJakub Kicinski (cmsg->cmsg_level == SOL_IPV6 && 332eb8f3116SJakub Kicinski cmsg->cmsg_type == IPV6_RECVERR)) { 333eb8f3116SJakub Kicinski if (cmsg->cmsg_len < sizeof(*see)) 334eb8f3116SJakub Kicinski error(ERN_CMSG_RD, EINVAL, "sock_err cmsg"); 335eb8f3116SJakub Kicinski 336eb8f3116SJakub Kicinski see = (void *)CMSG_DATA(cmsg); 337eb8f3116SJakub Kicinski } 338eb8f3116SJakub Kicinski } 339eb8f3116SJakub Kicinski 340eb8f3116SJakub Kicinski if (!ts) 341eb8f3116SJakub Kicinski error(ERN_CMSG_RCV, ENOENT, "TS cmsg not found"); 342eb8f3116SJakub Kicinski if (!see) 343eb8f3116SJakub Kicinski error(ERN_CMSG_RCV, ENOENT, "sock_err cmsg not found"); 344eb8f3116SJakub Kicinski 345eb8f3116SJakub Kicinski for (i = 0; i < 3; i++) { 346eb8f3116SJakub Kicinski unsigned long long rel_time; 347eb8f3116SJakub Kicinski 348eb8f3116SJakub Kicinski if (!ts->ts[i].tv_sec && !ts->ts[i].tv_nsec) 349eb8f3116SJakub Kicinski continue; 350eb8f3116SJakub Kicinski 351eb8f3116SJakub Kicinski rel_time = (ts->ts[i].tv_sec - time_start_real.tv_sec) * 352eb8f3116SJakub Kicinski (1000ULL * 1000) + 353eb8f3116SJakub Kicinski (ts->ts[i].tv_nsec - time_start_real.tv_nsec) / 354eb8f3116SJakub Kicinski 1000; 355eb8f3116SJakub Kicinski printf(" %5s ts%d %lluus\n", 356eb8f3116SJakub Kicinski cs_ts_info2str(see->ee_info), 357eb8f3116SJakub Kicinski i, rel_time); 358eb8f3116SJakub Kicinski } 359eb8f3116SJakub Kicinski } 360eb8f3116SJakub Kicinski } 361eb8f3116SJakub Kicinski 3626f97c7c6SJakub Kicinski static void ca_set_sockopts(int fd) 3636f97c7c6SJakub Kicinski { 3646f97c7c6SJakub Kicinski if (opt.sockopt.mark && 3656f97c7c6SJakub Kicinski setsockopt(fd, SOL_SOCKET, SO_MARK, 3666f97c7c6SJakub Kicinski &opt.sockopt.mark, sizeof(opt.sockopt.mark))) 3676f97c7c6SJakub Kicinski error(ERN_SOCKOPT, errno, "setsockopt SO_MARK"); 3686f97c7c6SJakub Kicinski if (opt.sockopt.dontfrag && 3696f97c7c6SJakub Kicinski setsockopt(fd, SOL_IPV6, IPV6_DONTFRAG, 3706f97c7c6SJakub Kicinski &opt.sockopt.dontfrag, sizeof(opt.sockopt.dontfrag))) 3716f97c7c6SJakub Kicinski error(ERN_SOCKOPT, errno, "setsockopt IPV6_DONTFRAG"); 3729657ad09SJakub Kicinski if (opt.sockopt.tclass && 3739657ad09SJakub Kicinski setsockopt(fd, SOL_IPV6, IPV6_TCLASS, 3749657ad09SJakub Kicinski &opt.sockopt.tclass, sizeof(opt.sockopt.tclass))) 3759657ad09SJakub Kicinski error(ERN_SOCKOPT, errno, "setsockopt IPV6_TCLASS"); 376*05ae83d5SJakub Kicinski if (opt.sockopt.hlimit && 377*05ae83d5SJakub Kicinski setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, 378*05ae83d5SJakub Kicinski &opt.sockopt.hlimit, sizeof(opt.sockopt.hlimit))) 379*05ae83d5SJakub Kicinski error(ERN_SOCKOPT, errno, "setsockopt IPV6_HOPLIMIT"); 3806f97c7c6SJakub Kicinski } 3816f97c7c6SJakub Kicinski 38249b78613SJakub Kicinski int main(int argc, char *argv[]) 38349b78613SJakub Kicinski { 38449b78613SJakub Kicinski struct addrinfo hints, *ai; 385a086ee24SJakub Kicinski struct iovec iov[1]; 386a086ee24SJakub Kicinski struct msghdr msg; 38749b78613SJakub Kicinski char cbuf[1024]; 3886f97c7c6SJakub Kicinski char *buf; 389a086ee24SJakub Kicinski int err; 390a086ee24SJakub Kicinski int fd; 391a086ee24SJakub Kicinski 39249b78613SJakub Kicinski cs_parse_args(argc, argv); 393a086ee24SJakub Kicinski 3946f97c7c6SJakub Kicinski buf = malloc(opt.size); 3956f97c7c6SJakub Kicinski memrnd(buf, opt.size); 3966f97c7c6SJakub Kicinski 397a086ee24SJakub Kicinski memset(&hints, 0, sizeof(hints)); 398de17e305SJakub Kicinski hints.ai_family = opt.sock.family; 399a086ee24SJakub Kicinski 400a086ee24SJakub Kicinski ai = NULL; 40149b78613SJakub Kicinski err = getaddrinfo(opt.host, opt.service, &hints, &ai); 402a086ee24SJakub Kicinski if (err) { 403de17e305SJakub Kicinski fprintf(stderr, "Can't resolve address [%s]:%s\n", 404de17e305SJakub Kicinski opt.host, opt.service); 40549b78613SJakub Kicinski return ERN_SOCK_CREATE; 406a086ee24SJakub Kicinski } 407a086ee24SJakub Kicinski 408de17e305SJakub Kicinski if (ai->ai_family == AF_INET6 && opt.sock.proto == IPPROTO_ICMP) 409de17e305SJakub Kicinski opt.sock.proto = IPPROTO_ICMPV6; 410de17e305SJakub Kicinski 411de17e305SJakub Kicinski fd = socket(ai->ai_family, opt.sock.type, opt.sock.proto); 412a086ee24SJakub Kicinski if (fd < 0) { 413a086ee24SJakub Kicinski fprintf(stderr, "Can't open socket: %s\n", strerror(errno)); 414a086ee24SJakub Kicinski freeaddrinfo(ai); 41549b78613SJakub Kicinski return ERN_RESOLVE; 416a086ee24SJakub Kicinski } 417a086ee24SJakub Kicinski 418de17e305SJakub Kicinski if (opt.sock.proto == IPPROTO_ICMP) { 419de17e305SJakub Kicinski buf[0] = ICMP_ECHO; 420de17e305SJakub Kicinski buf[1] = 0; 421de17e305SJakub Kicinski } else if (opt.sock.proto == IPPROTO_ICMPV6) { 422de17e305SJakub Kicinski buf[0] = ICMPV6_ECHO_REQUEST; 423de17e305SJakub Kicinski buf[1] = 0; 424de17e305SJakub Kicinski } else if (opt.sock.type == SOCK_RAW) { 4256f97c7c6SJakub Kicinski struct udphdr hdr = { 1, 2, htons(opt.size), 0 }; 426de17e305SJakub Kicinski struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;; 427de17e305SJakub Kicinski 428de17e305SJakub Kicinski memcpy(buf, &hdr, sizeof(hdr)); 429de17e305SJakub Kicinski sin6->sin6_port = htons(opt.sock.proto); 430de17e305SJakub Kicinski } 431de17e305SJakub Kicinski 4326f97c7c6SJakub Kicinski ca_set_sockopts(fd); 4339bbfbc92SJakub Kicinski 434eb8f3116SJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &time_start_real)) 435eb8f3116SJakub Kicinski error(ERN_GETTIME, errno, "gettime REALTIME"); 4364d397424SJakub Kicinski if (clock_gettime(CLOCK_MONOTONIC, &time_start_mono)) 43712d8c111SColin Ian King error(ERN_GETTIME, errno, "gettime MONOTONIC"); 4384d397424SJakub Kicinski 439de17e305SJakub Kicinski iov[0].iov_base = buf; 4406f97c7c6SJakub Kicinski iov[0].iov_len = opt.size; 441a086ee24SJakub Kicinski 44249b78613SJakub Kicinski memset(&msg, 0, sizeof(msg)); 443a086ee24SJakub Kicinski msg.msg_name = ai->ai_addr; 444a086ee24SJakub Kicinski msg.msg_namelen = ai->ai_addrlen; 445a086ee24SJakub Kicinski msg.msg_iov = iov; 446a086ee24SJakub Kicinski msg.msg_iovlen = 1; 447a086ee24SJakub Kicinski 4484d397424SJakub Kicinski cs_write_cmsg(fd, &msg, cbuf, sizeof(cbuf)); 449a086ee24SJakub Kicinski 450a086ee24SJakub Kicinski err = sendmsg(fd, &msg, 0); 45149b78613SJakub Kicinski if (err < 0) { 45249b78613SJakub Kicinski if (!opt.silent_send) 45349b78613SJakub Kicinski fprintf(stderr, "send failed: %s\n", strerror(errno)); 45449b78613SJakub Kicinski err = ERN_SEND; 455eb8f3116SJakub Kicinski goto err_out; 4566f97c7c6SJakub Kicinski } else if (err != (int)opt.size) { 45749b78613SJakub Kicinski fprintf(stderr, "short send\n"); 45849b78613SJakub Kicinski err = ERN_SEND_SHORT; 459eb8f3116SJakub Kicinski goto err_out; 46049b78613SJakub Kicinski } else { 46149b78613SJakub Kicinski err = ERN_SUCCESS; 46249b78613SJakub Kicinski } 463a086ee24SJakub Kicinski 464eb8f3116SJakub Kicinski /* Make sure all timestamps have time to loop back */ 465eb8f3116SJakub Kicinski usleep(opt.txtime.delay); 466eb8f3116SJakub Kicinski 467eb8f3116SJakub Kicinski cs_read_cmsg(fd, &msg, cbuf, sizeof(cbuf)); 468eb8f3116SJakub Kicinski 469eb8f3116SJakub Kicinski err_out: 470a086ee24SJakub Kicinski close(fd); 471a086ee24SJakub Kicinski freeaddrinfo(ai); 47249b78613SJakub Kicinski return err; 473a086ee24SJakub Kicinski } 474