1 /* 2 * Copyright 2014 Google Inc. 3 * Author: willemb (at) google.com (Willem de Bruijn) 4 * 5 * Test software tx timestamping, including 6 * 7 * - SCHED, SND and ACK timestamps 8 * - RAW, UDP and TCP 9 * - IPv4 and IPv6 10 * - various packet sizes (to test GSO and TSO) 11 * 12 * Consult the command line arguments for help on running 13 * the various testcases. 14 * 15 * This test requires a dummy TCP server. 16 * A simple `nc6 [-u] -l -p $DESTPORT` will do 17 * 18 * 19 * This program is free software; you can redistribute it and/or modify it 20 * under the terms and conditions of the GNU General Public License, 21 * version 2, as published by the Free Software Foundation. 22 * 23 * This program is distributed in the hope it will be useful, but WITHOUT 24 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 25 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for 26 * more details. 27 * 28 * You should have received a copy of the GNU General Public License along with 29 * this program; if not, write to the Free Software Foundation, Inc., 30 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 31 */ 32 33 #define _GNU_SOURCE 34 35 #include <arpa/inet.h> 36 #include <asm/types.h> 37 #include <error.h> 38 #include <errno.h> 39 #include <inttypes.h> 40 #include <linux/errqueue.h> 41 #include <linux/if_ether.h> 42 #include <linux/net_tstamp.h> 43 #include <netdb.h> 44 #include <net/if.h> 45 #include <netinet/in.h> 46 #include <netinet/ip.h> 47 #include <netinet/udp.h> 48 #include <netinet/tcp.h> 49 #include <netpacket/packet.h> 50 #include <poll.h> 51 #include <stdarg.h> 52 #include <stdbool.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <sys/ioctl.h> 57 #include <sys/select.h> 58 #include <sys/socket.h> 59 #include <sys/time.h> 60 #include <sys/types.h> 61 #include <time.h> 62 #include <unistd.h> 63 64 /* command line parameters */ 65 static int cfg_proto = SOCK_STREAM; 66 static int cfg_ipproto = IPPROTO_TCP; 67 static int cfg_num_pkts = 4; 68 static int do_ipv4 = 1; 69 static int do_ipv6 = 1; 70 static int cfg_payload_len = 10; 71 static bool cfg_show_payload; 72 static bool cfg_do_pktinfo; 73 static bool cfg_loop_nodata; 74 static uint16_t dest_port = 9000; 75 76 static struct sockaddr_in daddr; 77 static struct sockaddr_in6 daddr6; 78 static struct timespec ts_prev; 79 80 static void __print_timestamp(const char *name, struct timespec *cur, 81 uint32_t key, int payload_len) 82 { 83 if (!(cur->tv_sec | cur->tv_nsec)) 84 return; 85 86 fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)", 87 name, cur->tv_sec, cur->tv_nsec / 1000, 88 key, payload_len); 89 90 if ((ts_prev.tv_sec | ts_prev.tv_nsec)) { 91 int64_t cur_ms, prev_ms; 92 93 cur_ms = (long) cur->tv_sec * 1000 * 1000; 94 cur_ms += cur->tv_nsec / 1000; 95 96 prev_ms = (long) ts_prev.tv_sec * 1000 * 1000; 97 prev_ms += ts_prev.tv_nsec / 1000; 98 99 fprintf(stderr, " (%+" PRId64 " us)", cur_ms - prev_ms); 100 } 101 102 ts_prev = *cur; 103 fprintf(stderr, "\n"); 104 } 105 106 static void print_timestamp_usr(void) 107 { 108 struct timespec ts; 109 struct timeval tv; /* avoid dependency on -lrt */ 110 111 gettimeofday(&tv, NULL); 112 ts.tv_sec = tv.tv_sec; 113 ts.tv_nsec = tv.tv_usec * 1000; 114 115 __print_timestamp(" USR", &ts, 0, 0); 116 } 117 118 static void print_timestamp(struct scm_timestamping *tss, int tstype, 119 int tskey, int payload_len) 120 { 121 const char *tsname; 122 123 switch (tstype) { 124 case SCM_TSTAMP_SCHED: 125 tsname = " ENQ"; 126 break; 127 case SCM_TSTAMP_SND: 128 tsname = " SND"; 129 break; 130 case SCM_TSTAMP_ACK: 131 tsname = " ACK"; 132 break; 133 default: 134 error(1, 0, "unknown timestamp type: %u", 135 tstype); 136 } 137 __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); 138 } 139 140 /* TODO: convert to check_and_print payload once API is stable */ 141 static void print_payload(char *data, int len) 142 { 143 int i; 144 145 if (!len) 146 return; 147 148 if (len > 70) 149 len = 70; 150 151 fprintf(stderr, "payload: "); 152 for (i = 0; i < len; i++) 153 fprintf(stderr, "%02hhx ", data[i]); 154 fprintf(stderr, "\n"); 155 } 156 157 static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) 158 { 159 char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN]; 160 161 fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n", 162 ifindex, 163 saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown", 164 daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); 165 } 166 167 static void __poll(int fd) 168 { 169 struct pollfd pollfd; 170 int ret; 171 172 memset(&pollfd, 0, sizeof(pollfd)); 173 pollfd.fd = fd; 174 ret = poll(&pollfd, 1, 100); 175 if (ret != 1) 176 error(1, errno, "poll"); 177 } 178 179 static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len) 180 { 181 struct sock_extended_err *serr = NULL; 182 struct scm_timestamping *tss = NULL; 183 struct cmsghdr *cm; 184 int batch = 0; 185 186 for (cm = CMSG_FIRSTHDR(msg); 187 cm && cm->cmsg_len; 188 cm = CMSG_NXTHDR(msg, cm)) { 189 if (cm->cmsg_level == SOL_SOCKET && 190 cm->cmsg_type == SCM_TIMESTAMPING) { 191 tss = (void *) CMSG_DATA(cm); 192 } else if ((cm->cmsg_level == SOL_IP && 193 cm->cmsg_type == IP_RECVERR) || 194 (cm->cmsg_level == SOL_IPV6 && 195 cm->cmsg_type == IPV6_RECVERR)) { 196 serr = (void *) CMSG_DATA(cm); 197 if (serr->ee_errno != ENOMSG || 198 serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { 199 fprintf(stderr, "unknown ip error %d %d\n", 200 serr->ee_errno, 201 serr->ee_origin); 202 serr = NULL; 203 } 204 } else if (cm->cmsg_level == SOL_IP && 205 cm->cmsg_type == IP_PKTINFO) { 206 struct in_pktinfo *info = (void *) CMSG_DATA(cm); 207 print_pktinfo(AF_INET, info->ipi_ifindex, 208 &info->ipi_spec_dst, &info->ipi_addr); 209 } else if (cm->cmsg_level == SOL_IPV6 && 210 cm->cmsg_type == IPV6_PKTINFO) { 211 struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm); 212 print_pktinfo(AF_INET6, info6->ipi6_ifindex, 213 NULL, &info6->ipi6_addr); 214 } else 215 fprintf(stderr, "unknown cmsg %d,%d\n", 216 cm->cmsg_level, cm->cmsg_type); 217 218 if (serr && tss) { 219 print_timestamp(tss, serr->ee_info, serr->ee_data, 220 payload_len); 221 serr = NULL; 222 tss = NULL; 223 batch++; 224 } 225 } 226 227 if (batch > 1) 228 fprintf(stderr, "batched %d timestamps\n", batch); 229 } 230 231 static int recv_errmsg(int fd) 232 { 233 static char ctrl[1024 /* overprovision*/]; 234 static struct msghdr msg; 235 struct iovec entry; 236 static char *data; 237 int ret = 0; 238 239 data = malloc(cfg_payload_len); 240 if (!data) 241 error(1, 0, "malloc"); 242 243 memset(&msg, 0, sizeof(msg)); 244 memset(&entry, 0, sizeof(entry)); 245 memset(ctrl, 0, sizeof(ctrl)); 246 247 entry.iov_base = data; 248 entry.iov_len = cfg_payload_len; 249 msg.msg_iov = &entry; 250 msg.msg_iovlen = 1; 251 msg.msg_name = NULL; 252 msg.msg_namelen = 0; 253 msg.msg_control = ctrl; 254 msg.msg_controllen = sizeof(ctrl); 255 256 ret = recvmsg(fd, &msg, MSG_ERRQUEUE); 257 if (ret == -1 && errno != EAGAIN) 258 error(1, errno, "recvmsg"); 259 260 if (ret >= 0) { 261 __recv_errmsg_cmsg(&msg, ret); 262 if (cfg_show_payload) 263 print_payload(data, cfg_payload_len); 264 } 265 266 free(data); 267 return ret == -1; 268 } 269 270 static void do_test(int family, unsigned int opt) 271 { 272 char *buf; 273 int fd, i, val = 1, total_len; 274 275 if (family == AF_INET6 && cfg_proto != SOCK_STREAM) { 276 /* due to lack of checksum generation code */ 277 fprintf(stderr, "test: skipping datagram over IPv6\n"); 278 return; 279 } 280 281 total_len = cfg_payload_len; 282 if (cfg_proto == SOCK_RAW) { 283 total_len += sizeof(struct udphdr); 284 if (cfg_ipproto == IPPROTO_RAW) 285 total_len += sizeof(struct iphdr); 286 } 287 288 buf = malloc(total_len); 289 if (!buf) 290 error(1, 0, "malloc"); 291 292 fd = socket(family, cfg_proto, cfg_ipproto); 293 if (fd < 0) 294 error(1, errno, "socket"); 295 296 if (cfg_proto == SOCK_STREAM) { 297 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, 298 (char*) &val, sizeof(val))) 299 error(1, 0, "setsockopt no nagle"); 300 301 if (family == PF_INET) { 302 if (connect(fd, (void *) &daddr, sizeof(daddr))) 303 error(1, errno, "connect ipv4"); 304 } else { 305 if (connect(fd, (void *) &daddr6, sizeof(daddr6))) 306 error(1, errno, "connect ipv6"); 307 } 308 } 309 310 if (cfg_do_pktinfo) { 311 if (family == AF_INET6) { 312 if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO, 313 &val, sizeof(val))) 314 error(1, errno, "setsockopt pktinfo ipv6"); 315 } else { 316 if (setsockopt(fd, SOL_IP, IP_PKTINFO, 317 &val, sizeof(val))) 318 error(1, errno, "setsockopt pktinfo ipv4"); 319 } 320 } 321 322 opt |= SOF_TIMESTAMPING_SOFTWARE | 323 SOF_TIMESTAMPING_OPT_CMSG | 324 SOF_TIMESTAMPING_OPT_ID; 325 if (cfg_loop_nodata) 326 opt |= SOF_TIMESTAMPING_OPT_TSONLY; 327 328 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, 329 (char *) &opt, sizeof(opt))) 330 error(1, 0, "setsockopt timestamping"); 331 332 for (i = 0; i < cfg_num_pkts; i++) { 333 memset(&ts_prev, 0, sizeof(ts_prev)); 334 memset(buf, 'a' + i, total_len); 335 336 if (cfg_proto == SOCK_RAW) { 337 struct udphdr *udph; 338 int off = 0; 339 340 if (cfg_ipproto == IPPROTO_RAW) { 341 struct iphdr *iph = (void *) buf; 342 343 memset(iph, 0, sizeof(*iph)); 344 iph->ihl = 5; 345 iph->version = 4; 346 iph->ttl = 2; 347 iph->daddr = daddr.sin_addr.s_addr; 348 iph->protocol = IPPROTO_UDP; 349 /* kernel writes saddr, csum, len */ 350 351 off = sizeof(*iph); 352 } 353 354 udph = (void *) buf + off; 355 udph->source = ntohs(9000); /* random spoof */ 356 udph->dest = ntohs(dest_port); 357 udph->len = ntohs(sizeof(*udph) + cfg_payload_len); 358 udph->check = 0; /* not allowed for IPv6 */ 359 } 360 361 print_timestamp_usr(); 362 if (cfg_proto != SOCK_STREAM) { 363 if (family == PF_INET) 364 val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr)); 365 else 366 val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6)); 367 } else { 368 val = send(fd, buf, cfg_payload_len, 0); 369 } 370 if (val != total_len) 371 error(1, errno, "send"); 372 373 /* wait for all errors to be queued, else ACKs arrive OOO */ 374 usleep(50 * 1000); 375 376 __poll(fd); 377 378 while (!recv_errmsg(fd)) {} 379 } 380 381 if (close(fd)) 382 error(1, errno, "close"); 383 384 free(buf); 385 usleep(400 * 1000); 386 } 387 388 static void __attribute__((noreturn)) usage(const char *filepath) 389 { 390 fprintf(stderr, "\nUsage: %s [options] hostname\n" 391 "\nwhere options are:\n" 392 " -4: only IPv4\n" 393 " -6: only IPv6\n" 394 " -h: show this message\n" 395 " -I: request PKTINFO\n" 396 " -l N: send N bytes at a time\n" 397 " -n: set no-payload option\n" 398 " -r: use raw\n" 399 " -R: use raw (IP_HDRINCL)\n" 400 " -p N: connect to port N\n" 401 " -u: use udp\n" 402 " -x: show payload (up to 70 bytes)\n", 403 filepath); 404 exit(1); 405 } 406 407 static void parse_opt(int argc, char **argv) 408 { 409 int proto_count = 0; 410 char c; 411 412 while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { 413 switch (c) { 414 case '4': 415 do_ipv6 = 0; 416 break; 417 case '6': 418 do_ipv4 = 0; 419 break; 420 case 'I': 421 cfg_do_pktinfo = true; 422 break; 423 case 'n': 424 cfg_loop_nodata = true; 425 break; 426 case 'r': 427 proto_count++; 428 cfg_proto = SOCK_RAW; 429 cfg_ipproto = IPPROTO_UDP; 430 break; 431 case 'R': 432 proto_count++; 433 cfg_proto = SOCK_RAW; 434 cfg_ipproto = IPPROTO_RAW; 435 break; 436 case 'u': 437 proto_count++; 438 cfg_proto = SOCK_DGRAM; 439 cfg_ipproto = IPPROTO_UDP; 440 break; 441 case 'l': 442 cfg_payload_len = strtoul(optarg, NULL, 10); 443 break; 444 case 'p': 445 dest_port = strtoul(optarg, NULL, 10); 446 break; 447 case 'x': 448 cfg_show_payload = true; 449 break; 450 case 'h': 451 default: 452 usage(argv[0]); 453 } 454 } 455 456 if (!cfg_payload_len) 457 error(1, 0, "payload may not be nonzero"); 458 if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472) 459 error(1, 0, "udp packet might exceed expected MTU"); 460 if (!do_ipv4 && !do_ipv6) 461 error(1, 0, "pass -4 or -6, not both"); 462 if (proto_count > 1) 463 error(1, 0, "pass -r, -R or -u, not multiple"); 464 465 if (optind != argc - 1) 466 error(1, 0, "missing required hostname argument"); 467 } 468 469 static void resolve_hostname(const char *hostname) 470 { 471 struct addrinfo *addrs, *cur; 472 int have_ipv4 = 0, have_ipv6 = 0; 473 474 if (getaddrinfo(hostname, NULL, NULL, &addrs)) 475 error(1, errno, "getaddrinfo"); 476 477 cur = addrs; 478 while (cur && !have_ipv4 && !have_ipv6) { 479 if (!have_ipv4 && cur->ai_family == AF_INET) { 480 memcpy(&daddr, cur->ai_addr, sizeof(daddr)); 481 daddr.sin_port = htons(dest_port); 482 have_ipv4 = 1; 483 } 484 else if (!have_ipv6 && cur->ai_family == AF_INET6) { 485 memcpy(&daddr6, cur->ai_addr, sizeof(daddr6)); 486 daddr6.sin6_port = htons(dest_port); 487 have_ipv6 = 1; 488 } 489 cur = cur->ai_next; 490 } 491 if (addrs) 492 freeaddrinfo(addrs); 493 494 do_ipv4 &= have_ipv4; 495 do_ipv6 &= have_ipv6; 496 } 497 498 static void do_main(int family) 499 { 500 fprintf(stderr, "family: %s\n", 501 family == PF_INET ? "INET" : "INET6"); 502 503 fprintf(stderr, "test SND\n"); 504 do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE); 505 506 fprintf(stderr, "test ENQ\n"); 507 do_test(family, SOF_TIMESTAMPING_TX_SCHED); 508 509 fprintf(stderr, "test ENQ + SND\n"); 510 do_test(family, SOF_TIMESTAMPING_TX_SCHED | 511 SOF_TIMESTAMPING_TX_SOFTWARE); 512 513 if (cfg_proto == SOCK_STREAM) { 514 fprintf(stderr, "\ntest ACK\n"); 515 do_test(family, SOF_TIMESTAMPING_TX_ACK); 516 517 fprintf(stderr, "\ntest SND + ACK\n"); 518 do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE | 519 SOF_TIMESTAMPING_TX_ACK); 520 521 fprintf(stderr, "\ntest ENQ + SND + ACK\n"); 522 do_test(family, SOF_TIMESTAMPING_TX_SCHED | 523 SOF_TIMESTAMPING_TX_SOFTWARE | 524 SOF_TIMESTAMPING_TX_ACK); 525 } 526 } 527 528 const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" }; 529 530 int main(int argc, char **argv) 531 { 532 if (argc == 1) 533 usage(argv[0]); 534 535 parse_opt(argc, argv); 536 resolve_hostname(argv[argc - 1]); 537 538 fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]); 539 fprintf(stderr, "payload: %u\n", cfg_payload_len); 540 fprintf(stderr, "server port: %u\n", dest_port); 541 fprintf(stderr, "\n"); 542 543 if (do_ipv4) 544 do_main(PF_INET); 545 if (do_ipv6) 546 do_main(PF_INET6); 547 548 return 0; 549 } 550