at v5.3 777 lines 18 kB view raw
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/ipv6.h> 30#include <linux/net_tstamp.h> 31#include <netdb.h> 32#include <net/if.h> 33#include <netinet/in.h> 34#include <netinet/ip.h> 35#include <netinet/udp.h> 36#include <netinet/tcp.h> 37#include <netpacket/packet.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/ioctl.h> 45#include <sys/select.h> 46#include <sys/socket.h> 47#include <sys/time.h> 48#include <sys/types.h> 49#include <time.h> 50#include <unistd.h> 51 52/* command line parameters */ 53static int cfg_proto = SOCK_STREAM; 54static int cfg_ipproto = IPPROTO_TCP; 55static int cfg_num_pkts = 4; 56static int do_ipv4 = 1; 57static int do_ipv6 = 1; 58static int cfg_payload_len = 10; 59static int cfg_poll_timeout = 100; 60static int cfg_delay_snd; 61static int cfg_delay_ack; 62static bool cfg_show_payload; 63static bool cfg_do_pktinfo; 64static bool cfg_loop_nodata; 65static bool cfg_no_delay; 66static bool cfg_use_cmsg; 67static bool cfg_use_pf_packet; 68static bool cfg_do_listen; 69static uint16_t dest_port = 9000; 70 71static struct sockaddr_in daddr; 72static struct sockaddr_in6 daddr6; 73static struct timespec ts_usr; 74 75static int saved_tskey = -1; 76static int saved_tskey_type = -1; 77 78static bool test_failed; 79 80static int64_t timespec_to_us64(struct timespec *ts) 81{ 82 return ts->tv_sec * 1000 * 1000 + ts->tv_nsec / 1000; 83} 84 85static void validate_key(int tskey, int tstype) 86{ 87 int stepsize; 88 89 /* compare key for each subsequent request 90 * must only test for one type, the first one requested 91 */ 92 if (saved_tskey == -1) 93 saved_tskey_type = tstype; 94 else if (saved_tskey_type != tstype) 95 return; 96 97 stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1; 98 if (tskey != saved_tskey + stepsize) { 99 fprintf(stderr, "ERROR: key %d, expected %d\n", 100 tskey, saved_tskey + stepsize); 101 test_failed = true; 102 } 103 104 saved_tskey = tskey; 105} 106 107static void validate_timestamp(struct timespec *cur, int min_delay) 108{ 109 int max_delay = min_delay + 500 /* processing time upper bound */; 110 int64_t cur64, start64; 111 112 cur64 = timespec_to_us64(cur); 113 start64 = timespec_to_us64(&ts_usr); 114 115 if (cur64 < start64 + min_delay || cur64 > start64 + max_delay) { 116 fprintf(stderr, "ERROR: delay %lu expected between %d and %d\n", 117 cur64 - start64, min_delay, max_delay); 118 test_failed = true; 119 } 120} 121 122static void __print_timestamp(const char *name, struct timespec *cur, 123 uint32_t key, int payload_len) 124{ 125 if (!(cur->tv_sec | cur->tv_nsec)) 126 return; 127 128 fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)", 129 name, cur->tv_sec, cur->tv_nsec / 1000, 130 key, payload_len); 131 132 if (cur != &ts_usr) 133 fprintf(stderr, " (USR %+" PRId64 " us)", 134 timespec_to_us64(cur) - timespec_to_us64(&ts_usr)); 135 136 fprintf(stderr, "\n"); 137} 138 139static void print_timestamp_usr(void) 140{ 141 if (clock_gettime(CLOCK_REALTIME, &ts_usr)) 142 error(1, errno, "clock_gettime"); 143 144 __print_timestamp(" USR", &ts_usr, 0, 0); 145} 146 147static void print_timestamp(struct scm_timestamping *tss, int tstype, 148 int tskey, int payload_len) 149{ 150 const char *tsname; 151 152 validate_key(tskey, tstype); 153 154 switch (tstype) { 155 case SCM_TSTAMP_SCHED: 156 tsname = " ENQ"; 157 validate_timestamp(&tss->ts[0], 0); 158 break; 159 case SCM_TSTAMP_SND: 160 tsname = " SND"; 161 validate_timestamp(&tss->ts[0], cfg_delay_snd); 162 break; 163 case SCM_TSTAMP_ACK: 164 tsname = " ACK"; 165 validate_timestamp(&tss->ts[0], cfg_delay_ack); 166 break; 167 default: 168 error(1, 0, "unknown timestamp type: %u", 169 tstype); 170 } 171 __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); 172} 173 174/* TODO: convert to check_and_print payload once API is stable */ 175static void print_payload(char *data, int len) 176{ 177 int i; 178 179 if (!len) 180 return; 181 182 if (len > 70) 183 len = 70; 184 185 fprintf(stderr, "payload: "); 186 for (i = 0; i < len; i++) 187 fprintf(stderr, "%02hhx ", data[i]); 188 fprintf(stderr, "\n"); 189} 190 191static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) 192{ 193 char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN]; 194 195 fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n", 196 ifindex, 197 saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown", 198 daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); 199} 200 201static void __poll(int fd) 202{ 203 struct pollfd pollfd; 204 int ret; 205 206 memset(&pollfd, 0, sizeof(pollfd)); 207 pollfd.fd = fd; 208 ret = poll(&pollfd, 1, cfg_poll_timeout); 209 if (ret != 1) 210 error(1, errno, "poll"); 211} 212 213static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len) 214{ 215 struct sock_extended_err *serr = NULL; 216 struct scm_timestamping *tss = NULL; 217 struct cmsghdr *cm; 218 int batch = 0; 219 220 for (cm = CMSG_FIRSTHDR(msg); 221 cm && cm->cmsg_len; 222 cm = CMSG_NXTHDR(msg, cm)) { 223 if (cm->cmsg_level == SOL_SOCKET && 224 cm->cmsg_type == SCM_TIMESTAMPING) { 225 tss = (void *) CMSG_DATA(cm); 226 } else if ((cm->cmsg_level == SOL_IP && 227 cm->cmsg_type == IP_RECVERR) || 228 (cm->cmsg_level == SOL_IPV6 && 229 cm->cmsg_type == IPV6_RECVERR) || 230 (cm->cmsg_level == SOL_PACKET && 231 cm->cmsg_type == PACKET_TX_TIMESTAMP)) { 232 serr = (void *) CMSG_DATA(cm); 233 if (serr->ee_errno != ENOMSG || 234 serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { 235 fprintf(stderr, "unknown ip error %d %d\n", 236 serr->ee_errno, 237 serr->ee_origin); 238 serr = NULL; 239 } 240 } else if (cm->cmsg_level == SOL_IP && 241 cm->cmsg_type == IP_PKTINFO) { 242 struct in_pktinfo *info = (void *) CMSG_DATA(cm); 243 print_pktinfo(AF_INET, info->ipi_ifindex, 244 &info->ipi_spec_dst, &info->ipi_addr); 245 } else if (cm->cmsg_level == SOL_IPV6 && 246 cm->cmsg_type == IPV6_PKTINFO) { 247 struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm); 248 print_pktinfo(AF_INET6, info6->ipi6_ifindex, 249 NULL, &info6->ipi6_addr); 250 } else 251 fprintf(stderr, "unknown cmsg %d,%d\n", 252 cm->cmsg_level, cm->cmsg_type); 253 254 if (serr && tss) { 255 print_timestamp(tss, serr->ee_info, serr->ee_data, 256 payload_len); 257 serr = NULL; 258 tss = NULL; 259 batch++; 260 } 261 } 262 263 if (batch > 1) 264 fprintf(stderr, "batched %d timestamps\n", batch); 265} 266 267static int recv_errmsg(int fd) 268{ 269 static char ctrl[1024 /* overprovision*/]; 270 static struct msghdr msg; 271 struct iovec entry; 272 static char *data; 273 int ret = 0; 274 275 data = malloc(cfg_payload_len); 276 if (!data) 277 error(1, 0, "malloc"); 278 279 memset(&msg, 0, sizeof(msg)); 280 memset(&entry, 0, sizeof(entry)); 281 memset(ctrl, 0, sizeof(ctrl)); 282 283 entry.iov_base = data; 284 entry.iov_len = cfg_payload_len; 285 msg.msg_iov = &entry; 286 msg.msg_iovlen = 1; 287 msg.msg_name = NULL; 288 msg.msg_namelen = 0; 289 msg.msg_control = ctrl; 290 msg.msg_controllen = sizeof(ctrl); 291 292 ret = recvmsg(fd, &msg, MSG_ERRQUEUE); 293 if (ret == -1 && errno != EAGAIN) 294 error(1, errno, "recvmsg"); 295 296 if (ret >= 0) { 297 __recv_errmsg_cmsg(&msg, ret); 298 if (cfg_show_payload) 299 print_payload(data, cfg_payload_len); 300 } 301 302 free(data); 303 return ret == -1; 304} 305 306static uint16_t get_ip_csum(const uint16_t *start, int num_words, 307 unsigned long sum) 308{ 309 int i; 310 311 for (i = 0; i < num_words; i++) 312 sum += start[i]; 313 314 while (sum >> 16) 315 sum = (sum & 0xFFFF) + (sum >> 16); 316 317 return ~sum; 318} 319 320static uint16_t get_udp_csum(const struct udphdr *udph, int alen) 321{ 322 unsigned long pseudo_sum, csum_len; 323 const void *csum_start = udph; 324 325 pseudo_sum = htons(IPPROTO_UDP); 326 pseudo_sum += udph->len; 327 328 /* checksum ip(v6) addresses + udp header + payload */ 329 csum_start -= alen * 2; 330 csum_len = ntohs(udph->len) + alen * 2; 331 332 return get_ip_csum(csum_start, csum_len >> 1, pseudo_sum); 333} 334 335static int fill_header_ipv4(void *p) 336{ 337 struct iphdr *iph = p; 338 339 memset(iph, 0, sizeof(*iph)); 340 341 iph->ihl = 5; 342 iph->version = 4; 343 iph->ttl = 2; 344 iph->saddr = daddr.sin_addr.s_addr; /* set for udp csum calc */ 345 iph->daddr = daddr.sin_addr.s_addr; 346 iph->protocol = IPPROTO_UDP; 347 348 /* kernel writes saddr, csum, len */ 349 350 return sizeof(*iph); 351} 352 353static int fill_header_ipv6(void *p) 354{ 355 struct ipv6hdr *ip6h = p; 356 357 memset(ip6h, 0, sizeof(*ip6h)); 358 359 ip6h->version = 6; 360 ip6h->payload_len = htons(sizeof(struct udphdr) + cfg_payload_len); 361 ip6h->nexthdr = IPPROTO_UDP; 362 ip6h->hop_limit = 64; 363 364 ip6h->saddr = daddr6.sin6_addr; 365 ip6h->daddr = daddr6.sin6_addr; 366 367 /* kernel does not write saddr in case of ipv6 */ 368 369 return sizeof(*ip6h); 370} 371 372static void fill_header_udp(void *p, bool is_ipv4) 373{ 374 struct udphdr *udph = p; 375 376 udph->source = ntohs(dest_port + 1); /* spoof */ 377 udph->dest = ntohs(dest_port); 378 udph->len = ntohs(sizeof(*udph) + cfg_payload_len); 379 udph->check = 0; 380 381 udph->check = get_udp_csum(udph, is_ipv4 ? sizeof(struct in_addr) : 382 sizeof(struct in6_addr)); 383} 384 385static void do_test(int family, unsigned int report_opt) 386{ 387 char control[CMSG_SPACE(sizeof(uint32_t))]; 388 struct sockaddr_ll laddr; 389 unsigned int sock_opt; 390 struct cmsghdr *cmsg; 391 struct msghdr msg; 392 struct iovec iov; 393 char *buf; 394 int fd, i, val = 1, total_len; 395 396 total_len = cfg_payload_len; 397 if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) { 398 total_len += sizeof(struct udphdr); 399 if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) 400 if (family == PF_INET) 401 total_len += sizeof(struct iphdr); 402 else 403 total_len += sizeof(struct ipv6hdr); 404 405 /* special case, only rawv6_sendmsg: 406 * pass proto in sin6_port if not connected 407 * also see ANK comment in net/ipv4/raw.c 408 */ 409 daddr6.sin6_port = htons(cfg_ipproto); 410 } 411 412 buf = malloc(total_len); 413 if (!buf) 414 error(1, 0, "malloc"); 415 416 fd = socket(cfg_use_pf_packet ? PF_PACKET : family, 417 cfg_proto, cfg_ipproto); 418 if (fd < 0) 419 error(1, errno, "socket"); 420 421 /* reset expected key on each new socket */ 422 saved_tskey = -1; 423 424 if (cfg_proto == SOCK_STREAM) { 425 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, 426 (char*) &val, sizeof(val))) 427 error(1, 0, "setsockopt no nagle"); 428 429 if (family == PF_INET) { 430 if (connect(fd, (void *) &daddr, sizeof(daddr))) 431 error(1, errno, "connect ipv4"); 432 } else { 433 if (connect(fd, (void *) &daddr6, sizeof(daddr6))) 434 error(1, errno, "connect ipv6"); 435 } 436 } 437 438 if (cfg_do_pktinfo) { 439 if (family == AF_INET6) { 440 if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO, 441 &val, sizeof(val))) 442 error(1, errno, "setsockopt pktinfo ipv6"); 443 } else { 444 if (setsockopt(fd, SOL_IP, IP_PKTINFO, 445 &val, sizeof(val))) 446 error(1, errno, "setsockopt pktinfo ipv4"); 447 } 448 } 449 450 sock_opt = SOF_TIMESTAMPING_SOFTWARE | 451 SOF_TIMESTAMPING_OPT_CMSG | 452 SOF_TIMESTAMPING_OPT_ID; 453 454 if (!cfg_use_cmsg) 455 sock_opt |= report_opt; 456 457 if (cfg_loop_nodata) 458 sock_opt |= SOF_TIMESTAMPING_OPT_TSONLY; 459 460 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, 461 (char *) &sock_opt, sizeof(sock_opt))) 462 error(1, 0, "setsockopt timestamping"); 463 464 for (i = 0; i < cfg_num_pkts; i++) { 465 memset(&msg, 0, sizeof(msg)); 466 memset(buf, 'a' + i, total_len); 467 468 if (cfg_use_pf_packet || cfg_proto == SOCK_RAW) { 469 int off = 0; 470 471 if (cfg_use_pf_packet || cfg_ipproto == IPPROTO_RAW) { 472 if (family == PF_INET) 473 off = fill_header_ipv4(buf); 474 else 475 off = fill_header_ipv6(buf); 476 } 477 478 fill_header_udp(buf + off, family == PF_INET); 479 } 480 481 print_timestamp_usr(); 482 483 iov.iov_base = buf; 484 iov.iov_len = total_len; 485 486 if (cfg_proto != SOCK_STREAM) { 487 if (cfg_use_pf_packet) { 488 memset(&laddr, 0, sizeof(laddr)); 489 490 laddr.sll_family = AF_PACKET; 491 laddr.sll_ifindex = 1; 492 laddr.sll_protocol = htons(family == AF_INET ? ETH_P_IP : ETH_P_IPV6); 493 laddr.sll_halen = ETH_ALEN; 494 495 msg.msg_name = (void *)&laddr; 496 msg.msg_namelen = sizeof(laddr); 497 } else if (family == PF_INET) { 498 msg.msg_name = (void *)&daddr; 499 msg.msg_namelen = sizeof(daddr); 500 } else { 501 msg.msg_name = (void *)&daddr6; 502 msg.msg_namelen = sizeof(daddr6); 503 } 504 } 505 506 msg.msg_iov = &iov; 507 msg.msg_iovlen = 1; 508 509 if (cfg_use_cmsg) { 510 memset(control, 0, sizeof(control)); 511 512 msg.msg_control = control; 513 msg.msg_controllen = sizeof(control); 514 515 cmsg = CMSG_FIRSTHDR(&msg); 516 cmsg->cmsg_level = SOL_SOCKET; 517 cmsg->cmsg_type = SO_TIMESTAMPING; 518 cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); 519 520 *((uint32_t *) CMSG_DATA(cmsg)) = report_opt; 521 } 522 523 val = sendmsg(fd, &msg, 0); 524 if (val != total_len) 525 error(1, errno, "send"); 526 527 /* wait for all errors to be queued, else ACKs arrive OOO */ 528 if (!cfg_no_delay) 529 usleep(50 * 1000); 530 531 __poll(fd); 532 533 while (!recv_errmsg(fd)) {} 534 } 535 536 if (close(fd)) 537 error(1, errno, "close"); 538 539 free(buf); 540 usleep(100 * 1000); 541} 542 543static void __attribute__((noreturn)) usage(const char *filepath) 544{ 545 fprintf(stderr, "\nUsage: %s [options] hostname\n" 546 "\nwhere options are:\n" 547 " -4: only IPv4\n" 548 " -6: only IPv6\n" 549 " -h: show this message\n" 550 " -c N: number of packets for each test\n" 551 " -C: use cmsg to set tstamp recording options\n" 552 " -D: no delay between packets\n" 553 " -F: poll() waits forever for an event\n" 554 " -I: request PKTINFO\n" 555 " -l N: send N bytes at a time\n" 556 " -L listen on hostname and port\n" 557 " -n: set no-payload option\n" 558 " -p N: connect to port N\n" 559 " -P: use PF_PACKET\n" 560 " -r: use raw\n" 561 " -R: use raw (IP_HDRINCL)\n" 562 " -u: use udp\n" 563 " -v: validate SND delay (usec)\n" 564 " -V: validate ACK delay (usec)\n" 565 " -x: show payload (up to 70 bytes)\n", 566 filepath); 567 exit(1); 568} 569 570static void parse_opt(int argc, char **argv) 571{ 572 int proto_count = 0; 573 int c; 574 575 while ((c = getopt(argc, argv, "46c:CDFhIl:Lnp:PrRuv:V:x")) != -1) { 576 switch (c) { 577 case '4': 578 do_ipv6 = 0; 579 break; 580 case '6': 581 do_ipv4 = 0; 582 break; 583 case 'c': 584 cfg_num_pkts = strtoul(optarg, NULL, 10); 585 break; 586 case 'C': 587 cfg_use_cmsg = true; 588 break; 589 case 'D': 590 cfg_no_delay = true; 591 break; 592 case 'F': 593 cfg_poll_timeout = -1; 594 break; 595 case 'I': 596 cfg_do_pktinfo = true; 597 break; 598 case 'l': 599 cfg_payload_len = strtoul(optarg, NULL, 10); 600 break; 601 case 'L': 602 cfg_do_listen = true; 603 break; 604 case 'n': 605 cfg_loop_nodata = true; 606 break; 607 case 'p': 608 dest_port = strtoul(optarg, NULL, 10); 609 break; 610 case 'P': 611 proto_count++; 612 cfg_use_pf_packet = true; 613 cfg_proto = SOCK_DGRAM; 614 cfg_ipproto = 0; 615 break; 616 case 'r': 617 proto_count++; 618 cfg_proto = SOCK_RAW; 619 cfg_ipproto = IPPROTO_UDP; 620 break; 621 case 'R': 622 proto_count++; 623 cfg_proto = SOCK_RAW; 624 cfg_ipproto = IPPROTO_RAW; 625 break; 626 case 'u': 627 proto_count++; 628 cfg_proto = SOCK_DGRAM; 629 cfg_ipproto = IPPROTO_UDP; 630 break; 631 case 'v': 632 cfg_delay_snd = strtoul(optarg, NULL, 10); 633 break; 634 case 'V': 635 cfg_delay_ack = strtoul(optarg, NULL, 10); 636 break; 637 case 'x': 638 cfg_show_payload = true; 639 break; 640 case 'h': 641 default: 642 usage(argv[0]); 643 } 644 } 645 646 if (!cfg_payload_len) 647 error(1, 0, "payload may not be nonzero"); 648 if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472) 649 error(1, 0, "udp packet might exceed expected MTU"); 650 if (!do_ipv4 && !do_ipv6) 651 error(1, 0, "pass -4 or -6, not both"); 652 if (proto_count > 1) 653 error(1, 0, "pass -P, -r, -R or -u, not multiple"); 654 if (cfg_do_pktinfo && cfg_use_pf_packet) 655 error(1, 0, "cannot ask for pktinfo over pf_packet"); 656 657 if (optind != argc - 1) 658 error(1, 0, "missing required hostname argument"); 659} 660 661static void resolve_hostname(const char *hostname) 662{ 663 struct addrinfo hints = { .ai_family = do_ipv4 ? AF_INET : AF_INET6 }; 664 struct addrinfo *addrs, *cur; 665 int have_ipv4 = 0, have_ipv6 = 0; 666 667retry: 668 if (getaddrinfo(hostname, NULL, &hints, &addrs)) 669 error(1, errno, "getaddrinfo"); 670 671 cur = addrs; 672 while (cur && !have_ipv4 && !have_ipv6) { 673 if (!have_ipv4 && cur->ai_family == AF_INET) { 674 memcpy(&daddr, cur->ai_addr, sizeof(daddr)); 675 daddr.sin_port = htons(dest_port); 676 have_ipv4 = 1; 677 } 678 else if (!have_ipv6 && cur->ai_family == AF_INET6) { 679 memcpy(&daddr6, cur->ai_addr, sizeof(daddr6)); 680 daddr6.sin6_port = htons(dest_port); 681 have_ipv6 = 1; 682 } 683 cur = cur->ai_next; 684 } 685 if (addrs) 686 freeaddrinfo(addrs); 687 688 if (do_ipv6 && hints.ai_family != AF_INET6) { 689 hints.ai_family = AF_INET6; 690 goto retry; 691 } 692 693 do_ipv4 &= have_ipv4; 694 do_ipv6 &= have_ipv6; 695} 696 697static void do_listen(int family, void *addr, int alen) 698{ 699 int fd, type; 700 701 type = cfg_proto == SOCK_RAW ? SOCK_DGRAM : cfg_proto; 702 703 fd = socket(family, type, 0); 704 if (fd == -1) 705 error(1, errno, "socket rx"); 706 707 if (bind(fd, addr, alen)) 708 error(1, errno, "bind rx"); 709 710 if (type == SOCK_STREAM && listen(fd, 10)) 711 error(1, errno, "listen rx"); 712 713 /* leave fd open, will be closed on process exit. 714 * this enables connect() to succeed and avoids icmp replies 715 */ 716} 717 718static void do_main(int family) 719{ 720 fprintf(stderr, "family: %s %s\n", 721 family == PF_INET ? "INET" : "INET6", 722 cfg_use_pf_packet ? "(PF_PACKET)" : ""); 723 724 fprintf(stderr, "test SND\n"); 725 do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE); 726 727 fprintf(stderr, "test ENQ\n"); 728 do_test(family, SOF_TIMESTAMPING_TX_SCHED); 729 730 fprintf(stderr, "test ENQ + SND\n"); 731 do_test(family, SOF_TIMESTAMPING_TX_SCHED | 732 SOF_TIMESTAMPING_TX_SOFTWARE); 733 734 if (cfg_proto == SOCK_STREAM) { 735 fprintf(stderr, "\ntest ACK\n"); 736 do_test(family, SOF_TIMESTAMPING_TX_ACK); 737 738 fprintf(stderr, "\ntest SND + ACK\n"); 739 do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE | 740 SOF_TIMESTAMPING_TX_ACK); 741 742 fprintf(stderr, "\ntest ENQ + SND + ACK\n"); 743 do_test(family, SOF_TIMESTAMPING_TX_SCHED | 744 SOF_TIMESTAMPING_TX_SOFTWARE | 745 SOF_TIMESTAMPING_TX_ACK); 746 } 747} 748 749const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" }; 750 751int main(int argc, char **argv) 752{ 753 if (argc == 1) 754 usage(argv[0]); 755 756 parse_opt(argc, argv); 757 resolve_hostname(argv[argc - 1]); 758 759 fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]); 760 fprintf(stderr, "payload: %u\n", cfg_payload_len); 761 fprintf(stderr, "server port: %u\n", dest_port); 762 fprintf(stderr, "\n"); 763 764 if (do_ipv4) { 765 if (cfg_do_listen) 766 do_listen(PF_INET, &daddr, sizeof(daddr)); 767 do_main(PF_INET); 768 } 769 770 if (do_ipv6) { 771 if (cfg_do_listen) 772 do_listen(PF_INET6, &daddr6, sizeof(daddr6)); 773 do_main(PF_INET6); 774 } 775 776 return test_failed; 777}