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