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