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>
36 #ifndef SO_TIMESTAMPING
37 # define SO_TIMESTAMPING 37
38 # define SCM_TIMESTAMPING SO_TIMESTAMPING
41 #ifndef SO_TIMESTAMPNS
42 # define SO_TIMESTAMPNS 35
46 # define SIOCGSTAMPNS 0x8907
50 # define SIOCSHWTSTAMP 0x89b0
53 static void usage(const char *error)
56 printf("invalid option: %s\n", error);
57 printf("timestamping interface option*\n\n"
59 " IP_MULTICAST_LOOP - looping outgoing multicasts\n"
60 " SO_TIMESTAMP - normal software time stamping, ms resolution\n"
61 " SO_TIMESTAMPNS - more accurate software time stamping\n"
62 " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
63 " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
64 " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
65 " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
66 " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
67 " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
68 " SIOCGSTAMP - check last socket time stamp\n"
69 " SIOCGSTAMPNS - more accurate socket time stamp\n");
73 static void bail(const char *error)
75 printf("%s: %s\n", error, strerror(errno));
79 static const unsigned char sync[] = {
80 0x00, 0x01, 0x00, 0x01,
81 0x5f, 0x44, 0x46, 0x4c,
82 0x54, 0x00, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x00,
89 0x02, 0x03, 0x04, 0x05,
91 0x00, 0x01, 0x00, 0x37,
92 0x00, 0x00, 0x00, 0x08,
93 0x00, 0x00, 0x00, 0x00,
94 0x49, 0x05, 0xcd, 0x01,
95 0x29, 0xb1, 0x8d, 0xb0,
96 0x00, 0x00, 0x00, 0x00,
101 0x02, 0x03, 0x04, 0x05,
103 0x00, 0x00, 0x00, 0x37,
104 0x00, 0x00, 0x00, 0x04,
105 0x44, 0x46, 0x4c, 0x54,
106 0x00, 0x00, 0xf0, 0x60,
107 0x00, 0x01, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x01,
109 0x00, 0x00, 0xf0, 0x60,
110 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x04,
112 0x44, 0x46, 0x4c, 0x54,
117 0x02, 0x03, 0x04, 0x05,
119 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00
125 static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
130 res = sendto(sock, sync, sizeof(sync), 0,
132 gettimeofday(&now, 0);
134 printf("%s: %s\n", "send", strerror(errno));
136 printf("%ld.%06ld: sent %d bytes\n",
137 (long)now.tv_sec, (long)now.tv_usec,
141 static void printpacket(struct msghdr *msg, int res,
143 int sock, int recvmsg_flags,
144 int siocgstamp, int siocgstampns)
146 struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
147 struct cmsghdr *cmsg;
152 gettimeofday(&now, 0);
154 printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
155 (long)now.tv_sec, (long)now.tv_usec,
156 (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
158 inet_ntoa(from_addr->sin_addr),
159 msg->msg_controllen);
160 for (cmsg = CMSG_FIRSTHDR(msg);
162 cmsg = CMSG_NXTHDR(msg, cmsg)) {
163 printf(" cmsg len %zu: ", cmsg->cmsg_len);
164 switch (cmsg->cmsg_level) {
166 printf("SOL_SOCKET ");
167 switch (cmsg->cmsg_type) {
169 struct timeval *stamp =
170 (struct timeval *)CMSG_DATA(cmsg);
171 printf("SO_TIMESTAMP %ld.%06ld",
173 (long)stamp->tv_usec);
176 case SO_TIMESTAMPNS: {
177 struct timespec *stamp =
178 (struct timespec *)CMSG_DATA(cmsg);
179 printf("SO_TIMESTAMPNS %ld.%09ld",
181 (long)stamp->tv_nsec);
184 case SO_TIMESTAMPING: {
185 struct timespec *stamp =
186 (struct timespec *)CMSG_DATA(cmsg);
187 printf("SO_TIMESTAMPING ");
188 printf("SW %ld.%09ld ",
190 (long)stamp->tv_nsec);
192 /* skip deprecated HW transformed */
194 printf("HW raw %ld.%09ld",
196 (long)stamp->tv_nsec);
200 printf("type %d", cmsg->cmsg_type);
205 printf("IPPROTO_IP ");
206 switch (cmsg->cmsg_type) {
208 struct sock_extended_err *err =
209 (struct sock_extended_err *)CMSG_DATA(cmsg);
210 printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
211 strerror(err->ee_errno),
213 #ifdef SO_EE_ORIGIN_TIMESTAMPING
214 err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
215 "bounced packet" : "unexpected origin"
217 "probably SO_EE_ORIGIN_TIMESTAMPING"
220 if (res < sizeof(sync))
221 printf(" => truncated data?!");
222 else if (!memcmp(sync, data + res - sizeof(sync),
224 printf(" => GOT OUR DATA BACK (HURRAY!)");
228 struct in_pktinfo *pktinfo =
229 (struct in_pktinfo *)CMSG_DATA(cmsg);
230 printf("IP_PKTINFO interface index %u",
231 pktinfo->ipi_ifindex);
235 printf("type %d", cmsg->cmsg_type);
240 printf("level %d type %d",
249 if (ioctl(sock, SIOCGSTAMP, &tv))
250 printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno));
252 printf("SIOCGSTAMP %ld.%06ld\n",
257 if (ioctl(sock, SIOCGSTAMPNS, &ts))
258 printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
260 printf("SIOCGSTAMPNS %ld.%09ld\n",
266 static void recvpacket(int sock, int recvmsg_flags,
267 int siocgstamp, int siocgstampns)
272 struct sockaddr_in from_addr;
279 memset(&msg, 0, sizeof(msg));
280 msg.msg_iov = &entry;
282 entry.iov_base = data;
283 entry.iov_len = sizeof(data);
284 msg.msg_name = (caddr_t)&from_addr;
285 msg.msg_namelen = sizeof(from_addr);
286 msg.msg_control = &control;
287 msg.msg_controllen = sizeof(control);
289 res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
291 printf("%s %s: %s\n",
293 (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
296 printpacket(&msg, res, data,
298 siocgstamp, siocgstampns);
302 int main(int argc, char **argv)
304 int so_timestamping_flags = 0;
305 int so_timestamp = 0;
306 int so_timestampns = 0;
308 int siocgstampns = 0;
309 int ip_multicast_loop = 0;
315 struct ifreq hwtstamp;
316 struct hwtstamp_config hwconfig, hwconfig_requested;
317 struct sockaddr_in addr;
319 struct in_addr iaddr;
328 for (i = 2; i < argc; i++) {
329 if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
331 else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
333 else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
335 else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
337 else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
338 ip_multicast_loop = 1;
339 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
340 so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
341 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
342 so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
343 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
344 so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
345 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
346 so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
347 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
348 so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
349 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
350 so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
355 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
359 memset(&device, 0, sizeof(device));
360 strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
361 if (ioctl(sock, SIOCGIFADDR, &device) < 0)
362 bail("getting interface IP address");
364 memset(&hwtstamp, 0, sizeof(hwtstamp));
365 strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
366 hwtstamp.ifr_data = (void *)&hwconfig;
367 memset(&hwconfig, 0, sizeof(hwconfig));
369 (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
370 HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
372 (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
373 HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
374 hwconfig_requested = hwconfig;
375 if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
376 if ((errno == EINVAL || errno == ENOTSUP) &&
377 hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
378 hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
379 printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
381 bail("SIOCSHWTSTAMP");
383 printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
384 hwconfig_requested.tx_type, hwconfig.tx_type,
385 hwconfig_requested.rx_filter, hwconfig.rx_filter);
387 /* bind to PTP port */
388 addr.sin_family = AF_INET;
389 addr.sin_addr.s_addr = htonl(INADDR_ANY);
390 addr.sin_port = htons(319 /* PTP event port */);
392 (struct sockaddr *)&addr,
393 sizeof(struct sockaddr_in)) < 0)
396 /* set multicast group for outgoing packets */
397 inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
398 addr.sin_addr = iaddr;
399 imr.imr_multiaddr.s_addr = iaddr.s_addr;
400 imr.imr_interface.s_addr =
401 ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
402 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
403 &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
404 bail("set multicast");
406 /* join multicast group, loop our own packet */
407 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
408 &imr, sizeof(struct ip_mreq)) < 0)
409 bail("join multicast group");
411 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
412 &ip_multicast_loop, sizeof(enabled)) < 0) {
413 bail("loop multicast");
416 /* set socket options for time stamping */
418 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
419 &enabled, sizeof(enabled)) < 0)
420 bail("setsockopt SO_TIMESTAMP");
422 if (so_timestampns &&
423 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
424 &enabled, sizeof(enabled)) < 0)
425 bail("setsockopt SO_TIMESTAMPNS");
427 if (so_timestamping_flags &&
428 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
429 &so_timestamping_flags,
430 sizeof(so_timestamping_flags)) < 0)
431 bail("setsockopt SO_TIMESTAMPING");
433 /* request IP_PKTINFO for debugging purposes */
434 if (setsockopt(sock, SOL_IP, IP_PKTINFO,
435 &enabled, sizeof(enabled)) < 0)
436 printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
438 /* verify socket options */
440 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
441 printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
443 printf("SO_TIMESTAMP %d\n", val);
445 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
446 printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
449 printf("SO_TIMESTAMPNS %d\n", val);
451 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
452 printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
455 printf("SO_TIMESTAMPING %d\n", val);
456 if (val != so_timestamping_flags)
457 printf(" not the expected value %d\n",
458 so_timestamping_flags);
461 /* send packets forever every five seconds */
462 gettimeofday(&next, 0);
463 next.tv_sec = (next.tv_sec + 1) / 5 * 5;
467 struct timeval delta;
470 fd_set readfs, errorfs;
472 gettimeofday(&now, 0);
473 delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
474 (long)(next.tv_usec - now.tv_usec);
476 /* continue waiting for timeout or data */
477 delta.tv_sec = delta_us / 1000000;
478 delta.tv_usec = delta_us % 1000000;
482 FD_SET(sock, &readfs);
483 FD_SET(sock, &errorfs);
484 printf("%ld.%06ld: select %ldus\n",
485 (long)now.tv_sec, (long)now.tv_usec,
487 res = select(sock + 1, &readfs, 0, &errorfs, &delta);
488 gettimeofday(&now, 0);
489 printf("%ld.%06ld: select returned: %d, %s\n",
490 (long)now.tv_sec, (long)now.tv_usec,
492 res < 0 ? strerror(errno) : "success");
494 if (FD_ISSET(sock, &readfs))
495 printf("ready for reading\n");
496 if (FD_ISSET(sock, &errorfs))
497 printf("has error\n");
501 recvpacket(sock, MSG_ERRQUEUE,
506 /* write one packet */
508 (struct sockaddr *)&addr,