2 * Copyright 2014 Google Inc.
3 * Author: willemb@google.com (Willem de Bruijn)
5 * Test software tx timestamping, including
7 * - SCHED, SND and ACK timestamps
10 * - various packet sizes (to test GSO and TSO)
12 * Consult the command line arguments for help on running
13 * the various testcases.
15 * This test requires a dummy TCP server.
16 * A simple `nc6 [-u] -l -p $DESTPORT` will do
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.
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
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.
35 #include <arpa/inet.h>
36 #include <asm/types.h>
39 #include <linux/errqueue.h>
40 #include <linux/if_ether.h>
41 #include <linux/net_tstamp.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>
56 #include <sys/ioctl.h>
57 #include <sys/select.h>
58 #include <sys/socket.h>
60 #include <sys/types.h>
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;
76 static struct sockaddr_in daddr
;
77 static struct sockaddr_in6 daddr6
;
78 static struct timespec ts_prev
;
80 static void __print_timestamp(const char *name
, struct timespec
*cur
,
81 uint32_t key
, int payload_len
)
83 if (!(cur
->tv_sec
| cur
->tv_nsec
))
86 fprintf(stderr
, " %s: %lu s %lu us (seq=%u, len=%u)",
87 name
, cur
->tv_sec
, cur
->tv_nsec
/ 1000,
90 if ((ts_prev
.tv_sec
| ts_prev
.tv_nsec
)) {
91 int64_t cur_ms
, prev_ms
;
93 cur_ms
= (long) cur
->tv_sec
* 1000 * 1000;
94 cur_ms
+= cur
->tv_nsec
/ 1000;
96 prev_ms
= (long) ts_prev
.tv_sec
* 1000 * 1000;
97 prev_ms
+= ts_prev
.tv_nsec
/ 1000;
99 fprintf(stderr
, " (%+ld us)", cur_ms
- prev_ms
);
103 fprintf(stderr
, "\n");
106 static void print_timestamp_usr(void)
109 struct timeval tv
; /* avoid dependency on -lrt */
111 gettimeofday(&tv
, NULL
);
112 ts
.tv_sec
= tv
.tv_sec
;
113 ts
.tv_nsec
= tv
.tv_usec
* 1000;
115 __print_timestamp(" USR", &ts
, 0, 0);
118 static void print_timestamp(struct scm_timestamping
*tss
, int tstype
,
119 int tskey
, int payload_len
)
124 case SCM_TSTAMP_SCHED
:
134 error(1, 0, "unknown timestamp type: %u",
137 __print_timestamp(tsname
, &tss
->ts
[0], tskey
, payload_len
);
140 /* TODO: convert to check_and_print payload once API is stable */
141 static void print_payload(char *data
, int len
)
151 fprintf(stderr
, "payload: ");
152 for (i
= 0; i
< len
; i
++)
153 fprintf(stderr
, "%02hhx ", data
[i
]);
154 fprintf(stderr
, "\n");
157 static void print_pktinfo(int family
, int ifindex
, void *saddr
, void *daddr
)
159 char sa
[INET6_ADDRSTRLEN
], da
[INET6_ADDRSTRLEN
];
161 fprintf(stderr
, " pktinfo: ifindex=%u src=%s dst=%s\n",
163 saddr
? inet_ntop(family
, saddr
, sa
, sizeof(sa
)) : "unknown",
164 daddr
? inet_ntop(family
, daddr
, da
, sizeof(da
)) : "unknown");
167 static void __poll(int fd
)
169 struct pollfd pollfd
;
172 memset(&pollfd
, 0, sizeof(pollfd
));
174 ret
= poll(&pollfd
, 1, 100);
176 error(1, errno
, "poll");
179 static void __recv_errmsg_cmsg(struct msghdr
*msg
, int payload_len
)
181 struct sock_extended_err
*serr
= NULL
;
182 struct scm_timestamping
*tss
= NULL
;
186 for (cm
= CMSG_FIRSTHDR(msg
);
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",
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
);
215 fprintf(stderr
, "unknown cmsg %d,%d\n",
216 cm
->cmsg_level
, cm
->cmsg_type
);
219 print_timestamp(tss
, serr
->ee_info
, serr
->ee_data
,
228 fprintf(stderr
, "batched %d timestamps\n", batch
);
231 static int recv_errmsg(int fd
)
233 static char ctrl
[1024 /* overprovision*/];
234 static struct msghdr msg
;
239 data
= malloc(cfg_payload_len
);
241 error(1, 0, "malloc");
243 memset(&msg
, 0, sizeof(msg
));
244 memset(&entry
, 0, sizeof(entry
));
245 memset(ctrl
, 0, sizeof(ctrl
));
247 entry
.iov_base
= data
;
248 entry
.iov_len
= cfg_payload_len
;
249 msg
.msg_iov
= &entry
;
253 msg
.msg_control
= ctrl
;
254 msg
.msg_controllen
= sizeof(ctrl
);
256 ret
= recvmsg(fd
, &msg
, MSG_ERRQUEUE
);
257 if (ret
== -1 && errno
!= EAGAIN
)
258 error(1, errno
, "recvmsg");
261 __recv_errmsg_cmsg(&msg
, ret
);
262 if (cfg_show_payload
)
263 print_payload(data
, cfg_payload_len
);
270 static void do_test(int family
, unsigned int opt
)
273 int fd
, i
, val
= 1, total_len
;
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");
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
);
288 buf
= malloc(total_len
);
290 error(1, 0, "malloc");
292 fd
= socket(family
, cfg_proto
, cfg_ipproto
);
294 error(1, errno
, "socket");
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");
301 if (family
== PF_INET
) {
302 if (connect(fd
, (void *) &daddr
, sizeof(daddr
)))
303 error(1, errno
, "connect ipv4");
305 if (connect(fd
, (void *) &daddr6
, sizeof(daddr6
)))
306 error(1, errno
, "connect ipv6");
310 if (cfg_do_pktinfo
) {
311 if (family
== AF_INET6
) {
312 if (setsockopt(fd
, SOL_IPV6
, IPV6_RECVPKTINFO
,
314 error(1, errno
, "setsockopt pktinfo ipv6");
316 if (setsockopt(fd
, SOL_IP
, IP_PKTINFO
,
318 error(1, errno
, "setsockopt pktinfo ipv4");
322 opt
|= SOF_TIMESTAMPING_SOFTWARE
|
323 SOF_TIMESTAMPING_OPT_CMSG
|
324 SOF_TIMESTAMPING_OPT_ID
;
326 opt
|= SOF_TIMESTAMPING_OPT_TSONLY
;
328 if (setsockopt(fd
, SOL_SOCKET
, SO_TIMESTAMPING
,
329 (char *) &opt
, sizeof(opt
)))
330 error(1, 0, "setsockopt timestamping");
332 for (i
= 0; i
< cfg_num_pkts
; i
++) {
333 memset(&ts_prev
, 0, sizeof(ts_prev
));
334 memset(buf
, 'a' + i
, total_len
);
336 if (cfg_proto
== SOCK_RAW
) {
340 if (cfg_ipproto
== IPPROTO_RAW
) {
341 struct iphdr
*iph
= (void *) buf
;
343 memset(iph
, 0, sizeof(*iph
));
347 iph
->daddr
= daddr
.sin_addr
.s_addr
;
348 iph
->protocol
= IPPROTO_UDP
;
349 /* kernel writes saddr, csum, len */
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 */
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
));
366 val
= sendto(fd
, buf
, total_len
, 0, (void *) &daddr6
, sizeof(daddr6
));
368 val
= send(fd
, buf
, cfg_payload_len
, 0);
370 if (val
!= total_len
)
371 error(1, errno
, "send");
373 /* wait for all errors to be queued, else ACKs arrive OOO */
378 while (!recv_errmsg(fd
)) {}
382 error(1, errno
, "close");
388 static void __attribute__((noreturn
)) usage(const char *filepath
)
390 fprintf(stderr
, "\nUsage: %s [options] hostname\n"
391 "\nwhere options are:\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"
399 " -R: use raw (IP_HDRINCL)\n"
400 " -p N: connect to port N\n"
402 " -x: show payload (up to 70 bytes)\n",
407 static void parse_opt(int argc
, char **argv
)
412 while ((c
= getopt(argc
, argv
, "46hIl:np:rRux")) != -1) {
421 cfg_do_pktinfo
= true;
424 cfg_loop_nodata
= true;
428 cfg_proto
= SOCK_RAW
;
429 cfg_ipproto
= IPPROTO_UDP
;
433 cfg_proto
= SOCK_RAW
;
434 cfg_ipproto
= IPPROTO_RAW
;
438 cfg_proto
= SOCK_DGRAM
;
439 cfg_ipproto
= IPPROTO_UDP
;
442 cfg_payload_len
= strtoul(optarg
, NULL
, 10);
445 dest_port
= strtoul(optarg
, NULL
, 10);
448 cfg_show_payload
= true;
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");
463 error(1, 0, "pass -r, -R or -u, not multiple");
465 if (optind
!= argc
- 1)
466 error(1, 0, "missing required hostname argument");
469 static void resolve_hostname(const char *hostname
)
471 struct addrinfo
*addrs
, *cur
;
472 int have_ipv4
= 0, have_ipv6
= 0;
474 if (getaddrinfo(hostname
, NULL
, NULL
, &addrs
))
475 error(1, errno
, "getaddrinfo");
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
);
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
);
494 do_ipv4
&= have_ipv4
;
495 do_ipv6
&= have_ipv6
;
498 static void do_main(int family
)
500 fprintf(stderr
, "family: %s\n",
501 family
== PF_INET
? "INET" : "INET6");
503 fprintf(stderr
, "test SND\n");
504 do_test(family
, SOF_TIMESTAMPING_TX_SOFTWARE
);
506 fprintf(stderr
, "test ENQ\n");
507 do_test(family
, SOF_TIMESTAMPING_TX_SCHED
);
509 fprintf(stderr
, "test ENQ + SND\n");
510 do_test(family
, SOF_TIMESTAMPING_TX_SCHED
|
511 SOF_TIMESTAMPING_TX_SOFTWARE
);
513 if (cfg_proto
== SOCK_STREAM
) {
514 fprintf(stderr
, "\ntest ACK\n");
515 do_test(family
, SOF_TIMESTAMPING_TX_ACK
);
517 fprintf(stderr
, "\ntest SND + ACK\n");
518 do_test(family
, SOF_TIMESTAMPING_TX_SOFTWARE
|
519 SOF_TIMESTAMPING_TX_ACK
);
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
);
528 const char *sock_names
[] = { NULL
, "TCP", "UDP", "RAW" };
530 int main(int argc
, char **argv
)
535 parse_opt(argc
, argv
);
536 resolve_hostname(argv
[argc
- 1]);
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");