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