1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
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.
8  *
9  * Outgoing packets are time stamped with SO_TIMESTAMPING with or
10  * without hardware support.
11  *
12  * Incoming packets are time stamped with SO_TIMESTAMPING with or
13  * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
14  * SO_TIMESTAMP[NS].
15  *
16  * Copyright (C) 2009 Intel Corporation.
17  * Author: Patrick Ohly <patrick.ohly@intel.com>
18  */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <string.h>
24 
25 #include <sys/time.h>
26 #include <sys/socket.h>
27 #include <sys/select.h>
28 #include <sys/ioctl.h>
29 #include <arpa/inet.h>
30 #include <net/if.h>
31 
32 #include <asm/types.h>
33 #include <linux/net_tstamp.h>
34 #include <linux/errqueue.h>
35 #include <linux/sockios.h>
36 
37 #ifndef SO_TIMESTAMPING
38 # define SO_TIMESTAMPING         37
39 # define SCM_TIMESTAMPING        SO_TIMESTAMPING
40 #endif
41 
42 #ifndef SO_TIMESTAMPNS
43 # define SO_TIMESTAMPNS 35
44 #endif
45 
46 static void usage(const char *error)
47 {
48 	if (error)
49 		printf("invalid option: %s\n", error);
50 	printf("timestamping interface option*\n\n"
51 	       "Options:\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 	       "  SIOCGSTAMP - check last socket time stamp\n"
62 	       "  SIOCGSTAMPNS - more accurate socket time stamp\n");
63 	exit(1);
64 }
65 
66 static void bail(const char *error)
67 {
68 	printf("%s: %s\n", error, strerror(errno));
69 	exit(1);
70 }
71 
72 static const unsigned char sync[] = {
73 	0x00, 0x01, 0x00, 0x01,
74 	0x5f, 0x44, 0x46, 0x4c,
75 	0x54, 0x00, 0x00, 0x00,
76 	0x00, 0x00, 0x00, 0x00,
77 	0x00, 0x00, 0x00, 0x00,
78 	0x01, 0x01,
79 
80 	/* fake uuid */
81 	0x00, 0x01,
82 	0x02, 0x03, 0x04, 0x05,
83 
84 	0x00, 0x01, 0x00, 0x37,
85 	0x00, 0x00, 0x00, 0x08,
86 	0x00, 0x00, 0x00, 0x00,
87 	0x49, 0x05, 0xcd, 0x01,
88 	0x29, 0xb1, 0x8d, 0xb0,
89 	0x00, 0x00, 0x00, 0x00,
90 	0x00, 0x01,
91 
92 	/* fake uuid */
93 	0x00, 0x01,
94 	0x02, 0x03, 0x04, 0x05,
95 
96 	0x00, 0x00, 0x00, 0x37,
97 	0x00, 0x00, 0x00, 0x04,
98 	0x44, 0x46, 0x4c, 0x54,
99 	0x00, 0x00, 0xf0, 0x60,
100 	0x00, 0x01, 0x00, 0x00,
101 	0x00, 0x00, 0x00, 0x01,
102 	0x00, 0x00, 0xf0, 0x60,
103 	0x00, 0x00, 0x00, 0x00,
104 	0x00, 0x00, 0x00, 0x04,
105 	0x44, 0x46, 0x4c, 0x54,
106 	0x00, 0x01,
107 
108 	/* fake uuid */
109 	0x00, 0x01,
110 	0x02, 0x03, 0x04, 0x05,
111 
112 	0x00, 0x00, 0x00, 0x00,
113 	0x00, 0x00, 0x00, 0x00,
114 	0x00, 0x00, 0x00, 0x00,
115 	0x00, 0x00, 0x00, 0x00
116 };
117 
118 static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
119 {
120 	struct timeval now;
121 	int res;
122 
123 	res = sendto(sock, sync, sizeof(sync), 0,
124 		addr, addr_len);
125 	gettimeofday(&now, 0);
126 	if (res < 0)
127 		printf("%s: %s\n", "send", strerror(errno));
128 	else
129 		printf("%ld.%06ld: sent %d bytes\n",
130 		       (long)now.tv_sec, (long)now.tv_usec,
131 		       res);
132 }
133 
134 static void printpacket(struct msghdr *msg, int res,
135 			char *data,
136 			int sock, int recvmsg_flags,
137 			int siocgstamp, int siocgstampns)
138 {
139 	struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
140 	struct cmsghdr *cmsg;
141 	struct timeval tv;
142 	struct timespec ts;
143 	struct timeval now;
144 
145 	gettimeofday(&now, 0);
146 
147 	printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
148 	       (long)now.tv_sec, (long)now.tv_usec,
149 	       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
150 	       res,
151 	       inet_ntoa(from_addr->sin_addr),
152 	       msg->msg_controllen);
153 	for (cmsg = CMSG_FIRSTHDR(msg);
154 	     cmsg;
155 	     cmsg = CMSG_NXTHDR(msg, cmsg)) {
156 		printf("   cmsg len %zu: ", cmsg->cmsg_len);
157 		switch (cmsg->cmsg_level) {
158 		case SOL_SOCKET:
159 			printf("SOL_SOCKET ");
160 			switch (cmsg->cmsg_type) {
161 			case SO_TIMESTAMP: {
162 				struct timeval *stamp =
163 					(struct timeval *)CMSG_DATA(cmsg);
164 				printf("SO_TIMESTAMP %ld.%06ld",
165 				       (long)stamp->tv_sec,
166 				       (long)stamp->tv_usec);
167 				break;
168 			}
169 			case SO_TIMESTAMPNS: {
170 				struct timespec *stamp =
171 					(struct timespec *)CMSG_DATA(cmsg);
172 				printf("SO_TIMESTAMPNS %ld.%09ld",
173 				       (long)stamp->tv_sec,
174 				       (long)stamp->tv_nsec);
175 				break;
176 			}
177 			case SO_TIMESTAMPING: {
178 				struct timespec *stamp =
179 					(struct timespec *)CMSG_DATA(cmsg);
180 				printf("SO_TIMESTAMPING ");
181 				printf("SW %ld.%09ld ",
182 				       (long)stamp->tv_sec,
183 				       (long)stamp->tv_nsec);
184 				stamp++;
185 				/* skip deprecated HW transformed */
186 				stamp++;
187 				printf("HW raw %ld.%09ld",
188 				       (long)stamp->tv_sec,
189 				       (long)stamp->tv_nsec);
190 				break;
191 			}
192 			default:
193 				printf("type %d", cmsg->cmsg_type);
194 				break;
195 			}
196 			break;
197 		case IPPROTO_IP:
198 			printf("IPPROTO_IP ");
199 			switch (cmsg->cmsg_type) {
200 			case IP_RECVERR: {
201 				struct sock_extended_err *err =
202 					(struct sock_extended_err *)CMSG_DATA(cmsg);
203 				printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
204 					strerror(err->ee_errno),
205 					err->ee_origin,
206 #ifdef SO_EE_ORIGIN_TIMESTAMPING
207 					err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
208 					"bounced packet" : "unexpected origin"
209 #else
210 					"probably SO_EE_ORIGIN_TIMESTAMPING"
211 #endif
212 					);
213 				if (res < sizeof(sync))
214 					printf(" => truncated data?!");
215 				else if (!memcmp(sync, data + res - sizeof(sync),
216 							sizeof(sync)))
217 					printf(" => GOT OUR DATA BACK (HURRAY!)");
218 				break;
219 			}
220 			case IP_PKTINFO: {
221 				struct in_pktinfo *pktinfo =
222 					(struct in_pktinfo *)CMSG_DATA(cmsg);
223 				printf("IP_PKTINFO interface index %u",
224 					pktinfo->ipi_ifindex);
225 				break;
226 			}
227 			default:
228 				printf("type %d", cmsg->cmsg_type);
229 				break;
230 			}
231 			break;
232 		default:
233 			printf("level %d type %d",
234 				cmsg->cmsg_level,
235 				cmsg->cmsg_type);
236 			break;
237 		}
238 		printf("\n");
239 	}
240 
241 	if (siocgstamp) {
242 		if (ioctl(sock, SIOCGSTAMP, &tv))
243 			printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
244 		else
245 			printf("SIOCGSTAMP %ld.%06ld\n",
246 			       (long)tv.tv_sec,
247 			       (long)tv.tv_usec);
248 	}
249 	if (siocgstampns) {
250 		if (ioctl(sock, SIOCGSTAMPNS, &ts))
251 			printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
252 		else
253 			printf("SIOCGSTAMPNS %ld.%09ld\n",
254 			       (long)ts.tv_sec,
255 			       (long)ts.tv_nsec);
256 	}
257 }
258 
259 static void recvpacket(int sock, int recvmsg_flags,
260 		       int siocgstamp, int siocgstampns)
261 {
262 	char data[256];
263 	struct msghdr msg;
264 	struct iovec entry;
265 	struct sockaddr_in from_addr;
266 	struct {
267 		struct cmsghdr cm;
268 		char control[512];
269 	} control;
270 	int res;
271 
272 	memset(&msg, 0, sizeof(msg));
273 	msg.msg_iov = &entry;
274 	msg.msg_iovlen = 1;
275 	entry.iov_base = data;
276 	entry.iov_len = sizeof(data);
277 	msg.msg_name = (caddr_t)&from_addr;
278 	msg.msg_namelen = sizeof(from_addr);
279 	msg.msg_control = &control;
280 	msg.msg_controllen = sizeof(control);
281 
282 	res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
283 	if (res < 0) {
284 		printf("%s %s: %s\n",
285 		       "recvmsg",
286 		       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
287 		       strerror(errno));
288 	} else {
289 		printpacket(&msg, res, data,
290 			    sock, recvmsg_flags,
291 			    siocgstamp, siocgstampns);
292 	}
293 }
294 
295 int main(int argc, char **argv)
296 {
297 	int so_timestamping_flags = 0;
298 	int so_timestamp = 0;
299 	int so_timestampns = 0;
300 	int siocgstamp = 0;
301 	int siocgstampns = 0;
302 	int ip_multicast_loop = 0;
303 	char *interface;
304 	int i;
305 	int enabled = 1;
306 	int sock;
307 	struct ifreq device;
308 	struct ifreq hwtstamp;
309 	struct hwtstamp_config hwconfig, hwconfig_requested;
310 	struct sockaddr_in addr;
311 	struct ip_mreq imr;
312 	struct in_addr iaddr;
313 	int val;
314 	socklen_t len;
315 	struct timeval next;
316 
317 	if (argc < 2)
318 		usage(0);
319 	interface = argv[1];
320 
321 	for (i = 2; i < argc; i++) {
322 		if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
323 			so_timestamp = 1;
324 		else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
325 			so_timestampns = 1;
326 		else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
327 			siocgstamp = 1;
328 		else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
329 			siocgstampns = 1;
330 		else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
331 			ip_multicast_loop = 1;
332 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
333 			so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
334 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
335 			so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
336 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
337 			so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
338 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
339 			so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
340 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
341 			so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
342 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
343 			so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
344 		else
345 			usage(argv[i]);
346 	}
347 
348 	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
349 	if (sock < 0)
350 		bail("socket");
351 
352 	memset(&device, 0, sizeof(device));
353 	strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
354 	if (ioctl(sock, SIOCGIFADDR, &device) < 0)
355 		bail("getting interface IP address");
356 
357 	memset(&hwtstamp, 0, sizeof(hwtstamp));
358 	strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
359 	hwtstamp.ifr_data = (void *)&hwconfig;
360 	memset(&hwconfig, 0, sizeof(hwconfig));
361 	hwconfig.tx_type =
362 		(so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
363 		HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
364 	hwconfig.rx_filter =
365 		(so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
366 		HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
367 	hwconfig_requested = hwconfig;
368 	if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
369 		if ((errno == EINVAL || errno == ENOTSUP) &&
370 		    hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
371 		    hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
372 			printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
373 		else
374 			bail("SIOCSHWTSTAMP");
375 	}
376 	printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
377 	       hwconfig_requested.tx_type, hwconfig.tx_type,
378 	       hwconfig_requested.rx_filter, hwconfig.rx_filter);
379 
380 	/* bind to PTP port */
381 	addr.sin_family = AF_INET;
382 	addr.sin_addr.s_addr = htonl(INADDR_ANY);
383 	addr.sin_port = htons(319 /* PTP event port */);
384 	if (bind(sock,
385 		 (struct sockaddr *)&addr,
386 		 sizeof(struct sockaddr_in)) < 0)
387 		bail("bind");
388 
389 	/* set multicast group for outgoing packets */
390 	inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
391 	addr.sin_addr = iaddr;
392 	imr.imr_multiaddr.s_addr = iaddr.s_addr;
393 	imr.imr_interface.s_addr =
394 		((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
395 	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
396 		       &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
397 		bail("set multicast");
398 
399 	/* join multicast group, loop our own packet */
400 	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
401 		       &imr, sizeof(struct ip_mreq)) < 0)
402 		bail("join multicast group");
403 
404 	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
405 		       &ip_multicast_loop, sizeof(enabled)) < 0) {
406 		bail("loop multicast");
407 	}
408 
409 	/* set socket options for time stamping */
410 	if (so_timestamp &&
411 		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
412 			   &enabled, sizeof(enabled)) < 0)
413 		bail("setsockopt SO_TIMESTAMP");
414 
415 	if (so_timestampns &&
416 		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
417 			   &enabled, sizeof(enabled)) < 0)
418 		bail("setsockopt SO_TIMESTAMPNS");
419 
420 	if (so_timestamping_flags &&
421 		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
422 			   &so_timestamping_flags,
423 			   sizeof(so_timestamping_flags)) < 0)
424 		bail("setsockopt SO_TIMESTAMPING");
425 
426 	/* request IP_PKTINFO for debugging purposes */
427 	if (setsockopt(sock, SOL_IP, IP_PKTINFO,
428 		       &enabled, sizeof(enabled)) < 0)
429 		printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
430 
431 	/* verify socket options */
432 	len = sizeof(val);
433 	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
434 		printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
435 	else
436 		printf("SO_TIMESTAMP %d\n", val);
437 
438 	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
439 		printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
440 		       strerror(errno));
441 	else
442 		printf("SO_TIMESTAMPNS %d\n", val);
443 
444 	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
445 		printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
446 		       strerror(errno));
447 	} else {
448 		printf("SO_TIMESTAMPING %d\n", val);
449 		if (val != so_timestamping_flags)
450 			printf("   not the expected value %d\n",
451 			       so_timestamping_flags);
452 	}
453 
454 	/* send packets forever every five seconds */
455 	gettimeofday(&next, 0);
456 	next.tv_sec = (next.tv_sec + 1) / 5 * 5;
457 	next.tv_usec = 0;
458 	while (1) {
459 		struct timeval now;
460 		struct timeval delta;
461 		long delta_us;
462 		int res;
463 		fd_set readfs, errorfs;
464 
465 		gettimeofday(&now, 0);
466 		delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
467 			(long)(next.tv_usec - now.tv_usec);
468 		if (delta_us > 0) {
469 			/* continue waiting for timeout or data */
470 			delta.tv_sec = delta_us / 1000000;
471 			delta.tv_usec = delta_us % 1000000;
472 
473 			FD_ZERO(&readfs);
474 			FD_ZERO(&errorfs);
475 			FD_SET(sock, &readfs);
476 			FD_SET(sock, &errorfs);
477 			printf("%ld.%06ld: select %ldus\n",
478 			       (long)now.tv_sec, (long)now.tv_usec,
479 			       delta_us);
480 			res = select(sock + 1, &readfs, 0, &errorfs, &delta);
481 			gettimeofday(&now, 0);
482 			printf("%ld.%06ld: select returned: %d, %s\n",
483 			       (long)now.tv_sec, (long)now.tv_usec,
484 			       res,
485 			       res < 0 ? strerror(errno) : "success");
486 			if (res > 0) {
487 				if (FD_ISSET(sock, &readfs))
488 					printf("ready for reading\n");
489 				if (FD_ISSET(sock, &errorfs))
490 					printf("has error\n");
491 				recvpacket(sock, 0,
492 					   siocgstamp,
493 					   siocgstampns);
494 				recvpacket(sock, MSG_ERRQUEUE,
495 					   siocgstamp,
496 					   siocgstampns);
497 			}
498 		} else {
499 			/* write one packet */
500 			sendpacket(sock,
501 				   (struct sockaddr *)&addr,
502 				   sizeof(addr));
503 			next.tv_sec += 5;
504 			continue;
505 		}
506 	}
507 
508 	return 0;
509 }
510