1 // SPDX-License-Identifier: GPL-2.0-only
3 * This program demonstrates how the various time stamping features in
4 * the Linux kernel work. It emulates the behavior of a PTP
5 * implementation in stand-alone master mode by sending PTPv1 Sync
6 * multicasts once every second. It looks for similar packets, but
7 * beyond that doesn't actually implement PTP.
9 * Outgoing packets are time stamped with SO_TIMESTAMPING with or
10 * without hardware support.
12 * Incoming packets are time stamped with SO_TIMESTAMPING with or
13 * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
16 * Copyright (C) 2009 Intel Corporation.
17 * Author: Patrick Ohly <patrick.ohly@intel.com>
26 #include <sys/socket.h>
27 #include <sys/select.h>
28 #include <sys/ioctl.h>
29 #include <arpa/inet.h>
32 #include <asm/types.h>
33 #include <linux/net_tstamp.h>
34 #include <linux/errqueue.h>
35 #include <linux/sockios.h>
37 #ifndef SO_TIMESTAMPING
38 # define SO_TIMESTAMPING 37
39 # define SCM_TIMESTAMPING SO_TIMESTAMPING
42 #ifndef SO_TIMESTAMPNS
43 # define SO_TIMESTAMPNS 35
46 static void usage(const char *error)
49 printf("invalid option: %s\n", error);
50 printf("timestamping <interface> [bind_phc_index] [option]*\n\n"
52 " IP_MULTICAST_LOOP - looping outgoing multicasts\n"
53 " SO_TIMESTAMP - normal software time stamping, ms resolution\n"
54 " SO_TIMESTAMPNS - more accurate software time stamping\n"
55 " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
56 " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
57 " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
58 " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
59 " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
60 " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
61 " SOF_TIMESTAMPING_BIND_PHC - request to bind a PHC of PTP vclock\n"
62 " SIOCGSTAMP - check last socket time stamp\n"
63 " SIOCGSTAMPNS - more accurate socket time stamp\n"
64 " PTPV2 - use PTPv2 messages\n");
68 static void bail(const char *error)
70 printf("%s: %s\n", error, strerror(errno));
74 static const unsigned char sync[] = {
75 0x00, 0x01, 0x00, 0x01,
76 0x5f, 0x44, 0x46, 0x4c,
77 0x54, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00,
84 0x02, 0x03, 0x04, 0x05,
86 0x00, 0x01, 0x00, 0x37,
87 0x00, 0x00, 0x00, 0x08,
88 0x00, 0x00, 0x00, 0x00,
89 0x49, 0x05, 0xcd, 0x01,
90 0x29, 0xb1, 0x8d, 0xb0,
91 0x00, 0x00, 0x00, 0x00,
96 0x02, 0x03, 0x04, 0x05,
98 0x00, 0x00, 0x00, 0x37,
99 0x00, 0x00, 0x00, 0x04,
100 0x44, 0x46, 0x4c, 0x54,
101 0x00, 0x00, 0xf0, 0x60,
102 0x00, 0x01, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x01,
104 0x00, 0x00, 0xf0, 0x60,
105 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x04,
107 0x44, 0x46, 0x4c, 0x54,
112 0x02, 0x03, 0x04, 0x05,
114 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00
120 static const unsigned char sync_v2[] = {
121 0x00, 0x02, 0x00, 0x2C,
122 0x00, 0x00, 0x02, 0x00,
123 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0xFF,
127 0xFE, 0x00, 0x00, 0x00,
128 0x00, 0x01, 0x00, 0x01,
129 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00,
134 static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len, int ptpv2)
136 size_t sync_len = ptpv2 ? sizeof(sync_v2) : sizeof(sync);
137 const void *sync_p = ptpv2 ? sync_v2 : sync;
141 res = sendto(sock, sync_p, sync_len, 0, addr, addr_len);
142 gettimeofday(&now, 0);
144 printf("%s: %s\n", "send", strerror(errno));
146 printf("%ld.%06ld: sent %d bytes\n",
147 (long)now.tv_sec, (long)now.tv_usec,
151 static void printpacket(struct msghdr *msg, int res,
153 int sock, int recvmsg_flags,
154 int siocgstamp, int siocgstampns, int ptpv2)
156 struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
157 size_t sync_len = ptpv2 ? sizeof(sync_v2) : sizeof(sync);
158 const void *sync_p = ptpv2 ? sync_v2 : sync;
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"
233 printf(" => truncated data?!");
234 else if (!memcmp(sync_p, data + res - sync_len, sync_len))
235 printf(" => GOT OUR DATA BACK (HURRAY!)");
239 struct in_pktinfo *pktinfo =
240 (struct in_pktinfo *)CMSG_DATA(cmsg);
241 printf("IP_PKTINFO interface index %u",
242 pktinfo->ipi_ifindex);
246 printf("type %d", cmsg->cmsg_type);
251 printf("level %d type %d",
260 if (ioctl(sock, SIOCGSTAMP, &tv))
261 printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno));
263 printf("SIOCGSTAMP %ld.%06ld\n",
268 if (ioctl(sock, SIOCGSTAMPNS, &ts))
269 printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
271 printf("SIOCGSTAMPNS %ld.%09ld\n",
277 static void recvpacket(int sock, int recvmsg_flags,
278 int siocgstamp, int siocgstampns, int ptpv2)
283 struct sockaddr_in from_addr;
290 memset(&msg, 0, sizeof(msg));
291 msg.msg_iov = &entry;
293 entry.iov_base = data;
294 entry.iov_len = sizeof(data);
295 msg.msg_name = (caddr_t)&from_addr;
296 msg.msg_namelen = sizeof(from_addr);
297 msg.msg_control = &control;
298 msg.msg_controllen = sizeof(control);
300 res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
302 printf("%s %s: %s\n",
304 (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
307 printpacket(&msg, res, data,
309 siocgstamp, siocgstampns, ptpv2);
313 int main(int argc, char **argv)
315 int so_timestamp = 0;
316 int so_timestampns = 0;
318 int siocgstampns = 0;
319 int ip_multicast_loop = 0;
326 struct ifreq hwtstamp;
327 struct hwtstamp_config hwconfig, hwconfig_requested;
328 struct so_timestamping so_timestamping_get = { 0, 0 };
329 struct so_timestamping so_timestamping = { 0, 0 };
330 struct sockaddr_in addr;
332 struct in_addr iaddr;
341 if_len = strlen(interface);
342 if (if_len >= IFNAMSIZ) {
343 printf("interface name exceeds IFNAMSIZ\n");
347 if (argc >= 3 && sscanf(argv[2], "%d", &so_timestamping.bind_phc) == 1)
352 for (i = val; i < argc; i++) {
353 if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
355 else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
357 else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
359 else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
361 else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
362 ip_multicast_loop = 1;
363 else if (!strcasecmp(argv[i], "PTPV2"))
365 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
366 so_timestamping.flags |= SOF_TIMESTAMPING_TX_HARDWARE;
367 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
368 so_timestamping.flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
369 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
370 so_timestamping.flags |= SOF_TIMESTAMPING_RX_HARDWARE;
371 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
372 so_timestamping.flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
373 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
374 so_timestamping.flags |= SOF_TIMESTAMPING_SOFTWARE;
375 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
376 so_timestamping.flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
377 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_BIND_PHC"))
378 so_timestamping.flags |= SOF_TIMESTAMPING_BIND_PHC;
383 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
387 memset(&device, 0, sizeof(device));
388 memcpy(device.ifr_name, interface, if_len + 1);
389 if (ioctl(sock, SIOCGIFADDR, &device) < 0)
390 bail("getting interface IP address");
392 memset(&hwtstamp, 0, sizeof(hwtstamp));
393 memcpy(hwtstamp.ifr_name, interface, if_len + 1);
394 hwtstamp.ifr_data = (void *)&hwconfig;
395 memset(&hwconfig, 0, sizeof(hwconfig));
397 (so_timestamping.flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
398 HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
400 (so_timestamping.flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
401 ptpv2 ? HWTSTAMP_FILTER_PTP_V2_L4_SYNC :
402 HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
403 hwconfig_requested = hwconfig;
404 if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
405 if ((errno == EINVAL || errno == ENOTSUP) &&
406 hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
407 hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
408 printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
410 bail("SIOCSHWTSTAMP");
412 printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
413 hwconfig_requested.tx_type, hwconfig.tx_type,
414 hwconfig_requested.rx_filter, hwconfig.rx_filter);
416 /* bind to PTP port */
417 addr.sin_family = AF_INET;
418 addr.sin_addr.s_addr = htonl(INADDR_ANY);
419 addr.sin_port = htons(319 /* PTP event port */);
421 (struct sockaddr *)&addr,
422 sizeof(struct sockaddr_in)) < 0)
425 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface, if_len))
428 /* set multicast group for outgoing packets */
429 inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
430 addr.sin_addr = iaddr;
431 imr.imr_multiaddr.s_addr = iaddr.s_addr;
432 imr.imr_interface.s_addr =
433 ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
434 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
435 &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
436 bail("set multicast");
438 /* join multicast group, loop our own packet */
439 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
440 &imr, sizeof(struct ip_mreq)) < 0)
441 bail("join multicast group");
443 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
444 &ip_multicast_loop, sizeof(enabled)) < 0) {
445 bail("loop multicast");
448 /* set socket options for time stamping */
450 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
451 &enabled, sizeof(enabled)) < 0)
452 bail("setsockopt SO_TIMESTAMP");
454 if (so_timestampns &&
455 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
456 &enabled, sizeof(enabled)) < 0)
457 bail("setsockopt SO_TIMESTAMPNS");
459 if (so_timestamping.flags &&
460 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping,
461 sizeof(so_timestamping)) < 0)
462 bail("setsockopt SO_TIMESTAMPING");
464 /* request IP_PKTINFO for debugging purposes */
465 if (setsockopt(sock, SOL_IP, IP_PKTINFO,
466 &enabled, sizeof(enabled)) < 0)
467 printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
469 /* verify socket options */
471 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
472 printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
474 printf("SO_TIMESTAMP %d\n", val);
476 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
477 printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
480 printf("SO_TIMESTAMPNS %d\n", val);
482 len = sizeof(so_timestamping_get);
483 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &so_timestamping_get,
485 printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
488 printf("SO_TIMESTAMPING flags %d, bind phc %d\n",
489 so_timestamping_get.flags, so_timestamping_get.bind_phc);
490 if (so_timestamping_get.flags != so_timestamping.flags ||
491 so_timestamping_get.bind_phc != so_timestamping.bind_phc)
492 printf(" not expected, flags %d, bind phc %d\n",
493 so_timestamping.flags, so_timestamping.bind_phc);
496 /* send packets forever every five seconds */
497 gettimeofday(&next, 0);
498 next.tv_sec = (next.tv_sec + 1) / 5 * 5;
502 struct timeval delta;
505 fd_set readfs, errorfs;
507 gettimeofday(&now, 0);
508 delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
509 (long)(next.tv_usec - now.tv_usec);
511 /* continue waiting for timeout or data */
512 delta.tv_sec = delta_us / 1000000;
513 delta.tv_usec = delta_us % 1000000;
517 FD_SET(sock, &readfs);
518 FD_SET(sock, &errorfs);
519 printf("%ld.%06ld: select %ldus\n",
520 (long)now.tv_sec, (long)now.tv_usec,
522 res = select(sock + 1, &readfs, 0, &errorfs, &delta);
523 gettimeofday(&now, 0);
524 printf("%ld.%06ld: select returned: %d, %s\n",
525 (long)now.tv_sec, (long)now.tv_usec,
527 res < 0 ? strerror(errno) : "success");
529 if (FD_ISSET(sock, &readfs))
530 printf("ready for reading\n");
531 if (FD_ISSET(sock, &errorfs))
532 printf("has error\n");
535 siocgstampns, ptpv2);
536 recvpacket(sock, MSG_ERRQUEUE,
538 siocgstampns, ptpv2);
541 /* write one packet */
543 (struct sockaddr *)&addr,
544 sizeof(addr), ptpv2);