Compare commits

...

77 Commits
1.30 ... 1.31.1

Author SHA1 Message Date
Miroslav Lichvar
c4bedce1f4 doc: update NEWS 2015-04-07 16:35:16 +02:00
Miroslav Lichvar
79eacdb7e6 cmdmon: fix initialization of allocated reply slots
When allocating memory to save unacknowledged replies to authenticated
command requests, the last "next" pointer was not initialized to NULL.
When all allocated reply slots were used, the next reply could be
written to an invalid memory instead of allocating a new slot for it.

An attacker that has the command key and is allowed to access cmdmon
(only localhost is allowed by default) could exploit this to crash
chronyd or possibly execute arbitrary code with the privileges of the
chronyd process.
2015-04-07 16:35:16 +02:00
Miroslav Lichvar
cf19042ecb addrfilt: fix access configuration with subnet size indivisible by 4
When NTP or cmdmon access was configured (from chrony.conf or via
authenticated cmdmon) with a subnet size that is indivisible by 4 and
an address that has nonzero bits in the 4-bit subnet remainder (e.g.
192.168.15.0/22 or f000::/3), the new setting was written to an
incorrect location, possibly outside the allocated array.

An attacker that has the command key and is allowed to access cmdmon
(only localhost is allowed by default) could exploit this to crash
chronyd or possibly execute arbitrary code with the privileges of the
chronyd process.
2015-04-07 16:35:16 +02:00
Miroslav Lichvar
d856bd34c4 ntp: protect authenticated symmetric associations against DoS attacks
An attacker knowing that NTP hosts A and B are peering with each other
(symmetric association) can send a packet with random timestamps to host
A with source address of B which will set the NTP state variables on A
to the values sent by the attacker. Host A will then send on its next
poll to B a packet with originate timestamp that doesn't match the
transmit timestamp of B and the packet will be dropped. If the attacker
does this periodically for both hosts, they won't be able to synchronize
to each other. It is a denial-of-service attack.

According to [1], NTP authentication is supposed to protect symmetric
associations against this attack, but in the NTPv3 (RFC 1305) and NTPv4
(RFC 5905) specifications the state variables are updated before the
authentication check is performed, which means the association is
vulnerable to the attack even when authentication is enabled.

To fix this problem, save the originate and local timestamps only when
the authentication check (test5) passed.

[1] https://www.eecis.udel.edu/~mills/onwire.html
2015-04-07 16:34:42 +02:00
Miroslav Lichvar
ebab36e859 doc: update NEWS 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
3988a1e9a8 doc: mention that directives are not case-sensitive 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
949ef3e1dc doc: add section to FAQ on improving accuracy with NTP 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
dd12303276 doc: remove minpoll and maxpoll options from configuration example 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
f1379a6574 sched: fix Clang warning 2014-09-10 17:00:50 +02:00
Miroslav Lichvar
ad58384760 client: describe error when could not open config or keyfile 2014-09-10 11:34:48 +02:00
Miroslav Lichvar
0e786f5907 Ignore measurements around leap second
When current time is within 5 seconds of a leap second, don't accumulate
new samples or update the leap second status to increase the chances of
getting through safely.
2014-09-09 17:08:30 +02:00
Miroslav Lichvar
e1accce498 ntp: print warning 10 years before supported time ends 2014-09-09 17:08:30 +02:00
Miroslav Lichvar
28db0fdde9 configure: check if pkg-config is available
This is needed with some shells to prevent "pkg-config: not found"
errors from being displayed.
2014-09-09 17:08:26 +02:00
Miroslav Lichvar
584bf9382b Fix compiler warnings on NetBSD 2014-09-09 11:48:09 +02:00
Miroslav Lichvar
0168b405a3 examples: add NetworkManager dispatcher script 2014-09-04 17:43:27 +02:00
Miroslav Lichvar
b5e0d76337 examples: add systemd services 2014-09-04 17:30:36 +02:00
Miroslav Lichvar
c924fba4fa examples: add logrotate configuration 2014-09-04 17:28:32 +02:00
Miroslav Lichvar
8ec43a39af Move chrony.spec to examples 2014-09-04 17:25:56 +02:00
Miroslav Lichvar
9f16445464 sys: fix typo in prctl() error message 2014-08-25 17:25:14 +02:00
Miroslav Lichvar
1a795b04ee util: fix compiler warning with 32-bit time_t 2014-08-21 14:06:46 +02:00
Miroslav Lichvar
b862f3e64d Update NEWS 2014-08-21 10:06:09 +02:00
Miroslav Lichvar
4e66b5ce8a ntp: don't stop online burst when sending fails
Don't stop online burst for unreachable sources until sending succeeds.
This is mainly useful with iburst when chronyd is started before the
network is configured.
2014-08-20 16:54:26 +02:00
Miroslav Lichvar
d446950c6a ntp: don't adjust polling interval when sending fails 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
e3c77f9b4b ntp: return with status from functions sending packets 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
090ec985f3 doc: clarify description of -s option 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
e63bd490b0 sched: improve time jump detection
To detect forward time jumps, use a timestamp made before calling
select() instead of the first timeout in the queue. Also, if the timeout
value is modified by select() (e.g. on Linux) use it to get a more
accurate estimate of the elapsed time.
2014-08-20 16:54:26 +02:00
Miroslav Lichvar
badf97d4ba ntp: restart timer when poll interval changes on reset 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
ba283e6b6e ntp: add function to restart transmit timer 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
0bdac2c7b3 sched: make sure scheduler parameter change handler is first
This is needed to allow other handlers to add new timers.
2014-08-20 16:54:26 +02:00
Miroslav Lichvar
58b211d707 local: use common function to invoke parameter change handlers
This was missing in commit b69b648d.
2014-08-19 11:30:32 +02:00
Miroslav Lichvar
068ce237af reference: always update driftfile on exit
This is useful with the new fallback function of the -s option to
restore the system time at which chronyd was previously stopped.
2014-08-19 10:46:35 +02:00
Miroslav Lichvar
a5e9e5d0df rtc: set clock to mtime of driftfile when RTC preinit fails
When the RTC preinit function fails, set the system clock to the time of
the last modification of the driftfile if it's in the future. This makes
the -s option somewhat useful on systems where RTC is not supported or
missing.

This is similar to the functionality implemented in the fake-hwclock
script.
2014-08-19 10:46:35 +02:00
Miroslav Lichvar
e0af8069c1 rtc: don't try to open rtcfile when not set 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
696b05d6e6 rtc: use fscanf() to read coefficients 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
7e1a699616 rtc: return status from preinit function 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
716d73d982 rtc: use LCL functions to read and step system clock 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
38ac081114 rtc: improve accuracy of preinit step 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
5fce101f85 rtc: minor cleanup in RTC_Linux_TimePreInit() 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
c6e064200d rtc: move preinit call to RTC_Initialise() 2014-08-19 10:46:32 +02:00
Miroslav Lichvar
c52e9085d1 rtc: cleanup in error messages 2014-08-18 17:21:26 +02:00
Miroslav Lichvar
d0fb17d70c test: add 115-cmdmontime 2014-08-18 16:06:28 +02:00
Miroslav Lichvar
713153b610 util: update functions converting cmdmon timestamps 2014-08-18 16:06:28 +02:00
Miroslav Lichvar
09d039fba6 cmdmon: convert LOGON timestamp only with LOGON message
Avoid always calling UTI_TimevalNetworkToHost() and make the code more
readable.
2014-08-18 16:06:28 +02:00
Miroslav Lichvar
07f7f28058 sched: check that added file descriptor fits in fd_set 2014-08-15 16:52:37 +02:00
Miroslav Lichvar
a2b40f527d sched: use FD_SETSIZE if defined 2014-08-15 16:51:15 +02:00
Miroslav Lichvar
6d8ffeefd6 test: add 114-presend 2014-08-15 16:51:15 +02:00
Miroslav Lichvar
9ce25bab04 ntp: add debug message for received packet 2014-08-15 16:51:10 +02:00
Miroslav Lichvar
cd5105b1db ntp: use NTP instead of echo for presend
Switch to NTP for presend as the echo service (RFC 862) is rarely
enabled. When presend is active, send an NTP client packet to the
server/peer and ignore the reply.

This also fixes presend with separate client sockets. The destination
port can't be changed on connected sockets, so the echo packet was sent
to the NTP port instead of the echo port.
2014-08-15 16:49:45 +02:00
Miroslav Lichvar
ff4abc69c3 Check for truncated source address when receiving packets 2014-08-15 16:44:43 +02:00
Miroslav Lichvar
192f74f0a1 test: fix check in run script 2014-08-15 11:18:44 +02:00
Miroslav Lichvar
be203d9af0 test: add 008-ntpera 2014-08-15 11:18:44 +02:00
Miroslav Lichvar
f8af299321 test: allow tests to be skipped 2014-08-15 11:18:44 +02:00
Miroslav Lichvar
474b2af1a6 util: add support for other NTP eras
NTP timestamps use only 32 bits to count seconds and the current NTP era
ends in 2036. Add support for converting NTP timestamps from other NTP
eras on systems with 64-bit time_t.

The earliest assumed NTP time is set by the configure script (by default
to 50 years before the date of the build) and earlier NTP timestamps
underflow to the following NTP era.
2014-08-15 11:18:40 +02:00
Miroslav Lichvar
cb88cea3c4 ntp: move packet size asserts to ntp_core 2014-08-15 10:58:52 +02:00
Miroslav Lichvar
fc2892fbb0 util: fix printing of timestamps when time_t is longer than long 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
36b25cbd2b configure: check for 64-bit time_t 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
d18c071849 ntp: use one socket with random port when acquisitionport is 0 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
61b629fdad conf: return port numbers as int 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
29647c8280 cmdmon: allow binding to address that doesn't exist yet 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
97b15cb3ae ntp: allow binding to address that doesn't exist yet 2014-08-15 10:58:43 +02:00
Miroslav Lichvar
f725921dfb ntp: remove disabled code in prepare_socket() 2014-08-15 10:58:43 +02:00
Miroslav Lichvar
a4b4d0c0d8 ntp: bind socket only when port or address was specified
This removes an unnecessary system call when getting new connected
client socket.
2014-08-15 10:58:43 +02:00
Miroslav Lichvar
f59ade7f80 ntp: remove socket reconnecting
As new client socket is now created for each request, there is no need
to reconnect the socket. This is a partial revert of commit 43cca04c.
2014-08-15 10:58:43 +02:00
Miroslav Lichvar
a9b9e7befe ntp: create new socket for each client request
Create a new connected client socket before each request and close it
when a valid reply is received.

This is useful when the network configuration is changed and the client
socket should be reconnected, but the old bound address remains valid
and sendmsg() doesn't return with an error.
2014-08-15 10:58:43 +02:00
Miroslav Lichvar
ead9394a31 Regenerate getdate.c 2014-08-15 10:58:43 +02:00
Miroslav Lichvar
80129fa9ab makefile: regenerate getdate.c when missing 2014-08-14 14:51:24 +02:00
Joachim Wiedorn
18796a3c18 doc: fix small typo 2014-08-13 10:59:13 +02:00
Miroslav Lichvar
f632b6d4cb configure: remove ncurses_lib from first readline test 2014-08-13 10:59:13 +02:00
Miroslav Lichvar
7799e14770 test: increase default time rms limit
This is improves testing reliability with new default corrtimeratio.
2014-08-06 15:46:32 +02:00
Miroslav Lichvar
20aab86e12 test: require latest clknetsim 2014-08-06 15:26:35 +02:00
Miroslav Lichvar
b7766478a6 ntp: close socket when duplicating fails 2014-08-06 15:25:58 +02:00
Miroslav Lichvar
3d57b7a44d refclock: fix compiler warning in SOCK error message 2014-08-05 17:13:08 +02:00
Miroslav Lichvar
51a2b436f4 logging: move exit call from LOG_Message to LOG_FATAL 2014-08-05 15:15:15 +02:00
Miroslav Lichvar
88015081f2 ntp: shorten setsockopt error messages 2014-08-05 12:57:36 +02:00
Miroslav Lichvar
20cc1f6550 ntp: move debug message after sendmsg()
This should reduce the delay before sending the packet when debugging is
enabled.
2014-08-05 12:57:36 +02:00
Miroslav Lichvar
43cca04c33 ntp: reconnect client sockets
With separate client sockets, allow the initial connect() to fail (e.g.
when the network is not reachable yet) and try to connect later when
sending the packet.

Also, reconnect the socket when the local address has changed.
2014-08-05 12:57:36 +02:00
Miroslav Lichvar
17d944c333 doc: mention linuxcaps support in FAQ 2014-07-29 18:10:40 +02:00
46 changed files with 1303 additions and 851 deletions

View File

@@ -85,7 +85,10 @@ clean :
-rm -f *.o *.s chronyc chronyd core *~ chrony.info chrony.html chrony.txt
-rm -rf .deps
getdate.c : ;
getdate.c :
bison -o getdate.c getdate.y
# This can be used to force regeneration of getdate.c
getdate :
bison -o getdate.c getdate.y

34
NEWS
View File

@@ -1,3 +1,37 @@
New in version 1.31.1
=====================
Security fixes
--------------
* Protect authenticated symmetric NTP associations against DoS attacks
(CVE-2015-1799)
* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821)
* Fix initialization of reply slots for authenticated commands (CVE-2015-1822)
New in version 1.31
===================
Enhancements
------------
* Support operation in other NTP eras (next era begins in 2036),
NTP time is mapped to [-50, +86] years around build date by default
* Restore time from driftfile with -s when RTC is missing/unsupported
* Close connected client sockets when not waiting for reply
* Use one client socket with random port when acquisitionport is 0
* Use NTP packets instead of UDP echo for presend
* Don't adjust polling interval when sending fails
* Allow binding to addresses that don't exist yet
* Ignore measurements around leap second
* Improve detection of unexpected time jumps
* Include example of logrotate configuration, systemd services and
NetworkManager dispatcher script
Bug fixes
---------
* Reconnect client sockets for each request to follow changes
in network configuration automatically
* Restart timer when polling interval is changed on reset
New in version 1.30
===================

View File

@@ -199,7 +199,10 @@ set_subnet(TableNode *start_node,
/* How many subnet entries to set : 1->8, 2->4, 3->2 */
N = 1 << (NBITS-bits_to_go);
subnet = get_subnet(ip, bits_consumed);
subnet = get_subnet(ip, bits_consumed) & ~(N - 1);
assert(subnet + N <= TABLE_SIZE);
if (!(node->extended)) {
open_node(node);
}

View File

@@ -873,9 +873,9 @@ For the @file{@SYSCONFDIR@/chrony.conf} file, the following can be used as an
example.
@example
server 0.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline
server 1.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline
server 2.pool.ntp.org minpoll 5 maxpoll 10 maxdelay 0.4 offline
server 0.pool.ntp.org maxdelay 0.4 offline
server 1.pool.ntp.org maxdelay 0.4 offline
server 2.pool.ntp.org maxdelay 0.4 offline
logdir /var/log/chrony
log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift
@@ -1007,7 +1007,7 @@ used.
When this option is used, the @code{initstepslew} directive and the
@code{makestep} directive used with a positive limit will be ignored.
This option is useful when restarting @code{chronyd} and can be used
in conjuction with the `-r' option.
in conjunction with the `-r' option.
@item -s
This option will set the system clock from the computer's real-time
@@ -1018,18 +1018,20 @@ Support for real-time clocks is limited at present - the criteria are
described in the section on the @code{rtcfile} directive (@pxref{rtcfile
directive}).
If @code{chronyd} cannot support the real time clock on your computer,
this option cannot be used and a warning message will be logged to the
syslog.
If used in conjunction with the `-r' flag, @code{chronyd} will attempt
to preserve the old samples after setting the system clock from the real
time clock. This can be used to allow @code{chronyd} to perform long
time clock (RTC). This can be used to allow @code{chronyd} to perform long
term averaging of the gain or loss rate across system reboots, and is
useful for dial-up systems that are shut down when not in use. For this
to work well, it relies on @code{chronyd} having been able to determine
accurate statistics for the difference between the real time clock and
accurate statistics for the difference between the RTC and
system clock last time the computer was on.
If @code{chronyd} doesn't support the RTC on your computer or there is no RTC
installed, the system clock will be set with this option forward to the time of
the last modification of the drift file (specified by the @code{driftfile}
directive) to restore the system time at which @code{chronyd} was previously
stopped.
@item -u <user>
This option sets the name of the user to which will @code{chronyd} switch to
drop root privileges if compiled with Linux capabilities support (default
@@ -1112,7 +1114,8 @@ specified with a command line option.
Each command in the configuration file is placed on a separate line.
The following sections describe each of the commands in turn. The
directives can occur in any order in the file.
directives can occur in any order in the file and they are not
case-sensitive.
The configuration commands can also be specified directly on the
@code{chronyd} command line, each argument is parsed as a line and
@@ -1199,7 +1202,8 @@ By default, @code{chronyd} uses a separate client socket for each configured
server and their source port is chosen arbitrarily by the operating system.
However, you can use the @code{acquisitionport} directive to explicitly specify
a port and use only one socket (per IPv4/IPv6 address family) for all
configured servers. This may be useful for getting through firewalls.
configured servers. This may be useful for getting through firewalls. If set
to 0, the source port of the socket will be chosen arbitrarily.
It may be set to the same port as used by the NTP server (@pxref{port
directive}) to use only one socket for all NTP packets.
@@ -2835,18 +2839,18 @@ of the machines.
In order to avoid this problem, the @code{presend} option may be used.
It takes a single integer argument, which is the smallest polling
interval for which a pair of packets will be exchanged between the
client and the server prior to the actual measurement being initiated by
the client. For example, with the following option included in a
interval for which an extra pair of NTP packets will be exchanged
between the client and the server prior to the actual measurement.
For example, with the following option included in a
@code{server} directive :
@example
presend 9
@end example
when the polling interval is 512 seconds or more, a UDP echo datagram
will be sent to the server a short time (currently 4 seconds) before the
NTP client mode datagram.
when the polling interval is 512 seconds or more, an extra NTP client
packet will be sent to the server a short time (currently 4 seconds)
before making the actual measurement.
@item key
The NTP protocol supports the inclusion of checksums in the packets, to
@@ -4619,6 +4623,49 @@ bindcmdaddress ::1
If you don't need to use @code{chronyc} at all, you can disable the command
sockets by adding @code{cmdport 0} to the configuration file.
On Linux, if @code{chronyd} is compiled with support for Linux capabilities
(available in the libcap library), you can specify an unprivileged user with
the `-u' option or @code{user} directive in the @file{chrony.conf} file to drop
root privileges after start. The configure option @code{--with-user} can be
used to drop the privileges by default.
@subsection How can I improve the accuracy of the system clock with NTP sources?
Select NTP servers that are well synchronised, stable and close to your network.
It's better to use more than one server, three or four is usually recommended as
the minimum, so @code{chronyd} can detect falsetickers and combine measurements
from multiple sources.
There are also useful options which can be set in the @code{server} directive,
they are @code{minpoll}, @code{maxpoll}, @code{polltarget}, @code{maxdelay},
@code{maxdelayratio} and @code{maxdelaydevratio}.
The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their
default values are suitable for public NTP servers, which normally don't allow
too frequent polling, but if you run your own NTP servers or have permission to
poll the servers frequently, setting the options for shorter polling intervals
may significantly improve the accuracy of the system clock.
The optimal polling interval depends on many factors, this includes the ratio
between the wander of the clock and the network jitter (sometimes expressed in
NTP documents as the Allan intercept), the temperature sensitivity of the
crystal oscillator and the maximum rate of change of the temperature. An
example of the directive for a server located in the same LAN could be
@example
server ntp.local minpoll 2 maxpoll 4 polltarget 30
@end example
The maxdelay options are useful to ignore measurements with larger delay (e.g.
due to congestion in the network) and improve the stability of the
synchronisation. The @code{maxdelaydevratio} option could be added to the
previous example
@example
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
@end example
@c }}}
@c {{{ S:Computer is not synchronising
@node Computer is not synchronising

View File

@@ -76,7 +76,7 @@ should not be used.
.B \-R
When this option is used, the \fIinitstepslew\fR directive and the
\fImakestep\fR directive used with a positive limit will be ignored. This
option is useful when restarting \fBchronyd\fR and can be used in conjuction
option is useful when restarting \fBchronyd\fR and can be used in conjunction
with the \fB-r\fR option.
.TP
.B \-s
@@ -88,19 +88,20 @@ Support for real-time clocks is limited at present - the criteria
are described in the section on the \fIrtcfile\fR directive in the
documentation supplied with the distribution.
If \fBchronyd\fR cannot support the real time clock on your computer,
this option cannot be used and a warning message will be logged to
the syslog.
If used in conjunction with the \fB-r\fR flag, \fBchronyd\fR will attempt
to preserve the old samples after setting the system clock from
the real time clock. This can be used to allow \fBchronyd\fR to
the real time clock (RTC). This can be used to allow \fBchronyd\fR to
perform long term averaging of the gain or loss rate across system
reboots, and is useful for dial-up systems that are shut down when
not in use. For this to work well, it relies on \fBchronyd\fR having
been able to determine accurate statistics for the difference
between the real time clock and system clock last time the
computer was on.
between the RTC and system clock last time the computer was on.
If \fBchronyd\fR doesn't support the RTC on your computer or there is no RTC
installed, the system clock will be set with this option forward to the time of
the last modification of the drift file (specified by the \fIdriftfile\fR
directive) to restore the system time at which \fBchronyd\fR was previously
stopped.
.TP
\fB\-u\fR \fIuser\fR
This option sets the name of the user to which will \fBchronyd\fR switch to

View File

@@ -2616,7 +2616,7 @@ authenticate_from_config(const char *filename)
in = fopen(filename, "r");
if (!in) {
fprintf(stderr, "Could not open file %s\n", filename);
fprintf(stderr, "Could not open file %s : %s\n", filename, strerror(errno));
return 0;
}
@@ -2641,7 +2641,7 @@ authenticate_from_config(const char *filename)
in = fopen(keyfile, "r");
if (!in) {
fprintf(stderr, "Could not open keyfile %s\n", keyfile);
fprintf(stderr, "Could not open keyfile %s : %s\n", keyfile, strerror(errno));
return 0;
}

View File

@@ -199,6 +199,14 @@ prepare_socket(int family, int port_number)
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */
}
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
}
#endif
#ifdef HAVE_IPV6
if (family == AF_INET6) {
#ifdef IPV6_V6ONLY
@@ -558,6 +566,7 @@ get_more_replies(void)
for (i=1; i<REPLY_EXTEND_QUANTUM; i++) {
new_replies[i-1].next = new_replies + i;
}
new_replies[REPLY_EXTEND_QUANTUM - 1].next = NULL;
free_replies = new_replies;
}
}
@@ -1636,6 +1645,9 @@ read_from_cmd_socket(void *anything)
return;
}
if (from_length > sizeof (where_from))
LOG_FATAL(LOGF_CmdMon, "Truncated source address");
read_length = status;
LCL_ReadRawTime(&now);
@@ -1832,20 +1844,22 @@ read_from_cmd_socket(void *anything)
}
valid_ts = 0;
issue_token = 0;
if (auth_ok) {
struct timeval ts;
UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts);
if ((utoken_ok && token_ok) ||
((ntohl(rx_message.utoken) == SPECIAL_UTOKEN) &&
(rx_command == REQ_LOGON) &&
(valid_ts = ts_is_unique_and_not_stale(&ts, &now))))
if (utoken_ok && token_ok) {
issue_token = 1;
else
issue_token = 0;
} else {
issue_token = 0;
} else if (rx_command == REQ_LOGON &&
ntohl(rx_message.utoken) == SPECIAL_UTOKEN) {
struct timeval ts;
UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts);
valid_ts = ts_is_unique_and_not_stale(&ts, &now);
if (valid_ts) {
issue_token = 1;
}
}
}
authenticated = auth_ok & utoken_ok & token_ok;

View File

@@ -204,7 +204,7 @@ CPS_NormalizeLine(char *line)
/* Remove white-space at beginning and replace white-spaces with space char */
for (p = q = line; *p; p++) {
if (isspace(*p)) {
if (isspace((unsigned char)*p)) {
if (!space)
*q++ = ' ';
space = 1;
@@ -234,15 +234,15 @@ CPS_SplitWord(char *line)
char *p = line, *q = line;
/* Skip white-space before the word */
while (*q && isspace(*q))
while (*q && isspace((unsigned char)*q))
q++;
/* Move the word to the beginning */
while (*q && !isspace(*q))
while (*q && !isspace((unsigned char)*q))
*p++ = *q++;
/* Find the next word */
while (*q && isspace(*q))
while (*q && isspace((unsigned char)*q))
q++;
*p = '\0';

6
conf.c
View File

@@ -79,7 +79,7 @@ static void parse_tempcomp(char *);
static int restarted = 0;
static int generate_command_key = 0;
static char *rtc_device = "/dev/rtc";
static int acquisition_port = 0; /* 0 means let kernel choose port */
static int acquisition_port = -1;
static int ntp_port = 123;
static char *keys_file = NULL;
static char *drift_file = NULL;
@@ -1212,7 +1212,7 @@ CNF_AddBroadcasts(void)
/* ================================================== */
unsigned short
int
CNF_GetNTPPort(void)
{
return ntp_port;
@@ -1220,7 +1220,7 @@ CNF_GetNTPPort(void)
/* ================================================== */
unsigned short
int
CNF_GetAcquisitionPort(void)
{
return acquisition_port;

4
conf.h
View File

@@ -42,8 +42,8 @@ extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void);
extern unsigned short CNF_GetAcquisitionPort(void);
extern unsigned short CNF_GetNTPPort(void);
extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);

55
configure vendored
View File

@@ -114,6 +114,8 @@ For better control, use the options below.
--disable-linuxcaps Disable Linux capabilities support
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support
@@ -158,6 +160,13 @@ add_def () {
fi
}
#}}}
#{{{ pkg_config
pkg_config () {
type pkg-config > /dev/null 2> /dev/null || return 1
pkg-config $@ 2> /dev/null
}
#}}}
# ======================================================================
@@ -194,6 +203,7 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
ntp_era_split=""
default_user="root"
mail_program="/usr/lib/sendmail"
@@ -275,6 +285,9 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
--with-ntp-era=* )
ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
;;
--with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -376,6 +389,40 @@ case $SYSTEM in
;;
esac
if test_code '64-bit time_t' 'time.h' '' '' '
char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
return x[0];'
then
add_def HAVE_LONG_TIME_T 1
if [ "x$ntp_era_split" != "x" ]; then
split_seconds=$ntp_era_split
split_days=0
else
split_seconds=`date '+%s'`
if [ "x$split_seconds" = "" ]; then
echo "Could not get current time, --with-ntp-era option is needed"
exit 1
fi
split_days=$((50 * 365))
fi
add_def NTP_ERA_SPLIT "(${split_seconds}LL - $split_days * 24 * 3600)"
date_format='+%Y-%m-%dT%H:%M:%SZ'
# Print the full NTP interval if a suitable date is found
if [ "x`date -u -d '1970-01-01 UTC 9 days ago 5 seconds 3 seconds' \
$date_format 2> /dev/null`" = "x1969-12-23T00:00:08Z" ]
then
time1="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds" \
$date_format`"
time2="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds 4294967296 seconds" \
$date_format`"
echo "NTP time mapped to $time1/$time2"
fi
fi
MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));'
if test_code 'math' 'math.h' '' '' "$MATHCODE"; then
LIBS=""
@@ -535,12 +582,12 @@ if [ $feat_readline = "1" ]; then
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code readline 'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib $ncurses_lib -lreadline" \
"$readline_inc" "$readline_lib -lreadline" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
READLINE_COMPILE="$readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
@@ -564,8 +611,8 @@ HASH_COMPILE=""
HASH_LINK=""
if [ $try_nss = "1" ]; then
test_cflags="`pkg-config --cflags nss 2> /dev/null`"
test_link="`pkg-config --libs-only-L nss 2> /dev/null` -lfreebl3"
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
"$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Wait for chrony to synchronize system clock
After=chronyd.service
Requires=chronyd.service
Before=time-sync.target
Wants=time-sync.target
[Service]
Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc waitsync 60 0.1
RemainAfterExit=yes
StandardOutput=null
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,8 @@
/var/log/chrony/*.log {
missingok
nocreate
sharedscripts
postrotate
/usr/bin/chronyc -a cyclelogs > /dev/null 2>&1 || true
endscript
}

View File

@@ -0,0 +1,17 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
# online/offline when a default route is configured/removed on the system.
export LC_ALL=C
if [ "$2" = "up" ]; then
/sbin/ip route list dev "$1" | grep -q '^default' &&
/usr/bin/chronyc -a online > /dev/null 2>&1
fi
if [ "$2" = "down" ]; then
/sbin/ip route list | grep -q '^default' ||
/usr/bin/chronyc -a offline > /dev/null 2>&1
fi
exit 0

13
examples/chronyd.service Normal file
View File

@@ -0,0 +1,13 @@
[Unit]
Description=NTP client/server
After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service
[Service]
Type=forking
PIDFile=/var/run/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
[Install]
WantedBy=multi-user.target

864
getdate.c

File diff suppressed because it is too large Load Diff

View File

@@ -711,7 +711,7 @@ LookupWord (buff)
/* Make it lowercase. */
for (p = buff; *p; p++)
if (ISUPPER ((unsigned char) *p))
*p = tolower (*p);
*p = tolower ((unsigned char) *p);
if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
{

15
local.c
View File

@@ -252,6 +252,14 @@ void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *
/* ================================================== */
int
LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
{
return change_list.next->handler == handler;
}
/* ================================================== */
static void
invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
double dfreq, double doffset,
@@ -493,7 +501,6 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
double old_freq_ppm;
@@ -519,11 +526,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
(*drv_accrue_offset)(doffset, corr_rate);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(&raw, &cooked, dfreq, doffset, 0, ptr->anything);
}
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
}
/* ================================================== */

View File

@@ -92,6 +92,9 @@ extern void LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, vo
/* Remove a handler */
extern void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler, void *anything);
/* Check if a handler is invoked first when dispatching */
extern int LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler);
/* Function type for handlers to be called back when an indeterminate
offset is introduced into the local time. This situation occurs
when the frequency must be adjusted to effect a clock slew and

View File

@@ -187,9 +187,6 @@ void LOG_Message(LOG_Severity severity, LOG_Facility facility,
log_message(1, severity, buf);
}
}
exit(1);
break;
default:
assert(0);

View File

@@ -29,6 +29,8 @@
#ifndef GOT_LOGGING_H
#define GOT_LOGGING_H
#include "sysincl.h"
/* Flag indicating whether debug messages are logged */
extern int log_debug_enabled;
@@ -49,7 +51,11 @@ extern int log_debug_enabled;
LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
} while (0)
#define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_FATAL(facility, ...) LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_FATAL(facility, ...) \
do { \
LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
exit(1); \
} while (0)
/* Definition of severity */
typedef enum {

6
main.c
View File

@@ -452,16 +452,12 @@ int main
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
if (do_init_rtc) {
RTC_TimePreInit();
}
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();
NIO_Initialise(address_family);
CAM_Initialise(address_family);
RTC_Initialise();
RTC_Initialise(do_init_rtc);
SRC_Initialise();
RCL_Initialise();
KEY_Initialise();

View File

@@ -37,7 +37,7 @@ cd RELEASES/$subdir || exit 1
echo $version > version.txt
sed -e "s%@@VERSION@@%${version}%" < chrony.spec.sample > chrony.spec
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
for m in chrony.1 chronyc.1.in chrony.conf.5.in chronyd.8.in; do
sed -e "s%@VERSION@%${version}%;s%@MAN_DATE@%${mandate}%" \
@@ -69,7 +69,7 @@ if [ $(wc -l < FAQ) -gt 400 -o $(wc -l < FAQ) -lt 200 ]; then
exit 3
fi
rm -f config.h config.log faqgen.pl make_release chrony.spec.sample .gitignore
rm -f config.h config.log make_release .gitignore
cd ..
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz

View File

@@ -85,8 +85,8 @@ struct NCR_Instance_Record {
received packets) */
int presend_minpoll; /* If the current polling interval is
at least this, an echo datagram
will be send some time before every
at least this, an extra client packet
will be send some time before normal
transmit. This ensures that both
us and the server/peer have an ARP
entry for each other ready, which
@@ -191,7 +191,7 @@ struct NCR_Instance_Record {
#define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
/* Time to wait after sending echo to 'warm up' link */
/* Time to wait after sending packet to 'warm up' link */
#define WARM_UP_DELAY 4.0
/* The NTP protocol version that we support */
@@ -229,12 +229,79 @@ static ADF_AuthTable access_auth_table;
/* Forward prototypes */
static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
/* ================================================== */
static void
do_size_checks(void)
{
/* Assertions to check the sizes of certain data types
and the positions of certain record fields */
/* Check that certain invariants are true */
assert(sizeof(NTP_int32) == 4);
assert(sizeof(NTP_int64) == 8);
/* Check offsets of all fields in the NTP packet format */
assert(offsetof(NTP_Packet, lvm) == 0);
assert(offsetof(NTP_Packet, stratum) == 1);
assert(offsetof(NTP_Packet, poll) == 2);
assert(offsetof(NTP_Packet, precision) == 3);
assert(offsetof(NTP_Packet, root_delay) == 4);
assert(offsetof(NTP_Packet, root_dispersion) == 8);
assert(offsetof(NTP_Packet, reference_id) == 12);
assert(offsetof(NTP_Packet, reference_ts) == 16);
assert(offsetof(NTP_Packet, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40);
}
/* ================================================== */
static void
do_time_checks(void)
{
struct timeval now;
time_t warning_advance = 3600 * 24 * 365 * 10; /* 10 years */
#ifdef HAVE_LONG_TIME_T
/* Check that time before NTP_ERA_SPLIT underflows correctly */
struct timeval tv1 = {NTP_ERA_SPLIT, 1}, tv2 = {NTP_ERA_SPLIT - 1, 1};
NTP_int64 ntv1, ntv2;
int r;
UTI_TimevalToInt64(&tv1, &ntv1, 0);
UTI_TimevalToInt64(&tv2, &ntv2, 0);
UTI_Int64ToTimeval(&ntv1, &tv1);
UTI_Int64ToTimeval(&ntv2, &tv2);
r = tv1.tv_sec == NTP_ERA_SPLIT &&
tv1.tv_sec + (1ULL << 32) - 1 == tv2.tv_sec;
assert(r);
LCL_ReadRawTime(&now);
if (tv2.tv_sec - now.tv_sec < warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "Assumed NTP time ends at %s!",
UTI_TimeToLogForm(tv2.tv_sec));
#else
LCL_ReadRawTime(&now);
if (now.tv_sec > 0x7fffffff - warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "System time ends at %s!",
UTI_TimeToLogForm(0x7fffffff));
#endif
}
/* ================================================== */
void
NCR_Initialise(void)
{
do_size_checks();
do_time_checks();
logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements",
" Date (UTC) Time IP Address L St 1234 abc 5678 LP RP Score Offset Peer del. Peer disp. Root del. Root disp.")
: -1;
@@ -254,7 +321,7 @@ NCR_Finalise(void)
/* ================================================== */
static void
start_initial_timeout(NCR_Instance inst)
restart_timeout(NCR_Instance inst, double delay)
{
/* Check if we can transmit */
if (inst->tx_suspended) {
@@ -267,24 +334,36 @@ start_initial_timeout(NCR_Instance inst)
SCH_RemoveTimeout(inst->timeout_id);
/* Start new timer for transmission */
inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION,
inst->timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
inst->timer_running = 1;
}
/* ================================================== */
static void
start_initial_timeout(NCR_Instance inst)
{
if (!inst->timer_running) {
/* This will be the first transmission after mode change */
inst->timer_running = 1;
/* Mark source active */
SRC_SetActive(inst->source);
}
/* Open client socket */
if (inst->mode == MODE_CLIENT) {
assert(inst->local_addr.sock_fd == INVALID_SOCK_FD);
inst->local_addr.sock_fd = NIO_GetClientSocket(&inst->remote_addr);
}
restart_timeout(inst, INITIAL_DELAY);
}
/* ================================================== */
static void
close_client_socket(NCR_Instance inst)
{
if (inst->mode == MODE_CLIENT && inst->local_addr.sock_fd != INVALID_SOCK_FD) {
NIO_CloseClientSocket(inst->local_addr.sock_fd);
inst->local_addr.sock_fd = INVALID_SOCK_FD;
}
}
@@ -305,11 +384,7 @@ take_offline(NCR_Instance inst)
/* And inactive */
SRC_UnsetActive(inst->source);
/* Close client socket */
if (inst->mode == MODE_CLIENT && inst->local_addr.sock_fd != INVALID_SOCK_FD) {
NIO_CloseClientSocket(inst->local_addr.sock_fd);
inst->local_addr.sock_fd = INVALID_SOCK_FD;
}
close_client_socket(inst);
NCR_ResetInstance(inst);
}
@@ -328,7 +403,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
switch (type) {
case NTP_SERVER:
/* Client socket will be obtained when timer is started */
/* Client socket will be obtained when sending request */
result->local_addr.sock_fd = INVALID_SOCK_FD;
result->mode = MODE_CLIENT;
break;
@@ -377,6 +452,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->timeout_id = 0;
result->tx_suspended = 1;
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll;
NCR_ResetInstance(result);
@@ -423,7 +499,6 @@ NCR_ResetInstance(NCR_Instance instance)
instance->tx_count = 0;
instance->presend_done = 0;
instance->local_poll = instance->minpoll;
instance->poll_score = 0.0;
instance->remote_poll = 0;
instance->remote_stratum = 0;
@@ -436,6 +511,14 @@ NCR_ResetInstance(NCR_Instance instance)
instance->local_tx.tv_usec = 0;
instance->local_ntp_tx.hi = 0;
instance->local_ntp_tx.lo = 0;
if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll;
/* The timer was set with a longer poll interval, restart it */
if (instance->timer_running)
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
}
}
/* ================================================== */
@@ -598,7 +681,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
/* ================================================== */
static void
static int
transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
int my_poll, /* The log2 of the local poll interval */
int version, /* The NTP version to be set in the packet */
@@ -620,7 +703,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
)
{
NTP_Packet message;
int leap;
int leap, ret;
struct timeval local_transmit;
/* Parameters read from reference module */
@@ -704,17 +787,17 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
(unsigned char *)&message.auth_data, sizeof (message.auth_data));
if (auth_len > 0) {
message.auth_keyid = htonl(key_id);
NIO_SendAuthenticatedPacket(&message, where_to, from,
ret = NIO_SendAuthenticatedPacket(&message, where_to, from,
sizeof (message.auth_keyid) + auth_len);
} else {
DEBUG_LOG(LOGF_NtpCore,
"Could not generate auth data with key %lu to send packet",
key_id);
return;
return 0;
}
} else {
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
NIO_SendNormalPacket(&message, where_to, from);
ret = NIO_SendNormalPacket(&message, where_to, from);
}
if (local_tx) {
@@ -725,6 +808,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
*local_ntp_tx = message.transmit_ts;
}
return ret;
}
/* ================================================== */
@@ -734,7 +818,7 @@ static void
transmit_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance) arg;
double timeout_delay;
int sent;
inst->timer_running = 0;
@@ -758,8 +842,15 @@ transmit_timeout(void *arg)
DEBUG_LOG(LOGF_NtpCore, "Transmit timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
/* Open new client socket */
if (inst->mode == MODE_CLIENT) {
close_client_socket(inst);
assert(inst->local_addr.sock_fd == INVALID_SOCK_FD);
inst->local_addr.sock_fd = NIO_GetClientSocket(&inst->remote_addr);
}
/* Check whether we need to 'warm up' the link to the other end by
sending an echo exchange to ensure both ends' ARP caches are
sending an NTP exchange to ensure both ends' ARP caches are
primed. On loaded systems this might also help ensure that bits
of the program are paged in properly before we start. */
@@ -767,33 +858,42 @@ transmit_timeout(void *arg)
(inst->presend_minpoll <= inst->local_poll) &&
!inst->presend_done) {
/* Send */
NIO_SendEcho(&inst->remote_addr, &inst->local_addr);
/* Send a client packet, don't store the local tx values
as the reply will be ignored */
transmit_packet(MODE_CLIENT, inst->local_poll, NTP_VERSION, 0, 0,
&inst->remote_orig, &inst->local_rx, NULL, NULL,
&inst->remote_addr, &inst->local_addr);
inst->presend_done = 1;
/* Requeue timeout */
inst->timer_running = 1;
inst->timeout_id = SCH_AddTimeoutInClass(WARM_UP_DELAY, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
restart_timeout(inst, WARM_UP_DELAY);
return;
}
inst->presend_done = 0; /* Reset for next time */
sent = transmit_packet(inst->mode, inst->local_poll,
NTP_VERSION,
inst->do_auth, inst->auth_key_id,
&inst->remote_orig,
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
&inst->remote_addr,
&inst->local_addr);
++inst->tx_count;
/* If the source loses connectivity, back off the sampling rate to reduce
wasted sampling. If it's the source to which we are currently locked,
back off slower. */
/* If the source loses connectivity and our packets are still being sent,
back off the sampling rate to reduce the network traffic. If it's the
source to which we are currently locked, back off slowly. */
if (inst->tx_count >= 2) {
/* Implies we have missed at least one transmission */
adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25);
if (sent) {
adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25);
}
SRC_UpdateReachability(inst->source, 0);
@@ -802,16 +902,12 @@ transmit_timeout(void *arg)
}
}
transmit_packet(inst->mode, inst->local_poll,
NTP_VERSION,
inst->do_auth, inst->auth_key_id,
&inst->remote_orig,
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
&inst->remote_addr,
&inst->local_addr);
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
/* When not reachable, don't stop online burst until sending succeeds */
if (!sent && !SRC_IsReachable(inst->source))
break;
/* Fall through */
case MD_BURST_WAS_OFFLINE:
--inst->burst_total_samples_to_go;
break;
@@ -820,12 +916,7 @@ transmit_timeout(void *arg)
}
/* Restart timer for this message */
timeout_delay = get_transmit_delay(inst, 1, 0.0);
inst->timer_running = 1;
inst->timeout_id = SCH_AddTimeoutInClass(timeout_delay, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
}
@@ -914,9 +1005,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* ==================== */
/* Save local receive timestamp */
inst->local_rx = *now;
pkt_leap = (message->lvm >> 6) & 0x3;
if (pkt_leap == 0x3) {
source_is_synchronized = 0;
@@ -948,14 +1036,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
test2 = 1; /* Success */
}
/* Regardless of any validity checks we apply, we are required to
save this field from the packet into the ntp source
instance record. See RFC1305 section 3.4.4, peer.org <- pkt.xmt
& peer.peerpoll <- pkt.poll. Note we can't do this assignment
before test1 has been carried out!! */
inst->remote_orig = message->transmit_ts;
/* Test 3 requires that pkt.org != 0 and pkt.rec != 0. If
either of these are true it means the association is not properly
'up'. */
@@ -1128,6 +1208,14 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
kod_rate = 1;
}
/* The transmit timestamp and local receive timestamp must not be saved when
the authentication test failed to prevent denial-of-service attacks on
symmetric associations using authentication */
if (test5) {
inst->remote_orig = message->transmit_ts;
inst->local_rx = *now;
}
valid_kod = test1 && test2 && test5;
valid_data = test1 && test2 && test3 && test4 && test4a && test4b;
@@ -1257,6 +1345,10 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
adjust_poll(inst, 0.1);
}
/* If in client mode, no more packets are expected to be coming from the
server and the socket can be closed */
close_client_socket(inst);
requeue_transmit = 1;
}
@@ -1271,11 +1363,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Get rid of old timeout and start a new one */
assert(inst->timer_running);
SCH_RemoveTimeout(inst->timeout_id);
inst->timeout_id = SCH_AddTimeoutInClass(delay_time, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
restart_timeout(inst, delay_time);
}
/* Do measurement logging */
@@ -1418,6 +1506,9 @@ NCR_ProcessKnown
break;
case MODE_SERVER:
/* Ignore presend reply */
if (inst->presend_done)
break;
switch(inst->mode) {
case MODE_ACTIVE:

249
ntp_io.c
View File

@@ -71,33 +71,6 @@ static void read_from_socket(void *anything);
/* ================================================== */
static void
do_size_checks(void)
{
/* Assertions to check the sizes of certain data types
and the positions of certain record fields */
/* Check that certain invariants are true */
assert(sizeof(NTP_int32) == 4);
assert(sizeof(NTP_int64) == 8);
/* Check offsets of all fields in the NTP packet format */
assert(offsetof(NTP_Packet, lvm) == 0);
assert(offsetof(NTP_Packet, stratum) == 1);
assert(offsetof(NTP_Packet, poll) == 2);
assert(offsetof(NTP_Packet, precision) == 3);
assert(offsetof(NTP_Packet, root_delay) == 4);
assert(offsetof(NTP_Packet, root_dispersion) == 8);
assert(offsetof(NTP_Packet, reference_id) == 12);
assert(offsetof(NTP_Packet, reference_ts) == 16);
assert(offsetof(NTP_Packet, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40);
}
/* ================================================== */
static int
prepare_socket(int family, int port_number, int client_only)
{
@@ -109,11 +82,7 @@ prepare_socket(int family, int port_number, int client_only)
/* Open Internet domain UDP socket for NTP message transmissions */
#if 0
sock_fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
#else
sock_fd = socket(family, SOCK_DGRAM, 0);
#endif
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
@@ -124,6 +93,54 @@ prepare_socket(int family, int port_number, int client_only)
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
switch (family) {
case AF_INET:
if (!client_only)
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
if (bind_address.family == IPADDR_INET4)
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
else if (port_number)
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
else
break;
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
my_addr_len = sizeof (my_addr.in4);
break;
#ifdef HAVE_IPV6
case AF_INET6:
if (!client_only)
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
if (bind_address.family == IPADDR_INET6)
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
sizeof (my_addr.in6.sin6_addr.s6_addr));
else if (port_number)
my_addr.in6.sin6_addr = in6addr_any;
else
break;
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
my_addr_len = sizeof (my_addr.in6);
break;
#endif
default:
assert(0);
}
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
@@ -146,12 +163,20 @@ prepare_socket(int family, int port_number, int client_only)
}
#endif
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option");
}
#endif
if (family == AF_INET) {
#ifdef IP_PKTINFO
/* We want the local IP info on server sockets */
if (!client_only &&
setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request packet info using socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set packet info socket option");
/* Don't quit - we might survive anyway */
}
#endif
@@ -161,66 +186,26 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPV6_V6ONLY socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option");
}
#endif
if (!client_only) {
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
}
#elif defined(IPV6_PKTINFO)
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
}
#endif
}
}
#endif
/* Bind the port */
memset(&my_addr, 0, sizeof (my_addr));
switch (family) {
case AF_INET:
my_addr_len = sizeof (my_addr.in4);
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
if (!client_only)
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
if (bind_address.family == IPADDR_INET4)
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
else
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
break;
#ifdef HAVE_IPV6
case AF_INET6:
my_addr_len = sizeof (my_addr.in6);
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
if (!client_only)
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
if (bind_address.family == IPADDR_INET6)
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
sizeof (my_addr.in6.sin6_addr.s6_addr));
else
my_addr.in6.sin6_addr = in6addr_any;
break;
#endif
default:
assert(0);
}
if (bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
close(sock_fd);
@@ -230,20 +215,28 @@ prepare_socket(int family, int port_number, int client_only)
/* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
#if 0
if (fcntl(sock_fd, F_SETFL, O_NONBLOCK | O_NDELAY) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not make socket non-blocking");
}
if (ioctl(sock_fd, I_SETSIG, S_INPUT) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not enable signal");
}
#endif
return sock_fd;
}
/* ================================================== */
static int
prepare_separate_client_socket(int family)
{
switch (family) {
case IPADDR_INET4:
return prepare_socket(AF_INET, 0, 1);
#ifdef HAVE_IPV6
case IPADDR_INET6:
return prepare_socket(AF_INET6, 0, 1);
#endif
default:
return INVALID_SOCK_FD;
}
}
/* ================================================== */
static int
connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
{
@@ -273,7 +266,7 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
}
if (connect(sock_fd, &addr.u, addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
@@ -295,7 +288,6 @@ close_socket(int sock_fd)
}
/* ================================================== */
void
NIO_Initialise(int family)
{
@@ -304,13 +296,13 @@ NIO_Initialise(int family)
assert(!initialised);
initialised = 1;
do_size_checks();
server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort();
/* Use separate connected sockets if client port is not set */
separate_client_sockets = client_port == 0;
/* Use separate connected sockets if client port is negative */
separate_client_sockets = client_port < 0;
if (client_port < 0)
client_port = 0;
server_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
@@ -379,20 +371,7 @@ int
NIO_GetClientSocket(NTP_Remote_Address *remote_addr)
{
if (separate_client_sockets) {
int sock_fd;
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
sock_fd = prepare_socket(AF_INET, 0, 1);
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
sock_fd = prepare_socket(AF_INET6, 0, 1);
break;
#endif
default:
sock_fd = INVALID_SOCK_FD;
}
int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
if (sock_fd == INVALID_SOCK_FD)
return INVALID_SOCK_FD;
@@ -502,6 +481,9 @@ read_from_socket(void *anything)
reponse on a subsequent recvfrom). */
if (status > 0) {
if (msg.msg_namelen > sizeof (where_from))
LOG_FATAL(LOGF_NtpIO, "Truncated source address");
switch (where_from.u.sa_family) {
case AF_INET:
remote_addr.ip_addr.family = IPADDR_INET4;
@@ -555,6 +537,13 @@ read_from_socket(void *anything)
#endif
}
if (status > 0) {
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
status,
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
}
if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) {
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
@@ -571,7 +560,7 @@ read_from_socket(void *anything)
/* ================================================== */
/* Send a packet to given address */
static void
static int
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
union sockaddr_in46 remote;
@@ -586,7 +575,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return;
return 0;
}
switch (remote_addr->ip_addr.family) {
@@ -614,7 +603,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
break;
#endif
default:
return;
return 0;
}
if (addrlen) {
@@ -671,52 +660,40 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
#endif
DEBUG_LOG(LOGF_NtpIO, "Sending to %s:%d from %s fd %d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */
if (!cmsglen)
msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno));
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno));
return 0;
}
DEBUG_LOG(LOGF_NtpIO, "Sent to %s:%d from %s fd %d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
return 1;
}
/* ================================================== */
/* Send an unauthenticated packet to a given address */
void
int
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr, local_addr);
return send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr, local_addr);
}
/* ================================================== */
/* Send an authenticated packet to a given address */
void
int
NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len)
{
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr, local_addr);
}
/* ================================================== */
/* We ought to use getservbyname, but I can't really see this changing */
#define ECHO_PORT 7
void
NIO_SendEcho(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
unsigned long magic_message = 0xbe7ab1e7UL;
NTP_Remote_Address addr;
addr = *remote_addr;
addr.port = ECHO_PORT;
send_packet((void *) &magic_message, sizeof(unsigned long), &addr, local_addr);
return send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr, local_addr);
}

View File

@@ -51,12 +51,9 @@ extern void NIO_CloseClientSocket(int sock_fd);
extern int NIO_IsServerSocket(int sock_fd);
/* Function to transmit a packet */
extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
extern int NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
/* Function to transmit an authenticated packet */
extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len);
/* Function to send a datagram to a remote machine's UDP echo port. */
extern void NIO_SendEcho(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
extern int NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len);
#endif /* GOT_NTP_IO_H */

View File

@@ -64,7 +64,7 @@ static void read_sample(void *anything)
if (s != sizeof (sample)) {
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
s, sizeof (sample));
s, (long)sizeof (sample));
return;
}

View File

@@ -267,7 +267,7 @@ REF_Finalise(void)
LCL_SetLeap(0);
}
if (drift_file && drift_file_age > 0.0) {
if (drift_file) {
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
}
@@ -680,7 +680,7 @@ update_leap_status(NTP_Leap leap, time_t now)
}
}
if (leap_sec != our_leap_sec) {
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
LCL_SetLeap(leap_sec);
our_leap_sec = leap_sec;
}
@@ -1150,6 +1150,31 @@ REF_IsLocalActive(void)
/* ================================================== */
#define LEAP_SECOND_CLOSE 5
int REF_IsLeapSecondClose(void)
{
struct timeval now, now_raw;
time_t t;
if (!our_leap_sec)
return 0;
SCH_GetLastEventTime(&now, NULL, &now_raw);
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
return 0;
}
/* ================================================== */
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{

View File

@@ -161,6 +161,10 @@ extern void REF_EnableLocal(int stratum);
extern void REF_DisableLocal(void);
extern int REF_IsLocalActive(void);
/* Check if current raw or cooked time is close to a leap second
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
#endif /* GOT_REFERENCE_H */

74
rtc.c
View File

@@ -28,6 +28,7 @@
#include "sysincl.h"
#include "rtc.h"
#include "local.h"
#include "logging.h"
#include "conf.h"
@@ -42,7 +43,7 @@ static int driver_initialised = 0;
static struct {
int (*init)(void);
void (*fini)(void);
void (*time_pre_init)(void);
int (*time_pre_init)(void);
void (*time_init)(void (*after_hook)(void*), void *anything);
void (*start_measurements)(void);
int (*write_parameters)(void);
@@ -71,13 +72,51 @@ static struct {
#endif
};
/* ================================================== */
/* Set the system clock to the time of last modification of driftfile
if it's in the future */
static void
fallback_time_init(void)
{
struct timeval now;
struct stat buf;
char *drift_file;
drift_file = CNF_GetDriftFile();
if (!drift_file)
return;
if (stat(drift_file, &buf))
return;
LCL_ReadCookedTime(&now, NULL);
if (now.tv_sec < buf.st_mtime) {
LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime);
LOG(LOGS_INFO, LOGF_Rtc,
"System clock set from driftfile %s", drift_file);
}
}
/* ================================================== */
void
RTC_Initialise(void)
RTC_Initialise(int initial_set)
{
char *file_name;
int ok;
/* Do an initial read of the RTC and set the system time to it. This
is analogous to what /sbin/hwclock -s would do on Linux. If that fails
or RTC is not supported, set the clock to the time of the last
modification of driftfile, so we at least get closer to the truth. */
if (initial_set) {
if (!driver.time_pre_init || !driver.time_pre_init()) {
fallback_time_init();
}
}
driver_initialised = 0;
/* This is how we tell whether the user wants to load the RTC
driver, if he is on a machine where it is an option. */
@@ -90,23 +129,11 @@ RTC_Initialise(void)
if (driver.init) {
if ((driver.init)()) {
ok = 1;
} else {
ok = 0;
driver_initialised = 1;
}
} else {
ok = 0;
LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system");
}
if (ok) {
driver_initialised = 1;
} else {
driver_initialised = 0;
LOG(LOGS_ERR, LOGF_Rtc, "Real time clock not supported on this operating system");
}
} else {
driver_initialised = 0;
}
}
@@ -137,23 +164,10 @@ RTC_TimeInit(void (*after_hook)(void *), void *anything)
if (driver_initialised) {
(driver.time_init)(after_hook, anything);
} else {
LOG(LOGS_ERR, LOGF_Rtc, "Can't initialise from real time clock, driver not loaded");
(after_hook)(anything);
}
}
/* ================================================== */
/* Do an initial read of the RTC and set the system time to it. This
is analogous to what /sbin/hwclock -s would do on Linux. */
void
RTC_TimePreInit(void)
{
if (driver.time_pre_init) {
(driver.time_pre_init)();
}
}
/* ================================================== */
/* Start the RTC measurement process */

3
rtc.h
View File

@@ -28,9 +28,8 @@
#include "reports.h"
extern void RTC_Initialise(void);
extern void RTC_Initialise(int initial_set);
extern void RTC_Finalise(void);
extern void RTC_TimePreInit(void);
extern void RTC_TimeInit(void (*after_hook)(void *), void *anything);
extern void RTC_StartMeasurements(void);
extern int RTC_GetReport(RPT_RTC_Report *report);

View File

@@ -431,7 +431,6 @@ static void
read_coefs_from_file(void)
{
FILE *in;
char line[256];
if (!tried_to_load_coefs) {
@@ -439,26 +438,17 @@ read_coefs_from_file(void)
tried_to_load_coefs = 1;
in = fopen(coefs_file_name, "r");
if (in) {
if (fgets(line, sizeof(line), in)) {
if (sscanf(line, "%d%ld%lf%lf",
&valid_coefs_from_file,
&file_ref_time,
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not parse coefficients line from RTC file %s",
coefs_file_name);
}
if (coefs_file_name && (in = fopen(coefs_file_name, "r"))) {
if (fscanf(in, "%d%ld%lf%lf",
&valid_coefs_from_file,
&file_ref_time,
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read first line from RTC file %s",
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from RTC file %s",
coefs_file_name);
}
fclose(in);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open RTC file %s for reading",
coefs_file_name);
}
}
}
@@ -550,7 +540,8 @@ RTC_Linux_Initialise(void)
fd = open (CNF_GetRtcDevice(), O_RDWR);
if (fd < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open %s, %s", CNF_GetRtcDevice(), strerror(errno));
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open RTC device %s : %s",
CNF_GetRtcDevice(), strerror(errno));
return 0;
}
@@ -975,15 +966,14 @@ RTC_Linux_WriteParameters(void)
etc in this case, since we have fewer requirements regarding the
RTC behaviour than we do for the rest of the module. */
void
int
RTC_Linux_TimePreInit(void)
{
int fd, status;
struct rtc_time rtc_raw, rtc_raw_retry;
struct tm rtc_tm;
time_t rtc_t, estimated_correct_rtc_t;
long interval;
double accumulated_error = 0.0;
time_t rtc_t;
double accumulated_error, sys_offset;
struct timeval new_sys_time, old_sys_time;
coefs_file_name = CNF_GetRtcFile();
@@ -994,7 +984,7 @@ RTC_Linux_TimePreInit(void)
fd = open(CNF_GetRtcDevice(), O_RDONLY);
if (fd < 0) {
return; /* Can't open it, and won't be able to later */
return 0; /* Can't open it, and won't be able to later */
}
/* Retry reading the rtc until both read attempts give the same sec value.
@@ -1007,6 +997,11 @@ RTC_Linux_TimePreInit(void)
}
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
/* Read system clock */
LCL_ReadCookedTime(&old_sys_time, NULL);
close(fd);
if (status >= 0) {
/* Convert to seconds since 1970 */
rtc_tm.tm_sec = rtc_raw.tm_sec;
@@ -1023,37 +1018,35 @@ RTC_Linux_TimePreInit(void)
/* Work out approximatation to correct time (to about the
nearest second) */
if (valid_coefs_from_file) {
interval = rtc_t - file_ref_time;
accumulated_error = file_ref_offset + (double)(interval) * 1.0e-6 * file_rate_ppm;
/* Correct time */
estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
accumulated_error = file_ref_offset +
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
} else {
estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
accumulated_error = 0.0;
}
new_sys_time.tv_sec = estimated_correct_rtc_t;
new_sys_time.tv_usec = 0;
/* Correct time */
new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */
new_sys_time.tv_usec = 500000;
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (!(gettimeofday(&old_sys_time, NULL) < 0) &&
(old_sys_time.tv_sec - new_sys_time.tv_sec > 1 ||
old_sys_time.tv_sec - new_sys_time.tv_sec < -1)) {
if (fabs(sys_offset) >= 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
accumulated_error);
/* Tough luck if this fails */
if (settimeofday(&new_sys_time, NULL) < 0) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not settimeofday");
}
LCL_ApplyStepOffset(sys_offset);
}
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");
return 0;
}
}
close(fd);
return 1;
}
/* ================================================== */

View File

@@ -30,7 +30,7 @@
extern int RTC_Linux_Initialise(void);
extern void RTC_Linux_Finalise(void);
extern void RTC_Linux_TimePreInit(void);
extern int RTC_Linux_TimePreInit(void);
extern void RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything);
extern void RTC_Linux_StartMeasurements(void);

74
sched.c
View File

@@ -57,18 +57,18 @@ static unsigned int n_read_fds;
/* One more than the highest file descriptor that is registered */
static unsigned int one_highest_fd;
/* This assumes that fd_set is implemented as a fixed size array of
bits, possibly embedded inside a record. It might therefore
somewhat non-portable. */
#define FD_SET_SIZE (sizeof(fd_set) * 8)
#ifndef FD_SETSIZE
/* If FD_SETSIZE is not defined, assume that fd_set is implemented
as a fixed size array of bits, possibly embedded inside a record */
#define FD_SETSIZE (sizeof(fd_set) * 8)
#endif
typedef struct {
SCH_FileHandler handler;
SCH_ArbitraryArgument arg;
} FileHandlerEntry;
static FileHandlerEntry file_handlers[FD_SET_SIZE];
static FileHandlerEntry file_handlers[FD_SETSIZE];
/* Timestamp when last select() returned */
static struct timeval last_select_ts, last_select_ts_raw;
@@ -169,6 +169,9 @@ SCH_AddInputFileHandler
assert(initialised);
if (fd >= FD_SETSIZE)
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
@@ -511,6 +514,10 @@ handle_slew(struct timeval *raw,
int i;
if (change_type != LCL_ChangeAdjust) {
/* Make sure this handler is invoked first in order to not shift new timers
added from other handlers */
assert(LCL_IsFirstParameterChangeHandler(handle_slew));
/* If a step change occurs, just shift all raw time stamps by the offset */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
@@ -532,28 +539,49 @@ handle_slew(struct timeval *raw,
#define JUMP_DETECT_THRESHOLD 10
static int
check_current_time(struct timeval *raw, int timeout)
check_current_time(struct timeval *prev_raw, struct timeval *raw, int timeout,
struct timeval *orig_select_tv,
struct timeval *rem_select_tv)
{
double diff;
struct timeval elapsed_min, elapsed_max;
double step, elapsed;
if (last_select_ts_raw.tv_sec > raw->tv_sec + JUMP_DETECT_THRESHOLD) {
/* Get an estimate of the time spent waiting in the select() call. On some
systems (e.g. Linux) the timeout timeval is modified to return the
remaining time, use that information. */
if (timeout) {
elapsed_max = elapsed_min = *orig_select_tv;
} else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
(rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv);
elapsed_max = elapsed_min;
} else {
if (rem_select_tv)
elapsed_max = *orig_select_tv;
else
UTI_DiffTimevals(&elapsed_max, raw, prev_raw);
elapsed_min.tv_sec = 0;
elapsed_min.tv_usec = 0;
}
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
raw->tv_sec + JUMP_DETECT_THRESHOLD) {
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!");
} else if (n_timer_queue_entries > 0 &&
timer_queue.next->tv.tv_sec + JUMP_DETECT_THRESHOLD < raw->tv_sec) {
} else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
raw->tv_sec) {
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!");
} else {
return 1;
}
if (timeout) {
assert(n_timer_queue_entries > 0);
UTI_DiffTimevalsToDouble(&diff, &timer_queue.next->tv, raw);
} else {
UTI_DiffTimevalsToDouble(&diff, &last_select_ts_raw, raw);
}
UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw);
UTI_TimevalToDouble(&elapsed_min, &elapsed);
step += elapsed;
/* Cooked time may no longer be valid after dispatching the handlers */
LCL_NotifyExternalTimeStep(raw, raw, diff, fabs(diff));
LCL_NotifyExternalTimeStep(raw, raw, step, fabs(step));
return 0;
}
@@ -565,8 +593,8 @@ SCH_MainLoop(void)
{
fd_set rd;
int status, errsv;
struct timeval tv, *ptv;
struct timeval now, cooked;
struct timeval tv, saved_tv, *ptv;
struct timeval now, saved_now, cooked;
double err;
assert(initialised);
@@ -574,6 +602,7 @@ SCH_MainLoop(void)
while (!need_to_exit) {
/* Dispatch timeouts and fill now with current raw time */
dispatch_timeouts(&now);
saved_now = now;
/* The timeout handlers may request quit */
if (need_to_exit)
@@ -585,9 +614,12 @@ SCH_MainLoop(void)
UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
ptv = &tv;
assert(tv.tv_sec > 0 || tv.tv_usec > 0);
saved_tv = tv;
} else {
ptv = NULL;
/* This is needed to fix a compiler warning */
saved_tv.tv_sec = 0;
}
/* if there are no file descriptors being waited on and no
@@ -606,7 +638,7 @@ SCH_MainLoop(void)
LCL_CookTime(&now, &cooked, &err);
/* Check if the time didn't jump unexpectedly */
if (!check_current_time(&now, status == 0)) {
if (!check_current_time(&saved_now, &now, status == 0, &saved_tv, ptv)) {
/* Cook the time again after handling the step */
LCL_CookTime(&now, &cooked, &err);
}

View File

@@ -312,6 +312,11 @@ void SRC_AccumulateSample
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
return;
}
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
IS FLIPPED */
SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
@@ -1167,6 +1172,14 @@ SRC_IsSyncPeer(SRC_Instance inst)
/* ================================================== */
int
SRC_IsReachable(SRC_Instance inst)
{
return inst->reachability != 0;
}
/* ================================================== */
int
SRC_ReadNumberOfSources(void)
{

View File

@@ -171,6 +171,7 @@ extern void SRC_DumpSources(void);
extern void SRC_ReloadSources(void);
extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now);

View File

@@ -367,7 +367,7 @@ SYS_Linux_DropRoot(char *user)
}
if (prctl(PR_SET_KEEPCAPS, 1)) {
LOG_FATAL(LOGF_SysLinux, "prcap() failed");
LOG_FATAL(LOGF_SysLinux, "prctl() failed");
}
if (setgroups(0, NULL)) {

View File

@@ -281,7 +281,6 @@ SYS_NetBSD_Initialise(void)
};
kvm_t *kt;
FILE *fp;
kt = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
if (!kt) {

View File

@@ -62,10 +62,10 @@
#include <syslog.h>
#include <time.h>
#if HAS_STDINT_H
#include <stdint.h>
#elif defined(HAS_INTTYPES_H)
#ifdef HAS_INTTYPES_H
#include <inttypes.h>
#elif HAS_STDINT_H
#include <stdint.h>
#else
/* Tough */
#endif

40
test/simulation/008-ntpera Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
. test.common
test_start "NTP eras"
# Assume NTP_ERA_SPLIT is between years 1960 and 1990
# Set date to 500 seconds before NTP second overflows, this should
# work correctly with both 32-bit and 64-bit time_t
export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
# The following tests need 64-bit time_t
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
for year in 1990 2090; do
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
for year in 1950 2130; do
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
# This check is expected to fail
check_sync && test_fail
done
test_pass

22
test/simulation/114-presend Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
. test.common
test_start "presend option"
client_server_options="presend 6"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
base_delay=5
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

24
test/simulation/115-cmdmontime Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
. test.common
test_start "cmdmon timestamps"
# The following tests need 64-bit time_t
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
limit=2
client_server_options="noselect"
client_conf="local stratum 1"
chronyc_start="0.5"
chronyc_conf="tracking"
for year in `seq 1850 100 2300`; do
date="Jan 1 00:00:00 $year"
export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail
done
test_pass

View File

@@ -2,19 +2,25 @@
. test.common
passed=() failed=()
passed=() failed=() skipped=()
[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_])
for test in "${tests[@]}"; do
echo "$test ($[${#passed[@]} + ${#failed[@]} + 1]/${#tests[@]})"
./$test && passed=(${passed[@]} $test) || failed=(${failed[@]} $test)
./$test
case $? in
0) passed=(${passed[@]} $test);;
9) skipped=(${skipped[@]} $test);;
*) failed=(${failed[@]} $test);;
esac
echo
done
echo "SUMMARY:"
echo " TOTAL $[${#passed[@]} + ${#failed[@]}]"
echo " TOTAL $[${#passed[@]} + ${#failed[@]} + ${#skipped[@]}]"
echo " PASSED ${#passed[@]}"
echo " FAILED ${#failed[@]} (${failed[@]})"
echo " SKIPPED ${#skipped[@]} (${skipped[@]})"
[ ${#failed} -eq 0 ]
[ ${#failed[@]} -eq 0 ]

View File

@@ -18,7 +18,7 @@ export PATH=../../:$PATH
export CLKNETSIM_PATH=clknetsim
# Known working clknetsim revision
clknetsim_revision=2fa4c5eae095457ef7d045864dadec59afbffb18
clknetsim_revision=7ea71b32e0caec4d8da4cecc3499b5c87098e137
clknetsim_url=https://github.com/mlichvar/clknetsim/archive/$clknetsim_revision.tar.gz
# Only Linux is supported
@@ -74,7 +74,7 @@ default_chronyd_options=""
default_time_max_limit=1e-3
default_freq_max_limit=5e-4
default_time_rms_limit=2e-4
default_time_rms_limit=3e-4
default_freq_rms_limit=1e-5
default_min_sync_time=120
default_max_sync_time=210
@@ -101,6 +101,11 @@ test_fail() {
exit 1
}
test_skip() {
echo "SKIP"
exit 9
}
test_ok() {
pad_line
echo -e "\tOK"

61
util.c
View File

@@ -210,9 +210,13 @@ UTI_TimevalToString(struct timeval *tv)
char *result;
result = NEXT_BUFFER;
/* TODO: time_t may be wider than long, switch to int64_t before 2038 */
#ifdef HAVE_LONG_TIME_T
snprintf(result, BUFFER_LENGTH, "%"PRId64".%06lu",
(int64_t)tv->tv_sec, (unsigned long)tv->tv_usec);
#else
snprintf(result, BUFFER_LENGTH, "%ld.%06lu",
(long)tv->tv_sec, (unsigned long)tv->tv_usec);
#endif
return result;
}
@@ -494,16 +498,17 @@ void
UTI_TimevalToInt64(struct timeval *src,
NTP_int64 *dest, uint32_t fuzz)
{
unsigned long usec = src->tv_usec;
unsigned long sec = src->tv_sec;
uint32_t lo;
uint32_t lo, sec, usec;
sec = (uint32_t)src->tv_sec;
usec = (uint32_t)src->tv_usec;
/* Recognize zero as a special case - it always signifies
an 'unknown' value */
if (!usec && !sec) {
dest->hi = dest->lo = 0;
} else {
dest->hi = htonl(src->tv_sec + JAN_1970);
dest->hi = htonl(sec + JAN_1970);
/* This formula gives an error of about 0.1us worst case */
lo = 4295 * usec - (usec>>5) - (usec>>9);
@@ -521,13 +526,23 @@ void
UTI_Int64ToTimeval(NTP_int64 *src,
struct timeval *dest)
{
uint32_t ntp_sec, ntp_frac;
/* As yet, there is no need to check for zero - all processing that
has to detect that case is in the NTP layer */
dest->tv_sec = ntohl(src->hi) - JAN_1970;
ntp_sec = ntohl(src->hi);
ntp_frac = ntohl(src->lo);
#ifdef HAVE_LONG_TIME_T
dest->tv_sec = ntp_sec - (uint32_t)(NTP_ERA_SPLIT + JAN_1970) +
(time_t)NTP_ERA_SPLIT;
#else
dest->tv_sec = ntp_sec - JAN_1970;
#endif
/* Until I invent a slick way to do this, just do it the obvious way */
dest->tv_usec = (int)(0.5 + (double)(ntohl(src->lo)) / 4294.967296);
dest->tv_usec = (int)(0.5 + (double)(ntp_frac) / 4294.967296);
}
/* ================================================== */
@@ -535,21 +550,22 @@ UTI_Int64ToTimeval(NTP_int64 *src,
void
UTI_TimevalNetworkToHost(Timeval *src, struct timeval *dest)
{
uint32_t sec_low, sec_high;
uint32_t sec_low;
#ifdef HAVE_LONG_TIME_T
uint32_t sec_high;
#endif
dest->tv_usec = ntohl(src->tv_nsec) / 1000;
sec_high = ntohl(src->tv_sec_high);
sec_low = ntohl(src->tv_sec_low);
#ifdef HAVE_LONG_TIME_T
sec_high = ntohl(src->tv_sec_high);
if (sec_high == TV_NOHIGHSEC)
sec_high = 0;
/* get the missing bits from current time when received timestamp
is only 32-bit */
if (sizeof (time_t) > 4 && sec_high == TV_NOHIGHSEC) {
struct timeval now;
gettimeofday(&now, NULL);
sec_high = now.tv_sec >> 16 >> 16;
}
dest->tv_sec = (time_t)sec_high << 16 << 16 | sec_low;
dest->tv_sec = (uint64_t)sec_high << 32 | sec_low;
#else
dest->tv_sec = sec_low;
#endif
}
/* ================================================== */
@@ -558,10 +574,11 @@ void
UTI_TimevalHostToNetwork(struct timeval *src, Timeval *dest)
{
dest->tv_nsec = htonl(src->tv_usec * 1000);
if (sizeof (time_t) > 4)
dest->tv_sec_high = htonl(src->tv_sec >> 16 >> 16);
else
dest->tv_sec_high = htonl(TV_NOHIGHSEC);
#ifdef HAVE_LONG_TIME_T
dest->tv_sec_high = htonl((uint64_t)src->tv_sec >> 32);
#else
dest->tv_sec_high = htonl(TV_NOHIGHSEC);
#endif
dest->tv_sec_low = htonl(src->tv_sec);
}