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