15c6baef3SWillem de Bruijn // SPDX-License-Identifier: GPL-2.0 25c6baef3SWillem de Bruijn /* 35c6baef3SWillem de Bruijn * Test the SO_TXTIME API 45c6baef3SWillem de Bruijn * 55c6baef3SWillem de Bruijn * Takes a stream of { payload, delivery time }[], to be sent across two 65c6baef3SWillem de Bruijn * processes. Start this program on two separate network namespaces or 75c6baef3SWillem de Bruijn * connected hosts, one instance in transmit mode and the other in receive 85c6baef3SWillem de Bruijn * mode using the '-r' option. Receiver will compare arrival timestamps to 95c6baef3SWillem de Bruijn * the expected stream. Sender will read transmit timestamps from the error 105c6baef3SWillem de Bruijn * queue. The streams can differ due to out-of-order delivery and drops. 115c6baef3SWillem de Bruijn */ 125c6baef3SWillem de Bruijn 135c6baef3SWillem de Bruijn #define _GNU_SOURCE 145c6baef3SWillem de Bruijn 155c6baef3SWillem de Bruijn #include <arpa/inet.h> 165c6baef3SWillem de Bruijn #include <error.h> 175c6baef3SWillem de Bruijn #include <errno.h> 185c6baef3SWillem de Bruijn #include <inttypes.h> 195c6baef3SWillem de Bruijn #include <linux/net_tstamp.h> 205c6baef3SWillem de Bruijn #include <linux/errqueue.h> 215c6baef3SWillem de Bruijn #include <linux/if_ether.h> 225c6baef3SWillem de Bruijn #include <linux/ipv6.h> 235c6baef3SWillem de Bruijn #include <linux/udp.h> 245c6baef3SWillem de Bruijn #include <stdbool.h> 255c6baef3SWillem de Bruijn #include <stdlib.h> 265c6baef3SWillem de Bruijn #include <stdio.h> 275c6baef3SWillem de Bruijn #include <string.h> 285c6baef3SWillem de Bruijn #include <sys/socket.h> 295c6baef3SWillem de Bruijn #include <sys/stat.h> 305c6baef3SWillem de Bruijn #include <sys/time.h> 315c6baef3SWillem de Bruijn #include <sys/types.h> 325c6baef3SWillem de Bruijn #include <time.h> 335c6baef3SWillem de Bruijn #include <unistd.h> 345c6baef3SWillem de Bruijn #include <poll.h> 355c6baef3SWillem de Bruijn 365c6baef3SWillem de Bruijn #include "kselftest.h" 375c6baef3SWillem de Bruijn 385c6baef3SWillem de Bruijn static int cfg_clockid = CLOCK_TAI; 395c6baef3SWillem de Bruijn static uint16_t cfg_port = 8000; 405c6baef3SWillem de Bruijn static int cfg_variance_us = 4000; 41*543bdc15SWillem de Bruijn static bool cfg_machine_slow; 425c6baef3SWillem de Bruijn static uint64_t cfg_start_time_ns; 435c6baef3SWillem de Bruijn static int cfg_mark; 445c6baef3SWillem de Bruijn static bool cfg_rx; 455c6baef3SWillem de Bruijn 465c6baef3SWillem de Bruijn static uint64_t glob_tstart; 475c6baef3SWillem de Bruijn static uint64_t tdeliver_max; 485c6baef3SWillem de Bruijn 495c6baef3SWillem de Bruijn static int errors; 505c6baef3SWillem de Bruijn 515c6baef3SWillem de Bruijn /* encode one timed transmission (of a 1B payload) */ 525c6baef3SWillem de Bruijn struct timed_send { 535c6baef3SWillem de Bruijn char data; 545c6baef3SWillem de Bruijn int64_t delay_us; 555c6baef3SWillem de Bruijn }; 565c6baef3SWillem de Bruijn 575c6baef3SWillem de Bruijn #define MAX_NUM_PKT 8 585c6baef3SWillem de Bruijn static struct timed_send cfg_buf[MAX_NUM_PKT]; 595c6baef3SWillem de Bruijn static int cfg_num_pkt; 605c6baef3SWillem de Bruijn 615c6baef3SWillem de Bruijn static int cfg_errq_level; 625c6baef3SWillem de Bruijn static int cfg_errq_type; 635c6baef3SWillem de Bruijn 645c6baef3SWillem de Bruijn static struct sockaddr_storage cfg_dst_addr; 655c6baef3SWillem de Bruijn static struct sockaddr_storage cfg_src_addr; 665c6baef3SWillem de Bruijn static socklen_t cfg_alen; 675c6baef3SWillem de Bruijn 685c6baef3SWillem de Bruijn static uint64_t gettime_ns(clockid_t clock) 695c6baef3SWillem de Bruijn { 705c6baef3SWillem de Bruijn struct timespec ts; 715c6baef3SWillem de Bruijn 725c6baef3SWillem de Bruijn if (clock_gettime(clock, &ts)) 735c6baef3SWillem de Bruijn error(1, errno, "gettime"); 745c6baef3SWillem de Bruijn 755c6baef3SWillem de Bruijn return ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; 765c6baef3SWillem de Bruijn } 775c6baef3SWillem de Bruijn 785c6baef3SWillem de Bruijn static void do_send_one(int fdt, struct timed_send *ts) 795c6baef3SWillem de Bruijn { 805c6baef3SWillem de Bruijn char control[CMSG_SPACE(sizeof(uint64_t))]; 815c6baef3SWillem de Bruijn struct msghdr msg = {0}; 825c6baef3SWillem de Bruijn struct iovec iov = {0}; 835c6baef3SWillem de Bruijn struct cmsghdr *cm; 845c6baef3SWillem de Bruijn uint64_t tdeliver; 855c6baef3SWillem de Bruijn int ret; 865c6baef3SWillem de Bruijn 875c6baef3SWillem de Bruijn iov.iov_base = &ts->data; 885c6baef3SWillem de Bruijn iov.iov_len = 1; 895c6baef3SWillem de Bruijn 905c6baef3SWillem de Bruijn msg.msg_iov = &iov; 915c6baef3SWillem de Bruijn msg.msg_iovlen = 1; 925c6baef3SWillem de Bruijn msg.msg_name = (struct sockaddr *)&cfg_dst_addr; 935c6baef3SWillem de Bruijn msg.msg_namelen = cfg_alen; 945c6baef3SWillem de Bruijn 955c6baef3SWillem de Bruijn if (ts->delay_us >= 0) { 965c6baef3SWillem de Bruijn memset(control, 0, sizeof(control)); 975c6baef3SWillem de Bruijn msg.msg_control = &control; 985c6baef3SWillem de Bruijn msg.msg_controllen = sizeof(control); 995c6baef3SWillem de Bruijn 1005c6baef3SWillem de Bruijn tdeliver = glob_tstart + ts->delay_us * 1000; 1015c6baef3SWillem de Bruijn tdeliver_max = tdeliver_max > tdeliver ? 1025c6baef3SWillem de Bruijn tdeliver_max : tdeliver; 1035c6baef3SWillem de Bruijn 1045c6baef3SWillem de Bruijn cm = CMSG_FIRSTHDR(&msg); 1055c6baef3SWillem de Bruijn cm->cmsg_level = SOL_SOCKET; 1065c6baef3SWillem de Bruijn cm->cmsg_type = SCM_TXTIME; 1075c6baef3SWillem de Bruijn cm->cmsg_len = CMSG_LEN(sizeof(tdeliver)); 1085c6baef3SWillem de Bruijn memcpy(CMSG_DATA(cm), &tdeliver, sizeof(tdeliver)); 1095c6baef3SWillem de Bruijn } 1105c6baef3SWillem de Bruijn 1115c6baef3SWillem de Bruijn ret = sendmsg(fdt, &msg, 0); 1125c6baef3SWillem de Bruijn if (ret == -1) 1135c6baef3SWillem de Bruijn error(1, errno, "write"); 1145c6baef3SWillem de Bruijn if (ret == 0) 1155c6baef3SWillem de Bruijn error(1, 0, "write: 0B"); 1165c6baef3SWillem de Bruijn 1175c6baef3SWillem de Bruijn } 1185c6baef3SWillem de Bruijn 1195c6baef3SWillem de Bruijn static void do_recv_one(int fdr, struct timed_send *ts) 1205c6baef3SWillem de Bruijn { 1215c6baef3SWillem de Bruijn int64_t tstop, texpect; 1225c6baef3SWillem de Bruijn char rbuf[2]; 1235c6baef3SWillem de Bruijn int ret; 1245c6baef3SWillem de Bruijn 1255c6baef3SWillem de Bruijn ret = recv(fdr, rbuf, sizeof(rbuf), 0); 1265c6baef3SWillem de Bruijn if (ret == -1 && errno == EAGAIN) 1275c6baef3SWillem de Bruijn error(1, EAGAIN, "recv: timeout"); 1285c6baef3SWillem de Bruijn if (ret == -1) 1295c6baef3SWillem de Bruijn error(1, errno, "read"); 1305c6baef3SWillem de Bruijn if (ret != 1) 1315c6baef3SWillem de Bruijn error(1, 0, "read: %dB", ret); 1325c6baef3SWillem de Bruijn 1335c6baef3SWillem de Bruijn tstop = (gettime_ns(cfg_clockid) - glob_tstart) / 1000; 1345c6baef3SWillem de Bruijn texpect = ts->delay_us >= 0 ? ts->delay_us : 0; 1355c6baef3SWillem de Bruijn 1365c6baef3SWillem de Bruijn fprintf(stderr, "payload:%c delay:%lld expected:%lld (us)\n", 1375c6baef3SWillem de Bruijn rbuf[0], (long long)tstop, (long long)texpect); 1385c6baef3SWillem de Bruijn 1395c6baef3SWillem de Bruijn if (rbuf[0] != ts->data) { 1405c6baef3SWillem de Bruijn fprintf(stderr, "payload mismatch. expected %c\n", ts->data); 1415c6baef3SWillem de Bruijn errors++; 1425c6baef3SWillem de Bruijn } 1435c6baef3SWillem de Bruijn 1445c6baef3SWillem de Bruijn if (llabs(tstop - texpect) > cfg_variance_us) { 1455c6baef3SWillem de Bruijn fprintf(stderr, "exceeds variance (%d us)\n", cfg_variance_us); 146*543bdc15SWillem de Bruijn if (!cfg_machine_slow) 1475c6baef3SWillem de Bruijn errors++; 1485c6baef3SWillem de Bruijn } 1495c6baef3SWillem de Bruijn } 1505c6baef3SWillem de Bruijn 1515c6baef3SWillem de Bruijn static void do_recv_verify_empty(int fdr) 1525c6baef3SWillem de Bruijn { 1535c6baef3SWillem de Bruijn char rbuf[1]; 1545c6baef3SWillem de Bruijn int ret; 1555c6baef3SWillem de Bruijn 1565c6baef3SWillem de Bruijn ret = recv(fdr, rbuf, sizeof(rbuf), 0); 1575c6baef3SWillem de Bruijn if (ret != -1 || errno != EAGAIN) 1585c6baef3SWillem de Bruijn error(1, 0, "recv: not empty as expected (%d, %d)", ret, errno); 1595c6baef3SWillem de Bruijn } 1605c6baef3SWillem de Bruijn 1615c6baef3SWillem de Bruijn static int do_recv_errqueue_timeout(int fdt) 1625c6baef3SWillem de Bruijn { 1635c6baef3SWillem de Bruijn char control[CMSG_SPACE(sizeof(struct sock_extended_err)) + 1645c6baef3SWillem de Bruijn CMSG_SPACE(sizeof(struct sockaddr_in6))] = {0}; 1655c6baef3SWillem de Bruijn char data[sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + 1665c6baef3SWillem de Bruijn sizeof(struct udphdr) + 1]; 1675c6baef3SWillem de Bruijn struct sock_extended_err *err; 1685c6baef3SWillem de Bruijn int ret, num_tstamp = 0; 1695c6baef3SWillem de Bruijn struct msghdr msg = {0}; 1705c6baef3SWillem de Bruijn struct iovec iov = {0}; 1715c6baef3SWillem de Bruijn struct cmsghdr *cm; 1725c6baef3SWillem de Bruijn int64_t tstamp = 0; 1735c6baef3SWillem de Bruijn 1745c6baef3SWillem de Bruijn iov.iov_base = data; 1755c6baef3SWillem de Bruijn iov.iov_len = sizeof(data); 1765c6baef3SWillem de Bruijn 1775c6baef3SWillem de Bruijn msg.msg_iov = &iov; 1785c6baef3SWillem de Bruijn msg.msg_iovlen = 1; 1795c6baef3SWillem de Bruijn 1805c6baef3SWillem de Bruijn msg.msg_control = control; 1815c6baef3SWillem de Bruijn msg.msg_controllen = sizeof(control); 1825c6baef3SWillem de Bruijn 1835c6baef3SWillem de Bruijn while (1) { 1845c6baef3SWillem de Bruijn const char *reason = NULL; 1855c6baef3SWillem de Bruijn 1865c6baef3SWillem de Bruijn ret = recvmsg(fdt, &msg, MSG_ERRQUEUE); 1875c6baef3SWillem de Bruijn if (ret == -1 && errno == EAGAIN) 1885c6baef3SWillem de Bruijn break; 1895c6baef3SWillem de Bruijn if (ret == -1) 1905c6baef3SWillem de Bruijn error(1, errno, "errqueue"); 1915c6baef3SWillem de Bruijn if (msg.msg_flags != MSG_ERRQUEUE) 1925c6baef3SWillem de Bruijn error(1, 0, "errqueue: flags 0x%x\n", msg.msg_flags); 1935c6baef3SWillem de Bruijn 1945c6baef3SWillem de Bruijn cm = CMSG_FIRSTHDR(&msg); 1955c6baef3SWillem de Bruijn if (cm->cmsg_level != cfg_errq_level || 1965c6baef3SWillem de Bruijn cm->cmsg_type != cfg_errq_type) 1975c6baef3SWillem de Bruijn error(1, 0, "errqueue: type 0x%x.0x%x\n", 1985c6baef3SWillem de Bruijn cm->cmsg_level, cm->cmsg_type); 1995c6baef3SWillem de Bruijn 2005c6baef3SWillem de Bruijn err = (struct sock_extended_err *)CMSG_DATA(cm); 2015c6baef3SWillem de Bruijn if (err->ee_origin != SO_EE_ORIGIN_TXTIME) 2025c6baef3SWillem de Bruijn error(1, 0, "errqueue: origin 0x%x\n", err->ee_origin); 2035c6baef3SWillem de Bruijn 2045c6baef3SWillem de Bruijn switch (err->ee_errno) { 2055c6baef3SWillem de Bruijn case ECANCELED: 2065c6baef3SWillem de Bruijn if (err->ee_code != SO_EE_CODE_TXTIME_MISSED) 2075c6baef3SWillem de Bruijn error(1, 0, "errqueue: unknown ECANCELED %u\n", 2085c6baef3SWillem de Bruijn err->ee_code); 2095c6baef3SWillem de Bruijn reason = "missed txtime"; 2105c6baef3SWillem de Bruijn break; 2115c6baef3SWillem de Bruijn case EINVAL: 2125c6baef3SWillem de Bruijn if (err->ee_code != SO_EE_CODE_TXTIME_INVALID_PARAM) 2135c6baef3SWillem de Bruijn error(1, 0, "errqueue: unknown EINVAL %u\n", 2145c6baef3SWillem de Bruijn err->ee_code); 2155c6baef3SWillem de Bruijn reason = "invalid txtime"; 2165c6baef3SWillem de Bruijn break; 2175c6baef3SWillem de Bruijn default: 2185c6baef3SWillem de Bruijn error(1, 0, "errqueue: errno %u code %u\n", 2195c6baef3SWillem de Bruijn err->ee_errno, err->ee_code); 2205c6baef3SWillem de Bruijn } 2215c6baef3SWillem de Bruijn 2225c6baef3SWillem de Bruijn tstamp = ((int64_t) err->ee_data) << 32 | err->ee_info; 2235c6baef3SWillem de Bruijn tstamp -= (int64_t) glob_tstart; 2245c6baef3SWillem de Bruijn tstamp /= 1000 * 1000; 2255c6baef3SWillem de Bruijn fprintf(stderr, "send: pkt %c at %" PRId64 "ms dropped: %s\n", 2265c6baef3SWillem de Bruijn data[ret - 1], tstamp, reason); 2275c6baef3SWillem de Bruijn 2285c6baef3SWillem de Bruijn msg.msg_flags = 0; 2295c6baef3SWillem de Bruijn msg.msg_controllen = sizeof(control); 2305c6baef3SWillem de Bruijn num_tstamp++; 2315c6baef3SWillem de Bruijn } 2325c6baef3SWillem de Bruijn 2335c6baef3SWillem de Bruijn return num_tstamp; 2345c6baef3SWillem de Bruijn } 2355c6baef3SWillem de Bruijn 2365c6baef3SWillem de Bruijn static void recv_errqueue_msgs(int fdt) 2375c6baef3SWillem de Bruijn { 2385c6baef3SWillem de Bruijn struct pollfd pfd = { .fd = fdt, .events = POLLERR }; 2395c6baef3SWillem de Bruijn const int timeout_ms = 10; 2405c6baef3SWillem de Bruijn int ret, num_tstamp = 0; 2415c6baef3SWillem de Bruijn 2425c6baef3SWillem de Bruijn do { 2435c6baef3SWillem de Bruijn ret = poll(&pfd, 1, timeout_ms); 2445c6baef3SWillem de Bruijn if (ret == -1) 2455c6baef3SWillem de Bruijn error(1, errno, "poll"); 2465c6baef3SWillem de Bruijn 2475c6baef3SWillem de Bruijn if (ret && (pfd.revents & POLLERR)) 2485c6baef3SWillem de Bruijn num_tstamp += do_recv_errqueue_timeout(fdt); 2495c6baef3SWillem de Bruijn 2505c6baef3SWillem de Bruijn if (num_tstamp == cfg_num_pkt) 2515c6baef3SWillem de Bruijn break; 2525c6baef3SWillem de Bruijn 2535c6baef3SWillem de Bruijn } while (gettime_ns(cfg_clockid) < tdeliver_max); 2545c6baef3SWillem de Bruijn } 2555c6baef3SWillem de Bruijn 2565c6baef3SWillem de Bruijn static void start_time_wait(void) 2575c6baef3SWillem de Bruijn { 2585c6baef3SWillem de Bruijn uint64_t now; 2595c6baef3SWillem de Bruijn int err; 2605c6baef3SWillem de Bruijn 2615c6baef3SWillem de Bruijn if (!cfg_start_time_ns) 2625c6baef3SWillem de Bruijn return; 2635c6baef3SWillem de Bruijn 2645c6baef3SWillem de Bruijn now = gettime_ns(CLOCK_REALTIME); 2655c6baef3SWillem de Bruijn if (cfg_start_time_ns < now) { 2665c6baef3SWillem de Bruijn fprintf(stderr, "FAIL: start time already passed\n"); 267*543bdc15SWillem de Bruijn if (!cfg_machine_slow) 2685c6baef3SWillem de Bruijn errors++; 2695c6baef3SWillem de Bruijn return; 2705c6baef3SWillem de Bruijn } 2715c6baef3SWillem de Bruijn 2725c6baef3SWillem de Bruijn err = usleep((cfg_start_time_ns - now) / 1000); 2735c6baef3SWillem de Bruijn if (err) 2745c6baef3SWillem de Bruijn error(1, errno, "usleep"); 2755c6baef3SWillem de Bruijn } 2765c6baef3SWillem de Bruijn 2775c6baef3SWillem de Bruijn static void setsockopt_txtime(int fd) 2785c6baef3SWillem de Bruijn { 2795c6baef3SWillem de Bruijn struct sock_txtime so_txtime_val = { .clockid = cfg_clockid }; 2805c6baef3SWillem de Bruijn struct sock_txtime so_txtime_val_read = { 0 }; 2815c6baef3SWillem de Bruijn socklen_t vallen = sizeof(so_txtime_val); 2825c6baef3SWillem de Bruijn 2835c6baef3SWillem de Bruijn so_txtime_val.flags = SOF_TXTIME_REPORT_ERRORS; 2845c6baef3SWillem de Bruijn 2855c6baef3SWillem de Bruijn if (setsockopt(fd, SOL_SOCKET, SO_TXTIME, 2865c6baef3SWillem de Bruijn &so_txtime_val, sizeof(so_txtime_val))) 2875c6baef3SWillem de Bruijn error(1, errno, "setsockopt txtime"); 2885c6baef3SWillem de Bruijn 2895c6baef3SWillem de Bruijn if (getsockopt(fd, SOL_SOCKET, SO_TXTIME, 2905c6baef3SWillem de Bruijn &so_txtime_val_read, &vallen)) 2915c6baef3SWillem de Bruijn error(1, errno, "getsockopt txtime"); 2925c6baef3SWillem de Bruijn 2935c6baef3SWillem de Bruijn if (vallen != sizeof(so_txtime_val) || 2945c6baef3SWillem de Bruijn memcmp(&so_txtime_val, &so_txtime_val_read, vallen)) 2955c6baef3SWillem de Bruijn error(1, 0, "getsockopt txtime: mismatch"); 2965c6baef3SWillem de Bruijn } 2975c6baef3SWillem de Bruijn 2985c6baef3SWillem de Bruijn static int setup_tx(struct sockaddr *addr, socklen_t alen) 2995c6baef3SWillem de Bruijn { 3005c6baef3SWillem de Bruijn int fd; 3015c6baef3SWillem de Bruijn 3025c6baef3SWillem de Bruijn fd = socket(addr->sa_family, SOCK_DGRAM, 0); 3035c6baef3SWillem de Bruijn if (fd == -1) 3045c6baef3SWillem de Bruijn error(1, errno, "socket t"); 3055c6baef3SWillem de Bruijn 3065c6baef3SWillem de Bruijn if (connect(fd, addr, alen)) 3075c6baef3SWillem de Bruijn error(1, errno, "connect"); 3085c6baef3SWillem de Bruijn 3095c6baef3SWillem de Bruijn setsockopt_txtime(fd); 3105c6baef3SWillem de Bruijn 3115c6baef3SWillem de Bruijn if (cfg_mark && 3125c6baef3SWillem de Bruijn setsockopt(fd, SOL_SOCKET, SO_MARK, &cfg_mark, sizeof(cfg_mark))) 3135c6baef3SWillem de Bruijn error(1, errno, "setsockopt mark"); 3145c6baef3SWillem de Bruijn 3155c6baef3SWillem de Bruijn return fd; 3165c6baef3SWillem de Bruijn } 3175c6baef3SWillem de Bruijn 3185c6baef3SWillem de Bruijn static int setup_rx(struct sockaddr *addr, socklen_t alen) 3195c6baef3SWillem de Bruijn { 3205c6baef3SWillem de Bruijn struct timeval tv = { .tv_usec = 100 * 1000 }; 3215c6baef3SWillem de Bruijn int fd; 3225c6baef3SWillem de Bruijn 3235c6baef3SWillem de Bruijn fd = socket(addr->sa_family, SOCK_DGRAM, 0); 3245c6baef3SWillem de Bruijn if (fd == -1) 3255c6baef3SWillem de Bruijn error(1, errno, "socket r"); 3265c6baef3SWillem de Bruijn 3275c6baef3SWillem de Bruijn if (bind(fd, addr, alen)) 3285c6baef3SWillem de Bruijn error(1, errno, "bind"); 3295c6baef3SWillem de Bruijn 330*543bdc15SWillem de Bruijn if (cfg_machine_slow) 331*543bdc15SWillem de Bruijn tv.tv_sec = 2; 332*543bdc15SWillem de Bruijn 3335c6baef3SWillem de Bruijn if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) 3345c6baef3SWillem de Bruijn error(1, errno, "setsockopt rcv timeout"); 3355c6baef3SWillem de Bruijn 3365c6baef3SWillem de Bruijn return fd; 3375c6baef3SWillem de Bruijn } 3385c6baef3SWillem de Bruijn 3395c6baef3SWillem de Bruijn static void do_test_tx(struct sockaddr *addr, socklen_t alen) 3405c6baef3SWillem de Bruijn { 3415c6baef3SWillem de Bruijn int fdt, i; 3425c6baef3SWillem de Bruijn 3435c6baef3SWillem de Bruijn fprintf(stderr, "\nSO_TXTIME ipv%c clock %s\n", 3445c6baef3SWillem de Bruijn addr->sa_family == PF_INET ? '4' : '6', 3455c6baef3SWillem de Bruijn cfg_clockid == CLOCK_TAI ? "tai" : "monotonic"); 3465c6baef3SWillem de Bruijn 3475c6baef3SWillem de Bruijn fdt = setup_tx(addr, alen); 3485c6baef3SWillem de Bruijn 3495c6baef3SWillem de Bruijn start_time_wait(); 3505c6baef3SWillem de Bruijn glob_tstart = gettime_ns(cfg_clockid); 3515c6baef3SWillem de Bruijn 3525c6baef3SWillem de Bruijn for (i = 0; i < cfg_num_pkt; i++) 3535c6baef3SWillem de Bruijn do_send_one(fdt, &cfg_buf[i]); 3545c6baef3SWillem de Bruijn 3555c6baef3SWillem de Bruijn recv_errqueue_msgs(fdt); 3565c6baef3SWillem de Bruijn 3575c6baef3SWillem de Bruijn if (close(fdt)) 3585c6baef3SWillem de Bruijn error(1, errno, "close t"); 3595c6baef3SWillem de Bruijn } 3605c6baef3SWillem de Bruijn 3615c6baef3SWillem de Bruijn static void do_test_rx(struct sockaddr *addr, socklen_t alen) 3625c6baef3SWillem de Bruijn { 3635c6baef3SWillem de Bruijn int fdr, i; 3645c6baef3SWillem de Bruijn 3655c6baef3SWillem de Bruijn fdr = setup_rx(addr, alen); 3665c6baef3SWillem de Bruijn 3675c6baef3SWillem de Bruijn start_time_wait(); 3685c6baef3SWillem de Bruijn glob_tstart = gettime_ns(cfg_clockid); 3695c6baef3SWillem de Bruijn 3705c6baef3SWillem de Bruijn for (i = 0; i < cfg_num_pkt; i++) 3715c6baef3SWillem de Bruijn do_recv_one(fdr, &cfg_buf[i]); 3725c6baef3SWillem de Bruijn 3735c6baef3SWillem de Bruijn do_recv_verify_empty(fdr); 3745c6baef3SWillem de Bruijn 3755c6baef3SWillem de Bruijn if (close(fdr)) 3765c6baef3SWillem de Bruijn error(1, errno, "close r"); 3775c6baef3SWillem de Bruijn } 3785c6baef3SWillem de Bruijn 3795c6baef3SWillem de Bruijn static void setup_sockaddr(int domain, const char *str_addr, 3805c6baef3SWillem de Bruijn struct sockaddr_storage *sockaddr) 3815c6baef3SWillem de Bruijn { 3825c6baef3SWillem de Bruijn struct sockaddr_in6 *addr6 = (void *) sockaddr; 3835c6baef3SWillem de Bruijn struct sockaddr_in *addr4 = (void *) sockaddr; 3845c6baef3SWillem de Bruijn 3855c6baef3SWillem de Bruijn switch (domain) { 3865c6baef3SWillem de Bruijn case PF_INET: 3875c6baef3SWillem de Bruijn memset(addr4, 0, sizeof(*addr4)); 3885c6baef3SWillem de Bruijn addr4->sin_family = AF_INET; 3895c6baef3SWillem de Bruijn addr4->sin_port = htons(cfg_port); 3905c6baef3SWillem de Bruijn if (str_addr && 3915c6baef3SWillem de Bruijn inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1) 3925c6baef3SWillem de Bruijn error(1, 0, "ipv4 parse error: %s", str_addr); 3935c6baef3SWillem de Bruijn break; 3945c6baef3SWillem de Bruijn case PF_INET6: 3955c6baef3SWillem de Bruijn memset(addr6, 0, sizeof(*addr6)); 3965c6baef3SWillem de Bruijn addr6->sin6_family = AF_INET6; 3975c6baef3SWillem de Bruijn addr6->sin6_port = htons(cfg_port); 3985c6baef3SWillem de Bruijn if (str_addr && 3995c6baef3SWillem de Bruijn inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1) 4005c6baef3SWillem de Bruijn error(1, 0, "ipv6 parse error: %s", str_addr); 4015c6baef3SWillem de Bruijn break; 4025c6baef3SWillem de Bruijn } 4035c6baef3SWillem de Bruijn } 4045c6baef3SWillem de Bruijn 4055c6baef3SWillem de Bruijn static int parse_io(const char *optarg, struct timed_send *array) 4065c6baef3SWillem de Bruijn { 4075c6baef3SWillem de Bruijn char *arg, *tok; 4085c6baef3SWillem de Bruijn int aoff = 0; 4095c6baef3SWillem de Bruijn 4105c6baef3SWillem de Bruijn arg = strdup(optarg); 4115c6baef3SWillem de Bruijn if (!arg) 4125c6baef3SWillem de Bruijn error(1, errno, "strdup"); 4135c6baef3SWillem de Bruijn 4145c6baef3SWillem de Bruijn while ((tok = strtok(arg, ","))) { 4155c6baef3SWillem de Bruijn arg = NULL; /* only pass non-zero on first call */ 4165c6baef3SWillem de Bruijn 4175c6baef3SWillem de Bruijn if (aoff / 2 == MAX_NUM_PKT) 4185c6baef3SWillem de Bruijn error(1, 0, "exceeds max pkt count (%d)", MAX_NUM_PKT); 4195c6baef3SWillem de Bruijn 4205c6baef3SWillem de Bruijn if (aoff & 1) { /* parse delay */ 4215c6baef3SWillem de Bruijn array->delay_us = strtol(tok, NULL, 0) * 1000; 4225c6baef3SWillem de Bruijn array++; 4235c6baef3SWillem de Bruijn } else { /* parse character */ 4245c6baef3SWillem de Bruijn array->data = tok[0]; 4255c6baef3SWillem de Bruijn } 4265c6baef3SWillem de Bruijn 4275c6baef3SWillem de Bruijn aoff++; 4285c6baef3SWillem de Bruijn } 4295c6baef3SWillem de Bruijn 4305c6baef3SWillem de Bruijn free(arg); 4315c6baef3SWillem de Bruijn 4325c6baef3SWillem de Bruijn return aoff / 2; 4335c6baef3SWillem de Bruijn } 4345c6baef3SWillem de Bruijn 4355c6baef3SWillem de Bruijn static void usage(const char *progname) 4365c6baef3SWillem de Bruijn { 4375c6baef3SWillem de Bruijn fprintf(stderr, "\nUsage: %s [options] <payload>\n" 4385c6baef3SWillem de Bruijn "Options:\n" 4395c6baef3SWillem de Bruijn " -4 only IPv4\n" 4405c6baef3SWillem de Bruijn " -6 only IPv6\n" 4415c6baef3SWillem de Bruijn " -c <clock> monotonic or tai (default)\n" 4425c6baef3SWillem de Bruijn " -D <addr> destination IP address (server)\n" 4435c6baef3SWillem de Bruijn " -S <addr> source IP address (client)\n" 4445c6baef3SWillem de Bruijn " -r run rx mode\n" 4455c6baef3SWillem de Bruijn " -t <nsec> start time (UTC nanoseconds)\n" 4465c6baef3SWillem de Bruijn " -m <mark> socket mark\n" 4475c6baef3SWillem de Bruijn "\n", 4485c6baef3SWillem de Bruijn progname); 4495c6baef3SWillem de Bruijn exit(1); 4505c6baef3SWillem de Bruijn } 4515c6baef3SWillem de Bruijn 4525c6baef3SWillem de Bruijn static void parse_opts(int argc, char **argv) 4535c6baef3SWillem de Bruijn { 4545c6baef3SWillem de Bruijn char *daddr = NULL, *saddr = NULL; 4555c6baef3SWillem de Bruijn int domain = PF_UNSPEC; 4565c6baef3SWillem de Bruijn int c; 4575c6baef3SWillem de Bruijn 4585c6baef3SWillem de Bruijn while ((c = getopt(argc, argv, "46c:S:D:rt:m:")) != -1) { 4595c6baef3SWillem de Bruijn switch (c) { 4605c6baef3SWillem de Bruijn case '4': 4615c6baef3SWillem de Bruijn if (domain != PF_UNSPEC) 4625c6baef3SWillem de Bruijn error(1, 0, "Pass one of -4 or -6"); 4635c6baef3SWillem de Bruijn domain = PF_INET; 4645c6baef3SWillem de Bruijn cfg_alen = sizeof(struct sockaddr_in); 4655c6baef3SWillem de Bruijn cfg_errq_level = SOL_IP; 4665c6baef3SWillem de Bruijn cfg_errq_type = IP_RECVERR; 4675c6baef3SWillem de Bruijn break; 4685c6baef3SWillem de Bruijn case '6': 4695c6baef3SWillem de Bruijn if (domain != PF_UNSPEC) 4705c6baef3SWillem de Bruijn error(1, 0, "Pass one of -4 or -6"); 4715c6baef3SWillem de Bruijn domain = PF_INET6; 4725c6baef3SWillem de Bruijn cfg_alen = sizeof(struct sockaddr_in6); 4735c6baef3SWillem de Bruijn cfg_errq_level = SOL_IPV6; 4745c6baef3SWillem de Bruijn cfg_errq_type = IPV6_RECVERR; 4755c6baef3SWillem de Bruijn break; 4765c6baef3SWillem de Bruijn case 'c': 4775c6baef3SWillem de Bruijn if (!strcmp(optarg, "tai")) 4785c6baef3SWillem de Bruijn cfg_clockid = CLOCK_TAI; 4795c6baef3SWillem de Bruijn else if (!strcmp(optarg, "monotonic") || 4805c6baef3SWillem de Bruijn !strcmp(optarg, "mono")) 4815c6baef3SWillem de Bruijn cfg_clockid = CLOCK_MONOTONIC; 4825c6baef3SWillem de Bruijn else 4835c6baef3SWillem de Bruijn error(1, 0, "unknown clock id %s", optarg); 4845c6baef3SWillem de Bruijn break; 4855c6baef3SWillem de Bruijn case 'S': 4865c6baef3SWillem de Bruijn saddr = optarg; 4875c6baef3SWillem de Bruijn break; 4885c6baef3SWillem de Bruijn case 'D': 4895c6baef3SWillem de Bruijn daddr = optarg; 4905c6baef3SWillem de Bruijn break; 4915c6baef3SWillem de Bruijn case 'r': 4925c6baef3SWillem de Bruijn cfg_rx = true; 4935c6baef3SWillem de Bruijn break; 4945c6baef3SWillem de Bruijn case 't': 4955c6baef3SWillem de Bruijn cfg_start_time_ns = strtoll(optarg, NULL, 0); 4965c6baef3SWillem de Bruijn break; 4975c6baef3SWillem de Bruijn case 'm': 4985c6baef3SWillem de Bruijn cfg_mark = strtol(optarg, NULL, 0); 4995c6baef3SWillem de Bruijn break; 5005c6baef3SWillem de Bruijn default: 5015c6baef3SWillem de Bruijn usage(argv[0]); 5025c6baef3SWillem de Bruijn } 5035c6baef3SWillem de Bruijn } 5045c6baef3SWillem de Bruijn 5055c6baef3SWillem de Bruijn if (argc - optind != 1) 5065c6baef3SWillem de Bruijn usage(argv[0]); 5075c6baef3SWillem de Bruijn 5085c6baef3SWillem de Bruijn if (domain == PF_UNSPEC) 5095c6baef3SWillem de Bruijn error(1, 0, "Pass one of -4 or -6"); 5105c6baef3SWillem de Bruijn if (!daddr) 5115c6baef3SWillem de Bruijn error(1, 0, "-D <server addr> required\n"); 5125c6baef3SWillem de Bruijn if (!cfg_rx && !saddr) 5135c6baef3SWillem de Bruijn error(1, 0, "-S <client addr> required\n"); 5145c6baef3SWillem de Bruijn 5155c6baef3SWillem de Bruijn setup_sockaddr(domain, daddr, &cfg_dst_addr); 5165c6baef3SWillem de Bruijn setup_sockaddr(domain, saddr, &cfg_src_addr); 5175c6baef3SWillem de Bruijn 5185c6baef3SWillem de Bruijn cfg_num_pkt = parse_io(argv[optind], cfg_buf); 519*543bdc15SWillem de Bruijn 520*543bdc15SWillem de Bruijn cfg_machine_slow = getenv("KSFT_MACHINE_SLOW"); 5215c6baef3SWillem de Bruijn } 5225c6baef3SWillem de Bruijn 5235c6baef3SWillem de Bruijn int main(int argc, char **argv) 5245c6baef3SWillem de Bruijn { 5255c6baef3SWillem de Bruijn parse_opts(argc, argv); 5265c6baef3SWillem de Bruijn 5275c6baef3SWillem de Bruijn if (cfg_rx) 5285c6baef3SWillem de Bruijn do_test_rx((void *)&cfg_dst_addr, cfg_alen); 5295c6baef3SWillem de Bruijn else 5305c6baef3SWillem de Bruijn do_test_tx((void *)&cfg_src_addr, cfg_alen); 5315c6baef3SWillem de Bruijn 5325c6baef3SWillem de Bruijn if (errors) { 5335c6baef3SWillem de Bruijn fprintf(stderr, "FAIL: %d errors\n", errors); 5345c6baef3SWillem de Bruijn return KSFT_FAIL; 5355c6baef3SWillem de Bruijn } 5365c6baef3SWillem de Bruijn 5375c6baef3SWillem de Bruijn return KSFT_PASS; 5385c6baef3SWillem de Bruijn } 539