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