2 * ntp_packetstamp.c - grubby platform-dependent details of packet timestamps
4 * One of our serious platform dependencies (things POSIX doesn't
5 * specify a facility for) is isolated here.
9 #ifdef HAVE_SYS_IOCTL_H
10 # include <sys/ioctl.h>
14 #include "ntp_stdlib.h"
15 #include "timespecops.h"
17 /* We handle 3 flavors of timestamp:
18 * SO_TIMESTAMPNS/SCM_TIMESTAMPNS Linux (maybe others)
19 * SO_TS_CLOCK/SCM_REALTIME FreeBSD
20 * SO_TIMESTAMP/SCM_TIMESTAMP Everybody else
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.
25 * FreeBSD needs 2 setsockopt, SO_TIMESTAMP and SO_TS_CLOCK
27 * Note that the if/elif tests are done in several places.
28 * It's important that they all check in the same order to
29 * be consistent in case some systems support more than one.
31 * If SO_xxx exists, we assume that SCM_xxx does too.
32 * All flavors assume the CMSG_xxx macros exist.
44 static bool once
= false;
45 #if defined(SO_TS_CLOCK)
46 const int ts_type
= SO_TS_REALTIME
;
49 #if defined (SO_TIMESTAMPNS)
52 msyslog(LOG_INFO
, "INIT: Using SO_TIMESTAMPNS(ns)");
54 if (setsockopt(fd
, SOL_SOCKET
, SO_TIMESTAMPNS
,
55 (const void *)&on
, sizeof(on
)))
57 "ERR: setsockopt SO_TIMESTAMPNS on fails on address %s: %s",
58 socktoa(addr
), strerror(errno
));
60 DPRINT(4, ("ERR: setsockopt SO_TIMESTAMPNS enabled on fd %d address %s\n",
62 #elif defined(SO_TIMESTAMP)
63 #if !defined(SO_TS_CLOCK)
66 msyslog(LOG_INFO
, "INIT: Using SO_TIMESTAMP(us)");
69 if (setsockopt(fd
, SOL_SOCKET
, SO_TIMESTAMP
,
70 (const void*)&on
, sizeof(on
)))
72 "ERR: setsockopt SO_TIMESTAMP on fails on address %s: %s",
73 socktoa(addr
), strerror(errno
));
75 DPRINT(4, ("setsockopt SO_TIMESTAMP enabled on fd %d address %s\n",
77 #if defined(SO_TS_CLOCK)
81 msyslog(LOG_INFO
, "INIT: Using SO_TS_CLOCK(ns)");
83 if (setsockopt(fd
, SOL_SOCKET
, SO_TS_CLOCK
,
84 (const void*)&ts_type
, sizeof(ts_type
)))
86 "ERR: setsockop SO_TS_CLOCK on fails on address %s: %s",
87 socktoa(addr
), strerror(errno
));
89 DPRINT(4, ("setsockopt SO_TS_CLOCK enabled on fd %d address %s\n",
93 # error "Can't get packet timestamp"
99 * extract timestamps from control message buffer
103 struct msghdr
* msghdr
106 struct cmsghdr
* cmsghdr
;
107 #if defined(SO_TIMESTAMPNS) || defined(SO_TS_CLOCK)
108 struct timespec
* tsp
;
109 #elif defined(SO_TIMESTAMP)
110 struct timeval
* tvp
;
112 l_fp nts
= 0; /* network time stamp */
114 /* There should be only one cmsg. */
115 cmsghdr
= CMSG_FIRSTHDR(msghdr
);
116 if (NULL
== cmsghdr
) {
117 DPRINT(4, ("fetch_timestamp: can't find timestamp\n"));
118 msyslog(LOG_ERR
, "ERR: fetch_timestamp: no msghdrs");
120 /* return ts; ** Kludge to use time from select. */
122 #if defined(SO_TIMESTAMPNS)
124 if (SCM_TIMESTAMPNS
!= cmsghdr
->cmsg_type
) {
125 #elif defined(SO_TS_CLOCK)
127 if (SCM_REALTIME
!= cmsghdr
->cmsg_type
) {
128 #elif defined(SO_TIMESTAMP)
129 if (SCM_TIMESTAMP
!= cmsghdr
->cmsg_type
) {
131 # error "Can't get packet timestamp"
134 ("fetch_timestamp: strange control message 0x%x\n",
135 (unsigned)cmsghdr
->cmsg_type
));
137 "ERR: fetch_timestamp: strange control message 0x%x",
138 (unsigned)cmsghdr
->cmsg_type
);
140 /* Could loop and skip strange types. */
141 /* cmsghdr = CMSG_NXTHDR(msghdr, cmsghdr); */
144 /* cmsghdr now points to a timestamp slot */
146 #if defined(SO_TIMESTAMPNS) || defined(SO_TS_CLOCK)
147 tsp
= (struct timespec
*)CMSG_DATA(cmsghdr
);
148 DPRINT(4, ("fetch_timestamp: system nsec network time stamp: %ld.%09ld\n",
149 (long)tsp
->tv_sec
, tsp
->tv_nsec
));
150 nts
= tspec_stamp_to_lfp(*tsp
);
151 #elif defined(SO_TIMESTAMP)
152 tvp
= (struct timeval
*)CMSG_DATA(cmsghdr
);
153 DPRINT(4, ("fetch_timestamp: system usec network time stamp: %jd.%06ld\n",
154 (intmax_t)tvp
->tv_sec
, (long)tvp
->tv_usec
));
155 nts
= tspec_stamp_to_lfp(tval_to_tspec(*tvp
));
157 # error "Can't get packet timestamp"