1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2014 Google Inc.
4 * Author: willemb@google.com (Willem de Bruijn)
6 * Test software tx timestamping, including
8 * - SCHED, SND and ACK timestamps
11 * - various packet sizes (to test GSO and TSO)
13 * Consult the command line arguments for help on running
14 * the various testcases.
16 * This test requires a dummy TCP server.
17 * A simple `nc6 [-u] -l -p $DESTPORT` will do
22 #include <arpa/inet.h>
23 #include <asm/types.h>
27 #include <linux/errqueue.h>
28 #include <linux/if_ether.h>
29 #include <linux/if_packet.h>
30 #include <linux/ipv6.h>
31 #include <linux/net_tstamp.h>
34 #include <netinet/in.h>
35 #include <netinet/ip.h>
36 #include <netinet/udp.h>
37 #include <netinet/tcp.h>
44 #include <sys/epoll.h>
45 #include <sys/ioctl.h>
46 #include <sys/select.h>
47 #include <sys/socket.h>
49 #include <sys/types.h>
53 #define NSEC_PER_USEC 1000L
54 #define USEC_PER_SEC 1000000L
55 #define NSEC_PER_SEC 1000000000LL
57 /* command line parameters */
58 static int cfg_proto
= SOCK_STREAM
;
59 static int cfg_ipproto
= IPPROTO_TCP
;
60 static int cfg_num_pkts
= 4;
61 static int do_ipv4
= 1;
62 static int do_ipv6
= 1;
63 static int cfg_payload_len
= 10;
64 static int cfg_poll_timeout
= 100;
65 static int cfg_delay_snd
;
66 static int cfg_delay_ack
;
67 static int cfg_delay_tolerance_usec
= 500;
68 static bool cfg_show_payload
;
69 static bool cfg_do_pktinfo
;
70 static bool cfg_busy_poll
;
71 static int cfg_sleep_usec
= 50 * 1000;
72 static bool cfg_loop_nodata
;
73 static bool cfg_use_cmsg
;
74 static bool cfg_use_pf_packet
;
75 static bool cfg_use_epoll
;
76 static bool cfg_epollet
;
77 static bool cfg_do_listen
;
78 static uint16_t dest_port
= 9000;
79 static bool cfg_print_nsec
;
80 static uint32_t ts_opt_id
;
81 static bool cfg_use_cmsg_opt_id
;
83 static struct sockaddr_in daddr
;
84 static struct sockaddr_in6 daddr6
;
85 static struct timespec ts_usr
;
87 static int saved_tskey
= -1;
88 static int saved_tskey_type
= -1;
97 static struct timing_event usr_enq
;
98 static struct timing_event usr_snd
;
99 static struct timing_event usr_ack
;
101 static bool test_failed
;
103 static int64_t timespec_to_ns64(struct timespec
*ts
)
105 return ts
->tv_sec
* NSEC_PER_SEC
+ ts
->tv_nsec
;
108 static int64_t timespec_to_us64(struct timespec
*ts
)
110 return ts
->tv_sec
* USEC_PER_SEC
+ ts
->tv_nsec
/ NSEC_PER_USEC
;
113 static void init_timing_event(struct timing_event
*te
)
121 static void add_timing_event(struct timing_event
*te
,
122 struct timespec
*t_start
, struct timespec
*t_end
)
124 int64_t ts_delta
= timespec_to_ns64(t_end
) - timespec_to_ns64(t_start
);
127 if (ts_delta
< te
->min
)
129 if (ts_delta
> te
->max
)
131 te
->total
+= ts_delta
;
134 static void validate_key(int tskey
, int tstype
)
138 /* compare key for each subsequent request
139 * must only test for one type, the first one requested
141 if (saved_tskey
== -1 || cfg_use_cmsg_opt_id
)
142 saved_tskey_type
= tstype
;
143 else if (saved_tskey_type
!= tstype
)
146 stepsize
= cfg_proto
== SOCK_STREAM
? cfg_payload_len
: 1;
147 stepsize
= cfg_use_cmsg_opt_id
? 0 : stepsize
;
148 if (tskey
!= saved_tskey
+ stepsize
) {
149 fprintf(stderr
, "ERROR: key %d, expected %d\n",
150 tskey
, saved_tskey
+ stepsize
);
157 static void validate_timestamp(struct timespec
*cur
, int min_delay
)
159 int64_t cur64
, start64
;
162 cur64
= timespec_to_us64(cur
);
163 start64
= timespec_to_us64(&ts_usr
);
164 max_delay
= min_delay
+ cfg_delay_tolerance_usec
;
166 if (cur64
< start64
+ min_delay
|| cur64
> start64
+ max_delay
) {
167 fprintf(stderr
, "ERROR: %" PRId64
" us expected between %d and %d\n",
168 cur64
- start64
, min_delay
, max_delay
);
169 if (!getenv("KSFT_MACHINE_SLOW"))
174 static void __print_ts_delta_formatted(int64_t ts_delta
)
177 fprintf(stderr
, "%" PRId64
" ns", ts_delta
);
179 fprintf(stderr
, "%" PRId64
" us", ts_delta
/ NSEC_PER_USEC
);
182 static void __print_timestamp(const char *name
, struct timespec
*cur
,
183 uint32_t key
, int payload_len
)
187 if (!(cur
->tv_sec
| cur
->tv_nsec
))
191 fprintf(stderr
, " %s: %lu s %lu ns (seq=%u, len=%u)",
192 name
, cur
->tv_sec
, cur
->tv_nsec
,
195 fprintf(stderr
, " %s: %lu s %lu us (seq=%u, len=%u)",
196 name
, cur
->tv_sec
, cur
->tv_nsec
/ NSEC_PER_USEC
,
199 if (cur
!= &ts_usr
) {
200 ts_delta
= timespec_to_ns64(cur
) - timespec_to_ns64(&ts_usr
);
201 fprintf(stderr
, " (USR +");
202 __print_ts_delta_formatted(ts_delta
);
203 fprintf(stderr
, ")");
206 fprintf(stderr
, "\n");
209 static void print_timestamp_usr(void)
211 if (clock_gettime(CLOCK_REALTIME
, &ts_usr
))
212 error(1, errno
, "clock_gettime");
214 __print_timestamp(" USR", &ts_usr
, 0, 0);
217 static void print_timestamp(struct scm_timestamping
*tss
, int tstype
,
218 int tskey
, int payload_len
)
222 validate_key(tskey
, tstype
);
225 case SCM_TSTAMP_SCHED
:
227 validate_timestamp(&tss
->ts
[0], 0);
228 add_timing_event(&usr_enq
, &ts_usr
, &tss
->ts
[0]);
232 validate_timestamp(&tss
->ts
[0], cfg_delay_snd
);
233 add_timing_event(&usr_snd
, &ts_usr
, &tss
->ts
[0]);
237 validate_timestamp(&tss
->ts
[0], cfg_delay_ack
);
238 add_timing_event(&usr_ack
, &ts_usr
, &tss
->ts
[0]);
241 error(1, 0, "unknown timestamp type: %u",
244 __print_timestamp(tsname
, &tss
->ts
[0], tskey
, payload_len
);
247 static void print_timing_event(char *name
, struct timing_event
*te
)
252 fprintf(stderr
, " %s: count=%d", name
, te
->count
);
253 fprintf(stderr
, ", avg=");
254 __print_ts_delta_formatted((int64_t)(te
->total
/ te
->count
));
255 fprintf(stderr
, ", min=");
256 __print_ts_delta_formatted(te
->min
);
257 fprintf(stderr
, ", max=");
258 __print_ts_delta_formatted(te
->max
);
259 fprintf(stderr
, "\n");
262 /* TODO: convert to check_and_print payload once API is stable */
263 static void print_payload(char *data
, int len
)
273 fprintf(stderr
, "payload: ");
274 for (i
= 0; i
< len
; i
++)
275 fprintf(stderr
, "%02hhx ", data
[i
]);
276 fprintf(stderr
, "\n");
279 static void print_pktinfo(int family
, int ifindex
, void *saddr
, void *daddr
)
281 char sa
[INET6_ADDRSTRLEN
], da
[INET6_ADDRSTRLEN
];
283 fprintf(stderr
, " pktinfo: ifindex=%u src=%s dst=%s\n",
285 saddr
? inet_ntop(family
, saddr
, sa
, sizeof(sa
)) : "unknown",
286 daddr
? inet_ntop(family
, daddr
, da
, sizeof(da
)) : "unknown");
289 static void __epoll(int epfd
)
291 struct epoll_event events
;
294 memset(&events
, 0, sizeof(events
));
295 ret
= epoll_wait(epfd
, &events
, 1, cfg_poll_timeout
);
297 error(1, errno
, "epoll_wait");
300 static void __poll(int fd
)
302 struct pollfd pollfd
;
305 memset(&pollfd
, 0, sizeof(pollfd
));
307 ret
= poll(&pollfd
, 1, cfg_poll_timeout
);
309 error(1, errno
, "poll");
312 static void __recv_errmsg_cmsg(struct msghdr
*msg
, int payload_len
)
314 struct sock_extended_err
*serr
= NULL
;
315 struct scm_timestamping
*tss
= NULL
;
319 for (cm
= CMSG_FIRSTHDR(msg
);
321 cm
= CMSG_NXTHDR(msg
, cm
)) {
322 if (cm
->cmsg_level
== SOL_SOCKET
&&
323 cm
->cmsg_type
== SCM_TIMESTAMPING
) {
324 tss
= (void *) CMSG_DATA(cm
);
325 } else if ((cm
->cmsg_level
== SOL_IP
&&
326 cm
->cmsg_type
== IP_RECVERR
) ||
327 (cm
->cmsg_level
== SOL_IPV6
&&
328 cm
->cmsg_type
== IPV6_RECVERR
) ||
329 (cm
->cmsg_level
== SOL_PACKET
&&
330 cm
->cmsg_type
== PACKET_TX_TIMESTAMP
)) {
331 serr
= (void *) CMSG_DATA(cm
);
332 if (serr
->ee_errno
!= ENOMSG
||
333 serr
->ee_origin
!= SO_EE_ORIGIN_TIMESTAMPING
) {
334 fprintf(stderr
, "unknown ip error %d %d\n",
339 } else if (cm
->cmsg_level
== SOL_IP
&&
340 cm
->cmsg_type
== IP_PKTINFO
) {
341 struct in_pktinfo
*info
= (void *) CMSG_DATA(cm
);
342 print_pktinfo(AF_INET
, info
->ipi_ifindex
,
343 &info
->ipi_spec_dst
, &info
->ipi_addr
);
344 } else if (cm
->cmsg_level
== SOL_IPV6
&&
345 cm
->cmsg_type
== IPV6_PKTINFO
) {
346 struct in6_pktinfo
*info6
= (void *) CMSG_DATA(cm
);
347 print_pktinfo(AF_INET6
, info6
->ipi6_ifindex
,
348 NULL
, &info6
->ipi6_addr
);
350 fprintf(stderr
, "unknown cmsg %d,%d\n",
351 cm
->cmsg_level
, cm
->cmsg_type
);
354 print_timestamp(tss
, serr
->ee_info
, serr
->ee_data
,
363 fprintf(stderr
, "batched %d timestamps\n", batch
);
365 fprintf(stderr
, "Failed to report timestamps\n");
370 static int recv_errmsg(int fd
)
372 static char ctrl
[1024 /* overprovision*/];
373 static struct msghdr msg
;
378 data
= malloc(cfg_payload_len
);
380 error(1, 0, "malloc");
382 memset(&msg
, 0, sizeof(msg
));
383 memset(&entry
, 0, sizeof(entry
));
384 memset(ctrl
, 0, sizeof(ctrl
));
386 entry
.iov_base
= data
;
387 entry
.iov_len
= cfg_payload_len
;
388 msg
.msg_iov
= &entry
;
392 msg
.msg_control
= ctrl
;
393 msg
.msg_controllen
= sizeof(ctrl
);
395 ret
= recvmsg(fd
, &msg
, MSG_ERRQUEUE
);
396 if (ret
== -1 && errno
!= EAGAIN
)
397 error(1, errno
, "recvmsg");
400 __recv_errmsg_cmsg(&msg
, ret
);
401 if (cfg_show_payload
)
402 print_payload(data
, cfg_payload_len
);
409 static uint16_t get_ip_csum(const uint16_t *start
, int num_words
,
414 for (i
= 0; i
< num_words
; i
++)
418 sum
= (sum
& 0xFFFF) + (sum
>> 16);
423 static uint16_t get_udp_csum(const struct udphdr
*udph
, int alen
)
425 unsigned long pseudo_sum
, csum_len
;
426 const void *csum_start
= udph
;
428 pseudo_sum
= htons(IPPROTO_UDP
);
429 pseudo_sum
+= udph
->len
;
431 /* checksum ip(v6) addresses + udp header + payload */
432 csum_start
-= alen
* 2;
433 csum_len
= ntohs(udph
->len
) + alen
* 2;
435 return get_ip_csum(csum_start
, csum_len
>> 1, pseudo_sum
);
438 static int fill_header_ipv4(void *p
)
440 struct iphdr
*iph
= p
;
442 memset(iph
, 0, sizeof(*iph
));
447 iph
->saddr
= daddr
.sin_addr
.s_addr
; /* set for udp csum calc */
448 iph
->daddr
= daddr
.sin_addr
.s_addr
;
449 iph
->protocol
= IPPROTO_UDP
;
451 /* kernel writes saddr, csum, len */
456 static int fill_header_ipv6(void *p
)
458 struct ipv6hdr
*ip6h
= p
;
460 memset(ip6h
, 0, sizeof(*ip6h
));
463 ip6h
->payload_len
= htons(sizeof(struct udphdr
) + cfg_payload_len
);
464 ip6h
->nexthdr
= IPPROTO_UDP
;
465 ip6h
->hop_limit
= 64;
467 ip6h
->saddr
= daddr6
.sin6_addr
;
468 ip6h
->daddr
= daddr6
.sin6_addr
;
470 /* kernel does not write saddr in case of ipv6 */
472 return sizeof(*ip6h
);
475 static void fill_header_udp(void *p
, bool is_ipv4
)
477 struct udphdr
*udph
= p
;
479 udph
->source
= ntohs(dest_port
+ 1); /* spoof */
480 udph
->dest
= ntohs(dest_port
);
481 udph
->len
= ntohs(sizeof(*udph
) + cfg_payload_len
);
484 udph
->check
= get_udp_csum(udph
, is_ipv4
? sizeof(struct in_addr
) :
485 sizeof(struct in6_addr
));
488 static void do_test(int family
, unsigned int report_opt
)
490 char control
[2 * CMSG_SPACE(sizeof(uint32_t))];
491 struct sockaddr_ll laddr
;
492 unsigned int sock_opt
;
493 struct cmsghdr
*cmsg
;
497 int fd
, i
, val
= 1, total_len
, epfd
= 0;
499 init_timing_event(&usr_enq
);
500 init_timing_event(&usr_snd
);
501 init_timing_event(&usr_ack
);
503 total_len
= cfg_payload_len
;
504 if (cfg_use_pf_packet
|| cfg_proto
== SOCK_RAW
) {
505 total_len
+= sizeof(struct udphdr
);
506 if (cfg_use_pf_packet
|| cfg_ipproto
== IPPROTO_RAW
) {
507 if (family
== PF_INET
)
508 total_len
+= sizeof(struct iphdr
);
510 total_len
+= sizeof(struct ipv6hdr
);
512 /* special case, only rawv6_sendmsg:
513 * pass proto in sin6_port if not connected
514 * also see ANK comment in net/ipv4/raw.c
516 daddr6
.sin6_port
= htons(cfg_ipproto
);
519 buf
= malloc(total_len
);
521 error(1, 0, "malloc");
523 fd
= socket(cfg_use_pf_packet
? PF_PACKET
: family
,
524 cfg_proto
, cfg_ipproto
);
526 error(1, errno
, "socket");
529 struct epoll_event ev
;
531 memset(&ev
, 0, sizeof(ev
));
534 ev
.events
|= EPOLLET
;
535 epfd
= epoll_create(1);
537 error(1, errno
, "epoll_create");
538 if (epoll_ctl(epfd
, EPOLL_CTL_ADD
, fd
, &ev
))
539 error(1, errno
, "epoll_ctl");
542 /* reset expected key on each new socket */
545 if (cfg_proto
== SOCK_STREAM
) {
546 if (setsockopt(fd
, IPPROTO_TCP
, TCP_NODELAY
,
547 (char*) &val
, sizeof(val
)))
548 error(1, 0, "setsockopt no nagle");
550 if (family
== PF_INET
) {
551 if (connect(fd
, (void *) &daddr
, sizeof(daddr
)))
552 error(1, errno
, "connect ipv4");
554 if (connect(fd
, (void *) &daddr6
, sizeof(daddr6
)))
555 error(1, errno
, "connect ipv6");
559 if (cfg_do_pktinfo
) {
560 if (family
== AF_INET6
) {
561 if (setsockopt(fd
, SOL_IPV6
, IPV6_RECVPKTINFO
,
563 error(1, errno
, "setsockopt pktinfo ipv6");
565 if (setsockopt(fd
, SOL_IP
, IP_PKTINFO
,
567 error(1, errno
, "setsockopt pktinfo ipv4");
571 sock_opt
= SOF_TIMESTAMPING_SOFTWARE
|
572 SOF_TIMESTAMPING_OPT_CMSG
|
573 SOF_TIMESTAMPING_OPT_ID
;
576 sock_opt
|= report_opt
;
579 sock_opt
|= SOF_TIMESTAMPING_OPT_TSONLY
;
581 if (setsockopt(fd
, SOL_SOCKET
, SO_TIMESTAMPING
,
582 (char *) &sock_opt
, sizeof(sock_opt
)))
583 error(1, 0, "setsockopt timestamping");
585 for (i
= 0; i
< cfg_num_pkts
; i
++) {
586 memset(&msg
, 0, sizeof(msg
));
587 memset(buf
, 'a' + i
, total_len
);
589 if (cfg_use_pf_packet
|| cfg_proto
== SOCK_RAW
) {
592 if (cfg_use_pf_packet
|| cfg_ipproto
== IPPROTO_RAW
) {
593 if (family
== PF_INET
)
594 off
= fill_header_ipv4(buf
);
596 off
= fill_header_ipv6(buf
);
599 fill_header_udp(buf
+ off
, family
== PF_INET
);
602 print_timestamp_usr();
605 iov
.iov_len
= total_len
;
607 if (cfg_proto
!= SOCK_STREAM
) {
608 if (cfg_use_pf_packet
) {
609 memset(&laddr
, 0, sizeof(laddr
));
611 laddr
.sll_family
= AF_PACKET
;
612 laddr
.sll_ifindex
= 1;
613 laddr
.sll_protocol
= htons(family
== AF_INET
? ETH_P_IP
: ETH_P_IPV6
);
614 laddr
.sll_halen
= ETH_ALEN
;
616 msg
.msg_name
= (void *)&laddr
;
617 msg
.msg_namelen
= sizeof(laddr
);
618 } else if (family
== PF_INET
) {
619 msg
.msg_name
= (void *)&daddr
;
620 msg
.msg_namelen
= sizeof(daddr
);
622 msg
.msg_name
= (void *)&daddr6
;
623 msg
.msg_namelen
= sizeof(daddr6
);
630 if (cfg_use_cmsg
|| cfg_use_cmsg_opt_id
) {
631 memset(control
, 0, sizeof(control
));
633 msg
.msg_control
= control
;
634 msg
.msg_controllen
= cfg_use_cmsg
* CMSG_SPACE(sizeof(uint32_t));
635 msg
.msg_controllen
+= cfg_use_cmsg_opt_id
* CMSG_SPACE(sizeof(uint32_t));
639 cmsg
= CMSG_FIRSTHDR(&msg
);
640 cmsg
->cmsg_level
= SOL_SOCKET
;
641 cmsg
->cmsg_type
= SO_TIMESTAMPING
;
642 cmsg
->cmsg_len
= CMSG_LEN(sizeof(uint32_t));
644 *((uint32_t *)CMSG_DATA(cmsg
)) = report_opt
;
646 if (cfg_use_cmsg_opt_id
) {
647 cmsg
= cmsg
? CMSG_NXTHDR(&msg
, cmsg
) : CMSG_FIRSTHDR(&msg
);
648 cmsg
->cmsg_level
= SOL_SOCKET
;
649 cmsg
->cmsg_type
= SCM_TS_OPT_ID
;
650 cmsg
->cmsg_len
= CMSG_LEN(sizeof(uint32_t));
652 *((uint32_t *)CMSG_DATA(cmsg
)) = ts_opt_id
;
653 saved_tskey
= ts_opt_id
;
658 val
= sendmsg(fd
, &msg
, 0);
659 if (val
!= total_len
)
660 error(1, errno
, "send");
662 /* wait for all errors to be queued, else ACKs arrive OOO */
664 usleep(cfg_sleep_usec
);
666 if (!cfg_busy_poll
) {
673 while (!recv_errmsg(fd
)) {}
676 print_timing_event("USR-ENQ", &usr_enq
);
677 print_timing_event("USR-SND", &usr_snd
);
678 print_timing_event("USR-ACK", &usr_ack
);
681 error(1, errno
, "close");
684 usleep(100 * NSEC_PER_USEC
);
687 static void __attribute__((noreturn
)) usage(const char *filepath
)
689 fprintf(stderr
, "\nUsage: %s [options] hostname\n"
690 "\nwhere options are:\n"
693 " -h: show this message\n"
694 " -b: busy poll to read from error queue\n"
695 " -c N: number of packets for each test\n"
696 " -C: use cmsg to set tstamp recording options\n"
697 " -e: use level-triggered epoll() instead of poll()\n"
698 " -E: use event-triggered epoll() instead of poll()\n"
699 " -F: poll()/epoll() waits forever for an event\n"
700 " -I: request PKTINFO\n"
701 " -l N: send N bytes at a time\n"
702 " -L listen on hostname and port\n"
703 " -n: set no-payload option\n"
704 " -N: print timestamps and durations in nsec (instead of usec)\n"
705 " -o N: use SCM_TS_OPT_ID control message to provide N as tskey\n"
706 " -p N: connect to port N\n"
707 " -P: use PF_PACKET\n"
709 " -R: use raw (IP_HDRINCL)\n"
710 " -S N: usec to sleep before reading error queue\n"
711 " -t N: tolerance (usec) for timestamp validation\n"
713 " -v: validate SND delay (usec)\n"
714 " -V: validate ACK delay (usec)\n"
715 " -x: show payload (up to 70 bytes)\n",
720 static void parse_opt(int argc
, char **argv
)
725 while ((c
= getopt(argc
, argv
,
726 "46bc:CeEFhIl:LnNo:p:PrRS:t:uv:V:x")) != -1) {
735 cfg_busy_poll
= true;
738 cfg_num_pkts
= strtoul(optarg
, NULL
, 10);
744 cfg_use_epoll
= true;
747 cfg_use_epoll
= true;
750 cfg_poll_timeout
= -1;
753 cfg_do_pktinfo
= true;
756 cfg_payload_len
= strtoul(optarg
, NULL
, 10);
759 cfg_do_listen
= true;
762 cfg_loop_nodata
= true;
765 cfg_print_nsec
= true;
768 ts_opt_id
= strtoul(optarg
, NULL
, 10);
769 cfg_use_cmsg_opt_id
= true;
772 dest_port
= strtoul(optarg
, NULL
, 10);
776 cfg_use_pf_packet
= true;
777 cfg_proto
= SOCK_DGRAM
;
782 cfg_proto
= SOCK_RAW
;
783 cfg_ipproto
= IPPROTO_UDP
;
787 cfg_proto
= SOCK_RAW
;
788 cfg_ipproto
= IPPROTO_RAW
;
791 cfg_sleep_usec
= strtoul(optarg
, NULL
, 10);
794 cfg_delay_tolerance_usec
= strtoul(optarg
, NULL
, 10);
798 cfg_proto
= SOCK_DGRAM
;
799 cfg_ipproto
= IPPROTO_UDP
;
802 cfg_delay_snd
= strtoul(optarg
, NULL
, 10);
805 cfg_delay_ack
= strtoul(optarg
, NULL
, 10);
808 cfg_show_payload
= true;
816 if (!cfg_payload_len
)
817 error(1, 0, "payload may not be nonzero");
818 if (cfg_proto
!= SOCK_STREAM
&& cfg_payload_len
> 1472)
819 error(1, 0, "udp packet might exceed expected MTU");
820 if (!do_ipv4
&& !do_ipv6
)
821 error(1, 0, "pass -4 or -6, not both");
823 error(1, 0, "pass -P, -r, -R or -u, not multiple");
824 if (cfg_do_pktinfo
&& cfg_use_pf_packet
)
825 error(1, 0, "cannot ask for pktinfo over pf_packet");
826 if (cfg_busy_poll
&& cfg_use_epoll
)
827 error(1, 0, "pass epoll or busy_poll, not both");
828 if (cfg_proto
== SOCK_STREAM
&& cfg_use_cmsg_opt_id
)
829 error(1, 0, "TCP sockets don't support SCM_TS_OPT_ID");
831 if (optind
!= argc
- 1)
832 error(1, 0, "missing required hostname argument");
835 static void resolve_hostname(const char *hostname
)
837 struct addrinfo hints
= { .ai_family
= do_ipv4
? AF_INET
: AF_INET6
};
838 struct addrinfo
*addrs
, *cur
;
839 int have_ipv4
= 0, have_ipv6
= 0;
842 if (getaddrinfo(hostname
, NULL
, &hints
, &addrs
))
843 error(1, errno
, "getaddrinfo");
846 while (cur
&& !have_ipv4
&& !have_ipv6
) {
847 if (!have_ipv4
&& cur
->ai_family
== AF_INET
) {
848 memcpy(&daddr
, cur
->ai_addr
, sizeof(daddr
));
849 daddr
.sin_port
= htons(dest_port
);
852 else if (!have_ipv6
&& cur
->ai_family
== AF_INET6
) {
853 memcpy(&daddr6
, cur
->ai_addr
, sizeof(daddr6
));
854 daddr6
.sin6_port
= htons(dest_port
);
862 if (do_ipv6
&& hints
.ai_family
!= AF_INET6
) {
863 hints
.ai_family
= AF_INET6
;
867 do_ipv4
&= have_ipv4
;
868 do_ipv6
&= have_ipv6
;
871 static void do_listen(int family
, void *addr
, int alen
)
875 type
= cfg_proto
== SOCK_RAW
? SOCK_DGRAM
: cfg_proto
;
877 fd
= socket(family
, type
, 0);
879 error(1, errno
, "socket rx");
881 if (bind(fd
, addr
, alen
))
882 error(1, errno
, "bind rx");
884 if (type
== SOCK_STREAM
&& listen(fd
, 10))
885 error(1, errno
, "listen rx");
887 /* leave fd open, will be closed on process exit.
888 * this enables connect() to succeed and avoids icmp replies
892 static void do_main(int family
)
894 fprintf(stderr
, "family: %s %s\n",
895 family
== PF_INET
? "INET" : "INET6",
896 cfg_use_pf_packet
? "(PF_PACKET)" : "");
898 fprintf(stderr
, "test SND\n");
899 do_test(family
, SOF_TIMESTAMPING_TX_SOFTWARE
);
901 fprintf(stderr
, "test ENQ\n");
902 do_test(family
, SOF_TIMESTAMPING_TX_SCHED
);
904 fprintf(stderr
, "test ENQ + SND\n");
905 do_test(family
, SOF_TIMESTAMPING_TX_SCHED
|
906 SOF_TIMESTAMPING_TX_SOFTWARE
);
908 if (cfg_proto
== SOCK_STREAM
) {
909 fprintf(stderr
, "\ntest ACK\n");
910 do_test(family
, SOF_TIMESTAMPING_TX_ACK
);
912 fprintf(stderr
, "\ntest SND + ACK\n");
913 do_test(family
, SOF_TIMESTAMPING_TX_SOFTWARE
|
914 SOF_TIMESTAMPING_TX_ACK
);
916 fprintf(stderr
, "\ntest ENQ + SND + ACK\n");
917 do_test(family
, SOF_TIMESTAMPING_TX_SCHED
|
918 SOF_TIMESTAMPING_TX_SOFTWARE
|
919 SOF_TIMESTAMPING_TX_ACK
);
923 const char *sock_names
[] = { NULL
, "TCP", "UDP", "RAW" };
925 int main(int argc
, char **argv
)
930 parse_opt(argc
, argv
);
931 resolve_hostname(argv
[argc
- 1]);
933 fprintf(stderr
, "protocol: %s\n", sock_names
[cfg_proto
]);
934 fprintf(stderr
, "payload: %u\n", cfg_payload_len
);
935 fprintf(stderr
, "server port: %u\n", dest_port
);
936 fprintf(stderr
, "\n");
940 do_listen(PF_INET
, &daddr
, sizeof(daddr
));
946 do_listen(PF_INET6
, &daddr6
, sizeof(daddr6
));