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