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> |
Frans Klaver | 03e8f01 | 2015-06-04 21:27:38 +0200 | [diff] [blame] | 39 | #include <inttypes.h> |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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> |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 52 | #include <stdbool.h> |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 73 | static bool cfg_loop_nodata; |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | |
Frans Klaver | 03e8f01 | 2015-06-04 21:27:38 +0200 | [diff] [blame] | 99 | fprintf(stderr, " (%+" PRId64 " us)", cur_ms - prev_ms); |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 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 | |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 145 | if (!len) |
| 146 | return; |
| 147 | |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 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 | |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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; |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 184 | int batch = 0; |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 && |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 193 | cm->cmsg_type == IP_RECVERR) || |
| 194 | (cm->cmsg_level == SOL_IPV6 && |
| 195 | cm->cmsg_type == IPV6_RECVERR)) { |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | } |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 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); |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 214 | } else |
| 215 | fprintf(stderr, "unknown cmsg %d,%d\n", |
| 216 | cm->cmsg_level, cm->cmsg_type); |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 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 | } |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 225 | } |
| 226 | |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 227 | if (batch > 1) |
| 228 | fprintf(stderr, "batched %d timestamps\n", batch); |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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)); |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 260 | if (ret >= 0) { |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 261 | __recv_errmsg_cmsg(&msg, ret); |
| 262 | if (cfg_show_payload) |
| 263 | print_payload(data, cfg_payload_len); |
| 264 | } |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 265 | |
| 266 | free(data); |
| 267 | return ret == -1; |
| 268 | } |
| 269 | |
| 270 | static void do_test(int family, unsigned int opt) |
| 271 | { |
| 272 | char *buf; |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 273 | int fd, i, val = 1, total_len; |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 274 | |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 275 | if (family == AF_INET6 && cfg_proto != SOCK_STREAM) { |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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) { |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 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 | |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 322 | opt |= SOF_TIMESTAMPING_SOFTWARE | |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 323 | SOF_TIMESTAMPING_OPT_CMSG | |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 324 | SOF_TIMESTAMPING_OPT_ID; |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 325 | if (cfg_loop_nodata) |
| 326 | opt |= SOF_TIMESTAMPING_OPT_TSONLY; |
| 327 | |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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); |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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" |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 395 | " -I: request PKTINFO\n" |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 396 | " -l N: send N bytes at a time\n" |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 397 | " -n: set no-payload option\n" |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 398 | " -r: use raw\n" |
| 399 | " -R: use raw (IP_HDRINCL)\n" |
| 400 | " -p N: connect to port N\n" |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 401 | " -u: use udp\n" |
| 402 | " -x: show payload (up to 70 bytes)\n", |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 412 | while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 413 | switch (c) { |
| 414 | case '4': |
| 415 | do_ipv6 = 0; |
| 416 | break; |
| 417 | case '6': |
| 418 | do_ipv4 = 0; |
| 419 | break; |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 420 | case 'I': |
| 421 | cfg_do_pktinfo = true; |
| 422 | break; |
Willem de Bruijn | 2368592 | 2015-01-30 13:29:33 -0500 | [diff] [blame] | 423 | case 'n': |
| 424 | cfg_loop_nodata = true; |
| 425 | break; |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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; |
Willem de Bruijn | cbd3aad | 2014-11-30 22:22:35 -0500 | [diff] [blame] | 447 | case 'x': |
| 448 | cfg_show_payload = true; |
| 449 | break; |
Willem de Bruijn | 8fe2f76 | 2014-08-31 21:27:47 -0400 | [diff] [blame] | 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 | } |