2 * This program demonstrates how the various time stamping features in
3 * the Linux kernel work. It emulates the behavior of a PTP
4 * implementation in stand-alone master mode by sending PTPv1 Sync
5 * multicasts once every second. It looks for similar packets, but
6 * beyond that doesn't actually implement PTP.
8 * Outgoing packets are time stamped with SO_TIMESTAMPING with or
9 * without hardware support.
11 * Incoming packets are time stamped with SO_TIMESTAMPING with or
12 * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
15 * Copyright (C) 2009 Intel Corporation.
16 * Author: Patrick Ohly <patrick.ohly@intel.com>
18 * This program is free software; you can redistribute it and/or modify it
19 * under the terms and conditions of the GNU General Public License,
20 * version 2, as published by the Free Software Foundation.
22 * This program is distributed in the hope it will be useful, but WITHOUT
23 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
27 * You should have received a copy of the GNU General Public License along with
28 * this program; if not, write to the Free Software Foundation, Inc.,
29 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
38 #include <sys/socket.h>
39 #include <sys/select.h>
40 #include <sys/ioctl.h>
41 #include <arpa/inet.h>
44 #include <asm/types.h>
45 #include <linux/net_tstamp.h>
46 #include <linux/errqueue.h>
48 #ifndef SO_TIMESTAMPING
49 # define SO_TIMESTAMPING 37
50 # define SCM_TIMESTAMPING SO_TIMESTAMPING
53 #ifndef SO_TIMESTAMPNS
54 # define SO_TIMESTAMPNS 35
58 # define SIOCGSTAMPNS 0x8907
62 # define SIOCSHWTSTAMP 0x89b0
65 static void usage(const char *error
)
68 printf("invalid option: %s\n", error
);
69 printf("timestamping interface option*\n\n"
71 " IP_MULTICAST_LOOP - looping outgoing multicasts\n"
72 " SO_TIMESTAMP - normal software time stamping, ms resolution\n"
73 " SO_TIMESTAMPNS - more accurate software time stamping\n"
74 " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
75 " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
76 " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
77 " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
78 " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
79 " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
80 " SIOCGSTAMP - check last socket time stamp\n"
81 " SIOCGSTAMPNS - more accurate socket time stamp\n");
85 static void bail(const char *error
)
87 printf("%s: %s\n", error
, strerror(errno
));
91 static const unsigned char sync
[] = {
92 0x00, 0x01, 0x00, 0x01,
93 0x5f, 0x44, 0x46, 0x4c,
94 0x54, 0x00, 0x00, 0x00,
95 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00,
101 0x02, 0x03, 0x04, 0x05,
103 0x00, 0x01, 0x00, 0x37,
104 0x00, 0x00, 0x00, 0x08,
105 0x00, 0x00, 0x00, 0x00,
106 0x49, 0x05, 0xcd, 0x01,
107 0x29, 0xb1, 0x8d, 0xb0,
108 0x00, 0x00, 0x00, 0x00,
113 0x02, 0x03, 0x04, 0x05,
115 0x00, 0x00, 0x00, 0x37,
116 0x00, 0x00, 0x00, 0x04,
117 0x44, 0x46, 0x4c, 0x54,
118 0x00, 0x00, 0xf0, 0x60,
119 0x00, 0x01, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x01,
121 0x00, 0x00, 0xf0, 0x60,
122 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x04,
124 0x44, 0x46, 0x4c, 0x54,
129 0x02, 0x03, 0x04, 0x05,
131 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00
137 static void sendpacket(int sock
, struct sockaddr
*addr
, socklen_t addr_len
)
142 res
= sendto(sock
, sync
, sizeof(sync
), 0,
144 gettimeofday(&now
, 0);
146 printf("%s: %s\n", "send", strerror(errno
));
148 printf("%ld.%06ld: sent %d bytes\n",
149 (long)now
.tv_sec
, (long)now
.tv_usec
,
153 static void printpacket(struct msghdr
*msg
, int res
,
155 int sock
, int recvmsg_flags
,
156 int siocgstamp
, int siocgstampns
)
158 struct sockaddr_in
*from_addr
= (struct sockaddr_in
*)msg
->msg_name
;
159 struct cmsghdr
*cmsg
;
164 gettimeofday(&now
, 0);
166 printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
167 (long)now
.tv_sec
, (long)now
.tv_usec
,
168 (recvmsg_flags
& MSG_ERRQUEUE
) ? "error" : "regular",
170 inet_ntoa(from_addr
->sin_addr
),
171 msg
->msg_controllen
);
172 for (cmsg
= CMSG_FIRSTHDR(msg
);
174 cmsg
= CMSG_NXTHDR(msg
, cmsg
)) {
175 printf(" cmsg len %zu: ", cmsg
->cmsg_len
);
176 switch (cmsg
->cmsg_level
) {
178 printf("SOL_SOCKET ");
179 switch (cmsg
->cmsg_type
) {
181 struct timeval
*stamp
=
182 (struct timeval
*)CMSG_DATA(cmsg
);
183 printf("SO_TIMESTAMP %ld.%06ld",
185 (long)stamp
->tv_usec
);
188 case SO_TIMESTAMPNS
: {
189 struct timespec
*stamp
=
190 (struct timespec
*)CMSG_DATA(cmsg
);
191 printf("SO_TIMESTAMPNS %ld.%09ld",
193 (long)stamp
->tv_nsec
);
196 case SO_TIMESTAMPING
: {
197 struct timespec
*stamp
=
198 (struct timespec
*)CMSG_DATA(cmsg
);
199 printf("SO_TIMESTAMPING ");
200 printf("SW %ld.%09ld ",
202 (long)stamp
->tv_nsec
);
204 /* skip deprecated HW transformed */
206 printf("HW raw %ld.%09ld",
208 (long)stamp
->tv_nsec
);
212 printf("type %d", cmsg
->cmsg_type
);
217 printf("IPPROTO_IP ");
218 switch (cmsg
->cmsg_type
) {
220 struct sock_extended_err
*err
=
221 (struct sock_extended_err
*)CMSG_DATA(cmsg
);
222 printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
223 strerror(err
->ee_errno
),
225 #ifdef SO_EE_ORIGIN_TIMESTAMPING
226 err
->ee_origin
== SO_EE_ORIGIN_TIMESTAMPING
?
227 "bounced packet" : "unexpected origin"
229 "probably SO_EE_ORIGIN_TIMESTAMPING"
232 if (res
< sizeof(sync
))
233 printf(" => truncated data?!");
234 else if (!memcmp(sync
, data
+ res
- sizeof(sync
),
236 printf(" => GOT OUR DATA BACK (HURRAY!)");
240 struct in_pktinfo
*pktinfo
=
241 (struct in_pktinfo
*)CMSG_DATA(cmsg
);
242 printf("IP_PKTINFO interface index %u",
243 pktinfo
->ipi_ifindex
);
247 printf("type %d", cmsg
->cmsg_type
);
252 printf("level %d type %d",
261 if (ioctl(sock
, SIOCGSTAMP
, &tv
))
262 printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno
));
264 printf("SIOCGSTAMP %ld.%06ld\n",
269 if (ioctl(sock
, SIOCGSTAMPNS
, &ts
))
270 printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno
));
272 printf("SIOCGSTAMPNS %ld.%09ld\n",
278 static void recvpacket(int sock
, int recvmsg_flags
,
279 int siocgstamp
, int siocgstampns
)
284 struct sockaddr_in from_addr
;
291 memset(&msg
, 0, sizeof(msg
));
292 msg
.msg_iov
= &entry
;
294 entry
.iov_base
= data
;
295 entry
.iov_len
= sizeof(data
);
296 msg
.msg_name
= (caddr_t
)&from_addr
;
297 msg
.msg_namelen
= sizeof(from_addr
);
298 msg
.msg_control
= &control
;
299 msg
.msg_controllen
= sizeof(control
);
301 res
= recvmsg(sock
, &msg
, recvmsg_flags
|MSG_DONTWAIT
);
303 printf("%s %s: %s\n",
305 (recvmsg_flags
& MSG_ERRQUEUE
) ? "error" : "regular",
308 printpacket(&msg
, res
, data
,
310 siocgstamp
, siocgstampns
);
314 int main(int argc
, char **argv
)
316 int so_timestamping_flags
= 0;
317 int so_timestamp
= 0;
318 int so_timestampns
= 0;
320 int siocgstampns
= 0;
321 int ip_multicast_loop
= 0;
327 struct ifreq hwtstamp
;
328 struct hwtstamp_config hwconfig
, hwconfig_requested
;
329 struct sockaddr_in addr
;
331 struct in_addr iaddr
;
340 if_len
= strlen(interface
);
341 if (if_len
>= IFNAMSIZ
) {
342 printf("interface name exceeds IFNAMSIZ\n");
346 for (i
= 2; i
< argc
; i
++) {
347 if (!strcasecmp(argv
[i
], "SO_TIMESTAMP"))
349 else if (!strcasecmp(argv
[i
], "SO_TIMESTAMPNS"))
351 else if (!strcasecmp(argv
[i
], "SIOCGSTAMP"))
353 else if (!strcasecmp(argv
[i
], "SIOCGSTAMPNS"))
355 else if (!strcasecmp(argv
[i
], "IP_MULTICAST_LOOP"))
356 ip_multicast_loop
= 1;
357 else if (!strcasecmp(argv
[i
], "SOF_TIMESTAMPING_TX_HARDWARE"))
358 so_timestamping_flags
|= SOF_TIMESTAMPING_TX_HARDWARE
;
359 else if (!strcasecmp(argv
[i
], "SOF_TIMESTAMPING_TX_SOFTWARE"))
360 so_timestamping_flags
|= SOF_TIMESTAMPING_TX_SOFTWARE
;
361 else if (!strcasecmp(argv
[i
], "SOF_TIMESTAMPING_RX_HARDWARE"))
362 so_timestamping_flags
|= SOF_TIMESTAMPING_RX_HARDWARE
;
363 else if (!strcasecmp(argv
[i
], "SOF_TIMESTAMPING_RX_SOFTWARE"))
364 so_timestamping_flags
|= SOF_TIMESTAMPING_RX_SOFTWARE
;
365 else if (!strcasecmp(argv
[i
], "SOF_TIMESTAMPING_SOFTWARE"))
366 so_timestamping_flags
|= SOF_TIMESTAMPING_SOFTWARE
;
367 else if (!strcasecmp(argv
[i
], "SOF_TIMESTAMPING_RAW_HARDWARE"))
368 so_timestamping_flags
|= SOF_TIMESTAMPING_RAW_HARDWARE
;
373 sock
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
377 memset(&device
, 0, sizeof(device
));
378 memcpy(device
.ifr_name
, interface
, if_len
+ 1);
379 if (ioctl(sock
, SIOCGIFADDR
, &device
) < 0)
380 bail("getting interface IP address");
382 memset(&hwtstamp
, 0, sizeof(hwtstamp
));
383 memcpy(hwtstamp
.ifr_name
, interface
, if_len
+ 1);
384 hwtstamp
.ifr_data
= (void *)&hwconfig
;
385 memset(&hwconfig
, 0, sizeof(hwconfig
));
387 (so_timestamping_flags
& SOF_TIMESTAMPING_TX_HARDWARE
) ?
388 HWTSTAMP_TX_ON
: HWTSTAMP_TX_OFF
;
390 (so_timestamping_flags
& SOF_TIMESTAMPING_RX_HARDWARE
) ?
391 HWTSTAMP_FILTER_PTP_V1_L4_SYNC
: HWTSTAMP_FILTER_NONE
;
392 hwconfig_requested
= hwconfig
;
393 if (ioctl(sock
, SIOCSHWTSTAMP
, &hwtstamp
) < 0) {
394 if ((errno
== EINVAL
|| errno
== ENOTSUP
) &&
395 hwconfig_requested
.tx_type
== HWTSTAMP_TX_OFF
&&
396 hwconfig_requested
.rx_filter
== HWTSTAMP_FILTER_NONE
)
397 printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
399 bail("SIOCSHWTSTAMP");
401 printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
402 hwconfig_requested
.tx_type
, hwconfig
.tx_type
,
403 hwconfig_requested
.rx_filter
, hwconfig
.rx_filter
);
405 /* bind to PTP port */
406 addr
.sin_family
= AF_INET
;
407 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
408 addr
.sin_port
= htons(319 /* PTP event port */);
410 (struct sockaddr
*)&addr
,
411 sizeof(struct sockaddr_in
)) < 0)
414 /* set multicast group for outgoing packets */
415 inet_aton("224.0.1.130", &iaddr
); /* alternate PTP domain 1 */
416 addr
.sin_addr
= iaddr
;
417 imr
.imr_multiaddr
.s_addr
= iaddr
.s_addr
;
418 imr
.imr_interface
.s_addr
=
419 ((struct sockaddr_in
*)&device
.ifr_addr
)->sin_addr
.s_addr
;
420 if (setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_IF
,
421 &imr
.imr_interface
.s_addr
, sizeof(struct in_addr
)) < 0)
422 bail("set multicast");
424 /* join multicast group, loop our own packet */
425 if (setsockopt(sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
426 &imr
, sizeof(struct ip_mreq
)) < 0)
427 bail("join multicast group");
429 if (setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_LOOP
,
430 &ip_multicast_loop
, sizeof(enabled
)) < 0) {
431 bail("loop multicast");
434 /* set socket options for time stamping */
436 setsockopt(sock
, SOL_SOCKET
, SO_TIMESTAMP
,
437 &enabled
, sizeof(enabled
)) < 0)
438 bail("setsockopt SO_TIMESTAMP");
440 if (so_timestampns
&&
441 setsockopt(sock
, SOL_SOCKET
, SO_TIMESTAMPNS
,
442 &enabled
, sizeof(enabled
)) < 0)
443 bail("setsockopt SO_TIMESTAMPNS");
445 if (so_timestamping_flags
&&
446 setsockopt(sock
, SOL_SOCKET
, SO_TIMESTAMPING
,
447 &so_timestamping_flags
,
448 sizeof(so_timestamping_flags
)) < 0)
449 bail("setsockopt SO_TIMESTAMPING");
451 /* request IP_PKTINFO for debugging purposes */
452 if (setsockopt(sock
, SOL_IP
, IP_PKTINFO
,
453 &enabled
, sizeof(enabled
)) < 0)
454 printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno
));
456 /* verify socket options */
458 if (getsockopt(sock
, SOL_SOCKET
, SO_TIMESTAMP
, &val
, &len
) < 0)
459 printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno
));
461 printf("SO_TIMESTAMP %d\n", val
);
463 if (getsockopt(sock
, SOL_SOCKET
, SO_TIMESTAMPNS
, &val
, &len
) < 0)
464 printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
467 printf("SO_TIMESTAMPNS %d\n", val
);
469 if (getsockopt(sock
, SOL_SOCKET
, SO_TIMESTAMPING
, &val
, &len
) < 0) {
470 printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
473 printf("SO_TIMESTAMPING %d\n", val
);
474 if (val
!= so_timestamping_flags
)
475 printf(" not the expected value %d\n",
476 so_timestamping_flags
);
479 /* send packets forever every five seconds */
480 gettimeofday(&next
, 0);
481 next
.tv_sec
= (next
.tv_sec
+ 1) / 5 * 5;
485 struct timeval delta
;
488 fd_set readfs
, errorfs
;
490 gettimeofday(&now
, 0);
491 delta_us
= (long)(next
.tv_sec
- now
.tv_sec
) * 1000000 +
492 (long)(next
.tv_usec
- now
.tv_usec
);
494 /* continue waiting for timeout or data */
495 delta
.tv_sec
= delta_us
/ 1000000;
496 delta
.tv_usec
= delta_us
% 1000000;
500 FD_SET(sock
, &readfs
);
501 FD_SET(sock
, &errorfs
);
502 printf("%ld.%06ld: select %ldus\n",
503 (long)now
.tv_sec
, (long)now
.tv_usec
,
505 res
= select(sock
+ 1, &readfs
, 0, &errorfs
, &delta
);
506 gettimeofday(&now
, 0);
507 printf("%ld.%06ld: select returned: %d, %s\n",
508 (long)now
.tv_sec
, (long)now
.tv_usec
,
510 res
< 0 ? strerror(errno
) : "success");
512 if (FD_ISSET(sock
, &readfs
))
513 printf("ready for reading\n");
514 if (FD_ISSET(sock
, &errorfs
))
515 printf("has error\n");
519 recvpacket(sock
, MSG_ERRQUEUE
,
524 /* write one packet */
526 (struct sockaddr
*)&addr
,