1 /*
2  * ntp_packetstamp.c - grubby platform-dependent details of packet timestamps
3  *
4  * One of our serious platform dependencies (things POSIX doesn't
5  * specify a facility for) is isolated here.
6  */
7 #include "config.h"
8 
9 #ifdef HAVE_SYS_IOCTL_H
10 # include <sys/ioctl.h>
11 #endif
12 
13 #include "ntpd.h"
14 #include "ntp_stdlib.h"
15 #include "timespecops.h"
16 
17 /* We handle 2 flavors of timestamp:
18  * SO_TIMESTAMPNS/SCM_TIMESTAMPNS  Linux
19  * SO_TIMESTAMP/SCM_TIMESTAMP      FreeBSD, NetBSD, OpenBSD, Linux, macOS,
20  *                                 Solaris
21  *
22  * Linux supports both SO_TIMESTAMP and SO_TIMESTAMPNS so it's
23  * important to check for SO_TIMESTAMPNS first to get the better accuracy.
24  *
25  * Note that the if/elif tests are done in several places.
26  * It's important that they all check in the same order to
27  * be consistent in case some systems support more than one.
28  *
29  * If SO_xxx exists, we assume that SCM_xxx does too.
30  * All flavors assume the CMSG_xxx macros exist.
31  *
32  * FreeBSD has SO_BINTIME/SCM_BINTIME
33  *   It has better resolution, but it doesn't work for IPv6
34  *   bintime documentation is at
35  *   http://phk.freebsd.dk/pubs/timecounter.pdf
36  */
37 
38 
39 void
enable_packetstamps(int fd,sockaddr_u * addr)40 enable_packetstamps(
41     int fd,
42     sockaddr_u *	addr
43     )
44 {
45 	const int	on = 1;
46 	static bool	once = false;
47 
48 #if defined (SO_TIMESTAMPNS)
49 	if (!once) {
50 		once = true;
51 		msyslog(LOG_INFO, "INIT: Using SO_TIMESTAMPNS");
52 	}
53 	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS,
54 			       (const void *)&on, sizeof(on)))
55 		msyslog(LOG_DEBUG,
56 			"ERR: setsockopt SO_TIMESTAMPNS on fails on address %s: %s",
57 				socktoa(addr), strerror(errno));
58 	else
59 		DPRINT(4, ("ERR: setsockopt SO_TIMESTAMPNS enabled on fd %d address %s\n",
60 				    fd, socktoa(addr)));
61 #elif defined(SO_TIMESTAMP)
62 	if (!once) {
63 		once = true;
64 		msyslog(LOG_INFO, "INIT: Using SO_TIMESTAMP");
65 	}
66 	if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP,
67 			       (const void*)&on, sizeof(on)))
68 		msyslog(LOG_DEBUG,
69 			"ERR: setsockopt SO_TIMESTAMP on fails on address %s: %s",
70 			socktoa(addr), strerror(errno));
71 	else
72 		DPRINT(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n",
73 			    fd, socktoa(addr)));
74 #else
75 # error "Can't get packet timestamp"
76 #endif
77 }
78 
79 
80 /*
81  * extract timestamps from control message buffer
82  */
83 l_fp
fetch_packetstamp(struct msghdr * msghdr)84 fetch_packetstamp(
85 	struct msghdr *		msghdr
86 	)
87 {
88 	struct cmsghdr *	cmsghdr;
89 #if defined(SO_TIMESTAMPNS)
90 	struct timespec *	tsp;
91 #elif defined(SO_TIMESTAMP)
92 	struct timeval *	tvp;
93 #endif
94 #ifdef ENABLE_FUZZ
95 	unsigned long		ticks;
96 	double			fuzz;
97 	l_fp			lfpfuzz;
98 #endif
99 	l_fp			nts = 0;  /* network time stamp */
100 
101 /* There should be only one cmsg. */
102 	cmsghdr = CMSG_FIRSTHDR(msghdr);
103 	if (NULL == cmsghdr) {
104 		DPRINT(4, ("fetch_timestamp: can't find timestamp\n"));
105 		msyslog(LOG_ERR, "ERR: fetch_timestamp: no msghdrs");
106 		exit(2);
107 		/* return ts;	** Kludge to use time from select. */
108 	}
109 #if defined(SO_TIMESTAMPNS)
110 	if (SCM_TIMESTAMPNS != cmsghdr->cmsg_type) {
111 #elif defined(SO_TIMESTAMP)
112 	if (SCM_TIMESTAMP != cmsghdr->cmsg_type) {
113 #else
114 # error "Can't get packet timestamp"
115 #endif
116 		DPRINT(4,
117                         ("fetch_timestamp: strange control message 0x%x\n",
118 			     (unsigned)cmsghdr->cmsg_type));
119 		msyslog(LOG_ERR,
120 			"ERR: fetch_timestamp: strange control message 0x%x",
121                              (unsigned)cmsghdr->cmsg_type);
122 		exit(2);
123 		/* Could loop and skip strange types. */
124 		/* cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr); */
125 	}
126 
127 /* cmsghdr now points to a timestamp slot */
128 
129 #if defined(SO_TIMESTAMPNS)
130 	tsp = (struct timespec *)CMSG_DATA(cmsghdr);
131 #ifdef ENABLE_FUZZ
132 	if (sys_tick > measured_tick && sys_tick > S_PER_NS) {
133 	    ticks = (unsigned long) ((tsp->tv_nsec * S_PER_NS) / sys_tick);
134 	    tsp->tv_nsec = (long) (ticks * NS_PER_S * sys_tick);
135 	}
136 #endif
137 	DPRINT(4, ("fetch_timestamp: system nsec network time stamp: %ld.%09ld\n",
138 		tsp->tv_sec, tsp->tv_nsec));
139 	nts = tspec_stamp_to_lfp(*tsp);
140 #elif defined(SO_TIMESTAMP)
141 	tvp = (struct timeval *)CMSG_DATA(cmsghdr);
142 #ifdef ENABLE_FUZZ
143 	if (sys_tick > measured_tick && sys_tick > S_PER_NS) {
144 	    ticks = (unsigned long) ((tvp->tv_usec * S_PER_NS) / sys_tick);
145 	    tvp->tv_usec = (long)(ticks * US_PER_S * sys_tick);
146 	}
147 #endif
148 	DPRINT(4, ("fetch_timestamp: system usec network time stamp: %jd.%06ld\n",
149 		(intmax_t)tvp->tv_sec, (long)tvp->tv_usec));
150 	nts = tspec_stamp_to_lfp(tval_to_tspec(*tvp));
151 #else
152 # error "Can't get packet timestamp"
153 #endif
154 #ifdef ENABLE_FUZZ
155 /*	fuzz = ntp_random() * 2. / FRAC * sys_fuzz; */
156 	fuzz = random() * 2. / FRAC * sys_fuzz;
157 	lfpfuzz = dtolfp(fuzz);
158 	nts += lfpfuzz;
159 #endif
160 	return nts;
161 }
162 
163 // end
164