Compare commits

..

8 Commits
2.4 ... 1.31.2

Author SHA1 Message Date
Miroslav Lichvar
aabb564320 doc: update NEWS 2016-01-19 08:15:15 +01:00
Miroslav Lichvar
df46e5ca5d ntp: restrict authentication of server/peer to specified key
When a server/peer was specified with a key number to enable
authentication with a symmetric key, packets received from the
server/peer were accepted if they were authenticated with any of
the keys contained in the key file and not just the specified key.

This allowed an attacker who knew one key of a client/peer to modify
packets from its servers/peers that were authenticated with other
keys in a man-in-the-middle (MITM) attack. For example, in a network
where each NTP association had a separate key and all hosts had only
keys they needed, a client of a server could not attack other clients
of the server, but it could attack the server and also attack its own
clients (i.e. modify packets from other servers).

To not allow the server/peer to be authenticated with other keys
extend the authentication test to check if the key ID in the received
packet is equal to the configured key number. As a consequence, it's
no longer possible to authenticate two peers to each other with two
different keys, both peers have to be configured to use the same key.

This issue was discovered by Matt Street of Cisco ASIG.
2016-01-19 08:15:12 +01:00
Miroslav Lichvar
370ba5e8fc doc: warn that unauthenticated peers are vulnerable to DoS attack 2016-01-19 08:14:46 +01:00
Miroslav Lichvar
463093803d doc: fix CVE-ID in NEWS
CVE-2015-1853 is for chrony, CVE-2015-1799 is for ntp.
2015-04-08 08:47:13 +02:00
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
172 changed files with 17416 additions and 15987 deletions

32
.gitignore vendored
View File

@@ -2,24 +2,20 @@
.vimrc .vimrc
*.o *.o
*.swp *.swp
*.dSYM RELEASES
*.DS_Store Makefile
chrony.conf.5
chrony.info
chrony.html
chrony.texi
chrony.txt
chronyc
chronyc.1
chronyd
chronyd.8
config.h
config.log
tags tags
/RELEASES version.h
/Makefile
/chronyc
/chronyd
/config.h
/config.log
/doc/Makefile
/doc/*.html
/doc/*.man
/doc/*.man.in
/doc/*.txt
/getdate.c
/version.h
/test/simulation/clknetsim /test/simulation/clknetsim
/test/simulation/tmp /test/simulation/tmp
/test/unit/Makefile
/test/unit/*.test
/test/unit/*.o

View File

@@ -24,6 +24,9 @@
SYSCONFDIR=@SYSCONFDIR@ SYSCONFDIR=@SYSCONFDIR@
BINDIR=@BINDIR@ BINDIR=@BINDIR@
SBINDIR=@SBINDIR@ SBINDIR=@SBINDIR@
MANDIR=@MANDIR@
INFODIR=@INFODIR@
DOCDIR=@DOCDIR@
LOCALSTATEDIR=@LOCALSTATEDIR@ LOCALSTATEDIR=@LOCALSTATEDIR@
CHRONYVARDIR=@CHRONYVARDIR@ CHRONYVARDIR=@CHRONYVARDIR@
@@ -35,13 +38,18 @@ DESTDIR=
HASH_OBJ = @HASH_OBJ@ HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \ OBJS = util.o sched.o regress.o local.o \
reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \ sys.o main.o ntp_io.o ntp_core.o ntp_sources.o \
sys.o smooth.o tempcomp.o util.o $(HASH_OBJ) sources.o sourcestats.o reference.o \
logging.o conf.o cmdmon.o keys.o \
nameserv.o nameserv_async.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
broadcast.o refclock.o refclock_phc.o refclock_pps.o \
refclock_shm.o refclock_sock.o tempcomp.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@ EXTRA_OBJS=@EXTRA_OBJECTS@
CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \ CLI_OBJS = client.o nameserv.o getdate.o cmdparse.o \
pktlength.o util.o $(HASH_OBJ) pktlength.o util.o $(HASH_OBJ)
ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS) ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS)
@@ -70,17 +78,14 @@ $(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
$(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $< $(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
distclean : clean distclean : clean
-rm -f .DS_Store -rm -f Makefile
-rm -f Makefile config.h config.log -rm -f chrony.conf.5 chrony.texi chronyc.1 chronyd.8
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean
clean : clean :
-rm -f *.o *.s chronyc chronyd core *~ -rm -f *.o *.s chronyc chronyd core *~ chrony.info chrony.html chrony.txt
-rm -rf .deps -rm -rf .deps
-rm -rf *.dSYM
getdate.c : getdate.y getdate.c :
bison -o getdate.c getdate.y bison -o getdate.c getdate.y
# This can be used to force regeneration of getdate.c # This can be used to force regeneration of getdate.c
@@ -90,10 +95,15 @@ getdate :
# For install, don't use the install command, because its switches # For install, don't use the install command, because its switches
# seem to vary between systems. # seem to vary between systems.
install: chronyd chronyc install: chronyd chronyc chrony.txt
[ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR) [ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR)
[ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR) [ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR)
[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) [ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
[ -d $(DESTDIR)$(MANDIR)/man1 ] || mkdir -p $(DESTDIR)$(MANDIR)/man1
[ -d $(DESTDIR)$(MANDIR)/man5 ] || mkdir -p $(DESTDIR)$(MANDIR)/man5
[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
[ -d $(DESTDIR)$(CHRONYVARDIR) ] || mkdir -p $(DESTDIR)$(CHRONYVARDIR) [ -d $(DESTDIR)$(CHRONYVARDIR) ] || mkdir -p $(DESTDIR)$(CHRONYVARDIR)
if [ -f $(DESTDIR)$(SBINDIR)/chronyd ]; then rm -f $(DESTDIR)$(SBINDIR)/chronyd ; fi if [ -f $(DESTDIR)$(SBINDIR)/chronyd ]; then rm -f $(DESTDIR)$(SBINDIR)/chronyd ; fi
if [ -f $(DESTDIR)$(BINDIR)/chronyc ]; then rm -f $(DESTDIR)$(BINDIR)/chronyc ; fi if [ -f $(DESTDIR)$(BINDIR)/chronyc ]; then rm -f $(DESTDIR)$(BINDIR)/chronyc ; fi
@@ -101,13 +111,20 @@ install: chronyd chronyc
chmod 755 $(DESTDIR)$(SBINDIR)/chronyd chmod 755 $(DESTDIR)$(SBINDIR)/chronyd
cp chronyc $(DESTDIR)$(BINDIR)/chronyc cp chronyc $(DESTDIR)$(BINDIR)/chronyc
chmod 755 $(DESTDIR)$(BINDIR)/chronyc chmod 755 $(DESTDIR)$(BINDIR)/chronyc
$(MAKE) -C doc install cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt
chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt
docs : cp COPYING $(DESTDIR)$(DOCDIR)/COPYING
$(MAKE) -C doc docs chmod 644 $(DESTDIR)$(DOCDIR)/COPYING
cp README $(DESTDIR)$(DOCDIR)/README
install-docs : chmod 644 $(DESTDIR)$(DOCDIR)/README
$(MAKE) -C doc install-docs cp chrony.1 $(DESTDIR)$(MANDIR)/man1
chmod 644 $(DESTDIR)$(MANDIR)/man1/chrony.1
cp chronyc.1 $(DESTDIR)$(MANDIR)/man1
chmod 644 $(DESTDIR)$(MANDIR)/man1/chronyc.1
cp chronyd.8 $(DESTDIR)$(MANDIR)/man8
chmod 644 $(DESTDIR)$(MANDIR)/man8/chronyd.8
cp chrony.conf.5 $(DESTDIR)$(MANDIR)/man5
chmod 644 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
%.o : %.c %.o : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
@@ -116,14 +133,29 @@ install-docs :
$(CC) $(CFLAGS) $(CPPFLAGS) -S $< $(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run cd test/simulation && ./run
Makefile : Makefile.in configure install-docs : docs
@echo [ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
@echo Makefile needs to be regenerated, run ./configure cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt
@echo chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt
@exit 1 cp chrony.html $(DESTDIR)$(DOCDIR)/chrony.html
chmod 644 $(DESTDIR)$(DOCDIR)/chrony.html
[ -d $(DESTDIR)$(INFODIR) ] || mkdir -p $(DESTDIR)$(INFODIR)
cp chrony.info* $(DESTDIR)$(INFODIR)
chmod 644 $(DESTDIR)$(INFODIR)/chrony.info*
docs : chrony.txt chrony.html chrony.info
chrony.txt : chrony.texi
makeinfo --no-headers --number-sections -o chrony.txt chrony.texi
chrony.html : chrony.texi
command -v texi2html > /dev/null 2>&1 && texi2html chrony.texi || \
makeinfo --no-split --html --number-sections -o chrony.html chrony.texi
chrony.info : chrony.texi
makeinfo chrony.texi
.deps: .deps:
@mkdir .deps @mkdir .deps

157
NEWS
View File

@@ -1,163 +1,10 @@
New in version 2.4 New in version 1.31.2
================== =====================
Enhancements
------------
* Add orphan option to local directive for orphan mode compatible with ntpd
* Add distance option to local directive to set activation threshold
(1 second by default)
* Add maxdrift directive to set maximum allowed drift of system clock
* Try to replace NTP sources exceeding maximum distance
* Randomise source replacement to avoid getting stuck with bad sources
* Randomise selection of sources from pools on start
* Ignore reference timestamp as ntpd doesn't always set it correctly
* Modify tracking report to use same values as seen by NTP clients
* Add -c option to chronyc to write reports in CSV format
* Provide detailed manual pages
Bug fixes
---------
* Fix SOCK refclock to work correctly when not specified as last refclock
* Fix initstepslew and -q/-Q options to accept time from own NTP clients
* Fix authentication with keys using 512-bit hash functions
* Fix crash on exit when multiple signals are received
* Fix conversion of very small floating-point numbers in command packets
Removed features
----------------
* Drop documentation in Texinfo format
New in version 2.3
==================
Enhancements
------------
* Add support for NTP and command response rate limiting
* Add support for dropping root privileges on Mac OS X, FreeBSD, Solaris
* Add require and trust options for source selection
* Enable logchange by default (1 second threshold)
* Set RTC on Mac OS X with rtcsync directive
* Allow binding to NTP port after dropping root privileges on NetBSD
* Drop CAP_NET_BIND_SERVICE capability on Linux when NTP port is disabled
* Resolve names in separate process when seccomp filter is enabled
* Replace old records in client log when memory limit is reached
* Don't reveal local time and synchronisation state in client packets
* Don't keep client sockets open for longer than necessary
* Ignore poll in KoD RATE packets as ntpd doesn't always set it correctly
* Warn when using keys shorter than 80 bits
* Add keygen command to generate random keys easily
* Add serverstats command to report NTP and command packet statistics
Bug fixes
---------
* Fix clock correction after making step on Mac OS X
* Fix building on Solaris
New in version 2.2.1
====================
Security fixes Security fixes
-------------- --------------
* Restrict authentication of NTP server/peer to specified key (CVE-2016-1567) * Restrict authentication of NTP server/peer to specified key (CVE-2016-1567)
New in version 2.2
==================
Enhancements
------------
* Add support for configuration and monitoring over Unix domain socket
(accessible by root or chrony user when root privileges are dropped)
* Add support for system call filtering with seccomp on Linux (experimental)
* Add support for dropping root privileges on NetBSD
* Control frequency of system clock on FreeBSD, NetBSD, Solaris
* Add system leap second handling mode on FreeBSD, NetBSD, Solaris
* Add dynamic drift removal on Mac OS X
* Add support for setting real-time priority on Mac OS X
* Add maxdistance directive to limit source selection by root distance
(3 seconds by default)
* Add refresh command to get new addresses of NTP sources
* Allow wildcard patterns in include directive
* Restore time from driftfile with -s option if later than RTC time
* Add configure option to set default hwclockfile
* Add -d option to chronyc to enable debug messages
* Allow multiple addresses to be specified for chronyc with -h option
and reconnect when no valid reply is received
* Make check interval in waitsync command configurable
Bug fixes
---------
* Fix building on NetBSD, Solaris
* Restore time from driftfile with -s option if reading RTC failed
Removed features
----------------
* Drop support for authentication with command key (run-time configuration
is now allowed only for local users that can access the Unix domain socket)
New in version 2.1.1
====================
Bug fixes
---------
* Fix clock stepping by integer number of seconds on Linux
New in version 2.1
==================
Enhancements
------------
* Add support for Mac OS X
* Try to replace unreachable and falseticker servers/peers specified
by name like pool sources
* Add leaponly option to smoothtime directive to allow synchronised
leap smear between multiple servers
* Use specific reference ID when smoothing served time
* Add smoothing command to report time smoothing status
* Add smoothtime command to activate or reset time smoothing
Bug fixes
---------
* Fix crash in source selection with preferred sources
* Fix resetting of time smoothing
* Include packet precision in peer dispersion
* Fix crash in chronyc on invalid command syntax
New in version 2.0
==================
Enhancements
------------
* Update to NTP version 4 (RFC 5905)
* Add pool directive to specify pool of NTP servers
* Add leapsecmode directive to select how to correct clock for leap second
* Add smoothtime directive to smooth served time and enable leap smear
* Add minsources directive to set required number of selectable sources
* Add minsamples and maxsamples options for all sources
* Add tempcomp configuration with list of points
* Allow unlimited number of NTP sources, refclocks and keys
* Allow unreachable sources to remain selected
* Improve source selection
* Handle offline sources as unreachable
* Open NTP server port only when necessary (client access is allowed by
allow directive/command or peer/broadcast is configured)
* Change default bindcmdaddress to loopback address
* Change default maxdelay to 3 seconds
* Change default stratumweight to 0.001
* Update adjtimex synchronisation status
* Use system headers for adjtimex
* Check for memory allocation errors
* Reduce memory usage
* Add configure options to compile without NTP, cmdmon, refclock support
* Extend makestep command to set automatic clock stepping
Bug fixes
---------
* Add sanity checks for time and frequency offset
* Don't report synchronised status during leap second
* Don't combine reference clocks with close NTP sources
* Fix accepting requests from configured sources
* Fix initial fallback drift setting
New in version 1.31.1 New in version 1.31.1
===================== =====================

106
README
View File

@@ -3,35 +3,66 @@ This is the README for chrony.
What is chrony? What is chrony?
=============== ===============
chrony is a versatile implementation of the Network Time Protocol (NTP). Chrony is a pair of programs for maintaining the accuracy of computer
It can synchronize the system clock with NTP servers, reference clocks clocks.
(e.g. GPS receiver), and manual input using wristwatch and keyboard.
It can also operate as an NTPv4 (RFC 5905) server and peer to provide
a time service to other computers in the network.
It is designed to perform well in a wide range of conditions, including chronyd is a (background) daemon program that can be started at boot
intermittent network connections, heavily congested networks, changing time. This does most of the work.
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
Typical accuracy between two machines on a LAN is in tens, or a few chronyc is a command-line interface program which can be used to
hundreds, of microseconds; over the Internet, accuracy is typically monitor chronyd's performance and to change various operating
within a few milliseconds. With a good hardware reference clock parameters whilst it is running.
sub-microsecond accuracy is possible.
chronyd's main function is to obtain measurements of the true (UTC)
time from one of several sources, and correct the system clock
accordingly. It also works out the rate at which the system clock
gains or loses time and uses this information to keep it accurate
between measurements from the reference.
The reference time can be derived from Network Time Protocol (NTP)
servers, reference clocks, or wristwatch-and-keyboard (via chronyc).
The main source of information about the Network Time Protocol is
http://www.ntp.org.
It is designed so that it can work on computers which only have
intermittent access to reference sources, for example computers which
use a dial-up account to access the Internet or laptops. Of course, it
will work well on computers with permanent connections too.
In addition, on Linux it can monitor the system's real time clock
performance, so the system can maintain accurate time even across
reboots.
Typical accuracies available between 2 machines are
On an ethernet LAN : 100-200 microseconds, often much better
On a V32bis dial-up modem connection : 10's of milliseconds (from one
session to the next)
With a good reference clock the accuracy can reach one microsecond.
chronyd can also operate as an RFC1305-compatible NTP server and peer.
Two programs are included in chrony, chronyd is a daemon that can be
started at boot time and chronyc is a command-line interface program
which can be used to monitor chronyd's performance and to change various
operating parameters whilst it is running.
What will chrony run on? What will chrony run on?
======================== ========================
The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and Chrony can be successfully built and run on
Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one 1. Linux 2.2.x, 2.3.x, 2.4.x, 2.6.x, 3.x
of the existing system-specific drivers and look into the quirks of
certain system calls and the kernel on your target system. 2. Solaris 2.5/2.5.1/2.6/2.7/2.8 (various platforms)
3. SunOS 4.1.4 (Sparc 2 and Sparc 20)
4. BSD/386 v1.1 has been reported to work using the SunOS 4.1 driver.
5. NetBSD.
Any other system will require a porting exercise. You would need to
start from one of the existing system-specific drivers and look into
the quirks of certain system calls and the kernel on your target
system.
How do I set it up? How do I set it up?
=================== ===================
@@ -53,7 +84,7 @@ ready-formatted plain text (chrony.txt) in the distribution.
There is also information available on the chrony web pages, accessible There is also information available on the chrony web pages, accessible
through the URL through the URL
https://chrony.tuxfamily.org/ http://chrony.tuxfamily.org/
Where are new versions announced? Where are new versions announced?
================================= =================================
@@ -84,17 +115,6 @@ chrony-dev-request@chrony.tuxfamily.org
as applicable. as applicable.
When you are reporting a bug, please send us all the information you can.
Unfortunately, chrony has proven to be one of those programs where it is very
difficult to reproduce bugs in a different environment. So we may have to
interact with you quite a lot to obtain enough extra logging and tracing to
pin-point the problem in some cases. Please be patient and plan for this!
License
=======
chrony is distributed under the GNU General Public License version 2.
Author Author
====== ======
@@ -111,10 +131,6 @@ Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements Acknowledgements
================ ================
In writing the chronyd program, extensive use has been made of RFC 1305
and RFC 5905, written by David Mills. The source code of the NTP reference
implementation has been used to check the details of the protocol.
The following people have provided patches and other major contributions The following people have provided patches and other major contributions
to the program : to the program :
@@ -138,11 +154,6 @@ Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Erik Bryer <ebryer@spots.ab.ca> Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory Entries in contrib directory
Bryan Christianson <bryan@whatroute.net>
Support for Mac OS X
Support for privilege separation
Entries in contrib directory
Juliusz Chroboczek <jch@pps.jussieu.fr> Juliusz Chroboczek <jch@pps.jussieu.fr>
Fix install rule in Makefile if chronyd file is in use. Fix install rule in Makefile if chronyd file is in use.
@@ -157,9 +168,6 @@ Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for Changes to installation directory system to make it easier for
package builders. package builders.
Andrew Griffiths <agriffit@redhat.com>
Patch to add support for seccomp filter
Walter Haidinger <walter.haidinger@gmx.at> Walter Haidinger <walter.haidinger@gmx.at>
Providing me with login access to a Linux installation where v1.12 Providing me with login access to a Linux installation where v1.12
wouldn't compile, so I could develop the fixes for v1.13. Also, for wouldn't compile, so I could develop the fixes for v1.13. Also, for
@@ -219,18 +227,12 @@ Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de> Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values Handling arbitrary HZ values
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc
Andreas Piesk <apiesk@virbus.de> Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available Patch to make chronyc use the readline library if available
Timo Teras <timo.teras@iki.fi> Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts Patch to reply correctly on multihomed hosts
Stephen Wadeley <swadeley@redhat.com>
Improvements to man pages
Wolfgang Weisselberg <weissel@netcologne.de> Wolfgang Weisselberg <weissel@netcologne.de>
Entries in contrib directory Entries in contrib directory

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997,1998,1999,2000,2001,2002,2005 * Copyright (C) Richard P. Curnow 1997,1998,1999,2000,2001,2002,2005
* Copyright (C) Miroslav Lichvar 2009, 2015 * Copyright (C) Miroslav Lichvar 2009
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -62,7 +62,7 @@ split_ip6(IPAddr *ip, uint32_t *dst)
int i; int i;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
dst[i] = (uint32_t)ip->addr.in6[i * 4 + 0] << 24 | dst[i] = ip->addr.in6[i * 4 + 0] << 24 |
ip->addr.in6[i * 4 + 1] << 16 | ip->addr.in6[i * 4 + 1] << 16 |
ip->addr.in6[i * 4 + 2] << 8 | ip->addr.in6[i * 4 + 2] << 8 |
ip->addr.in6[i * 4 + 3]; ip->addr.in6[i * 4 + 3];
@@ -366,38 +366,114 @@ ADF_IsAllowed(ADF_AuthTable table,
/* ================================================== */ /* ================================================== */
static int #if defined TEST
is_any_allowed(TableNode *node, State parent)
static void print_node(TableNode *node, uint32_t *addr, int ip_len, int shift, int subnet_bits)
{ {
State state; uint32_t new_addr[4];
int i; int i;
TableNode *sub_node;
state = node->state != AS_PARENT ? node->state : parent; for (i=0; i<subnet_bits; i++) putchar(' ');
assert(state != AS_PARENT);
if (node->extended) { if (ip_len == 1)
for (i = 0; i < TABLE_SIZE; i++) { printf("%d.%d.%d.%d",
if (is_any_allowed(&node->extended[i], state)) ((addr[0] >> 24) & 255),
return 1; ((addr[0] >> 16) & 255),
((addr[0] >> 8) & 255),
((addr[0] ) & 255));
else {
for (i=0; i<4; i++) {
if (addr[i])
printf("%d.%d.%d.%d",
((addr[i] >> 24) & 255),
((addr[i] >> 16) & 255),
((addr[i] >> 8) & 255),
((addr[i] ) & 255));
putchar(i < 3 ? ':' : '\0');
} }
} else if (state == ALLOW) {
return 1;
} }
printf("/%d : %s\n",
subnet_bits,
(node->state == ALLOW) ? "allow" :
(node->state == DENY) ? "deny" : "as parent");
if (node->extended) {
for (i=0; i<16; i++) {
sub_node = &(node->extended[i]);
new_addr[0] = addr[0];
new_addr[1] = addr[1];
new_addr[2] = addr[2];
new_addr[3] = addr[3];
new_addr[ip_len - 1 - shift / 32] |= ((uint32_t)i << (shift % 32));
print_node(sub_node, new_addr, ip_len, shift - 4, subnet_bits + 4);
}
}
}
return 0;
static void print_table(ADF_AuthTable table)
{
uint32_t addr[4];
memset(addr, 0, sizeof (addr));
printf("IPv4 table:\n");
print_node(&table->base4, addr, 1, 28, 0);
memset(addr, 0, sizeof (addr));
printf("IPv6 table:\n");
print_node(&table->base6, addr, 4, 124, 0);
} }
/* ================================================== */ /* ================================================== */
int int main (int argc, char **argv)
ADF_IsAnyAllowed(ADF_AuthTable table, int family)
{ {
switch (family) { IPAddr ip;
case IPADDR_INET4: ADF_AuthTable table;
return is_any_allowed(&table->base4, AS_PARENT); table = ADF_CreateTable();
case IPADDR_INET6:
return is_any_allowed(&table->base6, AS_PARENT); ip.family = IPADDR_INET4;
default:
return 0; ip.addr.in4 = 0x7e800000;
} ADF_Allow(table, &ip, 9);
ip.addr.in4 = 0x7ecc0000;
ADF_Deny(table, &ip, 14);
#if 0
ip.addr.in4 = 0x7f000001;
ADF_Deny(table, &ip, 32);
ip.addr.in4 = 0x7f000000;
ADF_Allow(table, &ip, 8);
#endif
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.addr.in4 ^= 1;
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.family = IPADDR_INET6;
memcpy(ip.addr.in6, "abcdefghijklmnop", 16);
ADF_Deny(table, &ip, 66);
ADF_Allow(table, &ip, 59);
memcpy(ip.addr.in6, "xbcdefghijklmnop", 16);
ADF_Deny(table, &ip, 128);
ip.addr.in6[15] ^= 3;
ADF_Allow(table, &ip, 127);
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.addr.in4 ^= 1;
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
print_table(table);
ADF_DestroyTable(table);
return 0;
} }
#endif /* defined TEST */

View File

@@ -72,9 +72,4 @@ extern void ADF_DestroyTable(ADF_AuthTable table);
extern int ADF_IsAllowed(ADF_AuthTable table, extern int ADF_IsAllowed(ADF_AuthTable table,
IPAddr *ip); IPAddr *ip);
/* Check if at least one address from a given family is allowed by
the rules in the table */
extern int ADF_IsAnyAllowed(ADF_AuthTable table,
int family);
#endif /* GOT_ADDRFILT_H */ #endif /* GOT_ADDRFILT_H */

135
array.c
View File

@@ -1,135 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Functions implementing an array with automatic memory allocation.
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "memory.h"
struct ARR_Instance_Record {
void *data;
unsigned int elem_size;
unsigned int used;
unsigned int allocated;
};
ARR_Instance
ARR_CreateInstance(unsigned int elem_size)
{
ARR_Instance array;
assert(elem_size > 0);
array = MallocNew(struct ARR_Instance_Record);
array->data = NULL;
array->elem_size = elem_size;
array->used = 0;
array->allocated = 0;
return array;
}
void
ARR_DestroyInstance(ARR_Instance array)
{
Free(array->data);
Free(array);
}
static void
realloc_array(ARR_Instance array, unsigned int min_size)
{
size_t data_size;
assert(min_size <= 2 * min_size);
if (array->allocated >= min_size && array->allocated <= 2 * min_size)
return;
if (array->allocated < min_size) {
while (array->allocated < min_size)
array->allocated = array->allocated ? 2 * array->allocated : 1;
} else {
array->allocated = min_size;
}
data_size = (size_t)array->elem_size * array->allocated;
assert(data_size / array->elem_size == array->allocated);
array->data = Realloc(array->data, data_size);
}
void *
ARR_GetNewElement(ARR_Instance array)
{
array->used++;
realloc_array(array, array->used);
return ARR_GetElement(array, array->used - 1);
}
void *
ARR_GetElement(ARR_Instance array, unsigned int index)
{
assert(index < array->used);
return (void *)((char *)array->data + (size_t)index * array->elem_size);
}
void *
ARR_GetElements(ARR_Instance array)
{
/* Return a non-NULL pointer when the array has zero size */
if (!array->data) {
assert(!array->used);
return array;
}
return array->data;
}
void
ARR_AppendElement(ARR_Instance array, void *element)
{
void *e;
e = ARR_GetNewElement(array);
memcpy(e, element, array->elem_size);
}
void
ARR_SetSize(ARR_Instance array, unsigned int size)
{
realloc_array(array, size);
array->used = size;
}
unsigned int
ARR_GetSize(ARR_Instance array)
{
return array->used;
}

56
array.h
View File

@@ -1,56 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for array functions.
*/
#ifndef GOT_ARRAY_H
#define GOT_ARRAY_H
typedef struct ARR_Instance_Record *ARR_Instance;
/* Create a new array with given element size */
extern ARR_Instance ARR_CreateInstance(unsigned int elem_size);
/* Destroy the array */
extern void ARR_DestroyInstance(ARR_Instance array);
/* Return pointer to a new element added to the end of the array */
extern void *ARR_GetNewElement(ARR_Instance array);
/* Return element with given index */
extern void *ARR_GetElement(ARR_Instance array, unsigned int index);
/* Return pointer to the internal array of elements */
extern void *ARR_GetElements(ARR_Instance array);
/* Add a new element to the end of the array */
extern void ARR_AppendElement(ARR_Instance array, void *element);
/* Set the size of the array */
extern void ARR_SetSize(ARR_Instance array, unsigned int size);
/* Return current size of the array */
extern unsigned int ARR_GetSize(ARR_Instance array);
#endif

159
broadcast.c Normal file
View File

@@ -0,0 +1,159 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Deal with broadcast server functions.
*/
#include "config.h"
#include "sysincl.h"
#include "memory.h"
#include "addressing.h"
#include "broadcast.h"
#include "sched.h"
#include "ntp.h"
#include "local.h"
#include "reference.h"
#include "util.h"
#include "ntp_io.h"
typedef struct {
NTP_Remote_Address addr;
NTP_Local_Address local_addr;
int interval;
} Destination;
static Destination *destinations = 0;
static int n_destinations = 0;
static int max_destinations = 0;
void
BRD_Initialise(void)
{
}
/* ================================================== */
void
BRD_Finalise(void)
{
}
/* ================================================== */
/* This is a cut-down version of what transmit_packet in ntp_core.c does */
static void
timeout_handler(void *arbitrary)
{
Destination *d = (Destination *) arbitrary;
NTP_Packet message;
/* Parameters read from reference module */
int version;
int leap;
int are_we_synchronised, our_stratum;
NTP_Leap leap_status;
uint32_t our_ref_id, ts_fuzz;
struct timeval our_ref_time;
double our_root_delay, our_root_dispersion;
struct timeval local_transmit;
version = 3;
LCL_ReadCookedTime(&local_transmit, NULL);
REF_GetReferenceParams(&local_transmit,
&are_we_synchronised, &leap_status,
&our_stratum,
&our_ref_id, &our_ref_time,
&our_root_delay, &our_root_dispersion);
if (are_we_synchronised) {
leap = (int) leap_status;
} else {
leap = LEAP_Unsynchronised;
}
message.lvm = ((leap << 6) &0xc0) | ((version << 3) & 0x38) | (MODE_BROADCAST & 0x07);
message.stratum = our_stratum;
message.poll = 6; /* FIXME: what should this be? */
message.precision = LCL_GetSysPrecisionAsLog();
/* If we're sending a client mode packet and we aren't synchronized yet,
we might have to set up artificial values for some of these parameters */
message.root_delay = UTI_DoubleToInt32(our_root_delay);
message.root_dispersion = UTI_DoubleToInt32(our_root_dispersion);
message.reference_id = htonl((NTP_int32) our_ref_id);
/* Now fill in timestamps */
UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, 0);
message.originate_ts.hi = 0UL;
message.originate_ts.lo = 0UL;
message.receive_ts.hi = 0UL;
message.receive_ts.lo = 0UL;
ts_fuzz = UTI_GetNTPTsFuzz(message.precision);
LCL_ReadCookedTime(&local_transmit, NULL);
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
NIO_SendNormalPacket(&message, &d->addr, &d->local_addr);
/* Requeue timeout. Don't care if interval drifts gradually, so just do it
* at the end. */
SCH_AddTimeoutInClass((double) d->interval, 1.0, 0.02,
SCH_NtpBroadcastClass,
timeout_handler, (void *) d);
}
/* ================================================== */
void
BRD_AddDestination(IPAddr *addr, unsigned short port, int interval)
{
if (max_destinations == n_destinations) {
/* Expand array */
max_destinations += 8;
if (destinations) {
destinations = ReallocArray(Destination, max_destinations, destinations);
} else {
destinations = MallocArray(Destination, max_destinations);
}
}
destinations[n_destinations].addr.ip_addr = *addr;
destinations[n_destinations].addr.port = port;
destinations[n_destinations].local_addr.ip_addr.family = IPADDR_UNSPEC;
destinations[n_destinations].local_addr.sock_fd =
NIO_GetServerSocket(&destinations[n_destinations].addr);
destinations[n_destinations].interval = interval;
SCH_AddTimeoutInClass((double) interval, 1.0, 0.0,
SCH_NtpBroadcastClass,
timeout_handler, (void *)(destinations + n_destinations));
++n_destinations;
}

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2015 * Copyright (C) Richard P. Curnow 1997-2002
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -21,28 +21,17 @@
======================================================================= =======================================================================
This module implements time smoothing. Deal with broadcast server functions.
*/ */
#ifndef GOT_SMOOTH_H #ifndef GOT_BROADCAST_H
#define GOT_SMOOTH_H #define GOT_BROADCAST_H
#include "reports.h" #include "addressing.h"
extern void SMT_Initialise(void); extern void BRD_Initialise(void);
extern void BRD_Finalise(void);
extern void BRD_AddDestination(IPAddr *addr, unsigned short port, int interval);
extern void SMT_Finalise(void); #endif /* GOT_BROADCAST_H */
extern int SMT_IsEnabled(void);
extern double SMT_GetOffset(struct timeval *now);
extern void SMT_Activate(struct timeval *now);
extern void SMT_Reset(struct timeval *now);
extern void SMT_Leap(struct timeval *now, int leap);
extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now);
#endif

187
candm.h
View File

@@ -31,6 +31,7 @@
#include "sysincl.h" #include "sysincl.h"
#include "addressing.h" #include "addressing.h"
#include "hash.h"
/* This is the default port to use for CANDM, if no alternative is /* This is the default port to use for CANDM, if no alternative is
defined */ defined */
@@ -87,14 +88,11 @@
#define REQ_MODIFY_MAXDELAYDEVRATIO 47 #define REQ_MODIFY_MAXDELAYDEVRATIO 47
#define REQ_RESELECT 48 #define REQ_RESELECT 48
#define REQ_RESELECTDISTANCE 49 #define REQ_RESELECTDISTANCE 49
#define REQ_MODIFY_MAKESTEP 50 #define N_REQUEST_TYPES 50
#define REQ_SMOOTHING 51
#define REQ_SMOOTHTIME 52 /* Special utoken value used to log on with first exchange being the
#define REQ_REFRESH 53 password. (This time value has long since gone by) */
#define REQ_SERVER_STATS 54 #define SPECIAL_UTOKEN 0x10101010
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define REQ_LOCAL2 56
#define N_REQUEST_TYPES 57
/* Structure used to exchange timevals independent on size of time_t */ /* Structure used to exchange timevals independent on size of time_t */
typedef struct { typedef struct {
@@ -117,10 +115,6 @@ typedef struct {
pktlength.c, to get the number of bytes that ought to be pktlength.c, to get the number of bytes that ought to be
transmitted for each packet type. */ transmitted for each packet type. */
typedef struct {
int32_t EOR;
} REQ_Null;
typedef struct { typedef struct {
IPAddr mask; IPAddr mask;
IPAddr address; IPAddr address;
@@ -193,12 +187,6 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_Modify_Maxupdateskew; } REQ_Modify_Maxupdateskew;
typedef struct {
int32_t limit;
Float threshold;
int32_t EOR;
} REQ_Modify_Makestep;
typedef struct { typedef struct {
Timeval ts; Timeval ts;
int32_t EOR; int32_t EOR;
@@ -212,8 +200,6 @@ typedef struct {
typedef struct { typedef struct {
int32_t on_off; int32_t on_off;
int32_t stratum; int32_t stratum;
Float distance;
int32_t orphan;
int32_t EOR; int32_t EOR;
} REQ_Local; } REQ_Local;
@@ -222,11 +208,19 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_Manual; } REQ_Manual;
typedef struct {
int32_t EOR;
} REQ_N_Sources;
typedef struct { typedef struct {
int32_t index; int32_t index;
int32_t EOR; int32_t EOR;
} REQ_Source_Data; } REQ_Source_Data;
typedef struct {
int32_t EOR;
} REQ_Rekey;
typedef struct { typedef struct {
IPAddr ip; IPAddr ip;
int32_t subnet_bits; int32_t subnet_bits;
@@ -244,8 +238,6 @@ typedef struct {
#define REQ_ADDSRC_IBURST 0x4 #define REQ_ADDSRC_IBURST 0x4
#define REQ_ADDSRC_PREFER 0x8 #define REQ_ADDSRC_PREFER 0x8
#define REQ_ADDSRC_NOSELECT 0x10 #define REQ_ADDSRC_NOSELECT 0x10
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -265,6 +257,10 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_Del_Source; } REQ_Del_Source;
typedef struct {
int32_t EOR;
} REQ_WriteRtc;
typedef struct { typedef struct {
Float dfreq; Float dfreq;
int32_t EOR; int32_t EOR;
@@ -276,39 +272,68 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_Doffset; } REQ_Doffset;
typedef struct {
int32_t EOR;
} REQ_Tracking;
typedef struct { typedef struct {
uint32_t index; uint32_t index;
int32_t EOR; int32_t EOR;
} REQ_Sourcestats; } REQ_Sourcestats;
typedef struct {
int32_t EOR;
} REQ_RTCReport;
typedef struct {
int32_t EOR;
} REQ_TrimRTC;
typedef struct {
int32_t EOR;
} REQ_CycleLogs;
typedef struct {
IPAddr ip;
uint32_t bits_specd;
} REQ_SubnetsAccessed_Subnet;
/* This is based on the response size rather than the /* This is based on the response size rather than the
request size */ request size */
#define MAX_CLIENT_ACCESSES 8 #define MAX_CLIENT_ACCESSES 8
typedef struct { typedef struct {
uint32_t first_index; uint32_t first_index;
uint32_t n_clients; uint32_t n_indices;
int32_t EOR; int32_t EOR;
} REQ_ClientAccessesByIndex; } REQ_ClientAccessesByIndex;
typedef struct {
int32_t EOR;
} REQ_ManualList;
typedef struct { typedef struct {
int32_t index; int32_t index;
int32_t EOR; int32_t EOR;
} REQ_ManualDelete; } REQ_ManualDelete;
typedef struct {
int32_t EOR;
} REQ_MakeStep;
typedef struct {
int32_t EOR;
} REQ_Activity;
typedef struct {
int32_t EOR;
} REQ_Reselect;
typedef struct { typedef struct {
Float distance; Float distance;
int32_t EOR; int32_t EOR;
} REQ_ReselectDistance; } REQ_ReselectDistance;
#define REQ_SMOOTHTIME_RESET 0
#define REQ_SMOOTHTIME_ACTIVATE 1
typedef struct {
int32_t option;
int32_t EOR;
} REQ_SmoothTime;
/* ================================================== */ /* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1 #define PKT_TYPE_CMD_REQUEST 1
@@ -337,16 +362,7 @@ typedef struct {
subnets accessed and client accesses subnets accessed and client accesses
Version 6 : added padding to requests to prevent amplification attack, Version 6 : added padding to requests to prevent amplification attack,
changed maximum number of samples in manual list to 16, new commands: modify changed maximum number of samples in manual list to 16
makestep, smoothing, smoothtime
Support for authentication was removed later in version 6 of the protocol
and commands that required authentication are allowed only locally over Unix
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new flags in NTP source request and report,
new commands: refresh, serverstats
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -360,7 +376,7 @@ typedef struct {
#define PROTO_VERSION_PADDING 6 #define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently /* The maximum length of padding in request packet, currently
defined by MANUAL_LIST */ defined by CLIENT_ACCESSES_BY_INDEX and MANUAL_LIST */
#define MAX_PADDING_LENGTH 396 #define MAX_PADDING_LENGTH 396
/* ================================================== */ /* ================================================== */
@@ -375,11 +391,10 @@ typedef struct {
(count up from zero for same sequence (count up from zero for same sequence
number) */ number) */
uint32_t sequence; /* Client's sequence number */ uint32_t sequence; /* Client's sequence number */
uint32_t pad1; uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t pad2; uint32_t token; /* Command token (to prevent replay attack) */
union { union {
REQ_Null null;
REQ_Online online; REQ_Online online;
REQ_Offline offline; REQ_Offline offline;
REQ_Burst burst; REQ_Burst burst;
@@ -392,29 +407,43 @@ typedef struct {
REQ_Modify_Minstratum modify_minstratum; REQ_Modify_Minstratum modify_minstratum;
REQ_Modify_Polltarget modify_polltarget; REQ_Modify_Polltarget modify_polltarget;
REQ_Modify_Maxupdateskew modify_maxupdateskew; REQ_Modify_Maxupdateskew modify_maxupdateskew;
REQ_Modify_Makestep modify_makestep;
REQ_Logon logon; REQ_Logon logon;
REQ_Settime settime; REQ_Settime settime;
REQ_Local local; REQ_Local local;
REQ_Manual manual; REQ_Manual manual;
REQ_N_Sources n_sources;
REQ_Source_Data source_data; REQ_Source_Data source_data;
REQ_Rekey rekey;
REQ_Allow_Deny allow_deny; REQ_Allow_Deny allow_deny;
REQ_Ac_Check ac_check; REQ_Ac_Check ac_check;
REQ_NTP_Source ntp_source; REQ_NTP_Source ntp_source;
REQ_Del_Source del_source; REQ_Del_Source del_source;
REQ_WriteRtc writertc;
REQ_Dfreq dfreq; REQ_Dfreq dfreq;
REQ_Doffset doffset; REQ_Doffset doffset;
REQ_Tracking tracking;
REQ_Sourcestats sourcestats; REQ_Sourcestats sourcestats;
REQ_RTCReport rtcreport;
REQ_TrimRTC trimrtc;
REQ_CycleLogs cyclelogs;
REQ_ClientAccessesByIndex client_accesses_by_index; REQ_ClientAccessesByIndex client_accesses_by_index;
REQ_ManualList manual_list;
REQ_ManualDelete manual_delete; REQ_ManualDelete manual_delete;
REQ_MakeStep make_step;
REQ_Activity activity;
REQ_Reselect reselect;
REQ_ReselectDistance reselect_distance; REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
} data; /* Command specific parameters */ } data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the /* The following fields only set the maximum size of the packet.
maximum size of the packet, there is no hole after the data field. */ There are no holes between them and the actual data. */
/* Padding used to prevent traffic amplification */
uint8_t padding[MAX_PADDING_LENGTH]; uint8_t padding[MAX_PADDING_LENGTH];
/* Authentication data */
uint8_t auth[MAX_HASH_LENGTH];
} CMD_Request; } CMD_Request;
/* ================================================== */ /* ================================================== */
@@ -439,10 +468,7 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX 10 #define RPY_CLIENT_ACCESSES_BY_INDEX 10
#define RPY_MANUAL_LIST 11 #define RPY_MANUAL_LIST 11
#define RPY_ACTIVITY 12 #define RPY_ACTIVITY 12
#define RPY_SMOOTHING 13 #define N_REPLY_TYPES 13
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define N_REPLY_TYPES 16
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -489,8 +515,6 @@ typedef struct {
#define RPY_SD_FLAG_NOSELECT 0x1 #define RPY_SD_FLAG_NOSELECT 0x1
#define RPY_SD_FLAG_PREFER 0x2 #define RPY_SD_FLAG_PREFER 0x2
#define RPY_SD_FLAG_TRUST 0x4
#define RPY_SD_FLAG_REQUIRE 0x8
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -558,14 +582,17 @@ typedef struct {
typedef struct { typedef struct {
IPAddr ip; IPAddr ip;
uint32_t ntp_hits; uint32_t bits_specd;
uint32_t cmd_hits; uint32_t bitmap[8];
uint32_t ntp_drops; } RPY_SubnetsAccessed_Subnet;
uint32_t cmd_drops;
int8_t ntp_interval; typedef struct {
int8_t cmd_interval; IPAddr ip;
int8_t ntp_timeout_interval; uint32_t client_hits;
int8_t pad; uint32_t peer_hits;
uint32_t cmd_hits_auth;
uint32_t cmd_hits_normal;
uint32_t cmd_hits_bad;
uint32_t last_ntp_hit_ago; uint32_t last_ntp_hit_ago;
uint32_t last_cmd_hit_ago; uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client; } RPY_ClientAccesses_Client;
@@ -578,15 +605,6 @@ typedef struct {
int32_t EOR; int32_t EOR;
} RPY_ClientAccessesByIndex; } RPY_ClientAccessesByIndex;
typedef struct {
uint32_t ntp_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t cmd_drops;
uint32_t log_drops;
int32_t EOR;
} RPY_ServerStats;
#define MAX_MANUAL_LIST_SAMPLES 16 #define MAX_MANUAL_LIST_SAMPLES 16
typedef struct { typedef struct {
@@ -611,19 +629,6 @@ typedef struct {
int32_t EOR; int32_t EOR;
} RPY_Activity; } RPY_Activity;
#define RPY_SMT_FLAG_ACTIVE 0x1
#define RPY_SMT_FLAG_LEAPONLY 0x2
typedef struct {
uint32_t flags;
Float offset;
Float freq_ppm;
Float wander_ppm;
Float last_update_ago;
Float remaining_time;
int32_t EOR;
} RPY_Smoothing;
typedef struct { typedef struct {
uint8_t version; uint8_t version;
uint8_t pkt_type; uint8_t pkt_type;
@@ -636,9 +641,9 @@ typedef struct {
uint16_t pad2; uint16_t pad2;
uint16_t pad3; uint16_t pad3;
uint32_t sequence; /* Echo of client's sequence number */ uint32_t sequence; /* Echo of client's sequence number */
uint32_t pad4; uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t pad5; uint32_t token; /* New command token (only if command was successfully
authenticated) */
union { union {
RPY_Null null; RPY_Null null;
RPY_N_Sources n_sources; RPY_N_Sources n_sources;
@@ -648,12 +653,14 @@ typedef struct {
RPY_Sourcestats sourcestats; RPY_Sourcestats sourcestats;
RPY_Rtc rtc; RPY_Rtc rtc;
RPY_ClientAccessesByIndex client_accesses_by_index; RPY_ClientAccessesByIndex client_accesses_by_index;
RPY_ServerStats server_stats;
RPY_ManualList manual_list; RPY_ManualList manual_list;
RPY_Activity activity; RPY_Activity activity;
RPY_Smoothing smoothing;
} data; /* Reply specific parameters */ } data; /* Reply specific parameters */
/* authentication of the packet, there is no hole after the actual data
from the data union, this field only sets the maximum auth size */
uint8_t auth[MAX_HASH_LENGTH];
} CMD_Reply; } CMD_Reply;
/* ================================================== */ /* ================================================== */

71
chrony.1 Normal file
View File

@@ -0,0 +1,71 @@
.TH CHRONY 1 "@MAN_DATE@" "chrony @VERSION@" "User's Manual"
.SH NAME
chrony \- programs for keeping computer clocks accurate
.SH SYNOPSIS
\fBchronyc\fR [\fIOPTIONS\fR]
\fBchronyd\fR [\fIOPTIONS\fR]
.SH DESCRIPTION
\fBchrony\fR is a pair of programs for keeping computer clocks accurate.
\fIchronyd\fR is a background (daemon) program and \fIchronyc\fR is a
command-line interface to it. Time reference sources for chronyd can be
RFC1305 NTP servers, human (via keyboard and \fIchronyc\fR), or the computer's
real-time clock at boot time (Linux only). chronyd can determine the rate at
which the computer gains or loses time and compensate for it while no external
reference is present. Its use of NTP servers can be switched on and off
(through \fIchronyc\fR) to support computers with dial-up/intermittent access
to the Internet, and it can also act as an RFC1305-compatible NTP server.
.SH USAGE
\fIchronyc\fR is a command-line interface program which can be used to
monitor \fIchronyd\fR's performance and to change various operating
parameters whilst it is running.
\fIchronyd\fR's main function is to obtain measurements of the true (UTC)
time from one of several sources, and correct the system clock
accordingly. It also works out the rate at which the system clock
gains or loses time and uses this information to keep it accurate
between measurements from the reference.
The reference time can be derived from either Network Time Protocol
(NTP) servers, reference clocks, or wristwatch-and-keyboard (via \fIchronyc\fR).
The main source of information about the Network Time Protocol is
\fIhttp://www.ntp.org\fR.
It is designed so that it can work on computers which only have
intermittent access to reference sources, for example computers which
use a dial-up account to access the Internet or laptops. Of course, it
will work well on computers with permanent connections too.
In addition, on Linux it can monitor the system's real time clock
performance, so the system can maintain accurate time even across
reboots.
Typical accuracies available between 2 machines are
On an ethernet LAN : 100-200 microseconds, often much better
On a V32bis dial-up modem connection : 10's of milliseconds (from one
session to the next)
With a good reference clock the accuracy can reach one microsecond.
\fIchronyd\fR can also operate as an RFC1305-compatible NTP server and peer.
.SH "SEE ALSO"
.BR chronyc(1),
.BR chrony.conf(5),
.BR chronyd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part
of "The Missing Man Pages Project". Please see
\fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details.
The complete chrony documentation is supplied in texinfo format.

55
chrony.conf.5.in Normal file
View File

@@ -0,0 +1,55 @@
.TH chrony.conf 5 "@MAN_DATE@" "chrony @VERSION@" "Configuration Files"
.SH NAME
chrony.conf \- chronyd configuration file
.SH SYNOPSIS
.B @SYSCONFDIR@/chrony.conf
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
clocks. \fIchronyd\fR is a background daemon program that can be started at
boot time.
Assuming that you have found some servers, you need to set up a
configuration file to run \fIchrony\fR. The (compiled-in) default location
for this file is \fB@SYSCONFDIR@/chrony.conf\fR. Assuming that your ntp servers
are called `a.b.c' and `d.e.f', your \fBchrony.conf\fR file could contain
as a minimum
server a.b.c
server d.e.f
server g.h.i
However, you will probably want to include some of the other directives
described in detail in the documentation supplied with the distribution
(\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives may be
particularly useful : `driftfile', `generatecommandkey', `keyfile', `makestep'.
Also, the `iburst' server option is useful to speed up the initial
synchronization. The smallest useful configuration file would look something
like
server a.b.c iburst
server d.e.f iburst
server g.h.i iburst
keyfile @SYSCONFDIR@/chrony.keys
generatecommandkey
driftfile @CHRONYVARDIR@/drift
makestep 10 3
.SH "SEE ALSO"
.BR chrony(1),
.BR chronyc(1),
.BR chronyd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part of "The Missing
Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR
for details.
The complete chrony documentation is supplied in texinfo format.

5172
chrony.texi.in Normal file

File diff suppressed because it is too large Load Diff

73
chrony_timex.h Normal file
View File

@@ -0,0 +1,73 @@
/* Taken from /usr/include/linux/timex.h. Avoids the need to
* include kernel header files. */
#ifndef CHRONY_TIMEX_H
#define CHRONY_TIMEX_H
#include <sys/time.h>
struct timex {
unsigned int modes; /* mode selector */
long offset; /* time offset (usec) */
long freq; /* frequency offset (scaled ppm) */
long maxerror; /* maximum error (usec) */
long esterror; /* estimated error (usec) */
int status; /* clock command/status */
long constant; /* pll time constant */
long precision; /* clock precision (usec) (read only) */
long tolerance; /* clock frequency tolerance (ppm)
* (read only)
*/
struct timeval time; /* (read only) */
long tick; /* (modified) usecs between clock ticks */
long ppsfreq; /* pps frequency (scaled ppm) (ro) */
long jitter; /* pps jitter (us) (ro) */
int shift; /* interval duration (s) (shift) (ro) */
long stabil; /* pps stability (scaled ppm) (ro) */
long jitcnt; /* jitter limit exceeded (ro) */
long calcnt; /* calibration intervals (ro) */
long errcnt; /* calibration errors (ro) */
long stbcnt; /* stability limit exceeded (ro) */
int :32; int :32; int :32; int :32;
int :32; int :32; int :32; int :32;
int :32; int :32; int :32; int :32;
};
#define ADJ_OFFSET 0x0001 /* time offset */
#define ADJ_FREQUENCY 0x0002 /* frequency offset */
#define ADJ_MAXERROR 0x0004 /* maximum time error */
#define ADJ_STATUS 0x0010 /* clock status */
#define ADJ_TIMECONST 0x0020 /* pll time constant */
#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
#define ADJ_NANO 0x2000 /* select nanosecond resolution */
#define ADJ_TICK 0x4000 /* tick value */
#define ADJ_OFFSET_SINGLESHOT 0x8001 /* old-fashioned adjtime */
#define ADJ_OFFSET_SS_READ 0xa001 /* read-only adjtime */
#define SHIFT_USEC 16 /* frequency offset scale (shift) */
#define STA_PLL 0x0001 /* enable PLL updates (rw) */
#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
#define STA_FLL 0x0008 /* select frequency-lock mode (rw) */
#define STA_INS 0x0010 /* insert leap (rw) */
#define STA_DEL 0x0020 /* delete leap (rw) */
#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
#define STA_FREQHOLD 0x0080 /* hold frequency (rw) */
#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
#define STA_NANO 0x2000 /* resolution (0 = us, 1 = ns) (ro) */
/* This doesn't seem to be in any include files !! */
extern int adjtimex(struct timex *);
#endif /* CHRONY_TIMEX_H */

75
chronyc.1.in Normal file
View File

@@ -0,0 +1,75 @@
.TH CHRONYC 1 "@MAN_DATE@" "chrony @VERSION@" "User's Manual"
.SH NAME
chronyc \- command-line interface for chronyd
.SH SYNOPSIS
.B chronyc
[\fIOPTIONS\fR]
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
clocks.
\fBchronyc\fR is a command-line interface program which can be used to
monitor \fIchronyd\fR's performance and to change various operating
parameters whilst it is running.
.SH USAGE
A detailed description of all commands supported by \fBchronyc\fR is available
via the documentation supplied with the distribution (\fIchrony.txt\fR and
\fIchrony.texi\fR).
.SH OPTIONS
A summary of the options supported by \fBchronyc\fR is included below.
.TP
\fB\-h\fR \fIhostname\fR
specify hostname (default 127.0.0.1)
.TP
\fB\-p\fR \fIport-number\fR
specify port-number
.TP
\fB\-n\fR
display raw IP addresses (don't attempt to look up hostnames)
.TP
\fB\-4\fR
resolve hostnames only to IPv4 addresses
.TP
\fB\-6\fR
resolve hostnames only to IPv6 addresses
.TP
\fB\-m\fR
allow multiple commands to be specified on the command line. Each argument
will be interpreted as a whole command.
.TP
\fB\-f\fR \fIconf-file\fR
This option can be used to specify an alternate location for the
configuration file (default \fI@SYSCONFDIR@/chrony.conf\fR). The configuration file is
needed for the \fB-a\fR option.
.TP
\fB\-a\fR
With this option chronyc will try to authenticate automatically on
start. It will read the configuration file, read the command key from the
keyfile and run the authhash and password commands.
.TP
\fIcommand\fR
specify command. If no command is given, chronyc will read commands
interactively.
.SH BUGS
To report bugs, please visit \fIhttp://chrony.tuxfamily.org\fR
.SH "SEE ALSO"
.BR chronyd(8),
.BR chrony(1)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part of "The Missing
Man Pages Project". Please see \fIhttp://www.netmeister.org/misc/m2p2/index.html\fR
for details.
The complete chrony documentation is supplied in texinfo format.

154
chronyd.8.in Normal file
View File

@@ -0,0 +1,154 @@
.TH CHRONYD 8 "@MAN_DATE@" "chrony @VERSION@" "System Administration"
.SH NAME
chronyd \- chrony background daemon
.SH SYNOPSIS
.B chronyd
[\fIOPTIONS\fR] [\fIconfiguration commands\fR]
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
clocks. \fBchronyd\fR is a background daemon program that can be started at boot
time.
\fBchronyd\fR is a daemon which runs in background on the
system. It obtains measurements (e.g. via the network) of the
system's offset relative to other systems, and adjusts the system
time accordingly. For isolated systems, the user can periodically
enter the correct time by hand (using \fIchronyc\fR). In either case,
\fBchronyd\fR determines the rate at which the computer
gains or loses time, and compensates for this.
.SH USAGE
\fBchronyd\fR is usually started at boot-time and requires superuser
privileges.
If \fBchronyd\fR has been installed to its default location
\fI@SBINDIR@/chronyd\fR, starting it is simply a matter of entering the
command:
\fI@SBINDIR@/chronyd\fR
Information messages and warnings will be logged to syslog.
If no configuration commands are specified on the command line,
\fBchronyd\fR will read the commands from the configuration file
(default \fI@SYSCONFDIR@/chrony.conf\fR).
.SH OPTIONS
A summary of the options supported by \fBchronyd\fR is included below.
.TP
\fB\-P\fR \fIpriority\fR
This option will select the SCHED_FIFO real-time scheduler at the specified
priority (which must be between 0 and 100). This mode is supported only on
Linux.
.TP
.B \-m
This option will lock chronyd into RAM so that it will never be paged out.
This mode is only supported on Linux.
.TP
.B \-n
When run in this mode, the program will not detach itself from the
terminal.
.TP
.B \-d
When run in this mode, the program will not detach itself from the
terminal, and all messages will be sent to the terminal instead of
to syslog. When \fBchronyd\fR was compiled with debugging support,
this option can be used twice to print also debugging messages.
.TP
\fB\-f\fR \fIconf-file\fR
This option can be used to specify an alternate location for the
configuration file (default \fI@SYSCONFDIR@/chrony.conf\fR).
.TP
.B \-r
This option will reload sample histories for each of the servers being used.
These histories are created by using the \fIdump\fR command in \fIchronyc\fR,
or by setting the \fIdumponexit\fR directive in the configuration file. This
option is useful if you want to stop and restart \fBchronyd\fR briefly for any
reason, e.g. to install a new version. However, it only makes sense on
systems where the kernel can maintain clock compensation whilst not under
\fBchronyd\fR's control. The only version where this happens so far is Linux.
On systems where this is not the case, e.g. Solaris and SunOS the option
should not be used.
.TP
.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 conjunction
with the \fB-r\fR option.
.TP
.B \-s
This option will set the system clock from the computer's real-time
clock. This is analogous to supplying the \fI-s\fR flag to the
\fI/sbin/hwclock\fR program during the Linux boot sequence.
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 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 (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 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
drop root privileges if compiled with Linux capabilities support (default
\fB@DEFAULT_USER@\fR).
.TP
.B \-q
When run in this mode, chronyd will set the system clock once
and exit. It will not detach from the terminal.
.TP
.B \-Q
This option is similar to \fB\-q\fR, but it will only print the offset and
not correct the clock.
.TP
.B \-v
This option displays \fBchronyd\fR's version number to the terminal and exits
.TP
.B \-4
Resolve hostnames only to IPv4 addresses and create only IPv4 sockets.
.TP
.B \-6
Resolve hostnames only to IPv6 addresses and create only IPv6 sockets.
.SH FILES
\fI@SYSCONFDIR@/chrony.conf\fR
.SH BUGS
To report bugs, please visit \fIhttp://chrony.tuxfamily.org/\fR
.SH "SEE ALSO"
\fBchronyd\fR is documented in detail in the documentation supplied with the
distribution (\fIchrony.txt\fR and \fIchrony.texi\fR).
.BR chrony(1),
.BR chronyc(1),
.BR chrony.conf(5),
.BR hwclock(8),
.BR ntpd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>
This man-page was written by Jan Schaumann <jschauma@netmeister.org> as part
of "The Missing Man Pages Project". Please see
\fIhttp://www.netmeister.org/misc/m2p2/index.html\fR for details.
The complete chrony documentation is supplied in texinfo format.

2074
client.c

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2016 * Copyright (C) Miroslav Lichvar 2009
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -34,8 +34,6 @@
#include "config.h" #include "config.h"
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "clientlog.h" #include "clientlog.h"
#include "conf.h" #include "conf.h"
#include "memory.h" #include "memory.h"
@@ -43,240 +41,110 @@
#include "util.h" #include "util.h"
#include "logging.h" #include "logging.h"
typedef struct { /* Number of bits of address per layer of the table. This value has
been chosen on the basis that a server will predominantly be serving
a lot of hosts in a few subnets, rather than a few hosts scattered
across many subnets. */
#define NBITS 8
/* Number of entries in each subtable */
#define TABLE_SIZE (1UL<<NBITS)
typedef struct _Node {
IPAddr ip_addr; IPAddr ip_addr;
uint32_t last_ntp_hit; unsigned long client_hits;
uint32_t last_cmd_hit; unsigned long peer_hits;
uint32_t ntp_hits; unsigned long cmd_hits_bad;
uint32_t cmd_hits; unsigned long cmd_hits_normal;
uint16_t ntp_drops; unsigned long cmd_hits_auth;
uint16_t cmd_drops; time_t last_ntp_hit;
uint16_t ntp_tokens; time_t last_cmd_hit;
uint16_t cmd_tokens; } Node;
int8_t ntp_rate;
int8_t cmd_rate;
int8_t ntp_timeout_rate;
uint8_t flags;
} Record;
/* Hash table of records, there is a fixed number of records per slot */ typedef struct _Subnet {
static ARR_Instance records; void *entry[TABLE_SIZE];
} Subnet;
#define SLOT_BITS 4 /* ================================================== */
/* Number of records in one slot of the hash table */ /* Table for the IPv4 class A subnet */
#define SLOT_SIZE (1U << SLOT_BITS) static Subnet top_subnet4;
/* Table for IPv6 */
static Subnet top_subnet6;
/* Minimum number of slots */ /* Table containing pointers directly to all nodes that have been
#define MIN_SLOTS 1 allocated. */
static Node **nodes = NULL;
/* Maximum number of slots, this is a hard limit */ /* Number of nodes actually in the table. */
#define MAX_SLOTS (1U << (24 - SLOT_BITS)) static int n_nodes = 0;
/* Number of slots in the hash table */ /* Number of entries for which the table has been sized. */
static unsigned int slots; static int max_nodes = 0;
/* Maximum number of slots given memory allocation limit */
static unsigned int max_slots;
/* Times of last hits are saved as 32-bit fixed point values */
#define TS_FRAC 4
#define INVALID_TS 0
/* Request rates are saved in the record as 8-bit scaled log2 values */
#define RATE_SCALE 4
#define MIN_RATE (-14 * RATE_SCALE)
#define INVALID_RATE -128
/* Response rates are controlled by token buckets. The capacity and
number of tokens spent on response are determined from configured
minimum inverval between responses (in log2) and burst length. */
#define MIN_LIMIT_INTERVAL (-TS_FRAC)
#define MAX_LIMIT_INTERVAL 12
#define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255
static uint16_t max_ntp_tokens;
static uint16_t max_cmd_tokens;
static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters */
static int ntp_token_shift;
static int cmd_token_shift;
/* Rates at which responses are randomly allowed (in log2) when the
buckets don't have enough tokens. This is necessary in order to
prevent an attacker sending requests with spoofed source address
from blocking responses to the address completely. */
#define MIN_LEAK_RATE 1
#define MAX_LEAK_RATE 4
static int ntp_leak_rate;
static int cmd_leak_rate;
/* Flag indicating whether the last response was dropped */
#define FLAG_NTP_DROPPED 0x1
/* Flag indicating whether facility is turned on or not */ /* Flag indicating whether facility is turned on or not */
static int active; static int active = 0;
/* Global statistics */ /* Flag indicating whether memory allocation limit has been reached
static uint32_t total_ntp_hits; and no new nodes or subnets should be allocated */
static uint32_t total_cmd_hits; static int alloc_limit_reached;
static uint32_t total_ntp_drops;
static uint32_t total_cmd_drops; static unsigned long alloc_limit;
static uint32_t total_record_drops; static unsigned long alloced;
/* ================================================== */ /* ================================================== */
static int expand_hashtable(void); static void
split_ip6(IPAddr *ip, uint32_t *dst)
/* ================================================== */
static int
compare_ts(uint32_t x, uint32_t y)
{ {
if (x == y) int i;
return 0;
if (y == INVALID_TS) for (i = 0; i < 4; i++)
return 1; dst[i] = ip->addr.in6[i * 4 + 0] << 24 |
return (int32_t)(x - y) > 0 ? 1 : -1; ip->addr.in6[i * 4 + 1] << 16 |
ip->addr.in6[i * 4 + 2] << 8 |
ip->addr.in6[i * 4 + 3];
} }
/* ================================================== */ /* ================================================== */
static Record * inline static uint32_t
get_record(IPAddr *ip) get_subnet(uint32_t *addr, unsigned int where)
{ {
unsigned int first, i; int off;
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record;
if (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6) off = where / 32;
return NULL; where %= 32;
while (1) { return (addr[off] >> (32 - NBITS - where)) & ((1UL << NBITS) - 1);
/* Get index of the first record in the slot */
first = UTI_IPToHash(ip) % slots * SLOT_SIZE;
for (i = 0, oldest_record = NULL; i < SLOT_SIZE; i++) {
record = ARR_GetElement(records, first + i);
if (!UTI_CompareIPs(ip, &record->ip_addr, NULL))
return record;
if (record->ip_addr.family == IPADDR_UNSPEC)
break;
last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ?
record->last_ntp_hit : record->last_cmd_hit;
if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 ||
(oldest_hit == last_hit && record->ntp_hits + record->cmd_hits <
oldest_record->ntp_hits + oldest_record->cmd_hits)) {
oldest_record = record;
oldest_hit = last_hit;
}
}
/* If the slot still has an empty record, use it */
if (record->ip_addr.family == IPADDR_UNSPEC)
break;
/* Resize the table if possible and try again as the new slot may
have some empty records */
if (expand_hashtable())
continue;
/* There is no other option, replace the oldest record */
record = oldest_record;
total_record_drops++;
break;
}
record->ip_addr = *ip;
record->last_ntp_hit = record->last_cmd_hit = INVALID_TS;
record->ntp_hits = record->cmd_hits = 0;
record->ntp_drops = record->cmd_drops = 0;
record->ntp_tokens = max_ntp_tokens;
record->cmd_tokens = max_cmd_tokens;
record->ntp_rate = record->cmd_rate = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE;
record->flags = 0;
return record;
} }
/* ================================================== */ /* ================================================== */
static int
expand_hashtable(void) static void
clear_subnet(Subnet *subnet)
{ {
ARR_Instance old_records; int i;
Record *old_record, *new_record;
unsigned int i;
old_records = records; for (i=0; i<TABLE_SIZE; i++) {
subnet->entry[i] = NULL;
if (2 * slots > max_slots)
return 0;
records = ARR_CreateInstance(sizeof (Record));
slots = MAX(MIN_SLOTS, 2 * slots);
assert(slots <= max_slots);
ARR_SetSize(records, slots * SLOT_SIZE);
/* Mark all new records as empty */
for (i = 0; i < slots * SLOT_SIZE; i++) {
new_record = ARR_GetElement(records, i);
new_record->ip_addr.family = IPADDR_UNSPEC;
} }
if (!old_records)
return 1;
/* Copy old records to the new hash table */
for (i = 0; i < ARR_GetSize(old_records); i++) {
old_record = ARR_GetElement(old_records, i);
if (old_record->ip_addr.family == IPADDR_UNSPEC)
continue;
new_record = get_record(&old_record->ip_addr);
assert(new_record);
*new_record = *old_record;
}
ARR_DestroyInstance(old_records);
return 1;
} }
/* ================================================== */ /* ================================================== */
static void static void
set_bucket_params(int interval, int burst, uint16_t *max_tokens, clear_node(Node *node)
uint16_t *tokens_per_packet, int *token_shift)
{ {
interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); node->client_hits = 0;
burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST); node->peer_hits = 0;
node->cmd_hits_auth = 0;
/* Find smallest shift with which the maximum number fits in 16 bits */ node->cmd_hits_normal = 0;
for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) { node->cmd_hits_bad = 0;
if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16) node->last_ntp_hit = (time_t) 0;
break; node->last_cmd_hit = (time_t) 0;
}
*tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift);
*max_tokens = *tokens_per_packet * burst;
DEBUG_LOG(LOGF_ClientLog, "Tokens max %d packet %d shift %d",
*max_tokens, *tokens_per_packet, *token_shift);
} }
/* ================================================== */ /* ================================================== */
@@ -284,42 +152,21 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void void
CLG_Initialise(void) CLG_Initialise(void)
{ {
int interval, burst, leak_rate; clear_subnet(&top_subnet4);
clear_subnet(&top_subnet6);
max_ntp_tokens = max_cmd_tokens = 0; if (CNF_GetNoClientLog()) {
ntp_tokens_per_packet = cmd_tokens_per_packet = 0; active = 0;
ntp_token_shift = cmd_token_shift = 0; } else {
ntp_leak_rate = cmd_leak_rate = 0; active = 1;
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
&ntp_token_shift);
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
} }
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) { nodes = NULL;
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet, max_nodes = 0;
&cmd_token_shift); n_nodes = 0;
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
}
active = !CNF_GetNoClientLog(); alloced = 0;
if (!active) { alloc_limit = CNF_GetClientLogLimit();
if (ntp_leak_rate || cmd_leak_rate) alloc_limit_reached = 0;
LOG_FATAL(LOGF_ClientLog, "ratelimit cannot be used with noclientlog");
return;
}
/* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash
table where two copies exist at the same time. */
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2);
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
slots = 0;
records = NULL;
expand_hashtable();
} }
/* ================================================== */ /* ================================================== */
@@ -327,302 +174,335 @@ CLG_Initialise(void)
void void
CLG_Finalise(void) CLG_Finalise(void)
{ {
if (!active)
return;
ARR_DestroyInstance(records);
} }
/* ================================================== */ /* ================================================== */
static uint32_t static void check_alloc_limit() {
get_ts_from_timeval(struct timeval *tv) if (alloc_limit_reached)
{ return;
uint32_t sec = tv->tv_sec, usec = tv->tv_usec;
return sec << TS_FRAC | (4295U * usec - (usec >> 5)) >> (32 - TS_FRAC); if (alloced >= alloc_limit) {
LOG(LOGS_WARN, LOGF_ClientLog, "Client log memory limit reached");
alloc_limit_reached = 1;
}
} }
/* ================================================== */ /* ================================================== */
static void static void
update_record(struct timeval *now, uint32_t *last_hit, uint32_t *hits, create_subnet(Subnet *parent_subnet, int the_entry)
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{ {
uint32_t interval, now_ts, prev_hit, new_tokens; parent_subnet->entry[the_entry] = (void *) MallocNew(Subnet);
int interval2; clear_subnet((Subnet *) parent_subnet->entry[the_entry]);
alloced += sizeof (Subnet);
check_alloc_limit();
}
now_ts = get_ts_from_timeval(now); /* ================================================== */
prev_hit = *last_hit; static void
*last_hit = now_ts; create_node(Subnet *parent_subnet, int the_entry)
(*hits)++; {
Node *new_node;
new_node = MallocNew(Node);
parent_subnet->entry[the_entry] = (void *) new_node;
clear_node(new_node);
interval = now_ts - prev_hit; alloced += sizeof (Node);
if (prev_hit == INVALID_TS || (int32_t)interval < 0) if (n_nodes == max_nodes) {
return; if (nodes) {
assert(max_nodes > 0);
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); max_nodes *= 2;
*tokens = MIN(*tokens + new_tokens, max_tokens); nodes = ReallocArray(Node *, max_nodes, nodes);
} else {
/* Convert the interval to scaled and rounded log2 */ assert(max_nodes == 0);
if (interval) { max_nodes = 16;
interval += interval >> 1; nodes = MallocArray(Node *, max_nodes);
for (interval2 = -RATE_SCALE * TS_FRAC; interval2 < -MIN_RATE;
interval2 += RATE_SCALE) {
if (interval <= 1)
break;
interval >>= 1;
} }
} else { alloced += sizeof (Node *) * (max_nodes - n_nodes);
interval2 = -RATE_SCALE * (TS_FRAC + 1);
} }
nodes[n_nodes++] = (Node *) new_node;
check_alloc_limit();
}
/* Update the rate in a rough approximation of exponential moving average */ /* ================================================== */
if (*rate == INVALID_RATE) { /* Recursively seek out the Node entry for a particular address,
*rate = -interval2; expanding subnet tables and node entries as we go if necessary. */
static void *
find_subnet(Subnet *subnet, uint32_t *addr, int addr_len, int bits_consumed)
{
uint32_t this_subnet;
this_subnet = get_subnet(addr, bits_consumed);
bits_consumed += NBITS;
if (bits_consumed < 32 * addr_len) {
if (!subnet->entry[this_subnet]) {
if (alloc_limit_reached)
return NULL;
create_subnet(subnet, this_subnet);
}
return find_subnet((Subnet *) subnet->entry[this_subnet], addr, addr_len, bits_consumed);
} else { } else {
if (*rate < -interval2) { if (!subnet->entry[this_subnet]) {
(*rate)++; if (alloc_limit_reached)
} else if (*rate > -interval2) { return NULL;
if (*rate > RATE_SCALE * 5 / 2 - interval2) create_node(subnet, this_subnet);
*rate = RATE_SCALE * 5 / 2 - interval2; }
else return subnet->entry[this_subnet];
*rate = (*rate - interval2 - 1) / 2; }
}
/* ================================================== */
/* Search for the record for a particular subnet, but return NULL if
one of the parents does not exist - never open a node out */
static void *
find_subnet_dont_open(Subnet *subnet, uint32_t *addr, int addr_len, int bits_consumed)
{
uint32_t this_subnet;
if (bits_consumed >= 32 * addr_len) {
return subnet;
} else {
this_subnet = get_subnet(addr, bits_consumed);
bits_consumed += NBITS;
if (!subnet->entry[this_subnet]) {
return NULL;
} else {
return find_subnet_dont_open((Subnet *) subnet->entry[this_subnet], addr, addr_len, bits_consumed);
} }
} }
} }
/* ================================================== */ /* ================================================== */
static int
get_index(Record *record)
{
return record - (Record *)ARR_GetElements(records);
}
/* ================================================== */
int
CLG_LogNTPAccess(IPAddr *client, struct timeval *now)
{
Record *record;
total_ntp_hits++;
if (!active)
return -1;
record = get_record(client);
if (record == NULL)
return -1;
/* Update one of the two rates depending on whether the previous request
of the client had a reply or it timed out */
update_record(now, &record->last_ntp_hit, &record->ntp_hits,
&record->ntp_tokens, max_ntp_tokens, ntp_token_shift,
record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate);
DEBUG_LOG(LOGF_ClientLog, "NTP hits %"PRIu32" rate %d trate %d tokens %d",
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens);
return get_index(record);
}
/* ================================================== */
int
CLG_LogCommandAccess(IPAddr *client, struct timeval *now)
{
Record *record;
total_cmd_hits++;
if (!active)
return -1;
record = get_record(client);
if (record == NULL)
return -1;
update_record(now, &record->last_cmd_hit, &record->cmd_hits,
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate);
DEBUG_LOG(LOGF_ClientLog, "Cmd hits %"PRIu32" rate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens);
return get_index(record);
}
/* ================================================== */
static int
limit_response_random(int leak_rate)
{
static uint32_t rnd;
static int bits_left = 0;
int r;
if (bits_left < leak_rate) {
UTI_GetRandomBytes(&rnd, sizeof (rnd));
bits_left = 8 * sizeof (rnd);
}
/* Return zero on average once per 2^leak_rate */
r = rnd % (1U << leak_rate) ? 1 : 0;
rnd >>= leak_rate;
bits_left -= leak_rate;
return r;
}
/* ================================================== */
int
CLG_LimitNTPResponseRate(int index)
{
Record *record;
int drop;
if (!ntp_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
record->flags &= ~FLAG_NTP_DROPPED;
if (record->ntp_tokens >= ntp_tokens_per_packet) {
record->ntp_tokens -= ntp_tokens_per_packet;
return 0;
}
drop = limit_response_random(ntp_leak_rate);
/* Poorly implemented clients may send new requests at even a higher rate
when they are not getting replies. If the request rate seems to be more
than twice as much as when replies are sent, give up on rate limiting to
reduce the amount of traffic. Invert the sense of the leak to respond to
most of the requests, but still keep the estimated rate updated. */
if (record->ntp_timeout_rate != INVALID_RATE &&
record->ntp_timeout_rate > record->ntp_rate + RATE_SCALE)
drop = !drop;
if (!drop) {
record->ntp_tokens = 0;
return 0;
}
record->flags |= FLAG_NTP_DROPPED;
record->ntp_drops++;
total_ntp_drops++;
return 1;
}
/* ================================================== */
int
CLG_LimitCommandResponseRate(int index)
{
Record *record;
if (!cmd_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
if (record->cmd_tokens >= cmd_tokens_per_packet) {
record->cmd_tokens -= cmd_tokens_per_packet;
return 0;
}
if (!limit_response_random(cmd_leak_rate)) {
record->cmd_tokens = 0;
return 0;
}
record->cmd_drops++;
total_cmd_drops++;
return 1;
}
/* ================================================== */
extern int
CLG_GetNumberOfIndices(void)
{
if (!active)
return -1;
return ARR_GetSize(records);
}
/* ================================================== */
static int get_interval(int rate)
{
if (rate == INVALID_RATE)
return 127;
rate += rate > 0 ? RATE_SCALE / 2 : -RATE_SCALE / 2;
return rate / -RATE_SCALE;
}
/* ================================================== */
static uint32_t get_last_ago(uint32_t x, uint32_t y)
{
if (y == INVALID_TS || (int32_t)(x - y) < 0)
return -1;
return (x - y) >> TS_FRAC;
}
/* ================================================== */
int
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now)
{
Record *record;
uint32_t now_ts;
if (!active || index < 0 || index >= ARR_GetSize(records))
return 0;
record = ARR_GetElement(records, index);
if (record->ip_addr.family == IPADDR_UNSPEC)
return 0;
now_ts = get_ts_from_timeval(now);
report->ip_addr = record->ip_addr;
report->ntp_hits = record->ntp_hits;
report->cmd_hits = record->cmd_hits;
report->ntp_drops = record->ntp_drops;
report->cmd_drops = record->cmd_drops;
report->ntp_interval = get_interval(record->ntp_rate);
report->cmd_interval = get_interval(record->cmd_rate);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_ntp_hit);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_cmd_hit);
return 1;
}
/* ================================================== */
void void
CLG_GetServerStatsReport(RPT_ServerStatsReport *report) CLG_LogNTPClientAccess (IPAddr *client, time_t now)
{ {
report->ntp_hits = total_ntp_hits; uint32_t ip6[4];
report->cmd_hits = total_cmd_hits; Node *node;
report->ntp_drops = total_ntp_drops;
report->cmd_drops = total_cmd_drops; if (active) {
report->log_drops = total_record_drops; switch (client->family) {
case IPADDR_INET4:
node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(client, ip6);
node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0);
break;
default:
assert(0);
}
if (node == NULL)
return;
node->ip_addr = *client;
++node->client_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogNTPPeerAccess(IPAddr *client, time_t now)
{
uint32_t ip6[4];
Node *node;
if (active) {
switch (client->family) {
case IPADDR_INET4:
node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(client, ip6);
node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0);
break;
default:
assert(0);
}
if (node == NULL)
return;
node->ip_addr = *client;
++node->peer_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now)
{
uint32_t ip6[4];
Node *node;
if (active) {
switch (client->family) {
case IPADDR_INET4:
node = (Node *) find_subnet(&top_subnet4, &client->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(client, ip6);
node = (Node *) find_subnet(&top_subnet6, ip6, 4, 0);
break;
default:
assert(0);
}
if (node == NULL)
return;
node->ip_addr = *client;
node->last_cmd_hit = now;
switch (type) {
case CLG_CMD_AUTH:
++node->cmd_hits_auth;
break;
case CLG_CMD_NORMAL:
++node->cmd_hits_normal;
break;
case CLG_CMD_BAD_PKT:
++node->cmd_hits_bad;
break;
default:
assert(0);
break;
}
}
}
/* ================================================== */
CLG_Status
CLG_GetSubnetBitmap(IPAddr *subnet, int bits, CLG_Bitmap result)
{
Subnet *s;
uint32_t ip6[4];
unsigned long i;
unsigned long word, bit, mask;
if (bits >= 0 && bits % 8 == 0) {
memset (result, 0, TABLE_SIZE/8);
if (active) {
switch (subnet->family) {
case IPADDR_INET4:
if (bits >= 32)
return CLG_BADSUBNET;
s = find_subnet_dont_open(&top_subnet4, &subnet->addr.in4, 1, 32 - bits);
break;
case IPADDR_INET6:
if (bits >= 128)
return CLG_BADSUBNET;
split_ip6(subnet, ip6);
s = find_subnet_dont_open(&top_subnet6, ip6, 4, 128 - bits);
break;
default:
return CLG_BADSUBNET;
}
if (s) {
for (i=0; i<256; i++) {
if (s->entry[i]) {
word = i / 32;
bit = i % 32;
mask = 1UL << bit;
result[word] |= mask;
}
}
return CLG_SUCCESS;
} else {
return CLG_EMPTYSUBNET;
}
} else {
return CLG_INACTIVE;
}
} else {
return CLG_BADSUBNET;
}
}
/* ================================================== */
CLG_Status
CLG_GetClientAccessReportByIP(IPAddr *ip, RPT_ClientAccess_Report *report, time_t now)
{
uint32_t ip6[4];
Node *node;
if (!active) {
return CLG_INACTIVE;
} else {
switch (ip->family) {
case IPADDR_INET4:
node = (Node *) find_subnet_dont_open(&top_subnet4, &ip->addr.in4, 1, 0);
break;
case IPADDR_INET6:
split_ip6(ip, ip6);
node = (Node *) find_subnet_dont_open(&top_subnet6, ip6, 4, 0);
break;
default:
return CLG_EMPTYSUBNET;
}
if (!node) {
return CLG_EMPTYSUBNET;
} else {
report->client_hits = node->client_hits;
report->peer_hits = node->peer_hits;
report->cmd_hits_auth = node->cmd_hits_auth;
report->cmd_hits_normal = node->cmd_hits_normal;
report->cmd_hits_bad = node->cmd_hits_bad;
report->last_ntp_hit_ago = now - node->last_ntp_hit;
report->last_cmd_hit_ago = now - node->last_cmd_hit;
return CLG_SUCCESS;
}
}
}
/* ================================================== */
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices)
{
Node *node;
*n_indices = n_nodes;
if (!active) {
return CLG_INACTIVE;
} else {
if ((index < 0) || (index >= n_nodes)) {
return CLG_INDEXTOOLARGE;
}
node = nodes[index];
report->ip_addr = node->ip_addr;
report->client_hits = node->client_hits;
report->peer_hits = node->peer_hits;
report->cmd_hits_auth = node->cmd_hits_auth;
report->cmd_hits_normal = node->cmd_hits_normal;
report->cmd_hits_bad = node->cmd_hits_bad;
report->last_ntp_hit_ago = now - node->last_ntp_hit;
report->last_cmd_hit_ago = now - node->last_cmd_hit;
return CLG_SUCCESS;
}
} }

View File

@@ -31,17 +31,55 @@
#include "sysincl.h" #include "sysincl.h"
#include "reports.h" #include "reports.h"
/* Enough to hold flags for 256 hosts in a class C */
typedef uint32_t CLG_Bitmap[8];
extern void CLG_Initialise(void); extern void CLG_Initialise(void);
extern void CLG_Finalise(void); extern void CLG_Finalise(void);
extern int CLG_LogNTPAccess(IPAddr *client, struct timeval *now); extern void CLG_LogNTPClientAccess(IPAddr *client, time_t now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timeval *now); extern void CLG_LogNTPPeerAccess(IPAddr *client, time_t now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index); /* When logging command packets, there are several subtypes */
typedef enum {
CLG_CMD_AUTH, /* authenticated */
CLG_CMD_NORMAL, /* normal */
CLG_CMD_BAD_PKT /* bad version or packet length */
} CLG_Command_Type;
extern void CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now);
/* And some reporting functions, for use by chronyc. */ /* And some reporting functions, for use by chronyc. */
/* TBD */
typedef enum {
CLG_SUCCESS, /* All is well */
CLG_EMPTYSUBNET, /* No hosts logged in requested subnet */
CLG_BADSUBNET, /* Subnet requested is not 0, 8, 16 or 24 bits */
CLG_INACTIVE, /* Facility not active */
CLG_INDEXTOOLARGE /* Node index is higher than number of nodes present */
} CLG_Status;
/* For bits=0, 8, 16, flag which immediate subnets of that subnet are
known. For bits=24, flag which hosts in that subnet are known.
Other values, return 0 (failed) */
extern CLG_Status CLG_GetSubnetBitmap(IPAddr *subnet, int bits, CLG_Bitmap result);
extern CLG_Status
CLG_GetClientAccessReportByIP(IPAddr *ip, RPT_ClientAccess_Report *report, time_t now);
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices);
/* And an iterating function, to call 'fn' for each client or peer
that has accessed us since 'since'. */
extern void CLG_IterateNTPClients
(void (*fn)(IPAddr *client, void *arb),
void *arb,
time_t since);
extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */ #endif /* GOT_CLIENTLOG_H */

1420
cmdmon.c

File diff suppressed because it is too large Load Diff

View File

@@ -33,7 +33,6 @@ extern void CAM_Initialise(int family);
extern void CAM_Finalise(void); extern void CAM_Finalise(void);
extern void CAM_OpenUnixSocket(void);
extern int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); extern int CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int CAM_CheckAccessRestriction(IPAddr *ip_addr); extern int CAM_CheckAccessRestriction(IPAddr *ip_addr);

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014, 2016 * Copyright (C) Miroslav Lichvar 2013
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,6 @@
#include "cmdparse.h" #include "cmdparse.h"
#include "memory.h" #include "memory.h"
#include "nameserv.h" #include "nameserv.h"
#include "ntp.h"
#include "util.h" #include "util.h"
/* ================================================== */ /* ================================================== */
@@ -43,7 +42,7 @@ CPS_Status
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{ {
char *hostname, *cmd; char *hostname, *cmd;
int n, done; int ok, n, done;
CPS_Status result; CPS_Status result;
src->port = SRC_DEFAULT_PORT; src->port = SRC_DEFAULT_PORT;
@@ -59,11 +58,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.iburst = 0; src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM; src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET; src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = NTP_VERSION; src->params.sel_option = SRC_SelectNormal;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.sel_options = 0;
result = CPS_Success; result = CPS_Success;
@@ -72,10 +67,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (!*hostname) { if (!*hostname) {
result = CPS_BadHost; result = CPS_BadHost;
ok = 0;
} else { } else {
src->name = hostname;
/* Parse subfields */ /* Parse subfields */
ok = 1;
done = 0; done = 0;
do { do {
cmd = line; cmd = line;
@@ -85,6 +80,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (!strcasecmp(cmd, "port")) { if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1) { if (sscanf(line, "%hu%n", &src->port, &n) != 1) {
result = CPS_BadPort; result = CPS_BadPort;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -92,6 +88,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "minpoll")) { } else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) { if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
result = CPS_BadMinpoll; result = CPS_BadMinpoll;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -99,6 +96,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxpoll")) { } else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) { if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
result = CPS_BadMaxpoll; result = CPS_BadMaxpoll;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -106,6 +104,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "presend")) { } else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) { if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
result = CPS_BadPresend; result = CPS_BadPresend;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -113,6 +112,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelaydevratio")) { } else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) { if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) {
result = CPS_BadMaxdelaydevratio; result = CPS_BadMaxdelaydevratio;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -120,6 +120,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelayratio")) { } else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) { if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
result = CPS_BadMaxdelayratio; result = CPS_BadMaxdelayratio;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -127,14 +128,16 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelay")) { } else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) { if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
result = CPS_BadMaxdelay; result = CPS_BadMaxdelay;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
} }
} else if (!strcasecmp(cmd, "key")) { } else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 || if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) { src->params.authkey == INACTIVE_AUTHKEY) {
result = CPS_BadKey; result = CPS_BadKey;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -151,6 +154,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "minstratum")) { } else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) { if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1) {
result = CPS_BadMinstratum; result = CPS_BadMinstratum;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
@@ -159,57 +163,21 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "polltarget")) { } else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) { if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) {
result = CPS_BadPolltarget; result = CPS_BadPolltarget;
ok = 0;
done = 1; done = 1;
} else { } else {
line += n; line += n;
} }
} else if (!strcasecmp(cmd, "noselect")) { } else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT; src->params.sel_option = SRC_SelectNoselect;
} else if (!strcasecmp(cmd, "prefer")) { } else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER; src->params.sel_option = SRC_SelectPrefer;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1) {
result = CPS_BadVersion;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) {
result = CPS_BadMaxsources;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1) {
result = CPS_BadMinsamples;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1) {
result = CPS_BadMaxsamples;
done = 1;
} else {
line += n;
}
} else { } else {
result = CPS_BadOption; result = CPS_BadOption;
ok = 0;
done = 1; done = 1;
} }
} else { } else {
@@ -218,108 +186,12 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} while (!done); } while (!done);
} }
if (ok) {
src->name = strdup(hostname);
}
return result; return result;
}
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
{
int n;
char *cmd;
*stratum = 10;
*distance = 1.0;
*orphan = 0;
while (*line) {
cmd = line;
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
return 0;
} else if (!strcasecmp(cmd, "orphan")) {
*orphan = 1;
n = 0;
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
} else {
return 0;
}
line += n;
}
return 1;
}
/* ================================================== */
void
CPS_StatusToString(CPS_Status status, char *dest, int len)
{
const char *s = NULL;
if (len > 0)
dest[0] = '\0';
switch (status) {
case CPS_Success:
return;
case CPS_BadOption:
s = "server/peer/pool option";
break;
case CPS_BadHost:
s = "address";
break;
case CPS_BadPort:
s = "port";
break;
case CPS_BadMinpoll:
s = "minpoll";
break;
case CPS_BadMaxpoll:
s = "maxpoll";
break;
case CPS_BadPresend:
s = "presend";
break;
case CPS_BadMaxdelaydevratio:
s = "maxdelaydevratio";
break;
case CPS_BadMaxdelayratio:
s = "maxdelayratio";
break;
case CPS_BadMaxdelay:
s = "maxdelay";
break;
case CPS_BadKey:
s = "key";
break;
case CPS_BadMinstratum:
s = "minstratum";
break;
case CPS_BadPolltarget:
s = "polltarget";
break;
case CPS_BadVersion:
s = "version";
break;
case CPS_BadMaxsources:
s = "maxsources";
break;
case CPS_BadMinsamples:
s = "minsamples";
break;
case CPS_BadMaxsamples:
s = "maxsamples";
break;
}
snprintf(dest, len, "Invalid %s", s);
} }
/* ================================================== */ /* ================================================== */
@@ -382,7 +254,7 @@ CPS_SplitWord(char *line)
/* ================================================== */ /* ================================================== */
int int
CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key) CPS_ParseKey(char *line, unsigned long *id, const char **hash, char **key)
{ {
char *s1, *s2, *s3, *s4; char *s1, *s2, *s3, *s4;
@@ -395,7 +267,7 @@ CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
if (!*s2 || *s4) if (!*s2 || *s4)
return 0; return 0;
if (sscanf(s1, "%"SCNu32, id) != 1) if (sscanf(s1, "%lu", id) != 1)
return 0; return 0;
if (*s3) { if (*s3) {

View File

@@ -43,11 +43,7 @@ typedef enum {
CPS_BadMaxdelay, CPS_BadMaxdelay,
CPS_BadKey, CPS_BadKey,
CPS_BadMinstratum, CPS_BadMinstratum,
CPS_BadPolltarget, CPS_BadPolltarget
CPS_BadVersion,
CPS_BadMaxsources,
CPS_BadMinsamples,
CPS_BadMaxsamples,
} CPS_Status; } CPS_Status;
typedef struct { typedef struct {
@@ -59,12 +55,6 @@ typedef struct {
/* Parse a command to add an NTP server or peer */ /* Parse a command to add an NTP server or peer */
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
/* Get a string describing error status */
extern void CPS_StatusToString(CPS_Status status, char *dest, int len);
/* Remove extra white-space and comments */ /* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line); extern void CPS_NormalizeLine(char *line);
@@ -72,6 +62,6 @@ extern void CPS_NormalizeLine(char *line);
extern char *CPS_SplitWord(char *line); extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */ /* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key); extern int CPS_ParseKey(char *line, unsigned long *id, const char **hash, char **key);
#endif /* GOT_CMDPARSE_H */ #endif /* GOT_CMDPARSE_H */

787
conf.c

File diff suppressed because it is too large Load Diff

23
conf.h
View File

@@ -29,18 +29,14 @@
#define GOT_CONF_H #define GOT_CONF_H
#include "addressing.h" #include "addressing.h"
#include "reference.h"
extern void CNF_Initialise(int restarted); extern void CNF_SetRestarted(int);
extern void CNF_Finalise(void);
extern char *CNF_GetRtcDevice(void); extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename); extern void CNF_ReadFile(const char *filename);
extern void CNF_ParseLine(const char *filename, int number, char *line); extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
extern void CNF_AddInitSources(void); extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void); extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void); extern void CNF_AddBroadcasts(void);
@@ -60,6 +56,8 @@ extern int CNF_GetLogRefclocks(void);
extern int CNF_GetLogTempComp(void); extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void); extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void); extern char *CNF_GetRtcFile(void);
extern unsigned long CNF_GetCommandKey(void);
extern int CNF_GetGenerateCommandKey(void);
extern int CNF_GetDumpOnExit(void); extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void); extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void); extern int CNF_GetCommandPort(void);
@@ -67,7 +65,7 @@ extern int CNF_GetRtcOnUtc(void);
extern int CNF_GetRtcSync(void); extern int CNF_GetRtcSync(void);
extern void CNF_GetMakeStep(int *limit, double *threshold); extern void CNF_GetMakeStep(int *limit, double *threshold);
extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset); extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset);
extern double CNF_GetLogChange(void); extern void CNF_GetLogChange(int *enabled, double *threshold);
extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user); extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user);
extern int CNF_GetNoClientLog(void); extern int CNF_GetNoClientLog(void);
extern unsigned long CNF_GetClientLogLimit(void); extern unsigned long CNF_GetClientLogLimit(void);
@@ -75,42 +73,33 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr); extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr); extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr); extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindCommandPath(void);
extern char *CNF_GetPidFile(void); extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void); extern char *CNF_GetLeapSecTimezone(void);
/* Value returned in ppm, as read from file */ /* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void); extern double CNF_GetMaxUpdateSkew(void);
extern double CNF_GetMaxClockError(void); extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void); extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void); extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetReselectDistance(void); extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void); extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void); extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance); extern int CNF_AllowLocalReference(int *stratum);
extern void CNF_SetupAccessRestrictions(void); extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void); extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void); extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak); extern void CNF_GetTempComp(char **file, double *interval, double *T0, double *k0, double *k1, double *k2);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
extern char *CNF_GetUser(void); extern char *CNF_GetUser(void);
extern int CNF_GetMaxSamples(void); extern int CNF_GetMaxSamples(void);
extern int CNF_GetMinSamples(void); extern int CNF_GetMinSamples(void);
extern int CNF_GetMinSources(void);
extern double CNF_GetRtcAutotrim(void); extern double CNF_GetRtcAutotrim(void);
extern char *CNF_GetHwclockFile(void); extern char *CNF_GetHwclockFile(void);

382
configure vendored
View File

@@ -4,12 +4,34 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate. # chronyd/chronyc - Programs for keeping computer clocks accurate.
# #
# Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Miroslav Lichvar 2009, 2012-2015 # Copyright (C) Miroslav Lichvar 2009, 2012-2014
# #
# ======================================================================= # =======================================================================
rm -f config.h config.log
# This configure script determines the operating system type and version # This configure script determines the operating system type and version
if [ "x${CC}" = "x" ]; then
MYCC="gcc"
else
MYCC="${CC}"
fi
if [ "x${CFLAGS}" = "x" ]; then
MYCFLAGS="-O2 -g"
else
MYCFLAGS="${CFLAGS}"
fi
MYCPPFLAGS="${CPPFLAGS}"
if [ "x${MYCC}" = "xgcc" ]; then
MYCFLAGS="${MYCFLAGS} -Wmissing-prototypes -Wall"
fi
MYLDFLAGS="${LDFLAGS}"
# ====================================================================== # ======================================================================
# FUNCTIONS # FUNCTIONS
@@ -21,7 +43,7 @@ test_code () {
ldflags=$4 ldflags=$4
code=$5 code=$5
printf "%s" "Checking for $name : " echo -n "Checking for $name : "
( (
for h in $headers; do for h in $headers; do
@@ -83,26 +105,18 @@ For better control, use the options below.
--readline-inc-dir=DIR Specify where readline include directory is --readline-inc-dir=DIR Specify where readline include directory is
--readline-lib-dir=DIR Specify where readline lib directory is --readline-lib-dir=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is --with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
--without-nss Don't use NSS even if it is available --without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available --without-tomcrypt Don't use libtomcrypt even if it is available
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
--disable-phc Disable PHC refclock driver
--disable-pps Disable PPS refclock driver
--disable-ipv6 Disable IPv6 support --disable-ipv6 Disable IPv6 support
--disable-phc Disable PHC support
--disable-pps Disable PPS API support
--disable-rtc Don't include RTC even on Linux --disable-rtc Don't include RTC even on Linux
--disable-privdrop Disable support for dropping root privileges --disable-linuxcaps Disable Linux capabilities support
--without-libcap Don't use libcap even if it is available
--enable-scfilter Enable support for system call filtering
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving --disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error --disable-forcednsretry Don't retry on permanent DNS error
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds --with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago] since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root] --with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail] --with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support --enable-debug Enable debugging support
@@ -111,10 +125,10 @@ Fine tuning of the installation directories:
--bindir=DIR user executables [EPREFIX/bin] --bindir=DIR user executables [EPREFIX/bin]
--sbindir=DIR system admin executables [EPREFIX/sbin] --sbindir=DIR system admin executables [EPREFIX/sbin]
--datarootdir=DIR data root [PREFIX/share] --datarootdir=DIR data root [PREFIX/share]
--infodir=DIR info documentation [DATAROOTDIR/info]
--mandir=DIR man documentation [DATAROOTDIR/man] --mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/chrony] --docdir=DIR documentation root [DATAROOTDIR/doc/chrony]
--localstatedir=DIR modifiable single-machine data [/var] --localstatedir=DIR modifiable single-machine data [/var]
--chronysockdir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
--chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony] --chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony]
Overriding system detection when cross-compiling: Overriding system detection when cross-compiling:
@@ -153,22 +167,6 @@ pkg_config () {
pkg-config $@ 2> /dev/null pkg-config $@ 2> /dev/null
} }
#}}} #}}}
#{{{ get_features
get_features () {
ff=1
for f; do
if [ "$ff" = "0" ]; then
printf " "
fi
if grep "define FEAT_$f" config.h > /dev/null; then
printf "%s" "+$f"
else
printf "%s" "-$f"
fi
ff=0
done
}
#}}}
# ====================================================================== # ======================================================================
@@ -184,24 +182,16 @@ EXTRA_OBJECTS=""
EXTRA_DEFS="" EXTRA_DEFS=""
SYSDEFS="" SYSDEFS=""
feat_debug=0 debug=0
feat_cmdmon=1
feat_ntp=1
feat_refclock=1
feat_readline=1 feat_readline=1
try_readline=1 try_readline=1
try_editline=1 try_editline=1
feat_sechash=1
try_nss=1 try_nss=1
try_tomcrypt=1 try_tomcrypt=1
feat_rtc=1 feat_rtc=1
try_rtc=0 try_rtc=0
feat_droproot=1 feat_linuxcaps=1
try_libcap=-1 try_linuxcaps=0
try_clockctl=0
feat_scfilter=0
try_seccomp=-1
priv_ops=""
readline_lib="" readline_lib=""
readline_inc="" readline_inc=""
ncurses_lib="" ncurses_lib=""
@@ -215,14 +205,13 @@ feat_asyncdns=1
feat_forcednsretry=1 feat_forcednsretry=1
ntp_era_split="" ntp_era_split=""
default_user="root" default_user="root"
default_hwclockfile=""
mail_program="/usr/lib/sendmail" mail_program="/usr/lib/sendmail"
for option for option
do do
case "$option" in case "$option" in
--enable-debug ) --enable-debug )
feat_debug=1 debug=1
;; ;;
--disable-readline ) --disable-readline )
feat_readline=0 feat_readline=0
@@ -260,6 +249,9 @@ do
--datarootdir=* ) --datarootdir=* )
SETDATAROOTDIR=`echo $option | sed -e 's/^.*=//;'` SETDATAROOTDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--infodir=* )
SETINFODIR=`echo $option | sed -e 's/^.*=//;'`
;;
--mandir=* ) --mandir=* )
SETMANDIR=`echo $option | sed -e 's/^.*=//;'` SETMANDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
@@ -269,21 +261,9 @@ do
--localstatedir=* ) --localstatedir=* )
SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'` SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--chronysockdir=* )
SETCHRONYSOCKDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--chronyvardir=* ) --chronyvardir=* )
SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'` SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--disable-cmdmon)
feat_cmdmon=0
;;
--disable-ntp)
feat_ntp=0
;;
--disable-refclock)
feat_refclock=0
;;
--disable-rtc) --disable-rtc)
feat_rtc=0 feat_rtc=0
;; ;;
@@ -296,20 +276,8 @@ do
--disable-pps) --disable-pps)
feat_pps=0 feat_pps=0
;; ;;
--disable-privdrop) --disable-linuxcaps)
feat_droproot=0 feat_linuxcaps=0
;;
--without-libcap|--disable-linuxcaps)
try_libcap=0
;;
--enable-scfilter)
feat_scfilter=1
;;
--disable-scfilter)
feat_scfilter=0
;;
--without-seccomp)
try_seccomp=0
;; ;;
--disable-asyncdns) --disable-asyncdns)
feat_asyncdns=0 feat_asyncdns=0
@@ -323,15 +291,9 @@ do
--with-user=* ) --with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'` default_user=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;;
--with-sendmail=* ) --with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'` mail_program=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--disable-sechash )
feat_sechash=0
;;
--without-nss ) --without-nss )
try_nss=0 try_nss=0
;; ;;
@@ -356,126 +318,77 @@ do
esac esac
done done
rm -f config.h config.log
SYSTEM=${OPERATINGSYSTEM}-${MACHINE} SYSTEM=${OPERATINGSYSTEM}-${MACHINE}
case $OPERATINGSYSTEM in case $SYSTEM in
Linux) SunOS-sun4* )
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o" case $VERSION in
[ $try_libcap != "0" ] && try_libcap=1 4.* )
EXTRA_OBJECTS="sys_sunos.o strerror.o"
EXTRA_LIBS="-lkvm"
add_def SUNOS
echo "Configuring for SunOS (" $SYSTEM "version" $VERSION ")"
;;
5.* )
EXTRA_OBJECTS="sys_solaris.o"
EXTRA_LIBS="-lsocket -lnsl -lkvm -lelf"
EXTRA_CLI_LIBS="-lsocket -lnsl"
add_def SOLARIS
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
;;
esac
;;
Linux* )
EXTRA_OBJECTS="sys_generic.o sys_linux.o wrap_adjtimex.o"
try_linuxcaps=1
try_rtc=1 try_rtc=1
[ $try_seccomp != "0" ] && try_seccomp=1
try_setsched=1 try_setsched=1
try_lockmem=1 try_lockmem=1
try_phc=1 try_phc=1
add_def LINUX add_def LINUX
echo "Configuring for " $SYSTEM echo "Configuring for " $SYSTEM
;; if [ "${MACHINE}" = "alpha" ]; then
FreeBSD) echo "Enabling -mieee"
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o" # FIXME: Should really test for GCC
add_def FREEBSD MYCFLAGS="$MYCFLAGS -mieee"
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME ADJUSTTIMEX SETTIME BINDSOCKET"
fi fi
;;
BSD/386-i[3456]86|FreeBSD-i386|FreeBSD-amd64 )
# Antti Jrvinen <costello@iki.fi> reported that this system can
# be supported with the SunOS 4.x driver files.
EXTRA_OBJECTS="sys_sunos.o strerror.o"
EXTRA_LIBS="-lkvm"
add_def SUNOS
echo "Configuring for $SYSTEM (using SunOS driver)"
;;
NetBSD-* )
EXTRA_OBJECTS="sys_netbsd.o"
EXTRA_LIBS="-lkvm"
SYSDEFS=""
echo "Configuring for $SYSTEM" echo "Configuring for $SYSTEM"
;; ;;
NetBSD) SunOS-i86pc* )
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o" # Doug Woodward <dougw@whistler.com> reported that this configuration
try_clockctl=1 # works for Solaris 2.8 / SunOS 5.8 on x86 platforms
add_def NETBSD EXTRA_OBJECTS="sys_solaris.o"
echo "Configuring for $SYSTEM" EXTRA_LIBS="-lsocket -lnsl -lkvm -lelf"
;; EXTRA_CLI_LIBS="-lsocket -lnsl"
Darwin)
EXTRA_OBJECTS="sys_macosx.o"
EXTRA_LIBS="-lresolv"
EXTRA_CLI_LIBS="-lresolv"
add_def MACOSX
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi
echo "Configuring for MacOS X (" $SYSTEM "MacOS X version" $VERSION ")"
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
EXTRA_LIBS="-lsocket -lnsl -lresolv"
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
add_def SOLARIS add_def SOLARIS
# These are needed to have msg_control in struct msghdr
add_def __EXTENSIONS__
add_def _XOPEN_SOURCE 1
add_def _XOPEN_SOURCE_EXTENDED 1
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
;; ;;
CYGWIN32_NT-i[3456]86 )
EXTRA_OBJECTS="sys_winnt.o"
EXTRA_LIBS=""
add_def WINNT
echo "Configuring for Windows NT (Cygwin32)"
;;
* ) * )
echo "error: $SYSTEM is not supported (yet?)" echo "Sorry, I don't know how to build this software on your system."
exit 1 exit 1
;; ;;
esac esac
if [ $feat_debug = "1" ]; then
add_def FEAT_DEBUG
fi
add_def DEBUG $feat_debug
if [ $feat_cmdmon = "1" ]; then
add_def FEAT_CMDMON
EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o"
fi
if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
else
feat_asyncdns=0
fi
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS addrfilt.o clientlog.o keys.o nameserv.o"
else
feat_ipv6=0
fi
if [ $feat_refclock = "1" ]; then
add_def FEAT_REFCLOCK
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o"
fi
MYCC="$CC"
MYCFLAGS="$CFLAGS"
MYCPPFLAGS="$CPPFLAGS"
MYLDFLAGS="$LDFLAGS"
if [ "x$MYCC" = "x" ]; then
MYCC=gcc
if ! test_code "$MYCC" '' '' '' ''; then
MYCC=cc
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: no C compiler found"
exit 1
fi
fi
else
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: C compiler $MYCC cannot create executables"
exit 1
fi
fi
if [ "x$MYCFLAGS" = "x" ]; then
MYCFLAGS="-O2 -g"
fi
if [ "x$MYCC" = "xgcc" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
if test_code '64-bit time_t' 'time.h' '' '' ' if test_code '64-bit time_t' 'time.h' '' '' '
char x[sizeof(time_t) > 4 ? 1 : -1] = {0}; char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
return x[0];' return x[0];'
@@ -487,8 +400,8 @@ then
split_days=0 split_days=0
else else
split_seconds=`date '+%s'` split_seconds=`date '+%s'`
if [ "x$split_seconds" = "x" ]; then if [ "x$split_seconds" = "" ]; then
echo "error: could not get current time, --with-ntp-era option is needed" echo "Could not get current time, --with-ntp-era option is needed"
exit 1 exit 1
fi fi
split_days=$((50 * 365)) split_days=$((50 * 365))
@@ -517,41 +430,34 @@ else
if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then
LIBS="-lm" LIBS="-lm"
else else
echo "error: could not compile/link a program which uses sqrt(), log(), pow()" echo "Can't compile/link a program which uses sqrt(), log(), pow(), bailing out"
exit 1 exit 1
fi fi
fi fi
if test_code '<stdint.h>' 'stdint.h' '' '' ''; then if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
add_def HAVE_STDINT_H add_def HAS_STDINT_H
fi fi
if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
add_def HAVE_INTTYPES_H add_def HAS_INTTYPES_H
fi
if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
struct in_pktinfo ipi;
return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
then
add_def HAVE_IN_PKTINFO
fi fi
if [ $feat_ipv6 = "1" ] && \ if [ $feat_ipv6 = "1" ] && \
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" ' test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' '' '
struct sockaddr_in6 n; struct sockaddr_in6 n;
char p[100]; char p[100];
n.sin6_addr = in6addr_any; n.sin6_addr = in6addr_any;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));' return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then then
add_def FEAT_IPV6 add_def HAVE_IPV6
if test_code 'struct in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' ' if test_code 'in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;' return sizeof(struct in6_pktinfo);'
then then
add_def HAVE_IN6_PKTINFO add_def HAVE_IN6_PKTINFO
else else
if test_code 'struct in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \ if test_code 'in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \
'-D_GNU_SOURCE' '' 'return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;' '-D_GNU_SOURCE' '' 'return sizeof(struct in6_pktinfo);'
then then
add_def _GNU_SOURCE add_def _GNU_SOURCE
add_def HAVE_IN6_PKTINFO add_def HAVE_IN6_PKTINFO
@@ -559,7 +465,7 @@ then
fi fi
fi fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \ if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' '' \
'return getaddrinfo(0, 0, 0, 0);' 'return getaddrinfo(0, 0, 0, 0);'
then then
add_def HAVE_GETADDRINFO add_def HAVE_GETADDRINFO
@@ -571,16 +477,11 @@ if [ $feat_asyncdns = "1" ] && \
then then
add_def FEAT_ASYNCDNS add_def FEAT_ASYNCDNS
add_def USE_PTHREAD_ASYNCDNS add_def USE_PTHREAD_ASYNCDNS
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
MYCFLAGS="$MYCFLAGS -pthread" MYCFLAGS="$MYCFLAGS -pthread"
fi fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
add_def HAVE_ARC4RANDOM
fi
timepps_h="" timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then if [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
timepps_h="sys/timepps.h" timepps_h="sys/timepps.h"
add_def HAVE_SYS_TIMEPPS_H add_def HAVE_SYS_TIMEPPS_H
@@ -599,47 +500,20 @@ if [ "x$timepps_h" != "x" ] && \
struct timespec ts; struct timespec ts;
return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);' return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);'
then then
add_def FEAT_PPS add_def HAVE_PPSAPI
fi fi
if [ $feat_droproot = "1" ] && [ $try_libcap = "1" ] && \ if [ $feat_linuxcaps = "1" ] && [ $try_linuxcaps = "1" ] && \
test_code \ test_code \
libcap \ linuxcaps \
'sys/types.h pwd.h sys/prctl.h sys/capability.h grp.h' \ 'sys/types.h pwd.h sys/prctl.h sys/capability.h grp.h' \
'' '-lcap' \ '' '-lcap' \
'prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep"));' 'prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep"));'
then then
add_def FEAT_PRIVDROP add_def FEAT_LINUXCAPS
EXTRA_LIBS="$EXTRA_LIBS -lcap" EXTRA_LIBS="$EXTRA_LIBS -lcap"
fi fi
if [ $feat_droproot = "1" ] && [ $try_clockctl = "1" ] && \
test_code '<sys/clockctl.h>' 'sys/clockctl.h' '' '' ''
then
add_def FEAT_PRIVDROP
priv_ops="BINDSOCKET"
fi
if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
test_code seccomp 'seccomp.h' '' '-lseccomp' \
'seccomp_init(SCMP_ACT_KILL);'
then
add_def FEAT_SCFILTER
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could
# block the main thread
priv_ops="NAME2IPADDRESS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi
if [ "x$priv_ops" != "x" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS privops.o"
add_def PRIVOPS_HELPER
for o in $priv_ops; do
add_def PRIVOPS_$o
done
fi
if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \ if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \
test_code '<linux/rtc.h>' 'sys/ioctl.h linux/rtc.h' '' '' \ test_code '<linux/rtc.h>' 'sys/ioctl.h linux/rtc.h' '' '' \
'ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF);' 'ioctl(1, RTC_UIE_ON&RTC_UIE_OFF&RTC_RD_TIME&RTC_SET_TIME, 0&RTC_UF);'
@@ -648,7 +522,7 @@ then
add_def FEAT_RTC add_def FEAT_RTC
fi fi
if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \ if [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \ test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);' 'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
then then
@@ -736,7 +610,7 @@ HASH_OBJ="hash_intmd5.o"
HASH_COMPILE="" HASH_COMPILE=""
HASH_LINK="" HASH_LINK=""
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then if [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`" test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3" test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \ if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
@@ -747,11 +621,11 @@ if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
HASH_COMPILE="$test_cflags" HASH_COMPILE="$test_cflags"
HASH_LINK="$test_link" HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
add_def FEAT_SECHASH add_def GENERATE_SHA1_KEY
fi fi
fi fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then if [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \ if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);' 'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
then then
@@ -759,7 +633,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
HASH_COMPILE="-I/usr/include/tomcrypt" HASH_COMPILE="-I/usr/include/tomcrypt"
HASH_LINK="-ltomcrypt" HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
add_def FEAT_SECHASH add_def GENERATE_SHA1_KEY
fi fi
fi fi
@@ -793,6 +667,11 @@ if [ "x$SETDATAROOTDIR" != "x" ]; then
DATAROOTDIR=$SETDATAROOTDIR DATAROOTDIR=$SETDATAROOTDIR
fi fi
INFODIR=${DATAROOTDIR}/info
if [ "x$SETINFODIR" != "x" ]; then
INFODIR=$SETINFODIR
fi
MANDIR=${DATAROOTDIR}/man MANDIR=${DATAROOTDIR}/man
if [ "x$SETMANDIR" != "x" ]; then if [ "x$SETMANDIR" != "x" ]; then
MANDIR=$SETMANDIR MANDIR=$SETMANDIR
@@ -808,38 +687,23 @@ if [ "x$SETLOCALSTATEDIR" != "x" ]; then
LOCALSTATEDIR=$SETLOCALSTATEDIR LOCALSTATEDIR=$SETLOCALSTATEDIR
fi fi
CHRONYSOCKDIR=${LOCALSTATEDIR}/run/chrony
if [ "x$SETCHRONYSOCKDIR" != "x" ]; then
CHRONYSOCKDIR=$SETCHRONYSOCKDIR
fi
CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony
if [ "x$SETCHRONYVARDIR" != "x" ]; then if [ "x$SETCHRONYVARDIR" != "x" ]; then
CHRONYVARDIR=$SETCHRONYVARDIR CHRONYVARDIR=$SETCHRONYVARDIR
fi fi
add_def DEBUG $debug
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\"" add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
add_def DEFAULT_USER "\"$default_user\"" add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYSOCKDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\"" add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
if [ -f version.txt ]; then if [ -f version.txt ]; then
CHRONY_VERSION="`cat version.txt`" add_def CHRONY_VERSION "\"`cat version.txt`\""
else else
CHRONY_VERSION="DEVELOPMENT" add_def CHRONY_VERSION "\"DEVELOPMENT\""
fi fi
add_def CHRONY_VERSION "\"${CHRONY_VERSION}\"" for f in Makefile chrony.conf.5 chrony.texi chronyc.1 chronyd.8
for f in Makefile doc/Makefile test/unit/Makefile
do do
echo Creating $f echo Creating $f
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\ sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
@@ -858,12 +722,10 @@ do
s%@SBINDIR@%${SBINDIR}%;\ s%@SBINDIR@%${SBINDIR}%;\
s%@DOCDIR@%${DOCDIR}%;\ s%@DOCDIR@%${DOCDIR}%;\
s%@MANDIR@%${MANDIR}%;\ s%@MANDIR@%${MANDIR}%;\
s%@INFODIR@%${INFODIR}%;\
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\ s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
s%@CHRONYSOCKDIR@%${CHRONYSOCKDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\ s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\ s%@DEFAULT_USER@%${default_user}%;"\
s%@DEFAULT_USER@%${default_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f < ${f}.in > $f
done done

339
contrib/DNSchrony/COPYING Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

583
contrib/DNSchrony/DNSchrony.pl Executable file
View File

@@ -0,0 +1,583 @@
#!/usr/bin/perl
# Copyright (C) Paul Elliott 2002
my($copyrighttext) = <<'EOF';
# Copyright (C) Paul Elliott 2002
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# SEE COPYING FOR DETAILS
EOF
#modules we use.
use Socket;
use Getopt::Std;
use Net::DNS;
use Tie::Syslog;
use File::Temp qw/ :mktemp /;
use File::Copy;
local($res) = new Net::DNS::Resolver;
#dns lookup of IP address.
#returns ip or errorstring.
sub gethostaddr($) #get ip address from host
{
my($host) = shift;
$query = $res->search($host);
if ($query) {
foreach $rr ($query->answer) {
next unless $rr->type eq "A";
print $rr->address, "\n" if $pedebug;
return $rr->address;
}
} else {
print "query failed: ", $res->errorstring, "\n" if $pedebug;
return $res->errorstring;
}
}
#send messages to syslog
sub Log($$)
{
if ($log) {
my($level) = shift;
my($mess) =shift;
tie *MYLOG, 'Tie::Syslog',$level,$0,'pid','unix';
print MYLOG $mess;
untie *MYLOG;
}
}
#send message to output or syslog
#and die.
sub BadDie($)
{
my($myerr) =$!;
my($mess)=shift;
if($log){
tie *MYLOG, 'Tie::Syslog','local0.err',$0,'pid','unix';
print MYLOG $mess;
print MYLOG $myerr;
untie *MYLOG;
} else {
print "$mess\n$myerr\n";
}
die $mess;
}
sub isIpAddr($) #return true if looks like ip address
{
my($ip) = shift;
return 1 if ( $ip =~ m/$ipOnlyPAT/ );
return 0;
}
sub isHostname($) #return true if looks like ip address
{
my($ip) = shift;
return 1 if ( $ip =~ m/$hostnameOnlyPAT/ );
return 0;
}
#send commands to chronyc by piping.
sub chronyc($) #send commands to chronyc
{
my($command) = shift;
my($err) = "/var/tmp/chronyc.log";
my($chronyP) = "/usr/local/bin/chronyc";
open(CHRONY, "| $chronyP 1>$err 2>&1");
print CHRONY "$passwd$command\n";
close(CHRONY);
Log('local0.info',"chronyc command issued=$command");
#look at status lines till return bad.
open( IN, "<$err");
my($status);
while (<IN>) {
$status = $_;
unless ( m/\A200 OK/ ) {
last;
}
}
$status ="" if ( $status =~ m/\A200 OK/ );
close(IN);
unlink $err;
Log('local0.info',"chronyc results=$status");
return $status;
}
#common patterns
# an ip address patern
local($ipPAT) = qr/\d{1,3}(?:\.\d{1,3}){3}/;
# an hostname pattern
local($hostnamePAT) = qr/\w+(?:\.\w+)*/;
#line with hostname only
local($hostnameOnlyPAT) = qr/\A$hostnamePAT\Z/;
#line with ip address only
local($ipOnlyPAT) =qr/\A$ipPAT\Z/;
#options hash
my(%opts);
getopts('nuadslPSC', \%opts);
local($log) = ( $opts{'l'} ) ? 1 : 0;
my($offline) = !( $opts{'n'} ) ;
my($offlineS) = ( $opts{'n'} ) ? " " : " offline" ;
# paul elliotts secret debug var. no one will ever find out about it.
local($pedebug)=( ($ENV{"PAULELLIOTTDEBUG"}) or ($opts{P}) );
if ($opts{C}) {
print $copyrighttext;
exit 0;
}
print <<"EOF" unless $opts{'S'};
$0, Copyright (C) 2002 Paul Elliott
$0 comes with ABSOLUTELY NO WARRANTY; for details
invoke $0 -C. This is free software, and you are welcome
to redistribute it under certain conditions; invoke $0 -C
for details.
EOF
local($passwd);
# password to send to chronyc
my($pl) = $ENV{"CHRONYPASSWORD"};
#password comand to send to chronyc
if ( $pl ) {
$passwd = "password $pl\n";
} else {
$passwd = "";
}
print "passwd=$passwd\n" if ($pedebug);
my(%host2ip);
# hash of arrays. host2ip{$host}[0] is ip address for this host
# host2ip{$host}[1] is rest of paramenters for this host exc offline.
#if debuging do chrony.conf in current directory.
my($listfile) =( ($pedebug) ? "./chrony.conf" : "/etc/chrony.conf") ;
# This section reads in the old data about
# hostnames IP addresses and server parameters
# data is stored as it would be in chrony.conf
# file i.e.:
#># HOSTNAME
#>server IPADDR minpoll 5 maxpoll 10 maxdelay 0.4 offline
#
# the parameter offline is omitted if the -n switch is specified.
# first parameter is the filename of the file usually
# is /etc/DNSchrony.conf
# this is where we store the list of DNS hosts.
# hosts with static IP address shold be kept in chrony.conf
# this is header that marks dnyamic host section
my($noedithead)=<<'EOF';
## DNSchrony dynamic dns server section. DO NOT EDIT
## per entry FORMAT:
## |--------------------------------------------|
## |#HOSTNAME |
## |server IP-ADDRESS extra-params [ offline ] |
## |--------------------------------------------|
EOF
#patern that recognizes above.
my($noeditheadPAT) =
qr/\#\#\s+DNSchrony\s+dynamic\s+dns\s+server\s+section\.\s+DO\s+NOT\s+EDIT\s*/;
#end of header marker.
my($noeditheadend)=<<'EOF';
## END OF DNSchrony dynamic dns server section.
EOF
#pattern that matches above.
my($noeditheadendPAT)=
qr/\#\#\s+END\s+OF\s+DNSchrony\s+dynamic\s+dns\s+server\s+section.\s*/;
#array to hold non dns portion of chrony.conf
my(@chronyDconf);
my($ip);
my($rest);
my($host);
# for each entry in the list of hosts....
open(READIN, "<$listfile") or BadDie("Can not open $listfile");
# read till dynamic patern read save in @chronyDconf
while ( <READIN> ) {
my($line) = $_;
last if ( m/\A$noeditheadPAT\Z/ );
push(@chronyDconf,$line);
}
while ( <READIN> ) {
#end loop when end of header encountered
last if ( m/\A$noeditheadendPAT/ );
# parse the line giving ip address, extra pamamters, and host
#do host comment line first
($host) = m{
\A\#\s*
($hostnamePAT)
\s*\z
}xio;
#no match skip this line.
next unless ( $host );
# read next line
$_ = <READIN>;
# parse out ip address extra parameters.
($ip,$rest) =
m{
\A
\s*
server #server comand
\s+
($ipPAT) #ip address
(?ixo: \s )
\s*
(
(?(?!
(?iox: offline )? #skip to offline #
\s* #or #
\Z
).)*
)
(?ixo:
\s*
(?ixo: offline )? #consume to #
\s*
\Z
)
}xio ;
#if failure again.
next unless ( $ip );
$rest =~ s/\s*\z//; #remove trail blanks
#from parameters
# store the data in the list
# key is host name value is
# array [0] is ip address
# [1] is other parameters
$host2ip{$host} = [$ip,$rest] ;
print "ip=$ip rest=$rest host=$host<\n" if $pedebug;
}
#read trailing line into @chronyDconf
while ( <READIN> ) {
push(@chronyDconf,$_);
}
close(READIN) or BadDie("can not close $listfile");
#if the add command:
# command can be HOST=IPADDRESS OTHER_PARAMETERS
# means add the server trust the ip address geven with out a dns lookup
# good for when dns is down but we know the ip addres
# or
# HOST OTHER_PARAMETERS
#we lookup the ip address with dns.
if ($opts{'a'}) {
my($param)= shift;
# parse the param is it hostname
if ( ($host,$ip) = $param =~ m/\A($hostnamePAT)=($ipPAT)\Z/ ) {
printf "ip=$ip host=$host\n" if ($pedebug);
} else {
$host = $param;
# get the ip address
$ip = gethostaddr($host);
if ( ! isIpAddr($ip) or ! isHostname($host) ) {
print "query failed: ", $ip, "host=$host\n" if $pedebug;
exit 1;
}
}
printf "ip=$ip host=$host\n" if ($pedebug);
# add the server using chronyc
my($status) = chronyc("add server $ip $rest");
if ($status) { #chronyc error
print "chronyc failed, status=$status\n";
exit 1;
}
# get rest of arguements
$rest = join( ' ', @ARGV);
print "rest=$rest\n" if ($pedebug);
#save node in hash
$host2ip{$host} = [$ip,$rest] ;
print "ip=$ip rest=$rest host=$host<\n" if $pedebug;
}
#delete command if arguement is ip address
#just delete it
#if a hostname look it up
#then delete it.
if ($opts{'d'}) {
$host = shift;
#get host name is it ap address
if ( isIpAddr($host) ) { # if ip address
my($hostIT);
my($found) =0;
foreach $hostIT (keys(%host2ip) ) { #search for match
if ( $host2ip{$hostIT}[0] eq $host) {
$found=1; #record match
}
} #end of search
if ($found) { #if match found
my($status) = chronyc("delete $host"); #chronyc
if ($status) { #chronyc error
print "chronyc failed, status=$status\n";
exit 1;
} else { #reiterate
foreach $hostIT (keys(%host2ip) ) {
if ( $host2ip{$hostIT}[0] eq $host) {
delete $host2ip{$hostIT}; #deleting match hosts
}
}
}
}
} else { #else not ip address
#must be hostname
if ( ! $host2ip{$host} ) {
print "No such host as $host listed\n";
exit 1;
}
#get ip address
$ip=gethostaddr($host);
if ( ! isIpAddr($ip) ) { #no ip address
print "query failed: ", $ip, "\n" if $pedebug;
exit 1;
}
printf "ip=$ip host=$host\n" if ($pedebug);
my($listed_host_ip) = $host2ip{$host}[0]; # get the ip address saved
if ( $ip ne $listed_host_ip) {
print
"Info: listed host ip=>$listed_host_ip".
"< is different from DNS ip=>$ip<\n";
$ip = $listed_host_ip;
}
# delete the server
my($status) = chronyc("delete $listed_host_ip\n");
if ($status) {
print "chronyc failed, status=$status\n";
exit 1;
}
#delete table entry
delete$host2ip{$host};
}
}
#update for each host who's dns ip address has changed
#delete the old server and add the new. update the record.
if ($opts{'u'}) {
my($command);
my(%prospective); # store new IP address we
#are thinking of changing.
Log('local0.info',
"Now searching for modified DNS entries.");
foreach $host (keys(%host2ip)) { #for each listed host
my($old_ip) = $host2ip{$host}[0]; #get old ip
$rest = $host2ip{$host}[1]; #extra params
$ip = gethostaddr($host); #get new ip from dns
#if error
if ( ! isIpAddr($ip) or ! isHostname($host) ) {
print "query failed: ", $ip, "host=$host\n";
Log('local0.err',"query failed: ". $ip . "host=$host");
exit 1;
}
next if($ip eq $old_ip); #if ip not changed, skip
Log('local0.info',"Ip address for $host has changed. Old IP address=".
"$old_ip, new IP address=$ip");
# add command to delete old host, add the new.
$command = $command . "delete $old_ip\n" .
"add server $ip $rest\n";
# we are now thinking about changing this host ip
$prospective{$host} = [$ip,$rest];
}
# submit all the accumulated chronyc commands if any.
if ($command) {
$status = chronyc($command);
if ($status) {
print "chronyc failed, status=$status\n";
Log('local0.err',"query failed: ". $ip . "host=$host");
exit 1;
}
} else { #if no commands exit
exit 0; #because no rewrite of file needed
}
#copy prospective modifications back into main table.
#we now know that all these mods were done with chronyc
foreach $host (keys(%prospective)) {
my($ip) = $prospective{$host}[0];
$rest = $prospective{$host}[1];
$host2ip{$host} = [$ip,$rest];
}
}
#starting for each entry we have read in from the old list
# add the server in chronyc
# this option is seldom used.
if ($opts{'s'}) {
my($command)="";
foreach $host (keys(%host2ip)) {
$command = $command . "add server $host2ip{$host}[0] ".
"$host2ip{$host}[1]\n";
}
my($status) = chronyc($command);
if ($status) {
print "chronyc failed, status=$status\n";
exit 1;
}
}
# write out the data file in format
#># HOSTNAME
#>server IPADDRESS extra parameters [offline]
# offline is omitted if -n switch is specified.
my(@value);
my($such);
{
# to start out we write to temporary file.
(my($writeout) , my($outname)) = mkstemp( "${listfile}.outXXXXXXX");
$outname or BadDie("can not open for $listfile");
# save the chrony.conf part!
# and write the DYNAMIC header
print $writeout @chronyDconf, $noedithead;
# for each entry
foreach $host (keys(%host2ip) ){
#write the record
# write the comment that indicates the hostname
# and the server command.
print $writeout
"\# $host\nserver $host2ip{$host}[0] $host2ip{$host}[1]${offlineS}\n" ;
print
"server $host2ip{$host}[0] $host2ip{$host}[1]${offlineS}\# $host\n"
if $pedebug;
}
#WRITE THE end of dnyamic marker comment
print $writeout $noeditheadend;
# close the output file which was a temporary file.
close($writeout) or BadDie("can not close $outname");
# we now begin a intracate dance to make the the temporary
# the main chrony.conf
#
# if there is a chrony.conf.BAK save it to a temporary.
# rename chrony.conf to chrony.conf.BAK
# rename the temporary to chrony.conf
# if there already was a chrony.conf.BAK, unlink the copy of this.
my($backname) = "$listfile\.BAK";
my($backplain) = ( -f $backname );
my($saveback);
#if chrony.conf.BAK exists rename to a temporary.
if ($backplain ) {
$saveback = mktemp("${backname}.bakXXXXXXX");
move($backname,$saveback) or
BadDie "unable to move $backname to $savename";
}
# rename old chrony.conf to chrony.conf.BAK
move($listfile,$backname) or
BadDie "unable to move $listfile to $backname";
# rename our output to chrony.conf
move($outname,$listfile) or
BadDie "unable to move $outname to $listfile";
#if there was a temporary chrony.conf.BAK that we saved to temp
#unlink it
unlink($saveback) or BadDie "unable to unlink $saveback" if($backplain);
}

21
contrib/DNSchrony/DNSchronyADD Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/bash
# $1 is chrony password.
# $2 is hostname to add or hostname=ipaddres
# $3-$9 is rest of extra server parameters
FIRST="$1"
HOST="$2"
shift 2
#remaining parameters a the other paramaters to server command
#excluding "offline"
ARGS="$*"
#if none use default taken from chrony documentation.
DEF="minpoll 5 maxpoll 10 maxdelay 0.4"
DARGS=${ARGS:-$DEF}
CHRONYPASSWORD=$FIRST \
/usr/local/bin/DNSchrony.pl -a "$HOST" "$DARGS"

View File

@@ -0,0 +1,7 @@
#!/usr/bin/bash
# $1 is chrony password.
# $2 host to be deleted if ip nn.n.n.n then no DNS used
CHRONYPASSWORD=$1 \
/usr/local/bin/DNSchrony.pl -d $2

View File

@@ -0,0 +1,7 @@
#!/usr/bin/bash
# $1 is chrony password.
CHRONYPASSWORD=$1 \
/usr/local/bin/DNSchrony.pl -ulS

166
contrib/DNSchrony/README Normal file
View File

@@ -0,0 +1,166 @@
Copyright (C) Paul Elliott 2002
DNSchrony.pl version -2.0
Problem: If you look at the list of secondary NTP servers:
http://www.eecis.udel.edu/~mills/ntp/clock2.htm
you will find statements like this:
"Note: IP addresses are subject to change; please use DNS"
These servers represent a problem for chrony. Chrony is a program
designed to work on hosts with an intermittent connection to the
internet. Often no DNS is available when chrony starts. As chrony
is currently designed, chronyd never sees a DNS host name. If a
user specifies one when using chronyc's "add server" command, the
DNS lookup is done by chronyc and an IP address is passed to chronyd.
One can imagine I suppose, a redesign to chrony in which chronyd
keeps track of DNS changes. But this has problems, all the time
chronyd is fooling around with DNS, it would not be keeping track
of its prime function, what the clocks and NTP servers are saying.
This could result in poorer performance. Or perhaps you say that
chronyd should be multi threaded. One thread to fool with DNS
and another to keep track of time. But this introduces a great
deal of complexity, and complexity is the enemy of elegant robust
code. Besides, Richard probably has better things to do.
I have attempted to address this problem with a humble perl script,
which I now release under the GPL: DNSchrony.pl
PLEA FOR HELP FROM EXPERIENCED PERL HACKERS.
Please go thru the code and find errors and improvements.
I am not quite an polished perl hacker. Please fix bugs and
make improvements. It needs better documentation. Someone
who knows how, put in some POD.
END OF PLEA
Philosophy of DNSchrony.pl: keep a list of servers that use
DNS. From time to time, hopefully when DNS is up, go thru
the list lookup all the hostnames and see if any ip addresses have
changed. If any have changed, update our list and do chronyc
"delete" and "add server" commands so that chronyd now talks to
the right NTP server.
Additional nuance: keep the list in /etc/chrony.conf in the
form of comments starting with "#" and "server" commands
legal in a chrony.conf file. Format of a list entry:
# hostname
server IP-ADDRESS extra server parameters
These entries are delimited by special comments that allow
DNSchrony.pl to find them and also tell humans not to mess with them.
Example of such a section of a chrony.conf file:
dumpdir /var/log/chrony
rtcfile /etc/chrony.rtc
## DNSchrony dynamic dns server section. DO NOT EDIT
## per entry FORMAT:
## |--------------------------------------------|
## |#HOSTNAME |
## |server IP-ADDRESS extra-params [ offline ] |
## |--------------------------------------------|
# tock.greyware.com
server 208.14.208.44 minpoll 5 maxpoll 10 maxdelay 0.4 offline
# tick.greyware.com
server 208.14.208.19 minpoll 5 maxpoll 10 maxdelay 0.4 offline
# ntppub.tamu.edu
server 128.194.254.9 minpoll 5 maxpoll 10 maxdelay 0.4 offline
## END OF DNSchrony dynamic dns server section.
This allows the list of dynamic DNS servers to be preserved
when chronyd is stoped/started.
All servers that do not have ip addresses subject to change
should be put in the regular part of chrony.conf as described
in the chrony documentation.
Security philosophy: DNSchrony does no security checking but
relies on other security factors.
Users without the privilege to modify /etc/chrony.conf and the
directory /etc will be unable to use DNSchrony to do so, because
of file protections. DNSchrony passes thru passwords to chronyc.
Users that do not know the correct chronyc password will be
unable to get chronyd do do anything. Thus, DNSchrony passes
the buck to these other security features.
INSTALLATION:
copy the files: DNSchronyADD DNSchronyUPDATE DNSchronyDELETE DNSchrony.pl
to /usr/local/bin. Backup the file /etc/chrony.conf leave hosts
with static ip addresses in this file.
DNSchrony uses the following perl modules. See that they are installed.
Get them from CPAN if needed.
Net::DNS, Tie::Syslog, Getopt::Std, Socket, File.
Cause DNSchronyUPDATE bash script to run from time to time when DNS
is working. If you have a dialup, one way to do this would be to
modify your /etc/ppp/ip-up.local file as follows:
cat <<EOF | /usr/local/bin/chronyc
password mysecret
online
EOF
# update all of the dynamic servers and save the result.
# do not wait for response
nohup /usr/local/bin/DNSchronyUPDATE mysecret >/dev/null 2>&1 &
Since this file contains the chronyc password you will want to set the
file permissions so that just everybody will not be able to read
it. But you already did that when you put in the chronyc command. Any
other way to make DNSchronyUPDATE run perodicly when DNS is up will
also work.
To add a server with a varying IP address one could run:
/usr/local/bin/DNSchronyADD mysecret tock.greyware.com
or if you want to specify different server parameters you
could say:
/usr/local/bin/DNSchronyADD mysecret tock.greyware.com "minpoll 10 maxpoll 20 maxdelay 0.8"
The DNSchronyADD's default for these parameters is:
"minpoll 5 maxpoll 10 maxdelay 0.4" values that are often shown
as examples in the chrony documentation.
If DNS is not running now but you know the IP address, you can say:
/usr/local/bin/DNSchronyADD mysecret tock.greyware.com=208.14.208.44
Of course, the IP address will be checked next time DNSchronyUPDATE
runs.
To delete dynamic DNS a server:
/usr/local/bin/DNSchronyDELETE mysecret tock.greyware.com
To change parameters delete and re-add.
Of course, in all of the above "mysecret" is your chronyc password
which SHOULD NOT BE "mysecret".
----------------------------------------------
DNSchrony.pl is covered by the GPL
# Copyright (C) Paul Elliott 2002
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# SEE COPYING FOR DETAILS

View File

@@ -0,0 +1,22 @@
#example file /etc/ppp/ip-up.local
#originally from SuSE distribution
#modified for chrony
cat <<EOF | /usr/local/bin/chronyc
password mysecret
online
EOF
# update all of the dynamic servers and save the result.
# do not wait for response
nohup /usr/local/bin/DNSchronyUPDATE mysecret >/dev/null 2>&1 &
#other stuff who knows?
# The following lines added for Linux-HA support # Heartbeat
DEVFILE=`echo $DEVICE | sed -e 's!^/dev/!!' -e 's!/!.!g'` # Heartbeat
OUTFILE=/var/run/ppp.d/$DEVFILE # Heartbeat
( # Heartbeat
echo "$IPREMOTE" # Heartbeat
echo "$IFNAME" # Heartbeat
echo "$PPPD_PID" # Heartbeat
echo "$IPLOCAL" # Heartbeat
) > $OUTFILE # Heartbeat

View File

@@ -1,103 +0,0 @@
Notes for installing chrony on MacOS X
Author: Bryan Christianson (bryan@whatroute.net)
------------------------------------------------
These files are for those admins/users who would prefer to install chrony
from the source distribution and are intended as guidelines rather than
being definitive. They can be edited with a plain text editor, such as
vi, emacs or your favourite IDE (xcode)
It is assumed you are comfortable with installing software from the
terminal command line and know how to use sudo to acquire root access.
If you are not familiar with the MacOS X command line then
please consider using ChronyControl from http://whatroute.net/chronycontrol.html
ChronyControl provides a gui wrapper for installing these files and sets the
necessary permissions on each file.
Install the chrony software
---------------------------
You will need xcode and the commandline additions to build and install chrony.
These can be obtained from Apple's website via the App Store.
cd to the chrony directory
./configure
make
sudo make install
chrony is now installed in default locations (/usr/local/sbin/chronyd,
/usr/local/bin/chronyc)
Create a chrony.conf file - see the chrony website for details
The support files here assume the following directives are specified in the
chrony.conf file
keyfile /etc/chrony.d/chrony.keys
driftfile /var/db/chrony/chrony.drift
bindcmdaddress /var/db/chrony/chronyd.sock
logdir /var/log/chrony
dumpdir /var/db/chrony
Install this file as /etc/chrony.d/chrony.conf and create
the directories specified in the above directives if they don't exist.
You will need root permissions to create the directories.
Running chronyd
---------------
At this point chronyd *could* be run as a daemon. Apple discourage running
daemons and their preferred method uses the launchd facility. The
support files here provide a launchd configuration file for chronyd and also
a shell script and launchd configuration file to rotate the chronyd logs on a daily basis.
Support files
-------------
Dates and sizes may differ
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist
If you have used chrony support directories other than those suggested, you
will need to edit each file and make the appropriate changes.
Installing the support files
----------------------------
1. chronylogrotate.sh
This is a simple shell script that deletes old log files. Unfortunately because
of the need to run chronyc, the standard MacOS X logrotation does not work with
chrony logs.
This script runs on a daily basis under control of launchd and should be
installed in the /usr/local/bin directory
sudo cp chronylogrotate.sh /usr/local/bin
sudo chmod +x /usr/local/bin/chronylogrotate.sh
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
2. org.tuxfamily.chronyc.plist
This file is the launchd plist that runs logrotation each day. You may
wish to edit this file to change the time of day at which the rotation
will run, currently 04:05 am
sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
3. org.tuxfamily.chronyd.plist
This file is the launchd plist that runs chronyd when the Macintosh starts.
sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist

View File

@@ -1,58 +0,0 @@
#!/bin/sh
# chronyd/chronyc - Programs for keeping computer clocks accurate.
#
# **********************************************************************
# * Copyright (C) Bryan Christianson 2015
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of version 2 of the GNU General Public License as
# * published by the Free Software Foundation.
# *
# * This program is distributed in the hope that it will be useful, but
# * WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# * General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License along
# * with this program; if not, write to the Free Software Foundation, Inc.,
# * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# *
# **********************************************************************
LOGDIR=/var/log/chrony
rotate () {
prefix=$1
rm -f $prefix.log.10
for (( count=9; count>= 0; count-- ))
do
next=$(( $count+1 ))
if [ -f $prefix.log.$count ]; then
mv $prefix.log.$count $prefix.log.$next
fi
done
if [ -f $prefix.log ]; then
mv $prefix.log $prefix.log.0
fi
}
if [ ! -e "$LOGDIR" ]; then
logger -s "missing directory: $LOGDIR"
exit 1
fi
cd $LOGDIR
rotate measurements
rotate statistics
rotate tracking
#
# signal chronyd via chronyc
/usr/local/bin/chronyc cyclelogs > /dev/null
exit $?

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.logrotate</string>
<key>KeepAlive</key>
<false/>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>/usr/local/bin/chronylogrotate.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>5</integer>
<key>Hour</key>
<integer>4</integer>
</dict>
</dict>
</plist>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.chronyd</string>
<key>Program</key>
<string>/usr/local/sbin/chronyd</string>
<key>ProgramArguments</key>
<array>
<string>chronyd</string>
<string>-n</string>
<string>-f</string>
<string>/private/etc/chrony.d/chrony.conf</string>
</array>
<key>KeepAlive</key>
<true/>
</dict>
</plist>

View File

@@ -1,72 +0,0 @@
ADOC = asciidoctor
ADOC_FLAGS =
SED = sed
HTML_TO_TXT = w3m -dump -T text/html
MAN_FILES = chrony.conf.man chronyc.man chronyd.man
TXT_FILES = faq.txt installation.txt
HTML_FILES = $(MAN_FILES:%.man=%.html) $(TXT_FILES:%.txt=%.html)
MAN_IN_FILES = $(MAN_FILES:%.man=%.man.in)
SYSCONFDIR = @SYSCONFDIR@
BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
MANDIR = @MANDIR@
DOCDIR = @DOCDIR@
CHRONYSOCKDIR = @CHRONYSOCKDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
s%\@BINDIR\@%$(BINDIR)%g;\
s%\@SBINDIR\@%$(SBINDIR)%g;\
s%\@CHRONY_VERSION\@%$(CHRONY_VERSION)%g;\
s%\@DEFAULT_HWCLOCK_FILE\@%$(DEFAULT_HWCLOCK_FILE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@CHRONYSOCKDIR\@%$(CHRONYSOCKDIR)%g;\
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
man: $(MAN_FILES) $(MAN_IN_FILES)
html: $(HTML_FILES)
txt: $(TXT_FILES)
docs: man html
%.html: %.adoc
$(ADOC) $(ADOC_FLAGS) -b html -o - $< | $(SED) -e $(SED_COMMANDS) > $@
%.man.in: %.adoc
$(ADOC) $(ADOC_FLAGS) -b manpage -o $@ $<
%.man: %.man.in
$(SED) -e $(SED_COMMANDS) < $< > $@
%.txt: %.html
$(HTML_TO_TXT) < $< > $@
install: $(MAN_FILES)
[ -d $(DESTDIR)$(MANDIR)/man1 ] || mkdir -p $(DESTDIR)$(MANDIR)/man1
[ -d $(DESTDIR)$(MANDIR)/man5 ] || mkdir -p $(DESTDIR)$(MANDIR)/man5
[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
cp chronyc.man $(DESTDIR)$(MANDIR)/man1/chronyc.1
chmod 644 $(DESTDIR)$(MANDIR)/man1/chronyc.1
cp chronyd.man $(DESTDIR)$(MANDIR)/man8/chronyd.8
chmod 644 $(DESTDIR)$(MANDIR)/man8/chronyd.8
cp chrony.conf.man $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
chmod 644 $(DESTDIR)$(MANDIR)/man5/chrony.conf.5
install-docs: $(HTML_FILES)
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
for f in $(HTML_FILES); do \
cp $$f $(DESTDIR)$(DOCDIR); \
chmod 644 $(DESTDIR)$(DOCDIR)/$$f; \
done
clean:
rm -f $(MAN_FILES) $(TXT_FILES) $(HTML_FILES)
rm -f $(MAN_IN_FILES)
distclean:
rm -f $(MAN_FILES) $(TXT_FILES) $(HTML_FILES)
rm -f Makefile

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,164 +0,0 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2016
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
= chronyd(8)
:doctype: manpage
:man manual: System Administration
:man source: chrony @CHRONY_VERSION@
== NAME
chronyd - chrony daemon
== SYNOPSIS
*chronyd* [_OPTION_]... [_DIRECTIVE_]...
== DESCRIPTION
*chronyd* is a daemon for synchronisation of the system clock. It can
synchronise the clock with NTP servers, reference clocks (e.g. a GPS receiver),
and manual input using wristwatch and keyboard via *chronyc*. It can also
operate as an NTPv4 (RFC 5905) server and peer to provide a time service to
other computers in the network.
If no configuration directives are specified on the command line, *chronyd*
will read them from a configuration file. The compiled-in default location of
the file is _@SYSCONFDIR@/chrony.conf_.
Information messages and warnings will be logged to syslog.
== OPTIONS
*-4*::
With this option hostnames will be resolved only to IPv4 addresses and only
IPv4 sockets will be created.
*-6*::
With this option hostnames will be resolved only to IPv6 addresses and only
IPv6 sockets will be created.
*-f* _file_::
This option can be used to specify an alternate location for the configuration
file (default _@SYSCONFDIR@/chrony.conf_).
*-n*::
When run in this mode, the program will not detach itself from the terminal.
*-d*::
When run in this mode, the program will not detach itself from the terminal,
and all messages will be sent to the terminal instead of to syslog. When
*chronyd* was compiled with debugging support, this option can be used twice to
print also debugging messages.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
will not detach from the terminal.
*-Q*::
This option is similar to *-q*, but it will only print the offset without any
corrections of the clock.
*-r*::
This option will reload sample histories for each of the servers and refclocks
being used. These histories are created by using the
<<chronyc.adoc#dump,*dump*>> command in *chronyc*, or by setting the
<<chrony.conf.adoc#dumponexit,*dumponexit*>> directive in the configuration
file. This option is useful if you want to stop and restart *chronyd* briefly
for any reason, e.g. to install a new version. However, it should be used only
on systems where the kernel can maintain clock compensation whilst not under
*chronyd*'s control (i.e. Linux, FreeBSD, NetBSD and Solaris).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
directive and the <<chrony.conf.adoc#makestep,*makestep*>> directive used with
a positive limit will be ignored. This option is useful when restarting
*chronyd* and can be used in conjunction with the *-r* option.
*-s*::
This option will set the system clock from the computer's real-time clock (RTC)
or to the last modification time of the file specified by the
<<chrony.conf.adoc#driftfile,*driftfile*>> directive. Real-time clocks are
supported only on Linux.
+
If used in conjunction with the *-r* flag, *chronyd* will attempt to preserve
the old samples after setting the system clock from the RTC. This can be used
to allow *chronyd* to perform long term averaging of the gain or loss rate
across system reboots, and is useful for systems with intermittent access to
network that are shut down when not in use. For this to work well, it relies
on *chronyd* having been able to determine accurate statistics for the
difference between the RTC and system clock last time the computer was on.
+
If the last modification time of the drift file is later than both the current
time and the RTC time, the system time will be set to it to restore the time
when *chronyd* was previously stopped. This is useful on computers that have no
RTC or the RTC is broken (e.g. it has no battery).
*-u* _user_::
This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_).
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On Mac OS X, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
*-F* _level_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SYSSIG
signal is thrown instead and in level 0 the filter is disabled (default 0).
+
It's recommended to enable the filter only when it's known to work on the
version of the system where *chrony* is installed as the filter needs to allow
also system calls made from libraries that *chronyd* is using (e.g. libc) and
different versions or implementations of the libraries may make different
system calls. If the filter is missing some system call, *chronyd* could be
killed even in normal operation.
*-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On Mac OS X, this option
must have either a value of 0 (the default) to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option.
*-m*::
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
*-v*::
With this option *chronyd* will print version number to the terminal and exit.
== FILES
_@SYSCONFDIR@/chrony.conf_
== SEE ALSO
<<chronyc.adoc#,*chronyc(1)*>>, <<chrony.conf.adoc#,*chrony.conf(5)*>>
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
== AUTHORS
chrony was written by Richard Curnow, Miroslav Lichvar and others.

View File

@@ -1,404 +0,0 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
= Frequently Asked Questions
:toc:
:numbered:
== `chrony` compared to other programs
=== How does `chrony` compare to `ntpd`?
`chronyd` was designed to work well in a wide range of conditions and it can
usually synchronise the system clock faster and with better time accuracy. It
doesn't implement some of the less useful NTP modes like broadcast client or
multicast server/client.
If your computer is connected to the Internet only for few minutes at a time,
the network connection is often congested, you turn your computer off or
suspend it frequently, the clock is not very stable (e.g. there are rapid
changes in the temperature or it's a virtual machine), or you want to use NTP
on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work much better for you.
For a more detailed comparison of features and performance, see the
https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
website.
== Configuration issues
=== What is the minimum recommended configuration for an NTP client?
First, the client needs to know which NTP servers it should ask for the current
time. They are specified by the `server` or `pool` directive. The `pool`
directive can be used for names that resolve to multiple addresses. For good
reliability the client should have at least three servers. The `iburst` option
speeds up the initial synchronisation.
To stabilize the initial synchronisation on the next start, the estimated drift
of the system clock is saved to a file specified by the `driftfile` directive.
If the system clock can be far from the true time after boot for any reason,
`chronyd` should be allowed to correct it quickly by stepping instead of
slewing, which would take a very long time. The `makestep` directive does
that.
In order to keep the real-time clock (RTC) close to the true time, so the
system time is reasonably close to the true time when it's initialized on the
next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and Mac
OS X.
If you want to use public NTP servers from the
http://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
could be:
----
pool pool.ntp.org iburst
driftfile /var/lib/chrony/drift
makestep 1 3
rtcsync
----
=== How do I make an NTP server from an NTP client?
You need to add an `allow` directive to the _chrony.conf_ file in order to open
the NTP port and allow `chronyd` to reply to client requests. `allow` with no
specified subnet allows all IPv4 and IPv6 addresses.
=== I have several computers on a LAN. Should be all clients of an external server?
The best configuration is usually to make one computer the server, with
the others as clients of it. Add a `local` directive to the server's
_chrony.conf_ file. This configuration will be better because
* the load on the external connection is less
* the load on the external NTP server(s) is less
* if your external connection goes down, the computers on the LAN
will maintain a common time with each other.
=== Must I specify servers by IP address if DNS is not available on chronyd start?
No. Starting from version 1.25, `chronyd` will keep trying to resolve
the names specified by the `server`, `pool`, and `peer` directives in an
increasing interval until it succeeds. The `online` command can be issued from
`chronyc` to try to resolve them immediately.
=== How can I make `chronyd` more secure?
If you don't need to serve time to NTP clients or peers, you can add `port 0`
to the _chrony.conf_ file to completely disable the NTP server functionality
and prevent NTP requests from reaching `chronyd`. Starting from version 2.0,
the NTP server port is open only when client access is allowed by the `allow`
directive or command, an NTP peer is configured, or the `broadcast` directive
is used.
If you don't need to use `chronyc` remotely, you can add the following
directives to the configuration file to bind the command sockets to the
loopback interface. This is done by default since version 2.0.
----
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
----
If you don't need to use `chronyc` at all or you need to run `chronyc` only
under the root or _chrony_ user (which can access `chronyd` through a Unix
domain socket since version 2.2), you can disable the internet command sockets
completely by adding `cmdport 0` to the configuration file.
You can specify an unprivileged user with the `-u` option, or the `user`
directive in the _chrony.conf_ file, to which `chronyd` will switch after start
in order to drop root privileges. The configure script has a `--with-user`
option, which sets the default user. On Linux, `chronyd` needs to be compiled
with support for the `libcap` library. On other systems, `chronyd` forks into
two processes. The child process retains root privileges, but can only perform
a very limited range of privileged system calls on behalf of the parent.
Also, if `chronyd` is compiled with support for the Linux secure computing
(seccomp) facility, you can enable a system call filter with the `-F` option.
It will significantly reduce the kernel attack surface and possibly prevent
kernel exploits from the `chronyd` process if it's compromised. It's
recommended to enable the filter only when it's known to work on the version of
the system where `chrony` is installed as the filter needs to allow also system
calls made from libraries that `chronyd` is using (e.g. libc) and different
versions or implementations of the libraries may make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in
normal operation.
=== 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 `chronyd` can detect servers that serve false
time and combine measurements from multiple sources.
There are also useful options which can be set in the `server` directive, they
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio` and
`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 6 (64 seconds) for `minpoll`, 10 (1024 seconds) for
`maxpoll` and 6 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers or if have
permission to poll some servers more frequently, setting these options for
shorter polling intervals may significantly improve the accuracy of the system
clock.
The optimal polling interval depends mainly on two factors, stability of the
network latency and stability of the system clock (which mainly depends on the
temperature sensitivity of the crystal oscillator and the maximum rate of the
temperature change).
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
----
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
----
An example using very short polling intervals for a server located in the same
LAN could be
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30
----
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 `maxdelaydevratio` option could be added to the example
with local NTP server
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
=== What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the
command protocol. Commands that required authentication are now allowed only
through a Unix domain socket, which is accessible only by the root and _chrony_
users. If you need to configure `chronyd` remotely or locally without the root
password, please consider using ssh and/or sudo to run `chronyc` under the root
or _chrony_ user on the host where `chronyd` is running.
== Computer is not synchronising
This is the most common problem. There are a number of reasons, see the
following questions.
=== Behind a firewall?
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it's
zero, it means `chronyd` did not get any valid responses from the NTP server
you are trying to use. If there is a firewall between you and the server, the
packets may be blocked. Try using a tool like `wireshark` or `tcpdump` to see
if you're getting any responses from the server.
When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like
this:
----
210 Number of sources = 3
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
^- bar.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
^+ baz.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
----
=== Are NTP servers specified with the `offline` option?
Check that you're using ``chronyc``'s `online` and `offline` commands
appropriately. Again, check in _measurements.log_ to see if you're getting any
data back from the server.
=== Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or
speeding it up. If the clock is too far from the true time, it will take
a long time to correct the error. The `System time` value printed by the
``chronyc``'s `tracking` command is the remaining correction that needs to be
applied to the system clock.
The `makestep` directive can be used to allow `chronyd` to step the clock. For
example, if _chrony.conf_ had
----
makestep 1 3
----
the clock would be stepped in the first three updates if its offset was larger
than one second. Normally, it's recommended to allow the step only in the first
few updates, but in some cases (e.g. a computer without an RTC or virtual
machine which can be suspended and resumed with an incorrect time) it may be
necessary to allow the step on any clock update. The example above would change
to
----
makestep 1 -1
----
== Issues with `chronyc`
=== I keep getting the error `506 Cannot talk to daemon`
When accessing `chronyd` remotely, make sure that the _chrony.conf_ file (on
the computer where `chronyd` is running) has a `cmdallow` entry for the
computer you are running `chronyc` on and an appropriate `bindcmdaddress`
directive. This isn't necessary for localhost.
Perhaps `chronyd` is not running. Try using the `ps` command (e.g. on Linux,
`ps -auxw`) to see if it's running. Or try `netstat -a` and see if the ports
123/udp and 323/udp are listening. If `chronyd` is not running, you may have a
problem with the way you are trying to start it (e.g. at boot time).
Perhaps you have a firewall set up in a way that blocks packets on port
323/udp. You need to amend the firewall configuration in this case.
=== I keep getting the error `501 Not authorised`
Since version 2.2, the `password` command doesn't do anything and `chronyc`
needs to run locally under the root or _chrony_ user, which are allowed to
access the ``chronyd``'s Unix domain command socket.
With older versions, you need to authenticate with the `password` command first
or use the `-a` option to authenticate automatically on start. The
configuration file needs to specify a file which contains keys (`keyfile`
directive) and which key in the key file should be used for `chronyc`
authentication (`commandkey` directive).
=== Why does `chronyc tracking` always print an IPv4 address as reference ID?
The reference ID is a 32-bit value and is always printed in quad-dotted
notation, even if the reference source doesn't have an IPv4 address. For IPv4
addresses, the reference ID is equal to the address, but for IPv6 addresses it
is the first 32 bits of the MD5 sum of the address. For reference clocks, the
reference ID is the value specified with the `refid` option in the `refclock`
directive.
If you need to get the IP address of the current reference source, use the `-n`
option to disable resolving of IP addresses and read the second field (printed
in parentheses) on the `Reference ID` line.
=== Is the `chronyc` / `chronyd` protocol documented anywhere?
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
(`chronyc` side).
== Real-time clock issues
=== What is the real-time clock (RTC)?
This is the clock which keeps the time even when your computer is turned off.
It is used to initialize the system clock on boot. It normally doesn't drift
more than few seconds per day.
There are two approaches how `chronyd` can work with it. One is to use the
`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
the RTC from the system clock every 11 minutes. `chronyd` itself won't touch
the RTC. If the computer is not turned off for a long time, the RTC should
still be close to the true time when the system clock will be initialized from
it on the next boot.
The other option is to use the `rtcfile` directive, which tells `chronyd` to
monitor the rate at which the RTC gains or loses time. When `chronyd` is
started with the `-s` option on the next boot, it will set the system time from
the RTC and also compensate for the drift it has measured previously. The
`rtcautotrim` directive can be used to keep the RTC close to the true time, but
it's not strictly necessary if its only purpose is to set the system clock when
`chronyd` is started on boot. See the documentation for details.
=== I want to use ``chronyd``'s RTC support. Must I disable `hwclock`?
The `hwclock` program is often set-up by default in the boot and shutdown
scripts with many Linux installations. With the kernel RTC synchronisation
(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
(`rtcfile` directive), it's important to disable `hwclock` in the shutdown
procedure. If you don't, it will over-write the RTC with a new value, unknown
to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
compensate this (wrong) time with its estimate of how far the RTC has drifted
whilst the power was off, giving a meaningless initial system time.
There is no need to remove `hwclock` from the boot process, as long as `chronyd`
is started after it has run.
=== I just keep getting the `513 RTC driver not running` message
For the real-time clock support to work, you need the following three
things
* an RTC in your computer
* a Linux kernel with enabled RTC support
* an `rtcfile` directive in your _chrony.conf_ file
=== I get `Could not open /dev/rtc, Device or resource busy` in my syslog file
Some other program running on the system may be using the device.
== NTP-specific issues
=== Can `chronyd` be driven from broadcast NTP servers?
No, the broadcast client mode is not supported and there is currently no plan
to implement it. The broadcast and multicast modes are inherently less
accurate and less secure (even with authentication) than the ordinary
server/client mode and they are not as useful as they used to be. Even with
very modest hardware a single NTP server can serve time to hundreds of
thousands of clients using the ordinary mode.
=== Can `chronyd` transmit broadcast NTP packets?
Yes, the `broadcast` directive can be used to enable the broadcast server mode
to serve time to clients in the network which support the broadcast client mode
(it's not supported in `chronyd`, see the previous question).
=== Can `chronyd` keep the system clock a fixed offset away from real time?
This is not possible as the program currently stands.
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
`chronyd` will keep trying to access the server(s) that it thinks are online.
When the network is connected again, it will take some time (on average half of
the maximum polling interval) before new measurements are made and the clock is
corrected. If the servers were set to offline and the `online` command was
issued when the network was connected, `chronyd` would make new measurements
immediately.
The `auto_offline` option to the `server` entry in the _chrony.conf_ file may
be useful to switch the servers to the offline state automatically.
== Operating systems
=== Does `chrony` support Windows?
No. The `chronyc` program (the command-line client used for configuring
`chronyd` while it is running) has been successfully built and run under
Cygwin in the past. `chronyd` is not portable, because part of it is
very system-dependent. It needs adapting to work with Windows'
equivalent of the adjtimex() call, and it needs to be made to work as a
service.
=== Are there any plans to support Windows?
We have no plans to do this. Anyone is welcome to pick this work up and
contribute it back to the project.

View File

@@ -1,189 +0,0 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2016
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
= Installation
The software is distributed as source code which has to be compiled. The source
code is supplied in the form of a gzipped tar file, which unpacks to a
subdirectory identifying the name and version of the program.
After unpacking the source code, change directory into it, and type
----
./configure
----
This is a shell script that automatically determines the system type. There is
a single optional parameter, `--prefix` which indicates the directory tree
where the software should be installed. For example,
----
./configure --prefix=/opt/free
----
will install the `chronyd` daemon into `/opt/free/sbin` and the `chronyc`
control program into `/opt/free/bin`. The default value for the prefix is
`/usr/local`.
The configure script assumes you want to use gcc as your compiler. If you want
to use a different compiler, you can configure this way:
----
CC=cc CFLAGS=-O ./configure --prefix=/opt/free
----
for Bourne-family shells, or
----
setenv CC cc
setenv CFLAGS -O
./configure --prefix=/opt/free
----
for C-family shells.
If the software cannot (yet) be built on your system, an error message will be
shown. Otherwise, `Makefile` will be generated.
On Linux, if development files for the libcap library are available, `chronyd`
will be built with support for dropping root privileges. On other systems no
extra library is needed. The default user which `chronyd` should run as can be
specified with the `--with-user` option of the configure script.
If development files for the editline or readline library are available,
`chronyc` will be built with line editing support. If you don't want this,
specify the `--disable-readline` flag to configure.
If a `timepps.h` header is available (e.g. from the
http://linuxpps.org[LinuxPPS project]), `chronyd` will be built with PPS API
reference clock driver. If the header is installed in a location that isn't
normally searched by the compiler, you can add it to the searched locations by
setting the `CPPFLAGS` variable to `-I/path/to/timepps`.
Now type
----
make
----
to build the programs.
If you want to build the manual in HTML, type
----
make docs
----
Once the programs have been successfully compiled, they need to be installed in
their target locations. This step normally needs to be performed by the
superuser, and requires the following command to be entered.
----
make install
----
This will install the binaries and man pages.
To install the HTML version of the manual, enter the command
----
make install-docs
----
Now that the software is successfully installed, the next step is to set up a
configuration file. The default location of the file is _/etc/chrony.conf_.
Several examples of configuration with comments are included in the examples
directory. Suppose you want to use public NTP servers from the pool.ntp.org
project as your time reference. A minimal useful configuration file could be
----
pool pool.ntp.org iburst
makestep 1.0 3
rtcsync
----
Then, `chronyd` can be run. For security reasons, it's recommended to create an
unprivileged user for `chronyd` and specify it with the `-u` command-line
option or the `user` directive in the configuration file, or set the default
user with the `--with-user` configure option before building.
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
the cursor keys to replay and edit old commands. Two libraries are supported
which provide such functionality, editline and GNU readline.
Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
The configure script will automatically enable the line editing support if one
of the supported libraries is available. If they are both available, the
editline library will be used.
If you don't want to use it (in which case chronyc will use a minimal command
line interface), invoke configure like this:
----
./configure --disable-readline other-options...
----
If you have editline, readline or ncurses installed in locations that aren't
normally searched by the compiler and linker, you need to use extra options:
`--with-readline-includes=directory_name`::
This defines the name of the directory above the one where `readline.h` is.
`readline.h` is assumed to be in `editline` or `readline` subdirectory of the
named directory.
`--with-readline-library=directory_name`::
This defines the directory containing the `libedit.a` or `libedit.so` file,
or `libreadline.a` or `libreadline.so` file.
`--with-ncurses-library=directory_name`::
This defines the directory containing the `libncurses.a` or `libncurses.so`
file.
== Extra options for package builders
The configure and make procedures have some extra options that may be useful if
you are building a distribution package for chrony.
The `--mandir=DIR` option to configure specifies an install directory for the
man pages. This overrides the `man` subdirectory of the argument to the
--prefix option.
----
./configure --prefix=/usr --mandir=/usr/share/man
----
to set both options together.
The final option is the `DESTDIR` option to the make command. For example, you
could use the commands
----
./configure --prefix=/usr --mandir=/usr/share/man
make all docs
make install DESTDIR=./tmp
cd tmp
tar cvf - . | gzip -9 > chrony.tar.gz
----
to build a package. When untarred within the root directory, this will install
the files to the intended final locations.

View File

@@ -9,7 +9,7 @@ Wants=time-sync.target
Type=oneshot Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining # Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds # clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc waitsync 600 0.1 0.0 1 ExecStart=/usr/bin/chronyc waitsync 60 0.1
RemainAfterExit=yes RemainAfterExit=yes
StandardOutput=null StandardOutput=null

View File

@@ -5,6 +5,22 @@
# want to enable. The more obscure options are not included. Refer # want to enable. The more obscure options are not included. Refer
# to the documentation for these. # to the documentation for these.
# #
# Copyright 2002 Richard P. Curnow
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
####################################################################### #######################################################################
### COMMENTS ### COMMENTS
# Any of the following lines are comments (you have a choice of # Any of the following lines are comments (you have a choice of
@@ -27,29 +43,25 @@
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or # you can access at http://support.ntp.org/bin/view/Servers/WebHome or
# you can use servers from the pool.ntp.org project. # you can use servers from the pool.ntp.org project.
! server foo.example.net iburst ! server 0.pool.ntp.org iburst
! server bar.example.net iburst ! server 1.pool.ntp.org iburst
! server baz.example.net iburst ! server 2.pool.ntp.org iburst
! pool pool.ntp.org iburst
# However, for dial-up use you probably want these instead. The word # However, for dial-up use you probably want these instead. The word
# 'offline' means that the server is not visible at boot time. Use # 'offline' means that the server is not visible at boot time. Use
# chronyc's 'online' command to tell chronyd that these servers have # chronyc's 'online' command to tell chronyd that these servers have
# become visible after you go on-line. # become visible after you go on-line.
! server foo.example.net offline ! server 0.pool.ntp.org offline
! server bar.example.net offline ! server 1.pool.ntp.org offline
! server baz.example.net offline ! server 2.pool.ntp.org offline
! pool pool.ntp.org offline
# You may want to specify NTP 'peers' instead. If you run a network # You may want to specify NTP 'peers' instead. If you run a network
# with a lot of computers and want several computers running chrony to # with a lot of computers and want several computers running chrony to
# have the 'front-line' interface to the public NTP servers, you can # have the 'front-line' interface to the public NTP servers, you can
# 'peer' these machines together to increase robustness. # 'peer' these machines together to increase robustness.
! peer foo.example.net ! peer ntp0.my-company.com
# There are other options to the 'server' and 'peer' directives that you # There are other options to the 'server' and 'peer' directives that you
# might want to use. For example, you can ignore measurements whose # might want to use. For example, you can ignore measurements whose
@@ -79,10 +91,23 @@
driftfile /var/lib/chrony/drift driftfile /var/lib/chrony/drift
# If you want to enable NTP authentication with symmetric keys, you will need # If you want to use the program called chronyc to configure aspects of
# to uncomment the following line and edit the file to set up the keys. # chronyd's operation once it is running (e.g. tell it the Internet link
# has gone up or down), you need a password. This is stored in the
# following keys file. (You also need keys to support authenticated NTP
# exchanges between cooperating machines.) Again, this option is
# assumed by default.
! keyfile /etc/chrony.keys keyfile /etc/chrony.keys
# Tell chronyd which numbered key in the file is used as the password
# for chronyc. (You can pick any integer up to 2**32-1. '1' is just a
# default. Using another value will _NOT_ increase security.)
commandkey 1
# With this directive a random password will be generated automatically.
generatecommandkey
# chronyd can save the measurement history for the servers to files when # chronyd can save the measurement history for the servers to files when
# it it exits. This is useful in 2 situations: # it it exits. This is useful in 2 situations:
@@ -112,15 +137,15 @@ driftfile /var/lib/chrony/drift
####################################################################### #######################################################################
### INITIAL CLOCK CORRECTION ### INITIAL CLOCK CORRECTION
# This option is useful to quickly correct the clock on start if it's # This option is useful to quickly correct the clock on start if it's
# off by a large amount. The value '1.0' means that if the error is less # off by a large amount. The value '10' means that if the error is less
# than 1 second, it will be gradually removed by speeding up or slowing # than 10 seconds, it will be gradually removed by speeding up or
# down your computer's clock until it is correct. If the error is above # slowing down your computer's clock until it is correct. If the error
# 1 second, an immediate time jump will be applied to correct it. The # is above 10 seconds, an immediate time jump will be applied to correct
# value '3' means the step is allowed only in the first three updates of # it. The value '1' means the step is allowed only on the first update
# the clock. Some software can get upset if the system clock jumps # of the clock. Some software can get upset if the system clock jumps
# (especially backwards), so be careful! # (especially backwards), so be careful!
! makestep 1.0 3 ! makestep 10 1
####################################################################### #######################################################################
### LOGGING ### LOGGING
@@ -191,13 +216,6 @@ driftfile /var/lib/chrony/drift
! clientloglimit 4194304 ! clientloglimit 4194304
# By default, chronyd tries to respond to all valid NTP requests from
# allowed addresses. If you want to limit the response rate for NTP
# clients that are sending requests too frequently, uncomment and edit
# the following line.
! limitrate interval 3 burst 8
####################################################################### #######################################################################
### REPORTING BIG CLOCK CHANGES ### REPORTING BIG CLOCK CHANGES
# Perhaps you want to know if chronyd suddenly detects any large error # Perhaps you want to know if chronyd suddenly detects any large error
@@ -215,19 +233,13 @@ driftfile /var/lib/chrony/drift
# several people, you need to set up a mailing list or sendmail alias # several people, you need to set up a mailing list or sendmail alias
# for them and use the address of that.) # for them and use the address of that.)
! mailonchange wibble@foo.example.net 0.5 ! mailonchange wibble@foobar.org 0.5
####################################################################### #######################################################################
### COMMAND ACCESS ### COMMAND ACCESS
# The program chronyc is used to show the current operation of chronyd # The program chronyc is used to show the current operation of chronyd
# and to change parts of its configuration whilst it is running. # and to change parts of its configuration whilst it is running.
# By default chronyd binds to the loopback interface. Uncomment the
# following lines to allow receiving command packets from remote hosts.
! bindcmdaddress 0.0.0.0
! bindcmdaddress ::
# Normally, chronyd will only allow connections from chronyc on the same # Normally, chronyd will only allow connections from chronyc on the same
# machine as itself. This is for security. If you have a subnet # machine as itself. This is for security. If you have a subnet
# 192.168.*.* and you want to be able to use chronyc from any machine on # 192.168.*.* and you want to be able to use chronyc from any machine on
@@ -240,10 +252,10 @@ driftfile /var/lib/chrony/drift
# syntax and meaning is the same as for 'allow' and 'deny', except that # syntax and meaning is the same as for 'allow' and 'deny', except that
# 'cmdallow' and 'cmddeny' control access to the chronyd's command port. # 'cmdallow' and 'cmddeny' control access to the chronyd's command port.
# Rate limiting can be enabled also for command packets. (Note, # NOTE, even if the host where you run chronyc is granted access, you
# commands from localhost are never limited.) # still need a command key set up and you have to know the password to
# put into chronyc to allow you to modify chronyd's parameters. By
! cmdratelimit interval 1 burst 16 # default all you can do is view information about chronyd's operation.
####################################################################### #######################################################################
### REAL TIME CLOCK ### REAL TIME CLOCK

View File

@@ -1,12 +0,0 @@
# Use public NTP servers from the pool.ntp.org project.
pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

View File

@@ -1,28 +1,46 @@
# Use public servers from the pool.ntp.org project. # Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html). # Please consider joining the pool (http://www.pool.ntp.org/join.html).
pool pool.ntp.org iburst server 0.pool.ntp.org iburst
server 1.pool.ntp.org iburst
server 2.pool.ntp.org iburst
server 3.pool.ntp.org iburst
# Ignore stratum in source selection.
stratumweight 0
# Record the rate at which the system clock gains/losses time. # Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew # Enable kernel RTC synchronization.
# if the adjustment is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync rtcsync
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
# Allow NTP client access from local network. # Allow NTP client access from local network.
#allow 192.168/16 #allow 192.168/16
# Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
# Serve time even if not synchronized to any NTP server. # Serve time even if not synchronized to any NTP server.
#local stratum 10 #local stratum 10
# Specify file containing keys for NTP authentication. keyfile /etc/chrony.keys
#keyfile /etc/chrony.keys
# Specify the key used as password for chronyc.
commandkey 1
# Generate command key if missing.
generatecommandkey
# Disable logging of client accesses.
noclientlog
# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5
# Specify directory for log files.
logdir /var/log/chrony logdir /var/log/chrony
# Select which information is logged.
#log measurements statistics tracking #log measurements statistics tracking

View File

@@ -1,12 +1,29 @@
# This is an example chrony keys file. It is used for NTP authentication with #######################################################################
# symmetric keys. It should be readable only by root or the user to which
# chronyd is configured to switch to after start.
# #
# Don't use the example keys! It's recommended to generate random keys using # This is an example chrony keys file. You should copy it to /etc/chrony.keys
# the chronyc keygen command. # after editing it to set up the key(s) you want to use. It should be readable
# only by root or the user chronyd drops the root privileges to. In most
# situations, you will require a single key (the 'commandkey') so that you can
# supply a password to chronyc to enable you to modify chronyd's operation
# whilst it is running.
#
# Copyright 2002 Richard P. Curnow
#
######################################################################
# Examples of valid keys: # Examples of valid keys:
#1 MD5 AVeryLongAndRandomPassword #1 ALongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F #2 MD5 HEX:B028F91EA5C38D06C2E140B26C7F41EC
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995 #3 SHA1 HEX:1DC764E0791B11FA67EFC7ECBC4B0D73F68A070C
# The keys should be random for maximum security. If you wanted to use a key
# with ID 1 as your commandkey (i.e. chronyc password) you would put
# "commandkey 1" into chrony.conf. If no commandkey is present in the keys
# file and the generatecommandkey directive is specified in chrony.conf,
# a random commandkey will be generated and added to the keys file
# automatically on chronyd start.
# You might want to define more keys if you use the authentication facility
# in the network time protocol to authenticate request/response packets between
# trusted clients and servers.

View File

@@ -3,6 +3,6 @@
nocreate nocreate
sharedscripts sharedscripts
postrotate postrotate
/usr/bin/chronyc cyclelogs > /dev/null 2>&1 || true /usr/bin/chronyc -a cyclelogs > /dev/null 2>&1 || true
endscript endscript
} }

View File

@@ -6,12 +6,12 @@ export LC_ALL=C
if [ "$2" = "up" ]; then if [ "$2" = "up" ]; then
/sbin/ip route list dev "$1" | grep -q '^default' && /sbin/ip route list dev "$1" | grep -q '^default' &&
/usr/bin/chronyc online > /dev/null 2>&1 /usr/bin/chronyc -a online > /dev/null 2>&1
fi fi
if [ "$2" = "down" ]; then if [ "$2" = "down" ]; then
/sbin/ip route list | grep -q '^default' || /sbin/ip route list | grep -q '^default' ||
/usr/bin/chronyc offline > /dev/null 2>&1 /usr/bin/chronyc -a offline > /dev/null 2>&1
fi fi
exit 0 exit 0

View File

@@ -10,6 +10,7 @@ Source: chrony-%{version}%{?prerelease:-%{prerelease}}.tar.gz
License: GPLv2 License: GPLv2
Group: Applications/Utilities Group: Applications/Utilities
BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n) BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(id -u -n)
Requires: info
%description %description
chrony is a client and server for the Network Time Protocol (NTP). chrony is a client and server for the Network Time Protocol (NTP).
@@ -27,20 +28,30 @@ manual input as time references.
--prefix=%{_prefix} \ --prefix=%{_prefix} \
--bindir=%{_bindir} \ --bindir=%{_bindir} \
--sbindir=%{_sbindir} \ --sbindir=%{_sbindir} \
--infodir=%{_infodir} \
--mandir=%{_mandir} --mandir=%{_mandir}
make make
make chrony.txt
make chrony.info
%install %install
rm -rf $RPM_BUILD_ROOT rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT
rm -rf $RPM_BUILD_ROOT%{_docdir}
mkdir -p $RPM_BUILD_ROOT%{_infodir}
cp chrony.info* $RPM_BUILD_ROOT%{_infodir}
%files %files
%{_sbindir}/chronyd %{_sbindir}/chronyd
%{_bindir}/chronyc %{_bindir}/chronyc
%{_infodir}/chrony.info*
%{_mandir}/man1/chrony.1.gz
%{_mandir}/man1/chronyc.1.gz %{_mandir}/man1/chronyc.1.gz
%{_mandir}/man5/chrony.conf.5.gz %{_mandir}/man5/chrony.conf.5.gz
%{_mandir}/man8/chronyd.8.gz %{_mandir}/man8/chronyd.8.gz
%doc README FAQ NEWS COPYING %doc README
%doc chrony.txt
%doc COPYING
%doc examples/chrony.conf.example* %doc examples/chrony.conf.example*
%doc examples/chrony.keys.example %doc examples/chrony.keys.example

2870
getdate.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,12 @@
** This code is in the public domain and has no copyright. ** This code is in the public domain and has no copyright.
*/ */
#include "config.h" #ifdef HAVE_CONFIG_H
# include <config.h>
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# endif
#endif
/* Since the code of getdate.y is not included in the Emacs executable /* Since the code of getdate.y is not included in the Emacs executable
itself, there is no need to #define static in this file. Even if itself, there is no need to #define static in this file. Even if

2
hash.h
View File

@@ -38,6 +38,4 @@ extern unsigned int HSH_Hash(int id,
const unsigned char *in2, unsigned int in2_len, const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len); unsigned char *out, unsigned int out_len);
extern void HSH_Finalise(void);
#endif #endif

View File

@@ -62,8 +62,3 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
return 16; return 16;
} }
void
HSH_Finalise(void)
{
}

View File

@@ -25,12 +25,11 @@
*/ */
#include "config.h"
#include <nss.h> #include <nss.h>
#include <hasht.h> #include <hasht.h>
#include <nsslowhash.h> #include <nsslowhash.h>
/* #include "config.h" */
#include "hash.h" #include "hash.h"
static NSSLOWInitContext *ictx; static NSSLOWInitContext *ictx;
@@ -88,17 +87,3 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
return ret; return ret;
} }
void
HSH_Finalise(void)
{
int i;
for (i = 0; hashes[i].name; i++) {
if (hashes[i].context)
NSSLOWHASH_Destroy(hashes[i].context);
}
if (ictx)
NSSLOW_Shutdown(ictx);
}

View File

@@ -114,8 +114,3 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
return len; return len;
} }
void
HSH_Finalise(void)
{
}

234
keys.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2016 * Copyright (C) Miroslav Lichvar 2012-2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,6 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "keys.h" #include "keys.h"
#include "cmdparse.h" #include "cmdparse.h"
#include "conf.h" #include "conf.h"
@@ -39,35 +38,82 @@
#include "local.h" #include "local.h"
#include "logging.h" #include "logging.h"
/* Consider 80 bits as the absolute minimum for a secure key */
#define MIN_SECURE_KEY_LENGTH 10
typedef struct { typedef struct {
uint32_t id; unsigned long id;
char *val; char *val;
int len; int len;
int hash_id; int hash_id;
int auth_delay; int auth_delay;
} Key; } Key;
static ARR_Instance keys; #define MAX_KEYS 256
static int n_keys;
static Key keys[MAX_KEYS];
static int command_key_valid;
static int command_key_id;
static int cache_valid; static int cache_valid;
static uint32_t cache_key_id; static unsigned long cache_key_id;
static int cache_key_pos; static int cache_key_pos;
/* ================================================== */ /* ================================================== */
static void static int
free_keys(void) generate_key(unsigned long key_id)
{ {
unsigned int i; #ifdef GENERATE_SHA1_KEY
unsigned char key[20];
const char *hashname = "SHA1";
#else
unsigned char key[16];
const char *hashname = "MD5";
#endif
const char *key_file, *rand_dev = "/dev/urandom";
FILE *f;
struct stat st;
int i;
for (i = 0; i < ARR_GetSize(keys); i++) key_file = CNF_GetKeysFile();
Free(((Key *)ARR_GetElement(keys, i))->val);
ARR_SetSize(keys, 0); if (!key_file)
cache_valid = 0; return 0;
f = fopen(rand_dev, "r");
if (!f || fread(key, sizeof (key), 1, f) != 1) {
if (f)
fclose(f);
LOG_FATAL(LOGF_Keys, "Could not read %s", rand_dev);
return 0;
}
fclose(f);
f = fopen(key_file, "a");
if (!f) {
LOG_FATAL(LOGF_Keys, "Could not open keyfile %s for writing", key_file);
return 0;
}
/* Make sure the keyfile is not world-readable */
if (stat(key_file, &st) || chmod(key_file, st.st_mode & 0770)) {
fclose(f);
LOG_FATAL(LOGF_Keys, "Could not change permissions of keyfile %s", key_file);
return 0;
}
fprintf(f, "\n%lu %s HEX:", key_id, hashname);
for (i = 0; i < sizeof (key); i++)
fprintf(f, "%02hhX", key[i]);
fprintf(f, "\n");
fclose(f);
/* Erase the key from stack */
memset(key, 0, sizeof (key));
LOG(LOGS_INFO, LOGF_Keys, "Generated key %lu", key_id);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -75,9 +121,15 @@ free_keys(void)
void void
KEY_Initialise(void) KEY_Initialise(void)
{ {
keys = ARR_CreateInstance(sizeof (Key)); n_keys = 0;
command_key_valid = 0;
cache_valid = 0; cache_valid = 0;
KEY_Reload(); KEY_Reload();
if (CNF_GetGenerateCommandKey() && !KEY_KeyKnown(KEY_GetCommandKey())) {
if (generate_key(KEY_GetCommandKey()))
KEY_Reload();
}
} }
/* ================================================== */ /* ================================================== */
@@ -85,22 +137,12 @@ KEY_Initialise(void)
void void
KEY_Finalise(void) KEY_Finalise(void)
{ {
free_keys();
ARR_DestroyInstance(keys);
}
/* ================================================== */
static Key *
get_key(unsigned int index)
{
return ((Key *)ARR_GetElements(keys)) + index;
} }
/* ================================================== */ /* ================================================== */
static int static int
determine_hash_delay(uint32_t key_id) determine_hash_delay(unsigned long key_id)
{ {
NTP_Packet pkt; NTP_Packet pkt;
struct timeval before, after; struct timeval before, after;
@@ -109,7 +151,7 @@ determine_hash_delay(uint32_t key_id)
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before); LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH, KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_SIZE,
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data)); (unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
LCL_ReadRawTime(&after); LCL_ReadRawTime(&after);
@@ -123,7 +165,7 @@ determine_hash_delay(uint32_t key_id)
/* Add on a bit extra to allow for copying, conversions etc */ /* Add on a bit extra to allow for copying, conversions etc */
min_usecs += min_usecs >> 4; min_usecs += min_usecs >> 4;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %"PRIu32": %ld useconds", key_id, min_usecs); DEBUG_LOG(LOGF_Keys, "authentication delay for key %lu: %ld useconds", key_id, min_usecs);
return min_usecs; return min_usecs;
} }
@@ -153,14 +195,18 @@ compare_keys_by_id(const void *a, const void *b)
void void
KEY_Reload(void) KEY_Reload(void)
{ {
unsigned int i, line_number; int i, line_number;
FILE *in; FILE *in;
uint32_t key_id; unsigned long key_id;
char line[2048], *keyval, *key_file; char line[2048], *keyval, *key_file;
const char *hashname; const char *hashname;
Key key;
free_keys(); for (i=0; i<n_keys; i++) {
Free(keys[i].val);
}
n_keys = 0;
command_key_valid = 0;
cache_valid = 0;
key_file = CNF_GetKeysFile(); key_file = CNF_GetKeysFile();
line_number = 0; line_number = 0;
@@ -186,22 +232,22 @@ KEY_Reload(void)
continue; continue;
} }
key.hash_id = HSH_GetHashId(hashname); keys[n_keys].hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) { if (keys[n_keys].hash_id < 0) {
LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %"PRIu32, key_id); LOG(LOGS_WARN, LOGF_Keys, "Unknown hash function in key %lu", key_id);
continue; continue;
} }
key.len = UTI_DecodePasswordFromText(keyval); keys[n_keys].len = UTI_DecodePasswordFromText(keyval);
if (!key.len) { if (!keys[n_keys].len) {
LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %"PRIu32, key_id); LOG(LOGS_WARN, LOGF_Keys, "Could not decode password in key %lu", key_id);
continue; continue;
} }
key.id = key_id; keys[n_keys].id = key_id;
key.val = MallocArray(char, key.len); keys[n_keys].val = MallocArray(char, keys[n_keys].len);
memcpy(key.val, keyval, key.len); memcpy(keys[n_keys].val, keyval, keys[n_keys].len);
ARR_AppendElement(keys, &key); n_keys++;
} }
fclose(in); fclose(in);
@@ -209,50 +255,50 @@ KEY_Reload(void)
/* Sort keys into order. Note, if there's a duplicate, it is /* Sort keys into order. Note, if there's a duplicate, it is
arbitrary which one we use later - the user should have been arbitrary which one we use later - the user should have been
more careful! */ more careful! */
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id); qsort((void *) keys, n_keys, sizeof(Key), compare_keys_by_id);
/* Check for duplicates */ /* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) { for (i = 1; i < n_keys; i++) {
if (get_key(i - 1)->id == get_key(i)->id) if (keys[i - 1].id == keys[i].id) {
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %"PRIu32, get_key(i - 1)->id); LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %lu", keys[i].id);
}
} }
/* Erase any passwords from stack */ /* Erase any passwords from stack */
memset(line, 0, sizeof (line)); memset(line, 0, sizeof (line));
for (i = 0; i < ARR_GetSize(keys); i++) for (i=0; i<n_keys; i++) {
get_key(i)->auth_delay = determine_hash_delay(get_key(i)->id); keys[i].auth_delay = determine_hash_delay(keys[i].id);
}
} }
/* ================================================== */ /* ================================================== */
static int static int
lookup_key(uint32_t id) lookup_key(unsigned long id)
{ {
Key specimen, *where, *keys_ptr; Key specimen, *where;
int pos; int pos;
keys_ptr = ARR_GetElements(keys);
specimen.id = id; specimen.id = id;
where = (Key *)bsearch((void *)&specimen, keys_ptr, ARR_GetSize(keys), where = (Key *) bsearch((void *)&specimen, (void *)keys, n_keys, sizeof(Key), compare_keys_by_id);
sizeof (Key), compare_keys_by_id);
if (!where) { if (!where) {
return -1; return -1;
} else { } else {
pos = where - keys_ptr; pos = where - keys;
return pos; return pos;
} }
} }
/* ================================================== */ /* ================================================== */
static Key * static int
get_key_by_id(uint32_t key_id) get_key_pos(unsigned long key_id)
{ {
int position; int position;
if (cache_valid && key_id == cache_key_id) if (cache_valid && key_id == cache_key_id)
return get_key(cache_key_pos); return cache_key_pos;
position = lookup_key(key_id); position = lookup_key(key_id);
@@ -260,81 +306,81 @@ get_key_by_id(uint32_t key_id)
cache_valid = 1; cache_valid = 1;
cache_key_pos = position; cache_key_pos = position;
cache_key_id = key_id; cache_key_id = key_id;
return get_key(position);
} }
return NULL; return position;
}
/* ================================================== */
unsigned long
KEY_GetCommandKey(void)
{
if (!command_key_valid) {
command_key_id = CNF_GetCommandKey();
}
return command_key_id;
} }
/* ================================================== */ /* ================================================== */
int int
KEY_KeyKnown(uint32_t key_id) KEY_KeyKnown(unsigned long key_id)
{ {
return get_key_by_id(key_id) != NULL; return get_key_pos(key_id) >= 0;
} }
/* ================================================== */ /* ================================================== */
int int
KEY_GetAuthDelay(uint32_t key_id) KEY_GetAuthDelay(unsigned long key_id)
{ {
Key *key; int key_pos;
key = get_key_by_id(key_id); key_pos = get_key_pos(key_id);
if (!key) if (key_pos < 0) {
return 0; return 0;
}
return key->auth_delay; return keys[key_pos].auth_delay;
} }
/* ================================================== */ /* ================================================== */
int int
KEY_CheckKeyLength(uint32_t key_id) KEY_GenerateAuth(unsigned long key_id, const unsigned char *data, int data_len,
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return key->len >= MIN_SECURE_KEY_LENGTH;
}
/* ================================================== */
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len) unsigned char *auth, int auth_len)
{ {
Key *key; int key_pos;
key = get_key_by_id(key_id); key_pos = get_key_pos(key_id);
if (!key) if (key_pos < 0) {
return 0; return 0;
}
return UTI_GenerateNTPAuth(key->hash_id, (unsigned char *)key->val, return UTI_GenerateNTPAuth(keys[key_pos].hash_id,
key->len, data, data_len, auth, auth_len); (unsigned char *)keys[key_pos].val, keys[key_pos].len,
data, data_len, auth, auth_len);
} }
/* ================================================== */ /* ================================================== */
int int
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len, KEY_CheckAuth(unsigned long key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len) const unsigned char *auth, int auth_len)
{ {
Key *key; int key_pos;
key = get_key_by_id(key_id); key_pos = get_key_pos(key_id);
if (!key) if (key_pos < 0) {
return 0; return 0;
}
return UTI_CheckNTPAuth(key->hash_id, (unsigned char *)key->val, return UTI_CheckNTPAuth(keys[key_pos].hash_id,
key->len, data, data_len, auth, auth_len); (unsigned char *)keys[key_pos].val, keys[key_pos].len,
data, data_len, auth, auth_len);
} }

15
keys.h
View File

@@ -27,21 +27,20 @@
#ifndef GOT_KEYS_H #ifndef GOT_KEYS_H
#define GOT_KEYS_H #define GOT_KEYS_H
#include "sysincl.h"
extern void KEY_Initialise(void); extern void KEY_Initialise(void);
extern void KEY_Finalise(void); extern void KEY_Finalise(void);
extern void KEY_Reload(void); extern void KEY_Reload(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len); extern unsigned long KEY_GetCommandKey(void);
extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, extern int KEY_GetKey(unsigned long key_id, char **key, int *len);
extern int KEY_KeyKnown(unsigned long key_id);
extern int KEY_GetAuthDelay(unsigned long key_id);
extern int KEY_GenerateAuth(unsigned long key_id, const unsigned char *data,
int data_len, unsigned char *auth, int auth_len); int data_len, unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, extern int KEY_CheckAuth(unsigned long key_id, const unsigned char *data,
int data_len, const unsigned char *auth, int auth_len); int data_len, const unsigned char *auth, int auth_len);
#endif /* GOT_KEYS_H */ #endif /* GOT_KEYS_H */

124
local.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2014-2015 * Copyright (C) Miroslav Lichvar 2011, 2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -36,7 +36,6 @@
#include "local.h" #include "local.h"
#include "localp.h" #include "localp.h"
#include "memory.h" #include "memory.h"
#include "smooth.h"
#include "util.h" #include "util.h"
#include "logging.h" #include "logging.h"
@@ -45,9 +44,6 @@
/* Variable to store the current frequency, in ppm */ /* Variable to store the current frequency, in ppm */
static double current_freq_ppm; static double current_freq_ppm;
/* Maximum allowed frequency, in ppm */
static double max_freq_ppm;
/* Temperature compensation, in ppm */ /* Temperature compensation, in ppm */
static double temp_comp_ppm; static double temp_comp_ppm;
@@ -60,7 +56,6 @@ static lcl_AccrueOffsetDriver drv_accrue_offset;
static lcl_ApplyStepOffsetDriver drv_apply_step_offset; static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
static lcl_OffsetCorrectionDriver drv_offset_convert; static lcl_OffsetCorrectionDriver drv_offset_convert;
static lcl_SetLeapDriver drv_set_leap; static lcl_SetLeapDriver drv_set_leap;
static lcl_SetSyncStatusDriver drv_set_sync_status;
/* ================================================== */ /* ================================================== */
@@ -165,11 +160,6 @@ LCL_Initialise(void)
calculate_sys_precision(); calculate_sys_precision();
/* This is the maximum allowed frequency offset in ppm, the time must
never stop or run backwards */
max_freq_ppm = CNF_GetMaxDrift();
max_freq_ppm = CLAMP(0.0, max_freq_ppm, 500000.0);
max_clock_error = CNF_GetMaxClockError() * 1e-6; max_clock_error = CNF_GetMaxClockError() * 1e-6;
} }
@@ -178,13 +168,6 @@ LCL_Initialise(void)
void void
LCL_Finalise(void) LCL_Finalise(void)
{ {
while (change_list.next != &change_list)
LCL_RemoveParameterChangeHandler(change_list.next->handler,
change_list.next->anything);
while (dispersion_notify_list.next != &dispersion_notify_list)
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
dispersion_notify_list.next->anything);
} }
/* ================================================== */ /* ================================================== */
@@ -264,7 +247,7 @@ void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *
ptr->next->prev = ptr->prev; ptr->next->prev = ptr->prev;
ptr->prev->next = ptr->next; ptr->prev->next = ptr->next;
Free(ptr); free(ptr);
} }
/* ================================================== */ /* ================================================== */
@@ -341,7 +324,7 @@ void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void
ptr->next->prev = ptr->prev; ptr->next->prev = ptr->prev;
ptr->prev->next = ptr->next; ptr->prev->next = ptr->next;
Free(ptr); free(ptr);
} }
/* ================================================== */ /* ================================================== */
@@ -406,33 +389,6 @@ LCL_ReadAbsoluteFrequency(void)
} }
/* ================================================== */ /* ================================================== */
static double
clamp_freq(double freq)
{
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq;
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
}
/* ================================================== */
static int
check_offset(struct timeval *now, double offset)
{
/* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset))
return 1;
LOG(LOGS_WARN, LOGF_Local, "Adjustment of %.1f seconds is invalid", -offset);
return 0;
}
/* ================================================== */
/* This involves both setting the absolute frequency with the /* This involves both setting the absolute frequency with the
system-specific driver, as well as calling all notify handlers */ system-specific driver, as well as calling all notify handlers */
@@ -442,8 +398,6 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
struct timeval raw, cooked; struct timeval raw, cooked;
double dfreq; double dfreq;
afreq_ppm = clamp_freq(afreq_ppm);
/* Apply temperature compensation */ /* Apply temperature compensation */
if (temp_comp_ppm != 0.0) { if (temp_comp_ppm != 0.0) {
afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm; afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
@@ -481,8 +435,6 @@ LCL_AccumulateDeltaFrequency(double dfreq)
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm); current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
current_freq_ppm = clamp_freq(current_freq_ppm);
/* Call the system-specific driver for setting the frequency */ /* Call the system-specific driver for setting the frequency */
current_freq_ppm = (*drv_set_freq)(current_freq_ppm); current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm); dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
@@ -507,9 +459,6 @@ LCL_AccumulateOffset(double offset, double corr_rate)
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, offset))
return;
(*drv_accrue_offset)(offset, corr_rate); (*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */ /* Dispatch to all handlers */
@@ -518,7 +467,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
/* ================================================== */ /* ================================================== */
int void
LCL_ApplyStepOffset(double offset) LCL_ApplyStepOffset(double offset)
{ {
struct timeval raw, cooked; struct timeval raw, cooked;
@@ -529,21 +478,10 @@ LCL_ApplyStepOffset(double offset)
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&raw, offset)) (*drv_apply_step_offset)(offset);
return 0;
if (!(*drv_apply_step_offset)(offset)) {
LOG(LOGS_ERR, LOGF_Local, "Could not step clock");
return 0;
}
/* Reset smoothing on all clock steps */
SMT_Reset(&cooked);
/* Dispatch to all handlers */ /* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep); invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -560,23 +498,6 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
/* ================================================== */ /* ================================================== */
void
LCL_NotifyLeap(int leap)
{
struct timeval raw, cooked;
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
/* Smooth the leap second out */
SMT_Leap(&cooked, leap);
/* Dispatch to all handlers as if the clock was stepped */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
}
/* ================================================== */
void void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{ {
@@ -588,9 +509,6 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
to the change we are about to make */ to the change we are about to make */
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, doffset))
return;
old_freq_ppm = current_freq_ppm; old_freq_ppm = current_freq_ppm;
/* Work out new absolute frequency. Note that absolute frequencies /* Work out new absolute frequency. Note that absolute frequencies
@@ -598,8 +516,6 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
terms of the gradient of the (offset) v (local time) function. */ terms of the gradient of the (offset) v (local time) function. */
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm); current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
current_freq_ppm = clamp_freq(current_freq_ppm);
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec", DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset); old_freq_ppm, current_freq_ppm, doffset);
@@ -634,8 +550,7 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
lcl_AccrueOffsetDriver accrue_offset, lcl_AccrueOffsetDriver accrue_offset,
lcl_ApplyStepOffsetDriver apply_step_offset, lcl_ApplyStepOffsetDriver apply_step_offset,
lcl_OffsetCorrectionDriver offset_convert, lcl_OffsetCorrectionDriver offset_convert,
lcl_SetLeapDriver set_leap, lcl_SetLeapDriver set_leap)
lcl_SetSyncStatusDriver set_sync_status)
{ {
drv_read_freq = read_freq; drv_read_freq = read_freq;
drv_set_freq = set_freq; drv_set_freq = set_freq;
@@ -643,7 +558,6 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
drv_apply_step_offset = apply_step_offset; drv_apply_step_offset = apply_step_offset;
drv_offset_convert = offset_convert; drv_offset_convert = offset_convert;
drv_set_leap = set_leap; drv_set_leap = set_leap;
drv_set_sync_status = set_sync_status;
current_freq_ppm = (*drv_read_freq)(); current_freq_ppm = (*drv_read_freq)();
@@ -663,13 +577,9 @@ LCL_MakeStep(void)
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
LCL_GetOffsetCorrection(&raw, &correction, NULL); LCL_GetOffsetCorrection(&raw, &correction, NULL);
if (!check_offset(&raw, -correction))
return 0;
/* Cancel remaining slew and make the step */ /* Cancel remaining slew and make the step */
LCL_AccumulateOffset(correction, 0.0); LCL_AccumulateOffset(correction, 0.0);
if (!LCL_ApplyStepOffset(-correction)) LCL_ApplyStepOffset(-correction);
return 0;
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction); LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
@@ -678,16 +588,8 @@ LCL_MakeStep(void)
/* ================================================== */ /* ================================================== */
int
LCL_CanSystemLeap(void)
{
return drv_set_leap ? 1 : 0;
}
/* ================================================== */
void void
LCL_SetSystemLeap(int leap) LCL_SetLeap(int leap)
{ {
if (drv_set_leap) { if (drv_set_leap) {
(drv_set_leap)(leap); (drv_set_leap)(leap);
@@ -723,13 +625,3 @@ LCL_SetTempComp(double comp)
} }
/* ================================================== */ /* ================================================== */
void
LCL_SetSyncStatus(int synchronised, double est_error, double max_error)
{
if (drv_set_sync_status) {
(drv_set_sync_status)(synchronised, est_error, max_error);
}
}
/* ================================================== */

22
local.h
View File

@@ -159,17 +159,13 @@ extern void LCL_AccumulateOffset(double offset, double corr_rate);
the system clock is fast on true time, i.e. it needs to be stepped the system clock is fast on true time, i.e. it needs to be stepped
backwards. (Same convention as for AccumulateOffset routine). */ backwards. (Same convention as for AccumulateOffset routine). */
extern int LCL_ApplyStepOffset(double offset); extern void LCL_ApplyStepOffset(double offset);
/* Routine to invoke notify handlers on an unexpected time jump /* Routine to invoke notify handlers on an unexpected time jump
in system clock */ in system clock */
extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked, extern void LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
double offset, double dispersion); double offset, double dispersion);
/* Routine to invoke notify handlers on leap second when the system clock
doesn't correct itself */
extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying /* Perform the combination of modifying the frequency and applying
a slew, in one easy step */ a slew, in one easy step */
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
@@ -198,14 +194,10 @@ extern void LCL_Finalise(void);
to a timezone problem. */ to a timezone problem. */
extern int LCL_MakeStep(void); extern int LCL_MakeStep(void);
/* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap /* Routine to schedule a leap second. Leap second will be inserted
does something */ at the end of the day if argument is positive, deleted if negative,
extern int LCL_CanSystemLeap(void); and zero cancels scheduled leap second. */
extern void LCL_SetLeap(int leap);
/* Routine to set the system clock to correct itself for a leap second if
supported. Leap second will be inserted at the end of the day if the
argument is positive, deleted if negative, and zero resets the setting. */
extern void LCL_SetSystemLeap(int leap);
/* Routine to set a frequency correction (in ppm) that should be applied /* Routine to set a frequency correction (in ppm) that should be applied
to local clock to compensate for temperature changes. A positive to local clock to compensate for temperature changes. A positive
@@ -214,8 +206,4 @@ extern void LCL_SetSystemLeap(int leap);
due to clamping or rounding). */ due to clamping or rounding). */
extern double LCL_SetTempComp(double comp); extern double LCL_SetTempComp(double comp);
/* Routine to update the synchronisation status in the kernel to allow other
applications to know if the system clock is synchronised and error bounds */
extern void LCL_SetSyncStatus(int synchronised, double est_error, double max_error);
#endif /* GOT_LOCAL_H */ #endif /* GOT_LOCAL_H */

View File

@@ -47,7 +47,7 @@ typedef void (*lcl_AccrueOffsetDriver)(double offset, double corr_rate);
/* System driver to apply a step offset. A positive argument means step /* System driver to apply a step offset. A positive argument means step
the clock forwards. */ the clock forwards. */
typedef int (*lcl_ApplyStepOffsetDriver)(double offset); typedef void (*lcl_ApplyStepOffsetDriver)(double offset);
/* System driver to convert a raw time to an adjusted (cooked) time. /* System driver to convert a raw time to an adjusted (cooked) time.
The number of seconds returned in 'corr' have to be added to the The number of seconds returned in 'corr' have to be added to the
@@ -57,9 +57,6 @@ typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, do
/* System driver to schedule leap second */ /* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap); typedef void (*lcl_SetLeapDriver)(int leap);
/* System driver to set the synchronisation status */
typedef void (*lcl_SetSyncStatusDriver)(int synchronised, double est_error, double max_error);
extern void lcl_InvokeDispersionNotifyHandlers(double dispersion); extern void lcl_InvokeDispersionNotifyHandlers(double dispersion);
extern void extern void
@@ -68,7 +65,6 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
lcl_AccrueOffsetDriver accrue_offset, lcl_AccrueOffsetDriver accrue_offset,
lcl_ApplyStepOffsetDriver apply_step_offset, lcl_ApplyStepOffsetDriver apply_step_offset,
lcl_OffsetCorrectionDriver offset_convert, lcl_OffsetCorrectionDriver offset_convert,
lcl_SetLeapDriver set_leap, lcl_SetLeapDriver set_leap);
lcl_SetSyncStatusDriver set_sync_status);
#endif /* GOT_LOCALP_H */ #endif /* GOT_LOCALP_H */

View File

@@ -31,6 +31,7 @@
#include "conf.h" #include "conf.h"
#include "logging.h" #include "logging.h"
#include "mkdirpp.h"
#include "util.h" #include "util.h"
/* This is used by DEBUG_LOG macro */ /* This is used by DEBUG_LOG macro */
@@ -48,6 +49,10 @@ static int parent_fd = 0;
#define DEBUG_LEVEL_PRINT_DEBUG 2 #define DEBUG_LEVEL_PRINT_DEBUG 2
static int debug_level = 0; static int debug_level = 0;
#ifdef WINNT
static FILE *logfile;
#endif
struct LogFile { struct LogFile {
const char *name; const char *name;
const char *banner; const char *banner;
@@ -69,6 +74,10 @@ void
LOG_Initialise(void) LOG_Initialise(void)
{ {
initialised = 1; initialised = 1;
#ifdef WINNT
logfile = fopen("./chronyd.err", "a");
#endif
} }
/* ================================================== */ /* ================================================== */
@@ -77,9 +86,15 @@ LOG_Initialise(void)
void void
LOG_Finalise(void) LOG_Finalise(void)
{ {
#ifdef WINNT
if (logfile) {
fclose(logfile);
}
#else
if (system_log) { if (system_log) {
closelog(); closelog();
} }
#endif
LOG_CycleLogFiles(); LOG_CycleLogFiles();
@@ -90,6 +105,11 @@ LOG_Finalise(void)
static void log_message(int fatal, LOG_Severity severity, const char *message) static void log_message(int fatal, LOG_Severity severity, const char *message)
{ {
#ifdef WINNT
if (logfile) {
fprintf(logfile, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
#else
if (system_log) { if (system_log) {
int priority; int priority;
switch (severity) { switch (severity) {
@@ -115,33 +135,32 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
} else { } else {
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message); fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
} }
#endif
} }
/* ================================================== */ /* ================================================== */
void LOG_Message(LOG_Severity severity, void LOG_Message(LOG_Severity severity, LOG_Facility facility,
#if DEBUG > 0 int line_number, const char *filename,
LOG_Facility facility, int line_number, const char *function_name, const char *format, ...)
const char *filename, const char *function_name,
#endif
const char *format, ...)
{ {
char buf[2048]; char buf[2048];
va_list other_args; va_list other_args;
time_t t; time_t t;
struct tm stm; struct tm stm;
#ifdef WINNT
#else
if (!system_log) { if (!system_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */ /* Don't clutter up syslog with timestamps and internal debugging info */
time(&t); time(&t);
stm = *gmtime(&t); stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm); strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
fprintf(stderr, "%s ", buf); fprintf(stderr, "%s ", buf);
#if DEBUG > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION) if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name); fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
#endif
} }
#endif
va_start(other_args, format); va_start(other_args, format);
vsnprintf(buf, sizeof(buf), format, other_args); vsnprintf(buf, sizeof(buf), format, other_args);
@@ -179,8 +198,11 @@ void LOG_Message(LOG_Severity severity,
void void
LOG_OpenSystemLog(void) LOG_OpenSystemLog(void)
{ {
#ifdef WINNT
#else
system_log = 1; system_log = 1;
openlog("chronyd", LOG_PID, LOG_DAEMON); openlog("chronyd", LOG_PID, LOG_DAEMON);
#endif
} }
/* ================================================== */ /* ================================================== */
@@ -189,6 +211,8 @@ void LOG_SetDebugLevel(int level)
{ {
debug_level = level; debug_level = level;
if (level >= DEBUG_LEVEL_PRINT_DEBUG) { if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
if (!DEBUG)
LOG(LOGS_WARN, LOGF_Logging, "Not compiled with full debugging support");
log_debug_enabled = 1; log_debug_enabled = 1;
} }
} }
@@ -278,6 +302,20 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
/* ================================================== */ /* ================================================== */
void
LOG_CreateLogFileDir(void)
{
const char *logdir;
logdir = CNF_GetLogDir();
if (!mkdir_and_parents(logdir)) {
LOG(LOGS_ERR, LOGF_Logging, "Could not create directory %s", logdir);
}
}
/* ================================================== */
void void
LOG_CycleLogFiles(void) LOG_CycleLogFiles(void)
{ {

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2013-2015 * Copyright (C) Miroslav Lichvar 2013-2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -45,28 +45,18 @@ extern int log_debug_enabled;
#define FORMAT_ATTRIBUTE_PRINTF(str, first) #define FORMAT_ATTRIBUTE_PRINTF(str, first)
#endif #endif
#if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__);
#else
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, __VA_ARGS__);
#endif
#define DEBUG_LOG(facility, ...) \ #define DEBUG_LOG(facility, ...) \
do { \ do { \
if (DEBUG && log_debug_enabled) \ if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, facility, __VA_ARGS__); \ LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
} while (0) } while (0)
#define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_FATAL(facility, ...) \ #define LOG_FATAL(facility, ...) \
do { \ do { \
LOG_MESSAGE(LOGS_FATAL, facility, __VA_ARGS__); \ LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
exit(1); \ exit(1); \
} while (0) } while (0)
#define LOG(severity, facility, ...) LOG_MESSAGE(severity, facility, __VA_ARGS__)
/* Definition of severity */ /* Definition of severity */
typedef enum { typedef enum {
LOGS_INFO, LOGS_INFO,
@@ -90,8 +80,6 @@ typedef enum {
LOGF_Local, LOGF_Local,
LOGF_Util, LOGF_Util,
LOGF_Main, LOGF_Main,
LOGF_Memory,
LOGF_Client,
LOGF_ClientLog, LOGF_ClientLog,
LOGF_Configure, LOGF_Configure,
LOGF_CmdMon, LOGF_CmdMon,
@@ -100,21 +88,18 @@ typedef enum {
LOGF_Keys, LOGF_Keys,
LOGF_Logging, LOGF_Logging,
LOGF_Nameserv, LOGF_Nameserv,
LOGF_PrivOps,
LOGF_Rtc, LOGF_Rtc,
LOGF_Regress, LOGF_Regress,
LOGF_Sys, LOGF_Sys,
LOGF_SysGeneric, LOGF_SysGeneric,
LOGF_SysLinux, LOGF_SysLinux,
LOGF_SysMacOSX,
LOGF_SysNetBSD, LOGF_SysNetBSD,
LOGF_SysSolaris, LOGF_SysSolaris,
LOGF_SysTimex, LOGF_SysSunOS,
LOGF_SysWinnt, LOGF_SysWinnt,
LOGF_TempComp, LOGF_TempComp,
LOGF_RtcLinux, LOGF_RtcLinux,
LOGF_Refclock, LOGF_Refclock
LOGF_Smooth,
} LOG_Facility; } LOG_Facility;
/* Init function */ /* Init function */
@@ -124,15 +109,10 @@ extern void LOG_Initialise(void);
extern void LOG_Finalise(void); extern void LOG_Finalise(void);
/* Line logging function */ /* Line logging function */
#if DEBUG > 0
FORMAT_ATTRIBUTE_PRINTF(6, 7) FORMAT_ATTRIBUTE_PRINTF(6, 7)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility, extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename, int line_number, const char *filename,
const char *function_name, const char *format, ...); const char *function_name, const char *format, ...);
#else
FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_Message(LOG_Severity severity, const char *format, ...);
#endif
/* Set debug level: /* Set debug level:
0, 1 - only non-debug messages are logged 0, 1 - only non-debug messages are logged
@@ -159,6 +139,7 @@ extern LOG_FileID LOG_FileOpen(const char *name, const char *banner);
FORMAT_ATTRIBUTE_PRINTF(2, 3) FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_FileWrite(LOG_FileID id, const char *format, ...); extern void LOG_FileWrite(LOG_FileID id, const char *format, ...);
extern void LOG_CreateLogFileDir(void);
extern void LOG_CycleLogFiles(void); extern void LOG_CycleLogFiles(void);
#endif /* GOT_LOGGING_H */ #endif /* GOT_LOGGING_H */

108
main.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009 * Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2015 * Copyright (C) Miroslav Lichvar 2012-2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -48,11 +48,9 @@
#include "rtc.h" #include "rtc.h"
#include "refclock.h" #include "refclock.h"
#include "clientlog.h" #include "clientlog.h"
#include "broadcast.h"
#include "nameserv.h" #include "nameserv.h"
#include "privops.h"
#include "smooth.h"
#include "tempcomp.h" #include "tempcomp.h"
#include "util.h"
/* ================================================== */ /* ================================================== */
@@ -69,18 +67,6 @@ static REF_Mode ref_mode = REF_ModeNormal;
/* ================================================== */ /* ================================================== */
static void
do_platform_checks(void)
{
/* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4);
assert(-1 == ~0);
assert((int32_t)4294967295U == (int32_t)-1);
}
/* ================================================== */
static void static void
delete_pidfile(void) delete_pidfile(void)
{ {
@@ -100,35 +86,28 @@ MAI_CleanupAndExit(void)
SRC_DumpSources(); SRC_DumpSources();
} }
/* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore);
SMT_Finalise();
TMC_Finalise(); TMC_Finalise();
MNL_Finalise(); MNL_Finalise();
CLG_Finalise(); CLG_Finalise();
NSR_Finalise(); NSR_Finalise();
SST_Finalise();
NCR_Finalise(); NCR_Finalise();
NIO_Finalise(); BRD_Finalise();
CAM_Finalise(); SST_Finalise();
REF_Finalise();
KEY_Finalise(); KEY_Finalise();
RCL_Finalise(); RCL_Finalise();
SRC_Finalise(); SRC_Finalise();
REF_Finalise();
RTC_Finalise(); RTC_Finalise();
CAM_Finalise();
NIO_Finalise();
SYS_Finalise(); SYS_Finalise();
SCH_Finalise(); SCH_Finalise();
LCL_Finalise(); LCL_Finalise();
PRV_Finalise();
delete_pidfile(); delete_pidfile();
CNF_Finalise();
LOG_Finalise(); LOG_Finalise();
HSH_Finalise();
exit(exit_status); exit(exit_status);
} }
@@ -279,7 +258,7 @@ write_lockfile(void)
if (!out) { if (!out) {
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile); LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
} else { } else {
fprintf(out, "%d\n", (int)getpid()); fprintf(out, "%d\n", getpid());
fclose(out); fclose(out);
} }
} }
@@ -289,6 +268,11 @@ write_lockfile(void)
static void static void
go_daemon(void) go_daemon(void)
{ {
#ifdef WINNT
#else
int pid, fd, pipefd[2]; int pid, fd, pipefd[2];
/* Create pipe which will the daemon use to notify the grandparent /* Create pipe which will the daemon use to notify the grandparent
@@ -347,6 +331,8 @@ go_daemon(void)
LOG_SetParentFd(pipefd[1]); LOG_SetParentFd(pipefd[1]);
} }
} }
#endif
} }
/* ================================================== */ /* ================================================== */
@@ -355,18 +341,14 @@ int main
(int argc, char **argv) (int argc, char **argv)
{ {
const char *conf_file = DEFAULT_CONF_FILE; const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0];
char *user = NULL; char *user = NULL;
struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0; int do_init_rtc = 0, restarted = 0;
int other_pid; int other_pid;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0; int lock_memory = 0, sched_priority = 0;
int system_log = 1; int system_log = 1;
int config_args = 0; int config_args = 0;
do_platform_checks();
LOG_Initialise(); LOG_Initialise();
/* Parse command line options */ /* Parse command line options */
@@ -393,16 +375,12 @@ int main
} else { } else {
user = *argv; user = *argv;
} }
} else if (!strcmp("-F", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &scfilter_level) != 1)
LOG_FATAL(LOGF_Main, "Bad syscall filter level");
} else if (!strcmp("-s", *argv)) { } else if (!strcmp("-s", *argv)) {
do_init_rtc = 1; do_init_rtc = 1;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) { } else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
/* This write to the terminal is OK, it comes before we turn into a daemon */ /* This write to the terminal is OK, it comes before we turn into a daemon */
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES); printf("chronyd (chrony) version %s\n", CHRONY_VERSION);
return 0; exit(0);
} else if (!strcmp("-n", *argv)) { } else if (!strcmp("-n", *argv)) {
nofork = 1; nofork = 1;
} else if (!strcmp("-d", *argv)) { } else if (!strcmp("-d", *argv)) {
@@ -421,10 +399,6 @@ int main
address_family = IPADDR_INET4; address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) { } else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6; address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n",
progname);
return 0;
} else if (*argv[0] == '-') { } else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv); LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else { } else {
@@ -437,7 +411,7 @@ int main
if (getuid() != 0) { if (getuid() != 0) {
/* This write to the terminal is OK, it comes before we turn into a daemon */ /* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n"); fprintf(stderr,"Not superuser\n");
return 1; exit(1);
} }
/* Turn into a daemon */ /* Turn into a daemon */
@@ -451,12 +425,11 @@ int main
LOG_SetDebugLevel(debug); LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting (%s)", LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting", CHRONY_VERSION);
CHRONY_VERSION, CHRONYD_FEATURES);
DNS_SetAddressFamily(address_family); DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted); CNF_SetRestarted(restarted);
/* Parse the config file or the remaining command line arguments */ /* Parse the config file or the remaining command line arguments */
if (!config_args) { if (!config_args) {
@@ -479,21 +452,16 @@ int main
* be done *AFTER* the daemon-creation fork() */ * be done *AFTER* the daemon-creation fork() */
write_lockfile(); write_lockfile();
PRV_Initialise();
LCL_Initialise(); LCL_Initialise();
SCH_Initialise(); SCH_Initialise();
SYS_Initialise(); SYS_Initialise();
NIO_Initialise(address_family);
CAM_Initialise(address_family);
RTC_Initialise(do_init_rtc); RTC_Initialise(do_init_rtc);
SRC_Initialise(); SRC_Initialise();
RCL_Initialise(); RCL_Initialise();
KEY_Initialise(); KEY_Initialise();
/* Open privileged ports before dropping root */
CAM_Initialise(address_family);
NIO_Initialise(address_family);
NCR_Initialise();
CNF_SetupAccessRestrictions();
/* Command-line switch must have priority */ /* Command-line switch must have priority */
if (!sched_priority) { if (!sched_priority) {
sched_priority = CNF_GetSchedPriority(); sched_priority = CNF_GetSchedPriority();
@@ -509,34 +477,25 @@ int main
if (!user) { if (!user) {
user = CNF_GetUser(); user = CNF_GetUser();
} }
if (user && strcmp(user, "root")) {
SYS_DropRoot(user);
}
if ((pw = getpwnam(user)) == NULL) LOG_CreateLogFileDir();
LOG_FATAL(LOGF_Main, "Could not get %s uid/gid", user);
/* Create all directories before dropping root */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Drop root privileges if the user has non-zero uid or gid */
if (pw->pw_uid || pw->pw_gid)
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise(); REF_Initialise();
SST_Initialise(); SST_Initialise();
BRD_Initialise();
NCR_Initialise();
NSR_Initialise(); NSR_Initialise();
CLG_Initialise(); CLG_Initialise();
MNL_Initialise(); MNL_Initialise();
TMC_Initialise(); TMC_Initialise();
SMT_Initialise();
/* From now on, it is safe to do finalisation on exit */ /* From now on, it is safe to do finalisation on exit */
initialised = 1; initialised = 1;
UTI_SetQuitSignalsHandler(signal_cleanup); CNF_SetupAccessRestrictions();
CAM_OpenUnixSocket();
if (scfilter_level)
SYS_EnableSystemCallFilter(scfilter_level);
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) { if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew; ref_mode = REF_ModeInitStepSlew;
@@ -551,6 +510,13 @@ int main
post_init_rtc_hook(NULL); post_init_rtc_hook(NULL);
} }
signal(SIGINT, signal_cleanup);
signal(SIGTERM, signal_cleanup);
#if !defined(WINNT)
signal(SIGQUIT, signal_cleanup);
signal(SIGHUP, signal_cleanup);
#endif /* WINNT */
/* The program normally runs under control of the main loop in /* The program normally runs under control of the main loop in
the scheduler. */ the scheduler. */
SCH_MainLoop(); SCH_MainLoop();

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
LANG=C.UTF-8 LANG=C
export LANG export LANG
if [ $# -ne 1 ]; then if [ $# -ne 1 ]; then
@@ -11,6 +11,7 @@ fi
version=$1 version=$1
tag=$version tag=$version
subdir=chrony-${version} subdir=chrony-${version}
mandate=$(date +'%B %Y')
umask 022 umask 022
@@ -38,13 +39,37 @@ echo $version > version.txt
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
./configure && make -C doc man txt || exit 1 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}%" \
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL < $m > ${m}_
iconv -f utf-8 -t ascii//TRANSLIT < doc/faq.txt > FAQ mv -f ${m}_ $m
done
./configure && make chrony.txt || exit 1
mv chrony.txt chrony.txt_
make distclean make distclean
rm -f make_release .gitignore mv chrony.txt_ chrony.txt
awk '/^[1-9] Installation$/{p=1}
/^[1-9]\.. Support for line editing/{exit}; p' chrony.txt | \
tail -n +4 > INSTALL
if [ $(wc -l < INSTALL) -gt 100 -o $(wc -l < INSTALL) -lt 85 ]; then
echo "INSTALL generated incorrectly?"
exit 3
fi
awk '/^[1-9] Frequently asked questions$/{p=1}
/^Appendix A GNU General Public License$/{exit}; p' chrony.txt | \
tail -n +4 | sed 's/^[1-9]\.\([1-9]\)/\1/' | sed 's/^----/--/' | \
sed 's/^====/==/' > FAQ
if [ $(wc -l < FAQ) -gt 400 -o $(wc -l < FAQ) -lt 200 ]; then
echo "FAQ generated incorrectly?"
exit 3
fi
rm -f config.h config.log make_release .gitignore
cd .. cd ..
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz

View File

@@ -54,8 +54,6 @@ typedef struct {
(measured-predicted)) */ (measured-predicted)) */
} Sample; } Sample;
#define MIN_SAMPLE_SEPARATION 1.0
#define MAX_SAMPLES 16 #define MAX_SAMPLES 16
static Sample samples[16]; static Sample samples[16];
@@ -176,24 +174,14 @@ int
MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm) MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{ {
struct timeval now; struct timeval now;
double offset, diff; double offset;
int i; int i;
if (enabled) { if (enabled) {
/* Check whether timestamp is within margin of old one */
LCL_ReadCookedTime(&now, NULL); LCL_ReadCookedTime(&now, NULL);
/* Make sure the provided timestamp is sane and the sample
is not too close to the last one */
if (!UTI_IsTimeOffsetSane(ts, 0.0))
return 0;
if (n_samples) {
UTI_DiffTimevalsToDouble(&diff, &now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION)
return 0;
}
UTI_DiffTimevalsToDouble(&offset, &now, ts); UTI_DiffTimevalsToDouble(&offset, &now, ts);
/* Check if buffer full up */ /* Check if buffer full up */
@@ -270,14 +258,6 @@ MNL_Reset(void)
n_samples = 0; n_samples = 0;
} }
/* ================================================== */
int
MNL_IsEnabled(void)
{
return enabled;
}
/* ================================================== */ /* ================================================== */
/* Generate report data for the REQ_MANUAL_LIST command/monitoring /* Generate report data for the REQ_MANUAL_LIST command/monitoring
protocol */ protocol */

View File

@@ -38,7 +38,6 @@ extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfre
extern void MNL_Enable(void); extern void MNL_Enable(void);
extern void MNL_Disable(void); extern void MNL_Disable(void);
extern void MNL_Reset(void); extern void MNL_Reset(void);
extern int MNL_IsEnabled(void);
extern void MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n); extern void MNL_ReportSamples(RPT_ManualSamplesReport *report, int max, int *n);
extern int MNL_DeleteSample(int index); extern int MNL_DeleteSample(int index);

View File

@@ -27,15 +27,11 @@
#ifndef GOT_MEMORY_H #ifndef GOT_MEMORY_H
#define GOT_MEMORY_H #define GOT_MEMORY_H
/* Wrappers checking for errors */ #define Malloc(x) malloc(x)
extern void *Malloc(size_t size); #define MallocNew(T) ((T *) malloc(sizeof(T)))
extern void *Realloc(void *ptr, size_t size); #define MallocArray(T, n) ((T *) malloc((n) * sizeof(T)))
extern char *Strdup(const char *s); #define Realloc(x,y) realloc(x,y)
#define ReallocArray(T,n,x) ((T *) realloc((void *)(x), (n)*sizeof(T)))
/* Convenient macros */
#define MallocNew(T) ((T *) Malloc(sizeof(T)))
#define MallocArray(T, n) ((T *) Malloc((n) * sizeof(T)))
#define ReallocArray(T,n,x) ((T *) Realloc((void *)(x), (n)*sizeof(T)))
#define Free(x) free(x) #define Free(x) free(x)
#endif /* GOT_MEMORY_H */ #endif /* GOT_MEMORY_H */

134
mkdirpp.c Normal file
View File

@@ -0,0 +1,134 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
A function for creating a directory and any parent directories that
don't exist.
*/
#include "config.h"
#include "sysincl.h"
#include "mkdirpp.h"
static int
do_dir(char *p)
{
int status;
struct stat buf;
#if defined(TEST)
fprintf(stderr, "do_dir(%s)\n", p);
#endif
/* See if directory exists */
status = stat(p, &buf);
if (status < 0) {
if (errno == ENOENT) {
/* Try to create directory */
status = mkdir(p, 0755);
return status;
} else {
return status;
}
}
if (!S_ISDIR(buf.st_mode)) {
return -1;
}
return 0;
}
/* ================================================== */
/* Return 0 if the directory couldn't be created, 1 if it could (or
already existed) */
int
mkdir_and_parents(const char *path)
{
char *p;
int len;
int i, j, k, last;
len = strlen(path);
p = (char *) malloc(1 + len);
i = k = 0;
while (1) {
p[i++] = path[k++];
if (path[k] == '/' || !path[k]) {
p[i] = 0;
if (do_dir(p) < 0) {
free(p);
return 0;
}
if (!path[k]) {
/* End of the string */
break;
}
/* check whether its a trailing / or group of / */
last = 1;
j = k+1;
while (path[j]) {
if (path[j] != '/') {
k = j - 1; /* Pick up a / into p[] thru the assignment at the top of the loop */
last = 0;
break;
}
j++;
}
if (last) {
break;
}
}
if (!path[k]) break;
}
free(p);
return 1;
}
/* ================================================== */
#if defined(TEST)
int main(int argc, char **argv) {
if (argc > 1) {
/* Invert sense of result */
return mkdir_and_parents(argv[1]) ? 0 : 1;
} else {
return 1;
}
}
#endif

View File

@@ -1,6 +1,8 @@
/* /*
chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2016 * Copyright (C) Richard P. Curnow 1997-2002
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -16,28 +18,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* *
********************************************************************** **********************************************************************
*/
#ifndef GOT_TEST_H =======================================================================
#define GOT_TEST_H
#include <addressing.h> */
extern void test_unit(void); #ifndef GOT_MKDIRPP_H
#define GOT_MKDIRPP_H
#define TEST_CHECK(expr) \ extern int mkdir_and_parents(const char *path);
do { \
if (!(expr)) { \
TST_Fail(__LINE__); \
exit(1); \
} \
} while (0)
extern void TST_Fail(int line);
extern double TST_GetRandomDouble(double min, double max);
extern void TST_GetRandomAddress(IPAddr *ip, int family, int bits);
extern void TST_SwapAddressBit(IPAddr *ip, unsigned int b);
extern void TST_RegisterDummyDrivers(void);
#endif #endif

View File

@@ -44,13 +44,11 @@ DNS_SetAddressFamily(int family)
} }
DNS_Status DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs) DNS_Name2IPAddress(const char *name, IPAddr *addr)
{ {
#ifdef HAVE_GETADDRINFO #ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai; struct addrinfo hints, *res, *ai;
int i, result; int result;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints)); memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
@@ -66,42 +64,29 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
#endif #endif
} }
for (ai = res, i = 0; i < max_addrs && ai != NULL; ai = ai->ai_next) { for (ai = res; !result && ai != NULL; ai = ai->ai_next) {
switch (ai->ai_family) { switch (ai->ai_family) {
case AF_INET: case AF_INET:
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4) addr->family = IPADDR_INET4;
continue; addr->addr.in4 = ntohl(((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr);
ip_addrs[i].family = IPADDR_INET4; result = 1;
ip_addrs[i].addr.in4 = ntohl(((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr);
i++;
break; break;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
case AF_INET6: case AF_INET6:
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6) addr->family = IPADDR_INET6;
continue; memcpy(&addr->addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr, sizeof (addr->addr.in6));
ip_addrs[i].family = IPADDR_INET6; result = 1;
memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
sizeof (ip_addrs->addr.in6));
i++;
break; break;
#endif #endif
} }
if (result && address_family != IPADDR_UNSPEC && address_family != addr->family)
result = 0;
} }
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
freeaddrinfo(res); freeaddrinfo(res);
return result ? DNS_Success : DNS_Failure;
return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure;
#else #else
struct hostent *host; struct hostent *host;
int i;
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
return DNS_Failure;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
host = gethostbyname(name); host = gethostbyname(name);
@@ -109,17 +94,8 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
if (h_errno == TRY_AGAIN) if (h_errno == TRY_AGAIN)
return DNS_TryAgain; return DNS_TryAgain;
} else { } else {
if (host->h_addrtype != AF_INET || !host->h_addr_list[0]) addr->family = IPADDR_INET4;
return DNS_Failure; addr->addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[0]);
for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) {
ip_addrs[i].family = IPADDR_INET4;
ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]);
}
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
return DNS_Success; return DNS_Success;
} }
@@ -139,14 +115,33 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
{ {
char *result = NULL; char *result = NULL;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
struct sockaddr_in in4;
struct sockaddr_in6 in6; struct sockaddr_in6 in6;
socklen_t slen;
char hbuf[NI_MAXHOST]; char hbuf[NI_MAXHOST];
slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6); switch (ip_addr->family) {
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0)) case IPADDR_INET4:
result = hbuf; memset(&in4, 0, sizeof (in4));
#ifdef SIN6_LEN
in4.sin_len = sizeof (in4);
#endif
in4.sin_family = AF_INET;
in4.sin_addr.s_addr = htonl(ip_addr->addr.in4);
if (!getnameinfo((const struct sockaddr *)&in4, sizeof (in4), hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf;
break;
case IPADDR_INET6:
memset(&in6, 0, sizeof (in6));
#ifdef SIN6_LEN
in6.sin6_len = sizeof (in6);
#endif
in6.sin6_family = AF_INET6;
memcpy(&in6.sin6_addr.s6_addr, ip_addr->addr.in6, sizeof (in6.sin6_addr.s6_addr));
if (!getnameinfo((const struct sockaddr *)&in6, sizeof (in6), hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf;
break;
}
#else #else
struct hostent *host; struct hostent *host;
uint32_t addr; uint32_t addr;
@@ -156,7 +151,7 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
addr = htonl(ip_addr->addr.in4); addr = htonl(ip_addr->addr.in4);
host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET); host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET);
break; break;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
case IPADDR_INET6: case IPADDR_INET6:
host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6); host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6);
break; break;

View File

@@ -39,10 +39,7 @@ typedef enum {
/* Resolve names only to selected address family */ /* Resolve names only to selected address family */
extern void DNS_SetAddressFamily(int family); extern void DNS_SetAddressFamily(int family);
/* Maximum number of addresses returned by DNS_Name2IPAddress */ extern DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *addr);
#define DNS_MAX_ADDRESSES 16
extern DNS_Status DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs);
extern int DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len); extern int DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len);

View File

@@ -31,10 +31,11 @@
#include "nameserv_async.h" #include "nameserv_async.h"
#include "logging.h" #include "logging.h"
#include "memory.h" #include "memory.h"
#include "privops.h"
#include "sched.h" #include "sched.h"
#include "util.h" #include "util.h"
#ifdef FEAT_ASYNCDNS
#ifdef USE_PTHREAD_ASYNCDNS #ifdef USE_PTHREAD_ASYNCDNS
#include <pthread.h> #include <pthread.h>
@@ -43,7 +44,7 @@
struct DNS_Async_Instance { struct DNS_Async_Instance {
const char *name; const char *name;
DNS_Status status; DNS_Status status;
IPAddr addresses[DNS_MAX_ADDRESSES]; IPAddr addr;
DNS_NameResolveHandler handler; DNS_NameResolveHandler handler;
void *arg; void *arg;
@@ -60,7 +61,7 @@ start_resolving(void *anything)
{ {
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything; struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
inst->status = PRV_Name2IPAddress(inst->name, inst->addresses, DNS_MAX_ADDRESSES); inst->status = DNS_Name2IPAddress(inst->name, &inst->addr);
/* Notify the main thread that the result is ready */ /* Notify the main thread that the result is ready */
if (write(inst->pipe[1], "", 1) < 0) if (write(inst->pipe[1], "", 1) < 0)
@@ -75,7 +76,6 @@ static void
end_resolving(void *anything) end_resolving(void *anything)
{ {
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything; struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
int i;
if (pthread_join(inst->thread, NULL)) { if (pthread_join(inst->thread, NULL)) {
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed"); LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
@@ -87,11 +87,7 @@ end_resolving(void *anything)
close(inst->pipe[0]); close(inst->pipe[0]);
close(inst->pipe[1]); close(inst->pipe[1]);
for (i = 0; inst->status == DNS_Success && i < DNS_MAX_ADDRESSES && (inst->handler)(inst->status, &inst->addr, inst->arg);
inst->addresses[i].family != IPADDR_UNSPEC; i++)
;
(inst->handler)(inst->status, i, inst->addresses, inst->arg);
Free(inst); Free(inst);
} }
@@ -128,3 +124,21 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
#else #else
#error #error
#endif #endif
#else
/* This is a blocking implementation used when nothing else is available */
void
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
{
IPAddr addr;
DNS_Status status;
status = DNS_Name2IPAddress(name, &addr);
(handler)(status, &addr, anything);
}
/* ================================================== */
#endif

View File

@@ -31,10 +31,11 @@
#include "nameserv.h" #include "nameserv.h"
/* Function type for callback to process the result */ /* Function type for callback to process the result */
typedef void (*DNS_NameResolveHandler)(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything); typedef void (*DNS_NameResolveHandler)(DNS_Status status, IPAddr *ip_addr, void *anything);
/* Request resolving of a name to IP address. The handler will be /* Request resolving of a name to IP address. The handler will be
called when the result is available. */ called when the result is available, but it may be also called
directly from this function call. */
extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything); extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything);
#endif #endif

53
ntp.h
View File

@@ -38,23 +38,7 @@ typedef struct {
typedef uint32_t NTP_int32; typedef uint32_t NTP_int32;
/* The NTP protocol version that we support */ #define MAX_NTP_AUTH_DATA_LEN MAX_HASH_LENGTH
#define NTP_VERSION 4
/* Maximum stratum number (infinity) */
#define NTP_MAX_STRATUM 16
/* The minimum valid length of an extension field */
#define NTP_MIN_EXTENSION_LENGTH 16
/* The maximum assumed length of all extension fields in received
packets (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH 1024
/* The minimum and maximum supported length of MAC */
#define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* Type definition for leap bits */ /* Type definition for leap bits */
typedef enum { typedef enum {
@@ -85,33 +69,24 @@ typedef struct {
NTP_int64 originate_ts; NTP_int64 originate_ts;
NTP_int64 receive_ts; NTP_int64 receive_ts;
NTP_int64 transmit_ts; NTP_int64 transmit_ts;
/* Optional extension fields, we don't send packets with them yet */
/* uint8_t extensions[] */
/* Optional message authentication code (MAC) */
NTP_int32 auth_keyid; NTP_int32 auth_keyid;
uint8_t auth_data[NTP_MAX_MAC_LENGTH - 4]; uint8_t auth_data[MAX_NTP_AUTH_DATA_LEN];
} NTP_Packet; } NTP_Packet;
#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid) /* We have to declare a buffer type to hold a datagram read from the
network. Even though we won't be using them (yet?!), this must be
large enough to hold NTP control messages. */
/* The buffer used to hold a datagram read from the network */ /* Define the maximum number of bytes that can be read in a single
typedef struct { message. (This is cribbed from ntp.h in the xntpd source code). */
#define MAX_NTP_MESSAGE_SIZE (468+12+16+4)
typedef union {
NTP_Packet ntp_pkt; NTP_Packet ntp_pkt;
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH]; uint8_t arbitrary[MAX_NTP_MESSAGE_SIZE];
} NTP_Receive_Buffer; } ReceiveBuffer;
/* Macros to work with the lvm field */ #define NTP_NORMAL_PACKET_SIZE offsetof(NTP_Packet, auth_keyid)
#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)
#define NTP_LVM_TO_VERSION(lvm) (((lvm) >> 3) & 0x7)
#define NTP_LVM_TO_MODE(lvm) ((lvm) & 0x7)
#define NTP_LVM(leap, version, mode) \
((((leap) << 6) & 0xc0) | (((version) << 3) & 0x38) | ((mode) & 0x07))
/* Special NTP reference IDs */
#define NTP_REFID_UNSYNC 0x0UL
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
#endif /* GOT_NTP_H */ #endif /* GOT_NTP_H */

1334
ntp_core.c

File diff suppressed because it is too large Load Diff

View File

@@ -58,12 +58,9 @@ extern void NCR_StartInstance(NCR_Instance instance);
/* Reset an instance */ /* Reset an instance */
extern void NCR_ResetInstance(NCR_Instance inst); extern void NCR_ResetInstance(NCR_Instance inst);
/* Change the remote address of an instance */
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
/* This routine is called when a new packet arrives off the network, /* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */ and it relates to a source we have an ongoing protocol exchange with */
extern int NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, NTP_Local_Address *local_addr, int length); extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int sock_fd, int length);
/* This routine is called when a new packet arrives off the network, /* This routine is called when a new packet arrives off the network,
and we do not recognize its source */ and we do not recognize its source */
@@ -105,10 +102,6 @@ extern void NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *o
extern NTP_Remote_Address *NCR_GetRemoteAddress(NCR_Instance instance); extern NTP_Remote_Address *NCR_GetRemoteAddress(NCR_Instance instance);
extern uint32_t NCR_GetLocalRefid(NCR_Instance inst);
extern int NCR_IsSyncPeer(NCR_Instance instance); extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);
#endif /* GOT_NTP_CORE_H */ #endif /* GOT_NTP_CORE_H */

258
ntp_io.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2015 * Copyright (C) Miroslav Lichvar 2009, 2013-2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -37,14 +37,13 @@
#include "local.h" #include "local.h"
#include "logging.h" #include "logging.h"
#include "conf.h" #include "conf.h"
#include "privops.h"
#include "util.h" #include "util.h"
#define INVALID_SOCK_FD -1 #define INVALID_SOCK_FD -1
union sockaddr_in46 { union sockaddr_in46 {
struct sockaddr_in in4; struct sockaddr_in in4;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
struct sockaddr_in6 in6; struct sockaddr_in6 in6;
#endif #endif
struct sockaddr u; struct sockaddr u;
@@ -53,27 +52,15 @@ union sockaddr_in46 {
/* The server/peer and client sockets for IPv4 and IPv6 */ /* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4; static int server_sock_fd4;
static int client_sock_fd4; static int client_sock_fd4;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
static int server_sock_fd6; static int server_sock_fd6;
static int client_sock_fd6; static int client_sock_fd6;
#endif #endif
/* Reference counters for server sockets to keep them open only when needed */
static int server_sock_ref4;
#ifdef FEAT_IPV6
static int server_sock_ref6;
#endif
/* Flag indicating we create a new connected client socket for each /* Flag indicating we create a new connected client socket for each
server instead of sharing client_sock_fd4 and client_sock_fd6 */ server instead of sharing client_sock_fd4 and client_sock_fd6 */
static int separate_client_sockets; static int separate_client_sockets;
/* Flag indicating the server sockets are not created dynamically when needed,
either to have a socket for client requests when separate client sockets
are disabled and client port is equal to server port, or the server port is
disabled */
static int permanent_server_sockets;
/* Flag indicating that we have been initialised */ /* Flag indicating that we have been initialised */
static int initialised=0; static int initialised=0;
@@ -98,13 +85,8 @@ prepare_socket(int family, int port_number, int client_only)
sock_fd = socket(family, SOCK_DGRAM, 0); sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) { if (sock_fd < 0) {
if (!client_only) { LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
UTI_SockaddrFamilyToString(family), strerror(errno));
} else {
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
}
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
@@ -134,7 +116,7 @@ prepare_socket(int family, int port_number, int client_only)
my_addr_len = sizeof (my_addr.in4); my_addr_len = sizeof (my_addr.in4);
break; break;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
case AF_INET6: case AF_INET6:
if (!client_only) if (!client_only)
CNF_GetBindAddress(IPADDR_INET6, &bind_address); CNF_GetBindAddress(IPADDR_INET6, &bind_address);
@@ -162,21 +144,21 @@ prepare_socket(int family, int port_number, int client_only)
/* Make the socket capable of re-using an old address if binding to a specific port */ /* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number && if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) { setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_REUSEADDR"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */ /* Don't quit - we might survive anyway */
} }
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */ /* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only && if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) { setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_BROADCAST"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options");
/* Don't quit - we might survive anyway */ /* Don't quit - we might survive anyway */
} }
#ifdef SO_TIMESTAMP #ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */ /* Enable receiving of timestamp control messages */
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) { if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set timestamp socket options");
/* Don't quit - we might survive anyway */ /* Don't quit - we might survive anyway */
} }
#endif #endif
@@ -185,46 +167,47 @@ prepare_socket(int family, int port_number, int client_only)
/* Allow binding to address that doesn't exist yet */ /* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 && if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) { setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_FREEBIND"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option");
} }
#endif #endif
if (family == AF_INET) { if (family == AF_INET) {
#ifdef HAVE_IN_PKTINFO #ifdef IP_PKTINFO
/* We want the local IP info on server sockets */ /* We want the local IP info on server sockets */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { if (!client_only &&
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_PKTINFO"); setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set packet info socket option");
/* Don't quit - we might survive anyway */ /* Don't quit - we might survive anyway */
} }
#endif #endif
} }
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
else if (family == AF_INET6) { else if (family == AF_INET6) {
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */ /* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) { if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_V6ONLY"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option");
} }
#endif #endif
#ifdef HAVE_IN6_PKTINFO if (!client_only) {
#ifdef IPV6_RECVPKTINFO #ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_RECVPKTINFO"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
} }
#else #elif defined(IPV6_PKTINFO)
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) { if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_PKTINFO"); LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
} }
#endif
#endif #endif
}
} }
#endif #endif
/* Bind the socket if a port or address was specified */ /* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) { 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", LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno)); family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
close(sock_fd); close(sock_fd);
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
@@ -243,7 +226,7 @@ prepare_separate_client_socket(int family)
switch (family) { switch (family) {
case IPADDR_INET4: case IPADDR_INET4:
return prepare_socket(AF_INET, 0, 1); return prepare_socket(AF_INET, 0, 1);
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
case IPADDR_INET6: case IPADDR_INET6:
return prepare_socket(AF_INET6, 0, 1); return prepare_socket(AF_INET6, 0, 1);
#endif #endif
@@ -260,9 +243,27 @@ connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
union sockaddr_in46 addr; union sockaddr_in46 addr;
socklen_t addr_len; socklen_t addr_len;
addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u); memset(&addr, 0, sizeof (addr));
assert(addr_len); switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
addr_len = sizeof (addr.in4);
addr.in4.sin_family = AF_INET;
addr.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4);
addr.in4.sin_port = htons(remote_addr->port);
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
addr_len = sizeof (addr.in6);
addr.in6.sin6_family = AF_INET6;
memcpy(addr.in6.sin6_addr.s6_addr, remote_addr->ip_addr.addr.in6,
sizeof (addr.in6.sin6_addr.s6_addr));
addr.in6.sin6_port = htons(remote_addr->port);
break;
#endif
default:
assert(0);
}
if (connect(sock_fd, &addr.u, addr_len) < 0) { if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s", DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
@@ -303,20 +304,15 @@ NIO_Initialise(int family)
if (client_port < 0) if (client_port < 0)
client_port = 0; client_port = 0;
permanent_server_sockets = !server_port || (!separate_client_sockets &&
client_port == server_port);
server_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD; client_sock_fd4 = INVALID_SOCK_FD;
server_sock_ref4 = 0; #ifdef HAVE_IPV6
#ifdef FEAT_IPV6
server_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = INVALID_SOCK_FD;
client_sock_fd6 = INVALID_SOCK_FD; client_sock_fd6 = INVALID_SOCK_FD;
server_sock_ref6 = 0;
#endif #endif
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) { if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (permanent_server_sockets && server_port) if (server_port)
server_sock_fd4 = prepare_socket(AF_INET, server_port, 0); server_sock_fd4 = prepare_socket(AF_INET, server_port, 0);
if (!separate_client_sockets) { if (!separate_client_sockets) {
if (client_port != server_port || !server_port) if (client_port != server_port || !server_port)
@@ -325,9 +321,9 @@ NIO_Initialise(int family)
client_sock_fd4 = server_sock_fd4; client_sock_fd4 = server_sock_fd4;
} }
} }
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) { if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (permanent_server_sockets && server_port) if (server_port)
server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0); server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0);
if (!separate_client_sockets) { if (!separate_client_sockets) {
if (client_port != server_port || !server_port) if (client_port != server_port || !server_port)
@@ -338,13 +334,12 @@ NIO_Initialise(int family)
} }
#endif #endif
if ((server_port && server_sock_fd4 == INVALID_SOCK_FD && if ((server_port && server_sock_fd4 == INVALID_SOCK_FD
permanent_server_sockets #ifdef HAVE_IPV6
#ifdef FEAT_IPV6
&& server_sock_fd6 == INVALID_SOCK_FD && server_sock_fd6 == INVALID_SOCK_FD
#endif #endif
) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD ) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
&& client_sock_fd6 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD
#endif #endif
)) { )) {
@@ -361,7 +356,7 @@ NIO_Finalise(void)
close_socket(client_sock_fd4); close_socket(client_sock_fd4);
close_socket(server_sock_fd4); close_socket(server_sock_fd4);
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
if (server_sock_fd6 != client_sock_fd6) if (server_sock_fd6 != client_sock_fd6)
close_socket(client_sock_fd6); close_socket(client_sock_fd6);
close_socket(server_sock_fd6); close_socket(server_sock_fd6);
@@ -373,7 +368,7 @@ NIO_Finalise(void)
/* ================================================== */ /* ================================================== */
int int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) NIO_GetClientSocket(NTP_Remote_Address *remote_addr)
{ {
if (separate_client_sockets) { if (separate_client_sockets) {
int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
@@ -391,7 +386,7 @@ NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
switch (remote_addr->ip_addr.family) { switch (remote_addr->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
return client_sock_fd4; return client_sock_fd4;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
case IPADDR_INET6: case IPADDR_INET6:
return client_sock_fd6; return client_sock_fd6;
#endif #endif
@@ -404,25 +399,13 @@ NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
/* ================================================== */ /* ================================================== */
int int
NIO_OpenServerSocket(NTP_Remote_Address *remote_addr) NIO_GetServerSocket(NTP_Remote_Address *remote_addr)
{ {
switch (remote_addr->ip_addr.family) { switch (remote_addr->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
if (permanent_server_sockets)
return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD)
server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0);
if (server_sock_fd4 != INVALID_SOCK_FD)
server_sock_ref4++;
return server_sock_fd4; return server_sock_fd4;
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
case IPADDR_INET6: case IPADDR_INET6:
if (permanent_server_sockets)
return server_sock_fd6;
if (server_sock_fd6 == INVALID_SOCK_FD)
server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0);
if (server_sock_fd6 != INVALID_SOCK_FD)
server_sock_ref6++;
return server_sock_fd6; return server_sock_fd6;
#endif #endif
default: default:
@@ -441,39 +424,12 @@ NIO_CloseClientSocket(int sock_fd)
/* ================================================== */ /* ================================================== */
void
NIO_CloseServerSocket(int sock_fd)
{
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD)
return;
if (sock_fd == server_sock_fd4) {
if (--server_sock_ref4 <= 0) {
close_socket(server_sock_fd4);
server_sock_fd4 = INVALID_SOCK_FD;
}
}
#ifdef FEAT_IPV6
else if (sock_fd == server_sock_fd6) {
if (--server_sock_ref6 <= 0) {
close_socket(server_sock_fd6);
server_sock_fd6 = INVALID_SOCK_FD;
}
}
#endif
else {
assert(0);
}
}
/* ================================================== */
int int
NIO_IsServerSocket(int sock_fd) NIO_IsServerSocket(int sock_fd)
{ {
return sock_fd != INVALID_SOCK_FD && return sock_fd != INVALID_SOCK_FD &&
(sock_fd == server_sock_fd4 (sock_fd == server_sock_fd4
#ifdef FEAT_IPV6 #ifdef HAVE_IPV6
|| sock_fd == server_sock_fd6 || sock_fd == server_sock_fd6
#endif #endif
); );
@@ -488,7 +444,7 @@ read_from_socket(void *anything)
to read, otherwise it will block. */ to read, otherwise it will block. */
int status, sock_fd; int status, sock_fd;
NTP_Receive_Buffer message; ReceiveBuffer message;
union sockaddr_in46 where_from; union sockaddr_in46 where_from;
unsigned int flags = 0; unsigned int flags = 0;
struct timeval now; struct timeval now;
@@ -504,7 +460,7 @@ read_from_socket(void *anything)
SCH_GetLastEventTime(&now, &now_err, NULL); SCH_GetLastEventTime(&now, &now_err, NULL);
iov.iov_base = &message.ntp_pkt; iov.iov_base = message.arbitrary;
iov.iov_len = sizeof(message); iov.iov_len = sizeof(message);
msg.msg_name = &where_from; msg.msg_name = &where_from;
msg.msg_namelen = sizeof(where_from); msg.msg_namelen = sizeof(where_from);
@@ -528,13 +484,29 @@ read_from_socket(void *anything)
if (msg.msg_namelen > sizeof (where_from)) if (msg.msg_namelen > sizeof (where_from))
LOG_FATAL(LOGF_NtpIO, "Truncated source address"); LOG_FATAL(LOGF_NtpIO, "Truncated source address");
UTI_SockaddrToIPAndPort(&where_from.u, &remote_addr.ip_addr, &remote_addr.port); switch (where_from.u.sa_family) {
case AF_INET:
remote_addr.ip_addr.family = IPADDR_INET4;
remote_addr.ip_addr.addr.in4 = ntohl(where_from.in4.sin_addr.s_addr);
remote_addr.port = ntohs(where_from.in4.sin_port);
break;
#ifdef HAVE_IPV6
case AF_INET6:
remote_addr.ip_addr.family = IPADDR_INET6;
memcpy(&remote_addr.ip_addr.addr.in6, where_from.in6.sin6_addr.s6_addr,
sizeof (remote_addr.ip_addr.addr.in6));
remote_addr.port = ntohs(where_from.in6.sin6_port);
break;
#endif
default:
assert(0);
}
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = sock_fd; local_addr.sock_fd = sock_fd;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
#ifdef HAVE_IN_PKTINFO #ifdef IP_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi; struct in_pktinfo ipi;
@@ -544,7 +516,7 @@ read_from_socket(void *anything)
} }
#endif #endif
#ifdef HAVE_IN6_PKTINFO #if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi; struct in6_pktinfo ipi;
@@ -565,11 +537,14 @@ read_from_socket(void *anything)
#endif #endif
} }
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d", if (status > 0) {
status, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port, DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd); 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_LENGTH) { if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) {
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
&remote_addr, &local_addr, status); &remote_addr, &local_addr, status);
@@ -583,7 +558,7 @@ read_from_socket(void *anything)
} }
/* ================================================== */ /* ================================================== */
/* Send a packet to remote address from local address */ /* Send a packet to given address */
static int static int
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr) send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
@@ -603,15 +578,31 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
return 0; return 0;
} }
/* Don't set address with connected socket */ switch (remote_addr->ip_addr.family) {
if (local_addr->sock_fd == server_sock_fd4 || case IPADDR_INET4:
#ifdef FEAT_IPV6 /* Don't set address with connected socket */
local_addr->sock_fd == server_sock_fd6 || if (local_addr->sock_fd != server_sock_fd4 && separate_client_sockets)
break;
memset(&remote.in4, 0, sizeof (remote.in4));
addrlen = sizeof (remote.in4);
remote.in4.sin_family = AF_INET;
remote.in4.sin_port = htons(remote_addr->port);
remote.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4);
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
/* Don't set address with connected socket */
if (local_addr->sock_fd != server_sock_fd6 && separate_client_sockets)
break;
memset(&remote.in6, 0, sizeof (remote.in6));
addrlen = sizeof (remote.in6);
remote.in6.sin6_family = AF_INET6;
remote.in6.sin6_port = htons(remote_addr->port);
memcpy(&remote.in6.sin6_addr.s6_addr, &remote_addr->ip_addr.addr.in6,
sizeof (remote.in6.sin6_addr.s6_addr));
break;
#endif #endif
!separate_client_sockets) { default:
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port,
&remote.u);
if (!addrlen)
return 0; return 0;
} }
@@ -632,7 +623,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
msg.msg_flags = 0; msg.msg_flags = 0;
cmsglen = 0; cmsglen = 0;
#ifdef HAVE_IN_PKTINFO #ifdef IP_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) { if (local_addr->ip_addr.family == IPADDR_INET4) {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
struct in_pktinfo *ipi; struct in_pktinfo *ipi;
@@ -650,7 +641,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
} }
#endif #endif
#ifdef HAVE_IN6_PKTINFO #if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
if (local_addr->ip_addr.family == IPADDR_INET6) { if (local_addr->ip_addr.family == IPADDR_INET6) {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
struct in6_pktinfo *ipi; struct in6_pktinfo *ipi;
@@ -682,7 +673,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
return 0; return 0;
} }
DEBUG_LOG(LOGF_NtpIO, "Sent %d bytes to %s:%d from %s fd %d", packetlen, DEBUG_LOG(LOGF_NtpIO, "Sent to %s:%d from %s fd %d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd); UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
@@ -690,10 +681,19 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
} }
/* ================================================== */ /* ================================================== */
/* Send a packet to a given address */ /* Send an unauthenticated packet to a given address */
int int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{ {
return send_packet((void *) packet, length, 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 */
int
NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len)
{
return send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr, local_addr);
} }

View File

@@ -39,21 +39,21 @@ extern void NIO_Initialise(int family);
extern void NIO_Finalise(void); extern void NIO_Finalise(void);
/* Function to obtain a socket for sending client packets */ /* Function to obtain a socket for sending client packets */
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr); extern int NIO_GetClientSocket(NTP_Remote_Address *remote_addr);
/* Function to obtain a socket for sending server/peer packets */ /* Function to obtain a socket for sending server/peer packets */
extern int NIO_OpenServerSocket(NTP_Remote_Address *remote_addr); extern int NIO_GetServerSocket(NTP_Remote_Address *remote_addr);
/* Function to close a socket returned by NIO_OpenClientSocket() */ /* Function to close a socket returned by NIO_GetClientSocket() */
extern void NIO_CloseClientSocket(int sock_fd); extern void NIO_CloseClientSocket(int sock_fd);
/* Function to close a socket returned by NIO_OpenServerSocket() */
extern void NIO_CloseServerSocket(int sock_fd);
/* Function to check if socket is a server socket */ /* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd); extern int NIO_IsServerSocket(int sock_fd);
/* Function to transmit a packet */ /* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); extern int NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
/* Function to transmit an authenticated packet */
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 */ #endif /* GOT_NTP_IO_H */

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016 * Copyright (C) Miroslav Lichvar 2011-2012, 2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -31,7 +31,6 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "util.h" #include "util.h"
@@ -49,20 +48,20 @@ typedef struct {
NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL
means this slot in table is in use */ means this slot in table is in use */
NCR_Instance data; /* Data for the protocol engine for this source */ NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source, may be NULL */
int pool; /* Number of the pool from which was this source
added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response
received from the source yet */
} SourceRecord; } SourceRecord;
/* Hash table of SourceRecord, its size is a power of two and it's never #define N_RECORDS 256
more than half full */
static ARR_Instance records; /* Fixed size table, because we use a hard coded hash algorithm. It
is rather unlikely we would have anything approaching this number
of sources. */
static SourceRecord records[N_RECORDS];
/* Number of sources in the hash table */
static int n_sources; static int n_sources;
/* The largest number of sources we want to have stored in the hash table */
#define MAX_SOURCES 64
/* Flag indicating new sources will be started automatically when added */ /* Flag indicating new sources will be started automatically when added */
static int auto_start_sources = 0; static int auto_start_sources = 0;
@@ -70,24 +69,14 @@ static int auto_start_sources = 0;
struct UnresolvedSource { struct UnresolvedSource {
char *name; char *name;
int port; int port;
int random_order; NTP_Source_Type type;
int replacement; SourceParameters params;
union {
struct {
NTP_Source_Type type;
SourceParameters params;
int pool;
int max_new_sources;
} new_source;
NTP_Remote_Address replace_source;
};
struct UnresolvedSource *next; struct UnresolvedSource *next;
}; };
#define RESOLVE_INTERVAL_UNIT 7 #define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2 #define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9 #define MAX_RESOLVE_INTERVAL 9
#define MIN_REPLACEMENT_INTERVAL 8
static struct UnresolvedSource *unresolved_sources = NULL; static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0; static int resolving_interval = 0;
@@ -95,26 +84,10 @@ static SCH_TimeoutID resolving_id;
static struct UnresolvedSource *resolving_source = NULL; static struct UnresolvedSource *resolving_source = NULL;
static NSR_SourceResolvingEndHandler resolving_end_handler = NULL; static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
#define MAX_POOL_SOURCES 16
#define INVALID_POOL (-1)
/* Pool of sources with the same name */
struct SourcePool {
/* Number of sources added from this pool (ignoring tentative sources) */
int sources;
/* Maximum number of sources */
int max_sources;
};
/* Array of SourcePool */
static ARR_Instance pools;
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
static void resolve_sources(void *arg); static void resolve_sources(void *arg);
static void rehash_records(void);
static void clean_source_record(SourceRecord *record);
static void static void
slew_sources(struct timeval *raw, slew_sources(struct timeval *raw,
@@ -131,25 +104,16 @@ static int initialised = 0;
/* ================================================== */ /* ================================================== */
static SourceRecord *
get_record(unsigned index)
{
return (SourceRecord *)ARR_GetElement(records, index);
}
/* ================================================== */
void void
NSR_Initialise(void) NSR_Initialise(void)
{ {
int i;
for (i=0; i<N_RECORDS; i++) {
records[i].remote_addr = NULL;
}
n_sources = 0; n_sources = 0;
initialised = 1; initialised = 1;
records = ARR_CreateInstance(sizeof (SourceRecord));
rehash_records();
pools = ARR_CreateInstance(sizeof (struct SourcePool));
LCL_AddParameterChangeHandler(slew_sources, NULL); LCL_AddParameterChangeHandler(slew_sources, NULL);
} }
@@ -158,27 +122,6 @@ NSR_Initialise(void)
void void
NSR_Finalise(void) NSR_Finalise(void)
{ {
SourceRecord *record;
struct UnresolvedSource *us;
unsigned int i;
ARR_DestroyInstance(pools);
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr)
clean_source_record(record);
}
ARR_DestroyInstance(records);
while (unresolved_sources) {
us = unresolved_sources;
unresolved_sources = us->next;
Free(us->name);
Free(us);
}
initialised = 0; initialised = 0;
} }
@@ -199,92 +142,60 @@ NSR_Finalise(void)
static void static void
find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found) find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
{ {
SourceRecord *record; unsigned long hash;
uint32_t hash; unsigned long ip;
unsigned int i, size;
unsigned short port; unsigned short port;
uint8_t *ip6;
size = ARR_GetSize(records); assert(N_RECORDS == 256);
if (remote_addr->ip_addr.family != IPADDR_INET4 && switch (remote_addr->ip_addr.family) {
remote_addr->ip_addr.family != IPADDR_INET6) { case IPADDR_INET6:
*found = *slot = 0; ip6 = remote_addr->ip_addr.addr.in6;
return; ip = (ip6[0] ^ ip6[4] ^ ip6[8] ^ ip6[12]) |
} (ip6[1] ^ ip6[5] ^ ip6[9] ^ ip6[13]) << 8 |
(ip6[2] ^ ip6[6] ^ ip6[10] ^ ip6[14]) << 16 |
hash = UTI_IPToHash(&remote_addr->ip_addr); (ip6[3] ^ ip6[7] ^ ip6[11] ^ ip6[15]) << 24;
port = remote_addr->port;
for (i = 0; i < size / 2; i++) {
/* Use quadratic probing */
*slot = (hash + (i + i * i) / 2) % size;
record = get_record(*slot);
if (!record->remote_addr)
break; break;
case IPADDR_INET4:
if (!UTI_CompareIPs(&record->remote_addr->ip_addr, ip = remote_addr->ip_addr.addr.in4;
&remote_addr->ip_addr, NULL)) { break;
*found = record->remote_addr->port == port ? 2 : 1; default:
*found = *slot = 0;
return; return;
}
port = remote_addr->port;
/* Compute hash value just by xor'ing the 4 bytes of the address together */
hash = ip ^ (ip >> 16);
hash = (hash ^ (hash >> 8)) & 0xff;
while (records[hash].remote_addr &&
UTI_CompareIPs(&records[hash].remote_addr->ip_addr,
&remote_addr->ip_addr, NULL)) {
hash++;
if (hash == 256) hash = 0;
}
if (records[hash].remote_addr) {
if (records[hash].remote_addr->port == port) {
*found = 2;
} else {
*found = 1;
} }
*slot = hash;
} else {
*found = 0;
*slot = hash;
} }
*found = 0;
}
/* ================================================== */
/* Check if hash table of given size is sufficient to contain sources */
static int
check_hashtable_size(unsigned int sources, unsigned int size)
{
return sources * 2 <= size;
}
/* ================================================== */
static void
rehash_records(void)
{
SourceRecord *temp_records;
unsigned int i, old_size, new_size;
int slot, found;
old_size = ARR_GetSize(records);
temp_records = MallocArray(SourceRecord, old_size);
memcpy(temp_records, ARR_GetElements(records), old_size * sizeof (SourceRecord));
/* The size of the hash table is always a power of two */
for (new_size = 1; !check_hashtable_size(n_sources, new_size); new_size *= 2)
;
ARR_SetSize(records, new_size);
for (i = 0; i < new_size; i++)
get_record(i)->remote_addr = NULL;
for (i = 0; i < old_size; i++) {
if (!temp_records[i].remote_addr)
continue;
find_slot(temp_records[i].remote_addr, &slot, &found);
assert(!found);
*get_record(slot) = temp_records[i];
}
Free(temp_records);
} }
/* ================================================== */ /* ================================================== */
/* Procedure to add a new source */ /* Procedure to add a new source */
static NSR_Status NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, SourceParameters *params, int pool) NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{ {
SourceRecord *record;
int slot, found; int slot, found;
assert(initialised); assert(initialised);
@@ -294,28 +205,17 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
if (found) { if (found) {
return NSR_AlreadyInUse; return NSR_AlreadyInUse;
} else { } else {
if (remote_addr->ip_addr.family != IPADDR_INET4 && if (n_sources == MAX_SOURCES) {
return NSR_TooManySources;
} else if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6) { remote_addr->ip_addr.family != IPADDR_INET6) {
return NSR_InvalidAF; return NSR_InvalidAF;
} else { } else {
n_sources++; n_sources++;
records[slot].data = NCR_GetInstance(remote_addr, type, params); /* Will need params passing through */
if (!check_hashtable_size(n_sources, ARR_GetSize(records))) { records[slot].remote_addr = NCR_GetRemoteAddress(records[slot].data);
rehash_records();
find_slot(remote_addr, &slot, &found);
}
assert(!found);
record = get_record(slot);
record->data = NCR_GetInstance(remote_addr, type, params);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->name = name ? Strdup(name) : NULL;
record->pool = pool;
record->tentative = 1;
if (auto_start_sources) if (auto_start_sources)
NCR_StartInstance(record->data); NCR_StartInstance(records[slot].data);
return NSR_Success; return NSR_Success;
} }
} }
@@ -323,94 +223,24 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
/* ================================================== */ /* ================================================== */
static NSR_Status
replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
int slot1, slot2, found;
SourceRecord *record;
struct SourcePool *pool;
find_slot(old_addr, &slot1, &found);
if (!found)
return NSR_NoSuchSource;
find_slot(new_addr, &slot2, &found);
if (found)
return NSR_AlreadyInUse;
record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr);
record->remote_addr = NCR_GetRemoteAddress(record->data);
if (!record->tentative) {
record->tentative = 1;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources--;
}
}
/* The hash table must be rebuilt for the new address */
rehash_records();
LOG(LOGS_INFO, LOGF_NtpSources, "Source %s replaced with %s",
UTI_IPToString(&old_addr->ip_addr),
UTI_IPToString(&new_addr->ip_addr));
return NSR_Success;
}
/* ================================================== */
static void static void
process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs) name_resolve_handler(DNS_Status status, IPAddr *ip_addr, void *anything)
{
NTP_Remote_Address address;
int i, added;
unsigned short first = 0;
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
for (i = added = 0; i < n_addrs; i++) {
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port;
DEBUG_LOG(LOGF_NtpSources, "(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
if (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
break;
} else {
if (add_source(&address, us->name, us->new_source.type, &us->new_source.params,
us->new_source.pool) == NSR_Success)
added++;
if (added >= us->new_source.max_new_sources)
break;
}
}
}
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything)
{ {
struct UnresolvedSource *us, **i, *next; struct UnresolvedSource *us, **i, *next;
NTP_Remote_Address address;
us = (struct UnresolvedSource *)anything; us = (struct UnresolvedSource *)anything;
assert(us == resolving_source); assert(us == resolving_source);
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %d addrs", us->name, n_addrs);
switch (status) { switch (status) {
case DNS_TryAgain: case DNS_TryAgain:
break; break;
case DNS_Success: case DNS_Success:
process_resolved_name(us, ip_addrs, n_addrs); DEBUG_LOG(LOGF_NtpSources, "%s resolved to %s", us->name, UTI_IPToString(ip_addr));
address.ip_addr = *ip_addr;
address.port = us->port;
NSR_AddSource(&address, us->type, &us->params);
break; break;
case DNS_Failure: case DNS_Failure:
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name); LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
@@ -421,9 +251,8 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
next = us->next; next = us->next;
/* Remove the source from the list on success or failure, replacements if (status != DNS_TryAgain) {
are removed on any status */ /* Remove the source from the list */
if (us->replacement || status != DNS_TryAgain) {
for (i = &unresolved_sources; *i; i = &(*i)->next) { for (i = &unresolved_sources; *i; i = &(*i)->next) {
if (*i == us) { if (*i == us) {
*i = us->next; *i = us->next;
@@ -482,62 +311,24 @@ resolve_sources(void *arg)
/* ================================================== */ /* ================================================== */
static void /* Procedure to add a new server or peer source, but instead of an IP address
append_unresolved_source(struct UnresolvedSource *us) only a name is provided */
void
NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParameters *params)
{ {
struct UnresolvedSource **i; struct UnresolvedSource *us, **i;
us = MallocNew(struct UnresolvedSource);
us->name = name;
us->port = port;
us->type = type;
us->params = *params;
us->next = NULL;
for (i = &unresolved_sources; *i; i = &(*i)->next) for (i = &unresolved_sources; *i; i = &(*i)->next)
; ;
*i = us; *i = us;
us->next = NULL;
}
/* ================================================== */
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{
return add_source(remote_addr, NULL, type, params, INVALID_POOL);
}
/* ================================================== */
void
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params)
{
struct UnresolvedSource *us;
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
/* If the name is an IP address, don't bother with full resolving now
or later when trying to replace the source */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
NSR_AddSource(&remote_addr, type, params);
return;
}
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->port = port;
us->random_order = 0;
us->replacement = 0;
us->new_source.type = type;
us->new_source.params = *params;
if (!pool) {
us->new_source.pool = INVALID_POOL;
us->new_source.max_new_sources = 1;
} else {
sp = (struct SourcePool *)ARR_GetNewElement(pools);
sp->sources = 0;
sp->max_sources = params->max_sources;
us->new_source.pool = ARR_GetSize(pools) - 1;
us->new_source.max_new_sources = MAX_POOL_SOURCES;
}
append_unresolved_source(us);
} }
/* ================================================== */ /* ================================================== */
@@ -574,12 +365,12 @@ NSR_ResolveSources(void)
void NSR_StartSources(void) void NSR_StartSources(void)
{ {
unsigned int i; int i;
for (i = 0; i < ARR_GetSize(records); i++) { for (i = 0; i < N_RECORDS; i++) {
if (!get_record(i)->remote_addr) if (!records[i].remote_addr)
continue; continue;
NCR_StartInstance(get_record(i)->data); NCR_StartInstance(records[i].data);
} }
} }
@@ -592,20 +383,6 @@ void NSR_AutoStartSources(void)
/* ================================================== */ /* ================================================== */
static void
clean_source_record(SourceRecord *record)
{
assert(record->remote_addr);
record->remote_addr = NULL;
NCR_DestroyInstance(record->data);
if (record->name)
Free(record->name);
n_sources--;
}
/* ================================================== */
/* Procedure to remove a source. We don't bother whether the port /* Procedure to remove a source. We don't bother whether the port
address is matched - we're only interested in removing a record for address is matched - we're only interested in removing a record for
the right IP address. Thus the caller can specify the port number the right IP address. Thus the caller can specify the port number
@@ -613,7 +390,8 @@ clean_source_record(SourceRecord *record)
NSR_Status NSR_Status
NSR_RemoveSource(NTP_Remote_Address *remote_addr) NSR_RemoveSource(NTP_Remote_Address *remote_addr)
{ {
int slot, found; int i, slot, found;
SourceRecord temp_records[N_RECORDS];
assert(initialised); assert(initialised);
@@ -622,12 +400,29 @@ NSR_RemoveSource(NTP_Remote_Address *remote_addr)
return NSR_NoSuchSource; return NSR_NoSuchSource;
} }
clean_source_record(get_record(slot)); n_sources--;
records[slot].remote_addr = NULL;
NCR_DestroyInstance(records[slot].data);
/* Rehash the table to make sure there are no broken probe sequences. /* Rehash the table to make sure there are no broken probe sequences.
This is costly, but it's not expected to happen frequently. */ This is costly, but it's not expected to happen frequently. */
rehash_records(); memcpy(temp_records, records, sizeof (records));
for (i = 0; i < N_RECORDS; i++) {
records[i].remote_addr = NULL;
}
for (i = 0; i < N_RECORDS; i++) {
if (!temp_records[i].remote_addr)
continue;
find_slot(temp_records[i].remote_addr, &slot, &found);
assert(!found);
records[slot].remote_addr = temp_records[i].remote_addr;
records[slot].data = temp_records[i].data;
}
return NSR_Success; return NSR_Success;
} }
@@ -637,138 +432,14 @@ NSR_RemoveSource(NTP_Remote_Address *remote_addr)
void void
NSR_RemoveAllSources(void) NSR_RemoveAllSources(void)
{ {
SourceRecord *record; int i;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) { for (i = 0; i < N_RECORDS; i++) {
record = get_record(i); if (!records[i].remote_addr)
if (!record->remote_addr)
continue; continue;
clean_source_record(record); NCR_DestroyInstance(records[i].data);
records[i].remote_addr = NULL;
} }
rehash_records();
}
/* ================================================== */
static void
resolve_source_replacement(SourceRecord *record)
{
struct UnresolvedSource *us;
DEBUG_LOG(LOGF_NtpSources, "trying to replace %s",
UTI_IPToString(&record->remote_addr->ip_addr));
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
us->port = record->remote_addr->port;
/* If there never was a valid reply from this source (e.g. it was a bad
replacement), ignore the order of addresses from the resolver to not get
stuck to a pair of addresses if the order doesn't change, or a group of
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
us->random_order = record->tentative;
us->replacement = 1;
us->replace_source = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
}
/* ================================================== */
void
NSR_HandleBadSource(IPAddr *address)
{
static struct timeval last_replacement;
struct timeval now;
NTP_Remote_Address remote_addr;
SourceRecord *record;
int slot, found;
double diff;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return;
record = get_record(slot);
/* Only sources with a name can be replaced */
if (!record->name)
return;
/* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&diff, &now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
return;
}
last_replacement = now;
resolve_source_replacement(record);
}
/* ================================================== */
void
NSR_RefreshAddresses(void)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || !record->name)
continue;
resolve_source_replacement(record);
}
}
/* ================================================== */
static void remove_tentative_pool_sources(int pool)
{
SourceRecord *record;
unsigned int i, removed;
for (i = removed = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || record->pool != pool || !record->tentative)
continue;
DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s",
UTI_IPToString(&record->remote_addr->ip_addr));
clean_source_record(record);
removed++;
}
if (removed)
rehash_records();
}
/* ================================================== */
uint32_t
NSR_GetLocalRefid(IPAddr *address)
{
NTP_Remote_Address remote_addr;
int slot, found;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return 0;
return NCR_GetLocalRefid(get_record(slot)->data);
} }
/* ================================================== */ /* ================================================== */
@@ -778,36 +449,14 @@ NSR_GetLocalRefid(IPAddr *address)
void void
NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length) NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{ {
SourceRecord *record;
struct SourcePool *pool;
int slot, found; int slot, found;
assert(initialised); assert(initialised);
find_slot(remote_addr, &slot, &found); find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */ if (found == 2) { /* Must match IP address AND port number */
record = get_record(slot); NCR_ProcessKnown(message, now, now_err, records[slot].data,
local_addr->sock_fd, length);
if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length))
return;
if (record->tentative) {
/* This was the first valid reply from the source */
record->tentative = 0;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources++;
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
record->name, pool->sources);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
if (pool->sources >= pool->max_sources)
remove_tentative_pool_sources(record->pool);
}
}
} else { } else {
NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length); NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);
} }
@@ -823,16 +472,14 @@ slew_sources(struct timeval *raw,
LCL_ChangeType change_type, LCL_ChangeType change_type,
void *anything) void *anything)
{ {
SourceRecord *record; int i;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) { for (i=0; i<N_RECORDS; i++) {
record = get_record(i); if (records[i].remote_addr) {
if (record->remote_addr) {
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeUnknownStep) {
NCR_ResetInstance(record->data); NCR_ResetInstance(records[i].data);
} else { } else {
NCR_SlewTimes(record->data, cooked, dfreq, doffset); NCR_SlewTimes(records[i].data, cooked, dfreq, doffset);
} }
} }
} }
@@ -843,20 +490,18 @@ slew_sources(struct timeval *raw,
int int
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address) NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{ {
SourceRecord *record; int i;
unsigned int i;
int any; int any;
NSR_ResolveSources(); NSR_ResolveSources();
any = 0; any = 0;
for (i = 0; i < ARR_GetSize(records); i++) { for (i=0; i<N_RECORDS; i++) {
record = get_record(i); if (records[i].remote_addr) {
if (record->remote_addr) {
if (address->family == IPADDR_UNSPEC || if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { !UTI_CompareIPs(&records[i].remote_addr->ip_addr, address, mask)) {
any = 1; any = 1;
NCR_TakeSourceOnline(record->data); NCR_TakeSourceOnline(records[i].data);
} }
} }
} }
@@ -865,10 +510,8 @@ NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
struct UnresolvedSource *us; struct UnresolvedSource *us;
for (us = unresolved_sources; us; us = us->next) { for (us = unresolved_sources; us; us = us->next) {
if (us->replacement)
continue;
any = 1; any = 1;
us->new_source.params.online = 1; us->params.online = 1;
} }
} }
@@ -880,39 +523,35 @@ NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
int int
NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address) NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
{ {
SourceRecord *record, *syncpeer; int i, any, syncpeer;
unsigned int i, any;
any = 0; any = 0;
syncpeer = NULL; syncpeer = -1;
for (i = 0; i < ARR_GetSize(records); i++) { for (i=0; i<N_RECORDS; i++) {
record = get_record(i); if (records[i].remote_addr) {
if (record->remote_addr) {
if (address->family == IPADDR_UNSPEC || if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { !UTI_CompareIPs(&records[i].remote_addr->ip_addr, address, mask)) {
any = 1; any = 1;
if (NCR_IsSyncPeer(record->data)) { if (NCR_IsSyncPeer(records[i].data)) {
syncpeer = record; syncpeer = i;
continue; continue;
} }
NCR_TakeSourceOffline(record->data); NCR_TakeSourceOffline(records[i].data);
} }
} }
} }
/* Take sync peer offline as last to avoid reference switching */ /* Take sync peer offline as last to avoid reference switching */
if (syncpeer) { if (syncpeer >= 0) {
NCR_TakeSourceOffline(syncpeer->data); NCR_TakeSourceOffline(records[syncpeer].data);
} }
if (address->family == IPADDR_UNSPEC) { if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us; struct UnresolvedSource *us;
for (us = unresolved_sources; us; us = us->next) { for (us = unresolved_sources; us; us = us->next) {
if (us->replacement)
continue;
any = 1; any = 1;
us->new_source.params.online = 0; us->params.online = 0;
} }
} }
@@ -933,7 +572,7 @@ NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyMinpoll(get_record(slot)->data, new_minpoll); NCR_ModifyMinpoll(records[slot].data, new_minpoll);
return 1; return 1;
} }
} }
@@ -952,7 +591,7 @@ NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyMaxpoll(get_record(slot)->data, new_maxpoll); NCR_ModifyMaxpoll(records[slot].data, new_maxpoll);
return 1; return 1;
} }
} }
@@ -971,7 +610,7 @@ NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyMaxdelay(get_record(slot)->data, new_max_delay); NCR_ModifyMaxdelay(records[slot].data, new_max_delay);
return 1; return 1;
} }
} }
@@ -990,7 +629,7 @@ NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyMaxdelayratio(get_record(slot)->data, new_max_delay_ratio); NCR_ModifyMaxdelayratio(records[slot].data, new_max_delay_ratio);
return 1; return 1;
} }
} }
@@ -1009,7 +648,7 @@ NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyMaxdelaydevratio(get_record(slot)->data, new_max_delay_dev_ratio); NCR_ModifyMaxdelaydevratio(records[slot].data, new_max_delay_dev_ratio);
return 1; return 1;
} }
} }
@@ -1028,7 +667,7 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyMinstratum(get_record(slot)->data, new_min_stratum); NCR_ModifyMinstratum(records[slot].data, new_min_stratum);
return 1; return 1;
} }
} }
@@ -1047,7 +686,7 @@ NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
if (found == 0) { if (found == 0) {
return 0; return 0;
} else { } else {
NCR_ModifyPolltarget(get_record(slot)->data, new_poll_target); NCR_ModifyPolltarget(records[slot].data, new_poll_target);
return 1; return 1;
} }
} }
@@ -1058,18 +697,16 @@ int
NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
IPAddr *mask, IPAddr *address) IPAddr *mask, IPAddr *address)
{ {
SourceRecord *record; int i;
unsigned int i;
int any; int any;
any = 0; any = 0;
for (i = 0; i < ARR_GetSize(records); i++) { for (i=0; i<N_RECORDS; i++) {
record = get_record(i); if (records[i].remote_addr) {
if (record->remote_addr) {
if (address->family == IPADDR_UNSPEC || if (address->family == IPADDR_UNSPEC ||
!UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) { !UTI_CompareIPs(&records[i].remote_addr->ip_addr, address, mask)) {
any = 1; any = 1;
NCR_InitiateSampleBurst(record->data, n_good_samples, n_total_samples); NCR_InitiateSampleBurst(records[i].data, n_good_samples, n_total_samples);
} }
} }
} }
@@ -1092,7 +729,7 @@ NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
rem_addr.port = 0; rem_addr.port = 0;
find_slot(&rem_addr, &slot, &found); find_slot(&rem_addr, &slot, &found);
if (found) { if (found) {
NCR_ReportSource(get_record(slot)->data, report, now); NCR_ReportSource(records[slot].data, report, now);
} else { } else {
report->poll = 0; report->poll = 0;
report->latest_meas_ago = 0; report->latest_meas_ago = 0;
@@ -1104,8 +741,7 @@ NSR_ReportSource(RPT_SourceReport *report, struct timeval *now)
void void
NSR_GetActivityReport(RPT_ActivityReport *report) NSR_GetActivityReport(RPT_ActivityReport *report)
{ {
SourceRecord *record; int i;
unsigned int i;
struct UnresolvedSource *us; struct UnresolvedSource *us;
report->online = 0; report->online = 0;
@@ -1113,10 +749,9 @@ NSR_GetActivityReport(RPT_ActivityReport *report)
report->burst_online = 0; report->burst_online = 0;
report->burst_offline = 0; report->burst_offline = 0;
for (i = 0; i < ARR_GetSize(records); i++) { for (i=0; i<N_RECORDS; i++) {
record = get_record(i); if (records[i].remote_addr) {
if (record->remote_addr) { NCR_IncrementActivityCounters(records[i].data, &report->online, &report->offline,
NCR_IncrementActivityCounters(record->data, &report->online, &report->offline,
&report->burst_online, &report->burst_offline); &report->burst_online, &report->burst_offline);
} }
} }

View File

@@ -50,10 +50,10 @@ typedef enum {
/* Procedure to add a new server or peer source. */ /* Procedure to add a new server or peer source. */
extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params); extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
/* Procedure to add a new server, peer source, or pool of servers specified by /* Procedure to add a new server or peer source with currently unknown address.
name instead of address. The name is resolved in exponentially increasing The name will be periodically resolved in exponentially increasing intervals
intervals until it succeeds or fails with a non-temporary error. */ until it succeeds or fails with a non-temporary error. */
extern void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params); extern void NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParameters *params);
/* Function type for handlers to be called back when an attempt /* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */ * (possibly unsuccessful) to resolve unresolved sources ends */
@@ -77,15 +77,6 @@ extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
/* Procedure to remove all sources */ /* Procedure to remove all sources */
extern void NSR_RemoveAllSources(void); extern void NSR_RemoveAllSources(void);
/* Procedure to try to find a replacement for a bad source */
extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void);
/* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */ /* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length); extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);

View File

@@ -3,7 +3,6 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002 * Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014-2016
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -34,121 +33,138 @@
#include "util.h" #include "util.h"
#include "pktlength.h" #include "pktlength.h"
#define PADDING_LENGTH_(request_length, reply_length) \
(uint16_t)((request_length) < (reply_length) ? (reply_length) - (request_length) : 0)
#define PADDING_LENGTH(request_data, reply_data) \
PADDING_LENGTH_(offsetof(CMD_Request, request_data), offsetof(CMD_Reply, reply_data))
#define REQ_LENGTH_ENTRY(request_data_field, reply_data_field) \
{ offsetof(CMD_Request, data.request_data_field.EOR), \
PADDING_LENGTH(data.request_data_field.EOR, data.reply_data_field.EOR) }
#define RPY_LENGTH_ENTRY(reply_data_field) \
offsetof(CMD_Reply, data.reply_data_field.EOR)
/* ================================================== */ /* ================================================== */
struct request_length { static int
uint16_t command; command_unpadded_length(CMD_Request *r)
uint16_t padding; {
}; int type;
type = ntohs(r->command);
if (type < 0 || type >= N_REQUEST_TYPES) {
return 0;
} else {
switch (type) {
static const struct request_length request_lengths[] = { case REQ_NULL:
REQ_LENGTH_ENTRY(null, null), /* NULL */ return offsetof(CMD_Request, data);
REQ_LENGTH_ENTRY(online, null), /* ONLINE */ case REQ_ONLINE:
REQ_LENGTH_ENTRY(offline, null), /* OFFLINE */ return offsetof(CMD_Request, data.online.EOR);
REQ_LENGTH_ENTRY(burst, null), /* BURST */ case REQ_OFFLINE:
REQ_LENGTH_ENTRY(modify_minpoll, null), /* MODIFY_MINPOLL */ return offsetof(CMD_Request, data.offline.EOR);
REQ_LENGTH_ENTRY(modify_maxpoll, null), /* MODIFY_MAXPOLL */ case REQ_BURST:
REQ_LENGTH_ENTRY(dump, null), /* DUMP */ return offsetof(CMD_Request, data.burst.EOR);
REQ_LENGTH_ENTRY(modify_maxdelay, null), /* MODIFY_MAXDELAY */ case REQ_MODIFY_MINPOLL:
REQ_LENGTH_ENTRY(modify_maxdelayratio, null), /* MODIFY_MAXDELAYRATIO */ return offsetof(CMD_Request, data.modify_minpoll.EOR);
REQ_LENGTH_ENTRY(modify_maxupdateskew, null), /* MODIFY_MAXUPDATESKEW */ case REQ_MODIFY_MAXPOLL:
REQ_LENGTH_ENTRY(logon, null), /* LOGON */ return offsetof(CMD_Request, data.modify_maxpoll.EOR);
REQ_LENGTH_ENTRY(settime, manual_timestamp), /* SETTIME */ case REQ_DUMP:
{ 0, 0 }, /* LOCAL */ return offsetof(CMD_Request, data.dump.EOR);
REQ_LENGTH_ENTRY(manual, null), /* MANUAL */ case REQ_MODIFY_MAXDELAY:
REQ_LENGTH_ENTRY(null, n_sources), /* N_SOURCES */ return offsetof(CMD_Request, data.modify_maxdelay.EOR);
REQ_LENGTH_ENTRY(source_data, source_data), /* SOURCE_DATA */ case REQ_MODIFY_MAXDELAYRATIO:
REQ_LENGTH_ENTRY(null, null), /* REKEY */ return offsetof(CMD_Request, data.modify_maxdelayratio.EOR);
REQ_LENGTH_ENTRY(allow_deny, null), /* ALLOW */ case REQ_MODIFY_MAXDELAYDEVRATIO:
REQ_LENGTH_ENTRY(allow_deny, null), /* ALLOWALL */ return offsetof(CMD_Request, data.modify_maxdelaydevratio.EOR);
REQ_LENGTH_ENTRY(allow_deny, null), /* DENY */ case REQ_MODIFY_MAXUPDATESKEW:
REQ_LENGTH_ENTRY(allow_deny, null), /* DENYALL */ return offsetof(CMD_Request, data.modify_maxupdateskew.EOR);
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDALLOW */ case REQ_LOGON :
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDALLOWALL */ return offsetof(CMD_Request, data.logon.EOR);
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENY */ case REQ_SETTIME :
REQ_LENGTH_ENTRY(allow_deny, null), /* CMDDENYALL */ return offsetof(CMD_Request, data.settime.EOR);
REQ_LENGTH_ENTRY(ac_check, null), /* ACCHECK */ case REQ_LOCAL :
REQ_LENGTH_ENTRY(ac_check, null), /* CMDACCHECK */ return offsetof(CMD_Request, data.local.EOR);
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER */ case REQ_MANUAL :
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER */ return offsetof(CMD_Request, data.manual.EOR);
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */ case REQ_N_SOURCES :
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */ return offsetof(CMD_Request, data.n_sources.EOR);
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */ case REQ_SOURCE_DATA :
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET */ return offsetof(CMD_Request, data.source_data.EOR);
REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */ case REQ_REKEY :
REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */ return offsetof(CMD_Request, data.rekey.EOR);
REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */ case REQ_ALLOW :
REQ_LENGTH_ENTRY(null, null), /* TRIMRTC */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(null, null), /* CYCLELOGS */ case REQ_ALLOWALL :
{ 0, 0 }, /* SUBNETS_ACCESSED - not supported */ return offsetof(CMD_Request, data.allow_deny.EOR);
{ 0, 0 }, /* CLIENT_ACCESSES - not supported */ case REQ_DENY :
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX - not supported */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(null, manual_list), /* MANUAL_LIST */ case REQ_DENYALL :
REQ_LENGTH_ENTRY(manual_delete, null), /* MANUAL_DELETE */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(null, null), /* MAKESTEP */ case REQ_CMDALLOW :
REQ_LENGTH_ENTRY(null, activity), /* ACTIVITY */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(modify_minstratum, null), /* MODIFY_MINSTRATUM */ case REQ_CMDALLOWALL :
REQ_LENGTH_ENTRY(modify_polltarget, null), /* MODIFY_POLLTARGET */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(modify_maxdelaydevratio, null), /* MODIFY_MAXDELAYDEVRATIO */ case REQ_CMDDENY :
REQ_LENGTH_ENTRY(null, null), /* RESELECT */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(reselect_distance, null), /* RESELECTDISTANCE */ case REQ_CMDDENYALL :
REQ_LENGTH_ENTRY(modify_makestep, null), /* MODIFY_MAKESTEP */ return offsetof(CMD_Request, data.allow_deny.EOR);
REQ_LENGTH_ENTRY(null, smoothing), /* SMOOTHING */ case REQ_ACCHECK :
REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */ return offsetof(CMD_Request, data.ac_check.EOR);
REQ_LENGTH_ENTRY(null, null), /* REFRESH */ case REQ_CMDACCHECK :
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */ return offsetof(CMD_Request, data.ac_check.EOR);
REQ_LENGTH_ENTRY(client_accesses_by_index, case REQ_ADD_SERVER :
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ return offsetof(CMD_Request, data.ntp_source.EOR);
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */ case REQ_ADD_PEER :
}; return offsetof(CMD_Request, data.ntp_source.EOR);
case REQ_DEL_SOURCE :
return offsetof(CMD_Request, data.del_source.EOR);
case REQ_WRITERTC :
return offsetof(CMD_Request, data.writertc.EOR);
case REQ_DFREQ :
return offsetof(CMD_Request, data.dfreq.EOR);
case REQ_DOFFSET :
return offsetof(CMD_Request, data.doffset.EOR);
case REQ_TRACKING :
return offsetof(CMD_Request, data.tracking.EOR);
case REQ_SOURCESTATS :
return offsetof(CMD_Request, data.sourcestats.EOR);
case REQ_RTCREPORT :
return offsetof(CMD_Request, data.rtcreport.EOR);
case REQ_TRIMRTC :
return offsetof(CMD_Request, data.trimrtc.EOR);
case REQ_CYCLELOGS :
return offsetof(CMD_Request, data.cyclelogs.EOR);
case REQ_SUBNETS_ACCESSED :
case REQ_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case REQ_CLIENT_ACCESSES_BY_INDEX:
return offsetof(CMD_Request, data.client_accesses_by_index.EOR);
case REQ_MANUAL_LIST:
return offsetof(CMD_Request, data.manual_list.EOR);
case REQ_MANUAL_DELETE:
return offsetof(CMD_Request, data.manual_delete.EOR);
case REQ_MAKESTEP:
return offsetof(CMD_Request, data.make_step.EOR);
case REQ_ACTIVITY:
return offsetof(CMD_Request, data.activity.EOR);
case REQ_RESELECT:
return offsetof(CMD_Request, data.reselect.EOR);
case REQ_RESELECTDISTANCE:
return offsetof(CMD_Request, data.reselect_distance.EOR);
case REQ_MODIFY_MINSTRATUM:
return offsetof(CMD_Request, data.modify_minstratum.EOR);
case REQ_MODIFY_POLLTARGET:
return offsetof(CMD_Request, data.modify_polltarget.EOR);
default:
/* If we fall through the switch, it most likely means we've forgotten to implement a new case */
assert(0);
}
}
/* Catch-all case */
return 0;
}
static const uint16_t reply_lengths[] = {
0, /* empty slot */
RPY_LENGTH_ENTRY(null), /* NULL */
RPY_LENGTH_ENTRY(n_sources), /* N_SOURCES */
RPY_LENGTH_ENTRY(source_data), /* SOURCE_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP */
RPY_LENGTH_ENTRY(tracking), /* TRACKING */
RPY_LENGTH_ENTRY(sourcestats), /* SOURCESTATS */
RPY_LENGTH_ENTRY(rtc), /* RTC */
0, /* SUBNETS_ACCESSED - not supported */
0, /* CLIENT_ACCESSES - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX - not supported */
0, /* MANUAL_LIST - variable length */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
};
/* ================================================== */ /* ================================================== */
int int
PKL_CommandLength(CMD_Request *r) PKL_CommandLength(CMD_Request *r)
{ {
uint32_t type;
int command_length; int command_length;
assert(sizeof (request_lengths) / sizeof (request_lengths[0]) == N_REQUEST_TYPES); command_length = command_unpadded_length(r);
type = ntohs(r->command);
if (type >= N_REQUEST_TYPES)
return 0;
command_length = request_lengths[type].command;
if (!command_length) if (!command_length)
return 0; return 0;
@@ -157,20 +173,131 @@ PKL_CommandLength(CMD_Request *r)
/* ================================================== */ /* ================================================== */
#define PADDING_LENGTH_(request_length, reply_length) \
((request_length) < (reply_length) ? (reply_length) - (request_length) : 0)
#define PADDING_LENGTH(request_data, reply_data) \
PADDING_LENGTH_(offsetof(CMD_Request, request_data), offsetof(CMD_Reply, reply_data))
int int
PKL_CommandPaddingLength(CMD_Request *r) PKL_CommandPaddingLength(CMD_Request *r)
{ {
uint32_t type; int type;
if (r->version < PROTO_VERSION_PADDING) if (r->version < PROTO_VERSION_PADDING)
return 0; return 0;
type = ntohs(r->command); type = ntohs(r->command);
if (type >= N_REQUEST_TYPES) if (type < 0 || type >= N_REQUEST_TYPES)
return 0; return 0;
return request_lengths[ntohs(r->command)].padding; switch (type) {
case REQ_NULL:
return PADDING_LENGTH(data, data.null.EOR);
case REQ_ONLINE:
return PADDING_LENGTH(data.online.EOR, data.null.EOR);
case REQ_OFFLINE:
return PADDING_LENGTH(data.offline.EOR, data.null.EOR);
case REQ_BURST:
return PADDING_LENGTH(data.burst.EOR, data.null.EOR);
case REQ_MODIFY_MINPOLL:
return PADDING_LENGTH(data.modify_minpoll.EOR, data.null.EOR);
case REQ_MODIFY_MAXPOLL:
return PADDING_LENGTH(data.modify_maxpoll.EOR, data.null.EOR);
case REQ_DUMP:
return PADDING_LENGTH(data.dump.EOR, data.null.EOR);
case REQ_MODIFY_MAXDELAY:
return PADDING_LENGTH(data.modify_maxdelay.EOR, data.null.EOR);
case REQ_MODIFY_MAXDELAYRATIO:
return PADDING_LENGTH(data.modify_maxdelayratio.EOR, data.null.EOR);
case REQ_MODIFY_MAXDELAYDEVRATIO:
return PADDING_LENGTH(data.modify_maxdelaydevratio.EOR, data.null.EOR);
case REQ_MODIFY_MAXUPDATESKEW:
return PADDING_LENGTH(data.modify_maxupdateskew.EOR, data.null.EOR);
case REQ_LOGON:
return PADDING_LENGTH(data.logon.EOR, data.null.EOR);
case REQ_SETTIME:
return PADDING_LENGTH(data.settime.EOR, data.manual_timestamp.EOR);
case REQ_LOCAL:
return PADDING_LENGTH(data.local.EOR, data.null.EOR);
case REQ_MANUAL:
return PADDING_LENGTH(data.manual.EOR, data.null.EOR);
case REQ_N_SOURCES:
return PADDING_LENGTH(data.n_sources.EOR, data.n_sources.EOR);
case REQ_SOURCE_DATA:
return PADDING_LENGTH(data.source_data.EOR, data.source_data.EOR);
case REQ_REKEY:
return PADDING_LENGTH(data.rekey.EOR, data.null.EOR);
case REQ_ALLOW:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_ALLOWALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_DENY:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_DENYALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDALLOW:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDALLOWALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDDENY:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDDENYALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_ACCHECK:
return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR);
case REQ_CMDACCHECK:
return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR);
case REQ_ADD_SERVER:
return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR);
case REQ_ADD_PEER:
return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR);
case REQ_DEL_SOURCE:
return PADDING_LENGTH(data.del_source.EOR, data.null.EOR);
case REQ_WRITERTC:
return PADDING_LENGTH(data.writertc.EOR, data.null.EOR);
case REQ_DFREQ:
return PADDING_LENGTH(data.dfreq.EOR, data.null.EOR);
case REQ_DOFFSET:
return PADDING_LENGTH(data.doffset.EOR, data.null.EOR);
case REQ_TRACKING:
return PADDING_LENGTH(data.tracking.EOR, data.tracking.EOR);
case REQ_SOURCESTATS:
return PADDING_LENGTH(data.sourcestats.EOR, data.sourcestats.EOR);
case REQ_RTCREPORT:
return PADDING_LENGTH(data.rtcreport.EOR, data.rtc.EOR);
case REQ_TRIMRTC:
return PADDING_LENGTH(data.trimrtc.EOR, data.null.EOR);
case REQ_CYCLELOGS:
return PADDING_LENGTH(data.cyclelogs.EOR, data.null.EOR);
case REQ_SUBNETS_ACCESSED:
case REQ_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case REQ_CLIENT_ACCESSES_BY_INDEX:
return PADDING_LENGTH(data.client_accesses_by_index.EOR, data.client_accesses_by_index.EOR);
case REQ_MANUAL_LIST:
return PADDING_LENGTH(data.manual_list.EOR, data.manual_list.EOR);
case REQ_MANUAL_DELETE:
return PADDING_LENGTH(data.manual_delete.EOR, data.null.EOR);
case REQ_MAKESTEP:
return PADDING_LENGTH(data.make_step.EOR, data.null.EOR);
case REQ_ACTIVITY:
return PADDING_LENGTH(data.activity.EOR, data.activity.EOR);
case REQ_RESELECT:
return PADDING_LENGTH(data.reselect.EOR, data.null.EOR);
case REQ_RESELECTDISTANCE:
return PADDING_LENGTH(data.reselect_distance.EOR, data.null.EOR);
case REQ_MODIFY_MINSTRATUM:
return PADDING_LENGTH(data.modify_minstratum.EOR, data.null.EOR);
case REQ_MODIFY_POLLTARGET:
return PADDING_LENGTH(data.modify_polltarget.EOR, data.null.EOR);
default:
/* If we fall through the switch, it most likely means we've forgotten to implement a new case */
assert(0);
return 0;
}
} }
/* ================================================== */ /* ================================================== */
@@ -178,32 +305,64 @@ PKL_CommandPaddingLength(CMD_Request *r)
int int
PKL_ReplyLength(CMD_Reply *r) PKL_ReplyLength(CMD_Reply *r)
{ {
uint32_t type; int type;
assert(sizeof (reply_lengths) / sizeof (reply_lengths[0]) == N_REPLY_TYPES);
type = ntohs(r->reply); type = ntohs(r->reply);
/* Note that reply type codes start from 1, not 0 */ /* Note that reply type codes start from 1, not 0 */
if (type < 1 || type >= N_REPLY_TYPES) if (type < 1 || type >= N_REPLY_TYPES) {
return 0; return 0;
} else {
switch (type) {
case RPY_NULL:
return offsetof(CMD_Reply, data.null.EOR);
case RPY_N_SOURCES:
return offsetof(CMD_Reply, data.n_sources.EOR);
case RPY_SOURCE_DATA:
return offsetof(CMD_Reply, data.source_data.EOR);
case RPY_MANUAL_TIMESTAMP:
return offsetof(CMD_Reply, data.manual_timestamp.EOR);
case RPY_TRACKING:
return offsetof(CMD_Reply, data.tracking.EOR);
case RPY_SOURCESTATS:
return offsetof(CMD_Reply, data.sourcestats.EOR);
case RPY_RTC:
return offsetof(CMD_Reply, data.rtc.EOR);
case RPY_SUBNETS_ACCESSED :
case RPY_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case RPY_CLIENT_ACCESSES_BY_INDEX:
{
unsigned long nc = ntohl(r->data.client_accesses_by_index.n_clients);
if (r->status == htons(STT_SUCCESS)) {
if (nc > MAX_CLIENT_ACCESSES)
return 0;
return (offsetof(CMD_Reply, data.client_accesses_by_index.clients) +
nc * sizeof(RPY_ClientAccesses_Client));
} else {
return offsetof(CMD_Reply, data);
}
}
case RPY_MANUAL_LIST:
{
unsigned long ns = ntohl(r->data.manual_list.n_samples);
if (ns > MAX_MANUAL_LIST_SAMPLES)
return 0;
if (r->status == htons(STT_SUCCESS)) {
return (offsetof(CMD_Reply, data.manual_list.samples) +
ns * sizeof(RPY_ManualListSample));
} else {
return offsetof(CMD_Reply, data);
}
}
case RPY_ACTIVITY:
return offsetof(CMD_Reply, data.activity.EOR);
/* Length of MANUAL_LIST depends on number of samples stored in it */ default:
if (type == RPY_MANUAL_LIST) { assert(0);
uint32_t ns; }
if (r->status != htons(STT_SUCCESS))
return offsetof(CMD_Reply, data);
ns = ntohl(r->data.manual_list.n_samples);
if (ns > MAX_MANUAL_LIST_SAMPLES)
return 0;
return offsetof(CMD_Reply, data.manual_list.samples) +
ns * sizeof (RPY_ManualListSample);
} }
return reply_lengths[type]; return 0;
} }
/* ================================================== */ /* ================================================== */

689
privops.c
View File

@@ -1,689 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Bryan Christianson 2015
* Copyright (C) Miroslav Lichvar 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Perform privileged operations over a unix socket to a privileged fork.
*/
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "nameserv.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
#define OP_ADJUSTTIME 1024
#define OP_ADJUSTTIMEX 1025
#define OP_SETTIME 1026
#define OP_BINDSOCKET 1027
#define OP_NAME2IPADDRESS 1028
#define OP_QUIT 1099
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
/* daemon request structs */
typedef struct {
struct timeval tv;
} ReqAdjustTime;
#ifdef PRIVOPS_ADJUSTTIMEX
typedef struct {
struct timex tmx;
} ReqAdjustTimex;
#endif
typedef struct {
struct timeval tv;
} ReqSetTime;
typedef struct {
int sock;
socklen_t sa_len;
union sockaddr_in46 sa;
} ReqBindSocket;
typedef struct {
char name[256];
} ReqName2IPAddress;
typedef struct {
int op;
union {
ReqAdjustTime adjust_time;
#ifdef PRIVOPS_ADJUSTTIMEX
ReqAdjustTimex adjust_timex;
#endif
ReqSetTime set_time;
ReqBindSocket bind_socket;
#ifdef PRIVOPS_NAME2IPADDRESS
ReqName2IPAddress name_to_ipaddress;
#endif
} data;
} PrvRequest;
/* helper response structs */
typedef struct {
struct timeval tv;
} ResAdjustTime;
#ifdef PRIVOPS_ADJUSTTIMEX
typedef struct {
struct timex tmx;
} ResAdjustTimex;
#endif
typedef struct {
IPAddr addresses[DNS_MAX_ADDRESSES];
} ResName2IPAddress;
typedef struct {
char msg[256];
} ResFatalMsg;
typedef struct {
int fatal_error;
int rc;
int res_errno;
union {
ResFatalMsg fatal_msg;
ResAdjustTime adjust_time;
#ifdef PRIVOPS_ADJUSTTIMEX
ResAdjustTimex adjust_timex;
#endif
#ifdef PRIVOPS_NAME2IPADDRESS
ResName2IPAddress name_to_ipaddress;
#endif
} data;
} PrvResponse;
static int helper_fd;
static pid_t helper_pid;
static int
have_helper(void)
{
return helper_fd >= 0;
}
/* ======================================================================= */
/* HELPER - prepare fatal error for daemon */
static void
res_fatal(PrvResponse *res, const char *fmt, ...)
{
va_list ap;
res->fatal_error = 1;
va_start(ap, fmt);
vsnprintf(res->data.fatal_msg.msg, sizeof (res->data.fatal_msg.msg), fmt, ap);
va_end(ap);
}
/* ======================================================================= */
/* HELPER - send response to the fd */
static int
send_response(int fd, const PrvResponse *res)
{
if (send(fd, res, sizeof (*res), 0) != sizeof (*res))
return 0;
return 1;
}
/* ======================================================================= */
/* receive daemon request plus optional file descriptor over a unix socket */
static int
receive_from_daemon(int fd, PrvRequest *req)
{
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
char cmsgbuf[256];
iov.iov_base = req;
iov.iov_len = sizeof (*req);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (void *)cmsgbuf;
msg.msg_controllen = sizeof (cmsgbuf);
msg.msg_flags = MSG_WAITALL;
/* read the data */
if (recvmsg(fd, &msg, 0) != sizeof (*req))
return 0;
if (req->op == OP_BINDSOCKET) {
/* extract transferred descriptor */
req->data.bind_socket.sock = -1;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
memcpy(&req->data.bind_socket.sock, CMSG_DATA(cmsg), sizeof (int));
}
/* return error if valid descriptor not found */
if (req->data.bind_socket.sock < 0)
return 0;
}
return 1;
}
/* ======================================================================= */
/* HELPER - perform adjtime() */
#ifdef PRIVOPS_ADJUSTTIME
static void
do_adjust_time(const ReqAdjustTime *req, PrvResponse *res)
{
res->rc = adjtime(&req->tv, &res->data.adjust_time.tv);
if (res->rc)
res->res_errno = errno;
}
#endif
/* ======================================================================= */
/* HELPER - perform ntp_adjtime() */
#ifdef PRIVOPS_ADJUSTTIMEX
static void
do_adjust_timex(const ReqAdjustTimex *req, PrvResponse *res)
{
res->data.adjust_timex.tmx = req->tmx;
res->rc = ntp_adjtime(&res->data.adjust_timex.tmx);
if (res->rc < 0)
res->res_errno = errno;
}
#endif
/* ======================================================================= */
/* HELPER - perform settimeofday() */
#ifdef PRIVOPS_SETTIME
static void
do_set_time(const ReqSetTime *req, PrvResponse *res)
{
res->rc = settimeofday(&req->tv, NULL);
if (res->rc)
res->res_errno = errno;
}
#endif
/* ======================================================================= */
/* HELPER - perform bind() */
#ifdef PRIVOPS_BINDSOCKET
static void
do_bind_socket(ReqBindSocket *req, PrvResponse *res)
{
unsigned short port;
IPAddr ip;
int sock_fd;
struct sockaddr *sa;
socklen_t sa_len;
sa = &req->sa.u;
sa_len = req->sa_len;
sock_fd = req->sock;
UTI_SockaddrToIPAndPort(sa, &ip, &port);
if (port && port != CNF_GetNTPPort()) {
close(sock_fd);
res_fatal(res, "Invalid port %d", port);
return;
}
res->rc = bind(sock_fd, sa, sa_len);
if (res->rc)
res->res_errno = errno;
/* sock is still open on daemon side, but we're done with it in the helper */
close(sock_fd);
}
#endif
/* ======================================================================= */
/* HELPER - perform DNS_Name2IPAddress() */
#ifdef PRIVOPS_NAME2IPADDRESS
static void
do_name_to_ipaddress(ReqName2IPAddress *req, PrvResponse *res)
{
/* make sure the string is terminated */
req->name[sizeof (req->name) - 1] = '\0';
DNS_Reload();
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
DNS_MAX_ADDRESSES);
}
#endif
/* ======================================================================= */
/* HELPER - main loop - action requests from the daemon */
static void
helper_main(int fd)
{
PrvRequest req;
PrvResponse res;
int quit = 0;
while (!quit) {
if (!receive_from_daemon(fd, &req))
/* read error or closed input - we cannot recover - give up */
break;
memset(&res, 0, sizeof (res));
switch (req.op) {
#ifdef PRIVOPS_ADJUSTTIME
case OP_ADJUSTTIME:
do_adjust_time(&req.data.adjust_time, &res);
break;
#endif
#ifdef PRIVOPS_ADJUSTTIMEX
case OP_ADJUSTTIMEX:
do_adjust_timex(&req.data.adjust_timex, &res);
break;
#endif
#ifdef PRIVOPS_SETTIME
case OP_SETTIME:
do_set_time(&req.data.set_time, &res);
break;
#endif
#ifdef PRIVOPS_BINDSOCKET
case OP_BINDSOCKET:
do_bind_socket(&req.data.bind_socket, &res);
break;
#endif
#ifdef PRIVOPS_NAME2IPADDRESS
case OP_NAME2IPADDRESS:
do_name_to_ipaddress(&req.data.name_to_ipaddress, &res);
break;
#endif
case OP_QUIT:
quit = 1;
continue;
default:
res_fatal(&res, "Unexpected operator %d", req.op);
break;
}
send_response(fd, &res);
}
close(fd);
exit(0);
}
/* ======================================================================= */
/* DAEMON - receive helper response */
static void
receive_response(PrvResponse *res)
{
int resp_len;
resp_len = recv(helper_fd, res, sizeof (*res), 0);
if (resp_len < 0)
LOG_FATAL(LOGF_PrivOps, "Could not read from helper : %s", strerror(errno));
if (resp_len != sizeof (*res))
LOG_FATAL(LOGF_PrivOps, "Invalid helper response");
if (res->fatal_error)
LOG_FATAL(LOGF_PrivOps, "Error in helper : %s", res->data.fatal_msg.msg);
DEBUG_LOG(LOGF_PrivOps, "Received response rc=%d", res->rc);
/* if operation failed in the helper, set errno so daemon can print log message */
if (res->res_errno)
errno = res->res_errno;
}
/* ======================================================================= */
/* DAEMON - send daemon request to the helper */
static void
send_request(PrvRequest *req)
{
struct msghdr msg;
struct iovec iov;
char cmsgbuf[256];
iov.iov_base = req;
iov.iov_len = sizeof (*req);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
if (req->op == OP_BINDSOCKET) {
/* send file descriptor as a control message */
struct cmsghdr *cmsg;
int *ptr_send_fd;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_SPACE(sizeof (int));
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof (int)));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof (int));
ptr_send_fd = (int *)CMSG_DATA(cmsg);
*ptr_send_fd = req->data.bind_socket.sock;
}
if (sendmsg(helper_fd, &msg, 0) < 0) {
/* don't try to send another request from exit() */
helper_fd = -1;
LOG_FATAL(LOGF_PrivOps, "Could not send to helper : %s", strerror(errno));
}
DEBUG_LOG(LOGF_PrivOps, "Sent request op=%d", req->op);
}
/* ======================================================================= */
/* DAEMON - send daemon request and wait for response */
static void
submit_request(PrvRequest *req, PrvResponse *res)
{
send_request(req);
receive_response(res);
}
/* ======================================================================= */
/* DAEMON - send the helper a request to exit and wait until it exits */
static void
stop_helper(void)
{
PrvRequest req;
int status;
if (!have_helper())
return;
memset(&req, 0, sizeof (req));
req.op = OP_QUIT;
send_request(&req);
waitpid(helper_pid, &status, 0);
}
/* ======================================================================= */
/* DAEMON - request adjtime() */
#ifdef PRIVOPS_ADJUSTTIME
int
PRV_AdjustTime(const struct timeval *delta, struct timeval *olddelta)
{
PrvRequest req;
PrvResponse res;
if (!have_helper() || delta == NULL)
/* helper is not running or read adjustment call */
return adjtime(delta, olddelta);
memset(&req, 0, sizeof (req));
req.op = OP_ADJUSTTIME;
req.data.adjust_time.tv = *delta;
submit_request(&req, &res);
if (olddelta)
*olddelta = res.data.adjust_time.tv;
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request ntp_adjtime() */
#ifdef PRIVOPS_ADJUSTTIMEX
int
PRV_AdjustTimex(struct timex *tmx)
{
PrvRequest req;
PrvResponse res;
if (!have_helper())
return ntp_adjtime(tmx);
memset(&req, 0, sizeof (req));
req.op = OP_ADJUSTTIMEX;
req.data.adjust_timex.tmx = *tmx;
submit_request(&req, &res);
*tmx = res.data.adjust_timex.tmx;
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request settimeofday() */
#ifdef PRIVOPS_SETTIME
int
PRV_SetTime(const struct timeval *tp, const struct timezone *tzp)
{
PrvRequest req;
PrvResponse res;
/* only support setting the time */
assert(tp != NULL);
assert(tzp == NULL);
if (!have_helper())
return settimeofday(tp, NULL);
memset(&req, 0, sizeof (req));
req.op = OP_SETTIME;
req.data.set_time.tv = *tp;
submit_request(&req, &res);
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request bind() */
#ifdef PRIVOPS_BINDSOCKET
int
PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
{
PrvRequest req;
PrvResponse res;
IPAddr ip;
unsigned short port;
UTI_SockaddrToIPAndPort(address, &ip, &port);
assert(!port || port == CNF_GetNTPPort());
if (!have_helper())
return bind(sock, address, address_len);
memset(&req, 0, sizeof (req));
req.op = OP_BINDSOCKET;
req.data.bind_socket.sock = sock;
req.data.bind_socket.sa_len = address_len;
memcpy(&req.data.bind_socket.sa.u, address, address_len);
submit_request(&req, &res);
return res.rc;
}
#endif
/* ======================================================================= */
/* DAEMON - request DNS_Name2IPAddress() */
#ifdef PRIVOPS_NAME2IPADDRESS
int
PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{
PrvRequest req;
PrvResponse res;
int i;
if (!have_helper())
return DNS_Name2IPAddress(name, ip_addrs, max_addrs);
memset(&req, 0, sizeof (req));
req.op = OP_NAME2IPADDRESS;
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
"%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
DEBUG_LOG(LOGF_PrivOps, "Name too long");
return DNS_Failure;
}
submit_request(&req, &res);
for (i = 0; i < max_addrs && i < DNS_MAX_ADDRESSES; i++)
ip_addrs[i] = res.data.name_to_ipaddress.addresses[i];
return res.rc;
}
#endif
/* ======================================================================= */
void
PRV_Initialise(void)
{
helper_fd = -1;
}
/* ======================================================================= */
/* DAEMON - setup socket(s) then fork to run the helper */
/* must be called before privileges are dropped */
void
PRV_StartHelper(void)
{
pid_t pid;
int fd, sock_pair[2];
if (have_helper())
LOG_FATAL(LOGF_PrivOps, "Helper already running");
if (
#ifdef SOCK_SEQPACKET
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
#endif
socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair))
LOG_FATAL(LOGF_PrivOps, "socketpair() failed : %s", strerror(errno));
UTI_FdSetCloexec(sock_pair[0]);
UTI_FdSetCloexec(sock_pair[1]);
pid = fork();
if (pid < 0)
LOG_FATAL(LOGF_PrivOps, "fork() failed : %s", strerror(errno));
if (pid == 0) {
/* child process */
close(sock_pair[0]);
/* close other descriptors inherited from the parent process */
for (fd = 0; fd < 1024; fd++) {
if (fd != sock_pair[1])
close(fd);
}
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN);
helper_main(sock_pair[1]);
} else {
/* parent process */
close(sock_pair[1]);
helper_fd = sock_pair[0];
helper_pid = pid;
/* stop the helper even when not exiting cleanly from the main function */
atexit(stop_helper);
}
}
/* ======================================================================= */
/* DAEMON - graceful shutdown of the helper */
void
PRV_Finalise(void)
{
if (!have_helper())
return;
stop_helper();
close(helper_fd);
helper_fd = -1;
}

View File

@@ -1,71 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Bryan Christianson 2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Perform privileged operations over a unix socket to a privileged fork.
*/
#ifndef GOT_PRIVOPS_H
#define GOT_PRIVOPS_H
#ifdef PRIVOPS_ADJUSTTIME
int PRV_AdjustTime(const struct timeval *delta, struct timeval *olddelta);
#else
#define PRV_AdjustTime adjtime
#endif
#ifdef PRIVOPS_ADJUSTTIMEX
int PRV_AdjustTimex(struct timex *txc);
#else
#define PRV_AdjustTimex ntp_adjtime
#endif
#ifdef PRIVOPS_SETTIME
int PRV_SetTime(const struct timeval *tp, const struct timezone *tzp);
#else
#define PRV_SetTime settimeofday
#endif
#ifdef PRIVOPS_BINDSOCKET
int PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len);
#else
#define PRV_BindSocket bind
#endif
#ifdef PRIVOPS_NAME2IPADDRESS
int PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs);
#else
#define PRV_Name2IPAddress DNS_Name2IPAddress
#endif
#ifdef PRIVOPS_HELPER
void PRV_Initialise(void);
void PRV_StartHelper(void);
void PRV_Finalise(void);
#else
#define PRV_Initialise()
#define PRV_StartHelper()
#define PRV_Finalise()
#endif
#endif

View File

@@ -27,7 +27,6 @@
#include "config.h" #include "config.h"
#include "array.h"
#include "refclock.h" #include "refclock.h"
#include "reference.h" #include "reference.h"
#include "conf.h" #include "conf.h"
@@ -87,8 +86,10 @@ struct RCL_Instance_Record {
SRC_Instance source; SRC_Instance source;
}; };
/* Array of pointers to RCL_Instance_Record */ #define MAX_RCL_SOURCES 8
static ARR_Instance refclocks;
static struct RCL_Instance_Record refclocks[MAX_RCL_SOURCES];
static int n_sources = 0;
static LOG_FileID logfileid; static LOG_FileID logfileid;
@@ -111,20 +112,12 @@ static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset); static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion); static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
static RCL_Instance
get_refclock(unsigned int index)
{
return *(RCL_Instance *)ARR_GetElement(refclocks, index);
}
void void
RCL_Initialise(void) RCL_Initialise(void)
{ {
refclocks = ARR_CreateInstance(sizeof (RCL_Instance));
CNF_AddRefclocks(); CNF_AddRefclocks();
if (ARR_GetSize(refclocks) > 0) { if (n_sources > 0) {
LCL_AddParameterChangeHandler(slew_samples, NULL); LCL_AddParameterChangeHandler(slew_samples, NULL);
LCL_AddDispersionNotifyHandler(add_dispersion, NULL); LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
} }
@@ -137,36 +130,33 @@ RCL_Initialise(void)
void void
RCL_Finalise(void) RCL_Finalise(void)
{ {
unsigned int i; int i;
for (i = 0; i < ARR_GetSize(refclocks); i++) { for (i = 0; i < n_sources; i++) {
RCL_Instance inst = get_refclock(i); RCL_Instance inst = (RCL_Instance)&refclocks[i];
if (inst->driver->fini) if (inst->driver->fini)
inst->driver->fini(inst); inst->driver->fini(inst);
filter_fini(&inst->filter); filter_fini(&inst->filter);
Free(inst->driver_parameter); Free(inst->driver_parameter);
SRC_DestroyInstance(inst->source);
Free(inst);
} }
if (ARR_GetSize(refclocks) > 0) { if (n_sources > 0) {
LCL_RemoveParameterChangeHandler(slew_samples, NULL); LCL_RemoveParameterChangeHandler(slew_samples, NULL);
LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL); LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL);
} }
ARR_DestroyInstance(refclocks);
} }
int int
RCL_AddRefclock(RefclockParameters *params) RCL_AddRefclock(RefclockParameters *params)
{ {
int pps_source = 0; int pps_source = 0;
RCL_Instance inst;
inst = MallocNew(struct RCL_Instance_Record); RCL_Instance inst = &refclocks[n_sources];
*(RCL_Instance *)ARR_GetNewElement(refclocks) = inst;
if (n_sources == MAX_RCL_SOURCES)
return 0;
if (strcmp(params->driver_name, "SHM") == 0) { if (strcmp(params->driver_name, "SHM") == 0) {
inst->driver = &RCL_SHM_driver; inst->driver = &RCL_SHM_driver;
@@ -229,14 +219,9 @@ RCL_AddRefclock(RefclockParameters *params)
inst->ref_id = params->ref_id; inst->ref_id = params->ref_id;
else { else {
unsigned char ref[5] = { 0, 0, 0, 0, 0 }; unsigned char ref[5] = { 0, 0, 0, 0, 0 };
unsigned int index = ARR_GetSize(refclocks) - 1;
snprintf((char *)ref, sizeof (ref), "%3.3s", params->driver_name); snprintf((char *)ref, 5, "%3.3s%d", params->driver_name, n_sources % 10);
ref[3] = index % 10 + '0'; inst->ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
if (index >= 10)
ref[2] = (index / 10) % 10 + '0';
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} }
if (inst->driver->poll) { if (inst->driver->poll) {
@@ -263,12 +248,11 @@ RCL_AddRefclock(RefclockParameters *params)
filter_init(&inst->filter, params->filter_length, params->max_dispersion); filter_init(&inst->filter, params->filter_length, params->max_dispersion);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL, inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_option, NULL);
params->min_samples, params->max_samples);
DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d", DEBUG_LOG(LOGF_Refclock, "refclock %s added poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id), params->driver_name, inst->poll, inst->driver_poll, params->filter_length);
inst->poll, inst->driver_poll, params->filter_length); n_sources++;
Free(params->driver_name); Free(params->driver_name);
@@ -278,21 +262,20 @@ RCL_AddRefclock(RefclockParameters *params)
void void
RCL_StartRefclocks(void) RCL_StartRefclocks(void)
{ {
unsigned int i, j, n; int i, j;
n = ARR_GetSize(refclocks); for (i = 0; i < n_sources; i++) {
RCL_Instance inst = &refclocks[i];
for (i = 0; i < n; i++) {
RCL_Instance inst = get_refclock(i);
SRC_SetSelectable(inst->source);
SRC_SetActive(inst->source); SRC_SetActive(inst->source);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst); inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
if (inst->lock_ref) { if (inst->lock_ref) {
/* Replace lock refid with index to refclocks */ /* Replace lock refid with index to refclocks */
for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++) for (j = 0; j < n_sources && refclocks[j].ref_id != inst->lock_ref; j++)
; ;
inst->lock_ref = j < n ? j : -1; inst->lock_ref = (j < n_sources) ? j : -1;
} else } else
inst->lock_ref = -1; inst->lock_ref = -1;
} }
@@ -301,14 +284,14 @@ RCL_StartRefclocks(void)
void void
RCL_ReportSource(RPT_SourceReport *report, struct timeval *now) RCL_ReportSource(RPT_SourceReport *report, struct timeval *now)
{ {
unsigned int i; int i;
uint32_t ref_id; uint32_t ref_id;
assert(report->ip_addr.family == IPADDR_INET4); assert(report->ip_addr.family == IPADDR_INET4);
ref_id = report->ip_addr.addr.in4; ref_id = report->ip_addr.addr.in4;
for (i = 0; i < ARR_GetSize(refclocks); i++) { for (i = 0; i < n_sources; i++) {
RCL_Instance inst = get_refclock(i); RCL_Instance inst = &refclocks[i];
if (inst->ref_id == ref_id) { if (inst->ref_id == ref_id) {
report->poll = inst->poll; report->poll = inst->poll;
report->mode = RPT_LOCAL_REFERENCE; report->mode = RPT_LOCAL_REFERENCE;
@@ -370,11 +353,11 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time); UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
dispersion += instance->precision; dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */ if (!valid_sample_time(instance, sample_time))
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time))
return 0; return 0;
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
switch (leap) { switch (leap) {
case LEAP_Normal: case LEAP_Normal:
case LEAP_InsertSecond: case LEAP_InsertSecond:
@@ -382,11 +365,10 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
instance->leap_status = leap; instance->leap_status = leap;
break; break;
default: default:
DEBUG_LOG(LOGF_Refclock, "refclock sample ignored bad leap %d", leap); instance->leap_status = LEAP_Unsynchronised;
return 0; break;
} }
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
instance->pps_active = 0; instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion); log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
@@ -411,8 +393,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time); UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
dispersion += instance->precision; dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) || if (!valid_sample_time(instance, pulse_time))
!valid_sample_time(instance, pulse_time))
return 0; return 0;
rate = instance->pps_rate; rate = instance->pps_rate;
@@ -428,19 +409,16 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
offset -= 1.0 / rate; offset -= 1.0 / rate;
if (instance->lock_ref != -1) { if (instance->lock_ref != -1) {
RCL_Instance lock_refclock;
struct timeval ref_sample_time; struct timeval ref_sample_time;
double sample_diff, ref_offset, ref_dispersion, shift; double sample_diff, ref_offset, ref_dispersion, shift;
lock_refclock = get_refclock(instance->lock_ref); if (!filter_get_last_sample(&refclocks[instance->lock_ref].filter,
if (!filter_get_last_sample(&lock_refclock->filter,
&ref_sample_time, &ref_offset, &ref_dispersion)) { &ref_sample_time, &ref_offset, &ref_dispersion)) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample"); DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample");
return 0; return 0;
} }
ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter); ref_dispersion += filter_get_avg_sample_dispersion(&refclocks[instance->lock_ref].filter);
UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time); UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= 2.0 / rate) { if (fabs(sample_diff) >= 2.0 / rate) {
@@ -463,7 +441,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
return 0; return 0;
} }
leap = lock_refclock->leap_status; leap = refclocks[instance->lock_ref].leap_status;
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff); second, offset, ref_offset - offset, sample_diff);
@@ -473,16 +451,15 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
double root_delay, root_dispersion, distance; double root_delay, root_dispersion, distance;
uint32_t ref_id; uint32_t ref_id;
/* Ignore the pulse if we are not well synchronized and the local /* Ignore the pulse if we are not well synchronized */
reference is not active */
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum, REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion); &ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion; distance = fabs(root_delay) / 2 + root_dispersion;
if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) { if (!is_synchronised || distance >= 0.5 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
second, leap != LEAP_Unsynchronised, distance); second, is_synchronised, distance);
/* Drop also all stored samples */ /* Drop also all stored samples */
filter_reset(&instance->filter); filter_reset(&instance->filter);
return 0; return 0;
@@ -502,6 +479,15 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
return 1; return 1;
} }
static double
poll_interval(int poll)
{
if (poll >= 0)
return 1 << poll;
else
return 1.0 / (1 << -poll);
}
static int static int
valid_sample_time(RCL_Instance instance, struct timeval *tv) valid_sample_time(RCL_Instance instance, struct timeval *tv)
{ {
@@ -510,9 +496,9 @@ valid_sample_time(RCL_Instance instance, struct timeval *tv)
LCL_ReadRawTime(&raw_time); LCL_ReadRawTime(&raw_time);
UTI_DiffTimevalsToDouble(&diff, &raw_time, tv); UTI_DiffTimevalsToDouble(&diff, &raw_time, tv);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) { if (diff < 0.0 || diff > poll_interval(instance->poll + 1)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f tv=%s", DEBUG_LOG(LOGF_Refclock, "refclock sample not valid age=%.6f tv=%s",
UTI_RefidToString(instance->ref_id), diff, UTI_TimevalToString(tv)); diff, UTI_TimevalToString(tv));
return 0; return 0;
} }
return 1; return 1;
@@ -522,27 +508,23 @@ static int
pps_stratum(RCL_Instance instance, struct timeval *tv) pps_stratum(RCL_Instance instance, struct timeval *tv)
{ {
struct timeval ref_time; struct timeval ref_time;
int is_synchronised, stratum; int is_synchronised, stratum, i;
unsigned int i;
double root_delay, root_dispersion; double root_delay, root_dispersion;
NTP_Leap leap; NTP_Leap leap;
uint32_t ref_id; uint32_t ref_id;
RCL_Instance refclock;
REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum, REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion); &ref_id, &ref_time, &root_delay, &root_dispersion);
/* Don't change our stratum if the local reference is active /* Don't change our stratum if local stratum is active
or this is the current source */ or this is the current source */
if (ref_id == instance->ref_id || if (ref_id == instance->ref_id || REF_IsLocalActive())
(!is_synchronised && leap != LEAP_Unsynchronised))
return stratum - 1; return stratum - 1;
/* Or the current source is another PPS refclock */ /* Or the current source is another PPS refclock */
for (i = 0; i < ARR_GetSize(refclocks); i++) { for (i = 0; i < n_sources; i++) {
refclock = get_refclock(i); if (refclocks[i].ref_id == ref_id &&
if (refclock->ref_id == ref_id && refclocks[i].pps_active && refclocks[i].lock_ref == -1)
refclock->pps_active && refclock->lock_ref == -1)
return stratum - 1; return stratum - 1;
} }
@@ -590,30 +572,30 @@ poll_timeout(void *arg)
} }
} }
inst->timeout_id = SCH_AddTimeoutByDelay(UTI_Log2ToDouble(poll), poll_timeout, arg); inst->timeout_id = SCH_AddTimeoutByDelay(poll_interval(poll), poll_timeout, arg);
} }
static void static void
slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq, slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything) double doffset, LCL_ChangeType change_type, void *anything)
{ {
unsigned int i; int i;
for (i = 0; i < ARR_GetSize(refclocks); i++) { for (i = 0; i < n_sources; i++) {
if (change_type == LCL_ChangeUnknownStep) if (change_type == LCL_ChangeUnknownStep)
filter_reset(&get_refclock(i)->filter); filter_reset(&refclocks[i].filter);
else else
filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset); filter_slew_samples(&refclocks[i].filter, cooked, dfreq, doffset);
} }
} }
static void static void
add_dispersion(double dispersion, void *anything) add_dispersion(double dispersion, void *anything)
{ {
unsigned int i; int i;
for (i = 0; i < ARR_GetSize(refclocks); i++) for (i = 0; i < n_sources; i++)
filter_add_dispersion(&get_refclock(i)->filter, dispersion); filter_add_dispersion(&refclocks[i].filter, dispersion);
} }
static void static void

View File

@@ -38,15 +38,13 @@ typedef struct {
int poll; int poll;
int filter_length; int filter_length;
int pps_rate; int pps_rate;
int min_samples;
int max_samples;
int sel_options;
uint32_t ref_id; uint32_t ref_id;
uint32_t lock_ref_id; uint32_t lock_ref_id;
double offset; double offset;
double delay; double delay;
double precision; double precision;
double max_dispersion; double max_dispersion;
SRC_SelectOption sel_option;
} RefclockParameters; } RefclockParameters;
typedef struct RCL_Instance_Record *RCL_Instance; typedef struct RCL_Instance_Record *RCL_Instance;

View File

@@ -29,7 +29,7 @@
#include "refclock.h" #include "refclock.h"
#if FEAT_PPS #if HAVE_PPSAPI
#if defined(HAVE_SYS_TIMEPPS_H) #if defined(HAVE_SYS_TIMEPPS_H)
#include <sys/timepps.h> #include <sys/timepps.h>

View File

@@ -37,23 +37,11 @@
#define SOCK_MAGIC 0x534f434b #define SOCK_MAGIC 0x534f434b
struct sock_sample { struct sock_sample {
/* Time of the measurement (system time) */
struct timeval tv; struct timeval tv;
/* Offset between the true time and the system time (in seconds) */
double offset; double offset;
/* Non-zero if the sample is from a PPS signal, i.e. another source
is needed to obtain seconds */
int pulse; int pulse;
/* 0 - normal, 1 - insert leap second, 2 - delete leap second */
int leap; int leap;
/* Padding, ignored */
int _pad; int _pad;
/* Protocol identifier (0x534f434b) */
int magic; int magic;
}; };

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016 * Copyright (C) Miroslav Lichvar 2009-2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -45,14 +45,12 @@
static int are_we_synchronised; static int are_we_synchronised;
static int enable_local_stratum; static int enable_local_stratum;
static int local_stratum; static int local_stratum;
static int local_orphan;
static double local_distance;
static NTP_Leap our_leap_status; static NTP_Leap our_leap_status;
static int our_leap_sec; static int our_leap_sec;
static int our_stratum; static int our_stratum;
static uint32_t our_ref_id; static uint32_t our_ref_id;
static IPAddr our_ref_ip; static IPAddr our_ref_ip;
struct timeval our_ref_time; struct timeval our_ref_time; /* Stored relative to reference, NOT local time */
static double our_skew; static double our_skew;
static double our_residual_freq; static double our_residual_freq;
static double our_root_delay; static double our_root_delay;
@@ -82,7 +80,8 @@ static int max_offset_delay;
static int max_offset_ignore; static int max_offset_ignore;
static double max_offset; static double max_offset;
/* Threshold for logging clock changes to syslog */ /* Flag and threshold for logging clock changes to syslog */
static int do_log_change;
static double log_change_threshold; static double log_change_threshold;
/* Flag, threshold and user for sending mail notification on large clock changes */ /* Flag, threshold and user for sending mail notification on large clock changes */
@@ -99,16 +98,6 @@ static double drift_file_age;
static void update_drift_file(double, double); static void update_drift_file(double, double);
/* Leap second handling mode */
static REF_LeapMode leap_mode;
/* Flag indicating the clock was recently corrected for leap second and it may
not have correct time yet (missing 23:59:60 in the UTC time scale) */
static int leap_in_progress;
/* Timer for the leap second handler */
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */ /* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname; static char *leap_tzname;
static time_t last_tz_leap_check; static time_t last_tz_leap_check;
@@ -120,6 +109,11 @@ static LOG_FileID logfileid;
/* ================================================== */ /* ================================================== */
/* Reference ID supplied when we are locally referenced */
#define LOCAL_REFERENCE_ID 0x7f7f0101UL
/* ================================================== */
/* Exponential moving averages of absolute clock frequencies /* Exponential moving averages of absolute clock frequencies
used as a fallback when synchronisation is lost. */ used as a fallback when synchronisation is lost. */
@@ -142,7 +136,6 @@ static double last_ref_update_interval;
/* ================================================== */ /* ================================================== */
static NTP_Leap get_tz_leap(time_t when); static NTP_Leap get_tz_leap(time_t when);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */ /* ================================================== */
@@ -155,9 +148,6 @@ handle_slew(struct timeval *raw,
void *anything) void *anything)
{ {
double delta; double delta;
struct timeval now;
UTI_AdjustTimeval(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeUnknownStep) {
last_ref_update.tv_sec = 0; last_ref_update.tv_sec = 0;
@@ -165,13 +155,6 @@ handle_slew(struct timeval *raw,
} else if (last_ref_update.tv_sec) { } else if (last_ref_update.tv_sec) {
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset); UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
} }
/* When the clock was stepped, check if that doesn't change our leap status
and also reset the leap timeout to undo the shift in the scheduler */
if (change_type != LCL_ChangeAdjust && our_leap_sec && !leap_in_progress) {
LCL_ReadRawTime(&now);
update_leap_status(our_leap_status, now.tv_sec, 1);
}
} }
/* ================================================== */ /* ================================================== */
@@ -232,20 +215,13 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio(); correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance); enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
leap_timeout_id = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
/* Switch to step mode if the system driver doesn't support leap */
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
leap_mode = REF_LeapModeStep;
leap_tzname = CNF_GetLeapSecTimezone(); leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) { if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */ /* Check that the timezone has good data for Jun 30 2008 and Dec 31 2008 */
if (get_tz_leap(1341014400) == LEAP_InsertSecond && if (get_tz_leap(1214784000) == LEAP_Normal &&
get_tz_leap(1356912000) == LEAP_Normal) { get_tz_leap(1230681600) == LEAP_InsertSecond) {
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname); LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname);
} else { } else {
LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname); LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname);
@@ -255,8 +231,8 @@ REF_Initialise(void)
CNF_GetMakeStep(&make_step_limit, &make_step_threshold); CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset); CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
CNF_GetLogChange(&do_log_change, &log_change_threshold);
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user); CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
log_change_threshold = CNF_GetLogChange();
CNF_GetFallbackDrifts(&fb_drift_min, &fb_drift_max); CNF_GetFallbackDrifts(&fb_drift_min, &fb_drift_max);
@@ -264,7 +240,7 @@ REF_Initialise(void)
fb_drifts = MallocArray(struct fb_drift, fb_drift_max - fb_drift_min + 1); fb_drifts = MallocArray(struct fb_drift, fb_drift_max - fb_drift_min + 1);
memset(fb_drifts, 0, sizeof (struct fb_drift) * (fb_drift_max - fb_drift_min + 1)); memset(fb_drifts, 0, sizeof (struct fb_drift) * (fb_drift_max - fb_drift_min + 1));
next_fb_drift = 0; next_fb_drift = 0;
fb_drift_timeout_id = 0; fb_drift_timeout_id = -1;
} }
last_ref_update.tv_sec = 0; last_ref_update.tv_sec = 0;
@@ -273,6 +249,11 @@ REF_Initialise(void)
LCL_AddParameterChangeHandler(handle_slew, NULL); LCL_AddParameterChangeHandler(handle_slew, NULL);
/* And just to prevent anything wierd ... */
if (do_log_change) {
log_change_threshold = fabs(log_change_threshold);
}
/* Make first entry in tracking log */ /* Make first entry in tracking log */
REF_SetUnsynchronised(); REF_SetUnsynchronised();
} }
@@ -282,7 +263,9 @@ REF_Initialise(void)
void void
REF_Finalise(void) REF_Finalise(void)
{ {
update_leap_status(LEAP_Unsynchronised, 0, 0); if (our_leap_sec) {
LCL_SetLeap(0);
}
if (drift_file) { if (drift_file) {
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew); update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
@@ -318,14 +301,6 @@ REF_SetModeEndHandler(REF_ModeEndHandler handler)
/* ================================================== */ /* ================================================== */
REF_LeapMode
REF_GetLeapMode(void)
{
return leap_mode;
}
/* ================================================== */
static double static double
Sqr(double x) Sqr(double x)
{ {
@@ -423,13 +398,19 @@ update_fb_drifts(double freq_ppm, double update_interval)
next_fb_drift = 0; next_fb_drift = 0;
} }
SCH_RemoveTimeout(fb_drift_timeout_id); if (fb_drift_timeout_id != -1) {
fb_drift_timeout_id = 0; SCH_RemoveTimeout(fb_drift_timeout_id);
fb_drift_timeout_id = -1;
}
if (update_interval < 1.0 || update_interval > last_ref_update_interval * 4.0) if (update_interval < 0.0 || update_interval > last_ref_update_interval * 4.0)
return; return;
for (i = 0; i < fb_drift_max - fb_drift_min + 1; i++) { for (i = 0; i < fb_drift_max - fb_drift_min + 1; i++) {
/* Don't allow differences larger than 10 ppm */
if (fabs(freq_ppm - fb_drifts[i].freq) > 10.0)
fb_drifts[i].secs = 0.0;
secs = 1 << (i + fb_drift_min); secs = 1 << (i + fb_drift_min);
if (fb_drifts[i].secs < secs) { if (fb_drifts[i].secs < secs) {
/* Calculate average over 2 * secs interval before switching to /* Calculate average over 2 * secs interval before switching to
@@ -455,12 +436,11 @@ update_fb_drifts(double freq_ppm, double update_interval)
static void static void
fb_drift_timeout(void *arg) fb_drift_timeout(void *arg)
{ {
assert(are_we_synchronised == 0);
assert(next_fb_drift >= fb_drift_min && next_fb_drift <= fb_drift_max); assert(next_fb_drift >= fb_drift_min && next_fb_drift <= fb_drift_max);
fb_drift_timeout_id = 0; fb_drift_timeout_id = -1;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm",
next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq); LCL_SetAbsoluteFrequency(fb_drifts[next_fb_drift - fb_drift_min].freq);
REF_SetUnsynchronised(); REF_SetUnsynchronised();
} }
@@ -474,7 +454,7 @@ schedule_fb_drift(struct timeval *now)
double unsynchronised; double unsynchronised;
struct timeval when; struct timeval when;
if (fb_drift_timeout_id) if (fb_drift_timeout_id != -1)
return; /* already scheduled */ return; /* already scheduled */
UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update); UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update);
@@ -532,7 +512,8 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset); abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) { if (do_log_change &&
(abs_offset > log_change_threshold)) {
LOG(LOGS_WARN, LOGF_Reference, LOG(LOGS_WARN, LOGF_Reference,
"System clock wrong by %.6f seconds, adjustment started", "System clock wrong by %.6f seconds, adjustment started",
-offset); -offset);
@@ -676,86 +657,7 @@ get_tz_leap(time_t when)
/* ================================================== */ /* ================================================== */
static void static void
leap_end_timeout(void *arg) update_leap_status(NTP_Leap leap, time_t now)
{
leap_timeout_id = 0;
leap_in_progress = 0;
our_leap_sec = 0;
if (leap_mode == REF_LeapModeSystem)
LCL_SetSystemLeap(0);
if (our_leap_status == LEAP_InsertSecond ||
our_leap_status == LEAP_DeleteSecond)
our_leap_status = LEAP_Normal;
}
/* ================================================== */
static void
leap_start_timeout(void *arg)
{
leap_in_progress = 1;
switch (leap_mode) {
case REF_LeapModeSystem:
DEBUG_LOG(LOGF_Reference, "Waiting for system clock leap second correction");
break;
case REF_LeapModeSlew:
LCL_NotifyLeap(our_leap_sec);
LCL_AccumulateOffset(our_leap_sec, 0.0);
LOG(LOGS_WARN, LOGF_Reference, "Adjusting system clock for leap second");
break;
case REF_LeapModeStep:
LCL_NotifyLeap(our_leap_sec);
LCL_ApplyStepOffset(our_leap_sec);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped for leap second");
break;
case REF_LeapModeIgnore:
LOG(LOGS_WARN, LOGF_Reference, "Ignoring leap second");
break;
default:
break;
}
/* Wait until the leap second is over with some extra room to be safe */
leap_timeout_id = SCH_AddTimeoutByDelay(2.0, leap_end_timeout, NULL);
}
/* ================================================== */
static void
set_leap_timeout(time_t now)
{
struct timeval when;
/* Stop old timer if there is one */
SCH_RemoveTimeout(leap_timeout_id);
leap_timeout_id = 0;
leap_in_progress = 0;
if (!our_leap_sec)
return;
/* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_usec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
if (leap_mode == REF_LeapModeSystem) {
when.tv_sec--;
when.tv_usec = 500000;
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
}
/* ================================================== */
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{ {
int leap_sec; int leap_sec;
@@ -779,23 +681,8 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
} }
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) { if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
LCL_SetLeap(leap_sec);
our_leap_sec = leap_sec; our_leap_sec = leap_sec;
switch (leap_mode) {
case REF_LeapModeSystem:
LCL_SetSystemLeap(our_leap_sec);
/* Fall through */
case REF_LeapModeSlew:
case REF_LeapModeStep:
case REF_LeapModeIgnore:
set_leap_timeout(now);
break;
default:
assert(0);
break;
}
} else if (reset) {
set_leap_timeout(now);
} }
our_leap_status = leap; our_leap_status = leap;
@@ -1033,16 +920,14 @@ REF_SetReference(int stratum,
our_residual_freq = frequency; our_residual_freq = frequency;
} }
update_leap_status(leap, raw_now.tv_sec, 0); update_leap_status(leap, raw_now.tv_sec);
maybe_log_offset(our_offset, raw_now.tv_sec); maybe_log_offset(our_offset, raw_now.tv_sec);
if (step_offset != 0.0) { if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset)) LCL_ApplyStepOffset(step_offset);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset); LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
} }
LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
abs_freq_ppm = LCL_ReadAbsoluteFrequency(); abs_freq_ppm = LCL_ReadAbsoluteFrequency();
write_log(&now, write_log(&now,
@@ -1068,7 +953,6 @@ REF_SetReference(int stratum,
/* Update fallback drifts */ /* Update fallback drifts */
if (fb_drifts) { if (fb_drifts) {
update_fb_drifts(abs_freq_ppm, update_interval); update_fb_drifts(abs_freq_ppm, update_interval);
schedule_fb_drift(&now);
} }
last_ref_update_interval = update_interval; last_ref_update_interval = update_interval;
@@ -1095,10 +979,12 @@ REF_SetManualReference
double skew double skew
) )
{ {
uint32_t manual_refid = 0x4D414E55; /* MANU */
/* We are not synchronised to an external source, as such. This is /* We are not synchronised to an external source, as such. This is
only supposed to be used with the local source option, really. only supposed to be used with the local source option, really
Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */ ... */
REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL, REF_SetReference(0, LEAP_Unsynchronised, 1, manual_refid, NULL,
ref_time, offset, 0.0, frequency, skew, 0.0, 0.0); ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
} }
@@ -1127,11 +1013,9 @@ REF_SetUnsynchronised(void)
schedule_fb_drift(&now); schedule_fb_drift(&now);
} }
update_leap_status(LEAP_Unsynchronised, 0, 0); update_leap_status(LEAP_Unsynchronised, 0);
are_we_synchronised = 0; are_we_synchronised = 0;
LCL_SetSyncStatus(0, 0.0, 0.0);
write_log(&now, write_log(&now,
"0.0.0.0", "0.0.0.0",
0, 0,
@@ -1159,43 +1043,35 @@ REF_GetReferenceParams
double *root_dispersion double *root_dispersion
) )
{ {
double elapsed, dispersion; double elapsed;
double extra_dispersion;
assert(initialised); assert(initialised);
if (are_we_synchronised) { if (are_we_synchronised) {
UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
dispersion = our_root_dispersion +
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
} else {
dispersion = 0.0;
}
/* Local reference is active when enabled and the clock is not synchronised
or the root distance exceeds the threshold */
if (are_we_synchronised &&
!(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
*is_synchronised = 1; *is_synchronised = 1;
*stratum = our_stratum; *stratum = our_stratum;
*leap_status = !leap_in_progress ? our_leap_status : LEAP_Unsynchronised; UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
extra_dispersion = (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
*leap_status = our_leap_status;
*ref_id = our_ref_id; *ref_id = our_ref_id;
*ref_time = our_ref_time; *ref_time = our_ref_time;
*root_delay = our_root_delay; *root_delay = our_root_delay;
*root_dispersion = dispersion; *root_dispersion = our_root_dispersion + extra_dispersion;
} else if (enable_local_stratum) { } else if (enable_local_stratum) {
*is_synchronised = 0; *is_synchronised = 1;
*stratum = local_stratum; *stratum = local_stratum;
*ref_id = NTP_REFID_LOCAL; *ref_id = LOCAL_REFERENCE_ID;
/* Make the reference time be now less a second - this will /* Make the reference time be now less a second - this will
scarcely affect the client, but will ensure that the transmit scarcely affect the client, but will ensure that the transmit
timestamp cannot come before this (which would cause test 7 to timestamp cannot come before this (which would cause test 6 to
fail in the client's read routine) if the local system clock's fail in the client's read routine) if the local system clock's
read routine is broken in any way. */ read routine is broken in any way. */
*ref_time = *local_time; *ref_time = *local_time;
@@ -1213,8 +1089,8 @@ REF_GetReferenceParams
*is_synchronised = 0; *is_synchronised = 0;
*leap_status = LEAP_Unsynchronised; *leap_status = LEAP_Unsynchronised;
*stratum = NTP_MAX_STRATUM; *stratum = 0;
*ref_id = NTP_REFID_UNSYNC; *ref_id = 0;
ref_time->tv_sec = ref_time->tv_usec = 0; ref_time->tv_sec = ref_time->tv_usec = 0;
/* These values seem to be standard for a client, and /* These values seem to be standard for a client, and
any peer or client of ours will ignore them anyway because any peer or client of ours will ignore them anyway because
@@ -1230,35 +1106,13 @@ REF_GetReferenceParams
int int
REF_GetOurStratum(void) REF_GetOurStratum(void)
{ {
struct timeval now_cooked, ref_time; if (are_we_synchronised) {
int synchronised, stratum; return our_stratum;
NTP_Leap leap_status; } else if (enable_local_stratum) {
uint32_t ref_id; return local_stratum;
double root_delay, root_dispersion; } else {
return 16;
SCH_GetLastEventTime(&now_cooked, NULL, NULL); }
REF_GetReferenceParams(&now_cooked, &synchronised, &leap_status, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
return stratum;
}
/* ================================================== */
int
REF_GetOrphanStratum(void)
{
if (!enable_local_stratum || !local_orphan || mode != REF_ModeNormal)
return NTP_MAX_STRATUM;
return local_stratum;
}
/* ================================================== */
double
REF_GetSkew(void)
{
return our_skew;
} }
/* ================================================== */ /* ================================================== */
@@ -1272,21 +1126,10 @@ REF_ModifyMaxupdateskew(double new_max_update_skew)
/* ================================================== */ /* ================================================== */
void void
REF_ModifyMakestep(int limit, double threshold) REF_EnableLocal(int stratum)
{
make_step_limit = limit;
make_step_threshold = threshold;
}
/* ================================================== */
void
REF_EnableLocal(int stratum, double distance, int orphan)
{ {
enable_local_stratum = 1; enable_local_stratum = 1;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1); local_stratum = stratum;
local_distance = distance;
local_orphan = !!orphan;
} }
/* ================================================== */ /* ================================================== */
@@ -1299,6 +1142,14 @@ REF_DisableLocal(void)
/* ================================================== */ /* ================================================== */
int
REF_IsLocalActive(void)
{
return !are_we_synchronised && enable_local_stratum;
}
/* ================================================== */
#define LEAP_SECOND_CLOSE 5 #define LEAP_SECOND_CLOSE 5
int REF_IsLeapSecondClose(void) int REF_IsLeapSecondClose(void)
@@ -1327,34 +1178,52 @@ int REF_IsLeapSecondClose(void)
void void
REF_GetTrackingReport(RPT_TrackingReport *rep) REF_GetTrackingReport(RPT_TrackingReport *rep)
{ {
double elapsed;
double extra_dispersion;
struct timeval now_raw, now_cooked; struct timeval now_raw, now_cooked;
double correction; double correction;
int synchronised;
LCL_ReadRawTime(&now_raw); LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &correction, NULL); LCL_GetOffsetCorrection(&now_raw, &correction, NULL);
UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked); UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked);
REF_GetReferenceParams(&now_cooked, &synchronised, rep->ref_id = 0;
&rep->leap_status, &rep->stratum,
&rep->ref_id, &rep->ref_time,
&rep->root_delay, &rep->root_dispersion);
if (rep->stratum == NTP_MAX_STRATUM)
rep->stratum = 0;
rep->ip_addr.family = IPADDR_UNSPEC; rep->ip_addr.family = IPADDR_UNSPEC;
rep->stratum = 0;
rep->leap_status = our_leap_status;
rep->ref_time.tv_sec = 0;
rep->ref_time.tv_usec = 0;
rep->current_correction = correction; rep->current_correction = correction;
rep->freq_ppm = LCL_ReadAbsoluteFrequency(); rep->freq_ppm = LCL_ReadAbsoluteFrequency();
rep->resid_freq_ppm = 0.0; rep->resid_freq_ppm = 0.0;
rep->skew_ppm = 0.0; rep->skew_ppm = 0.0;
rep->root_delay = 0.0;
rep->root_dispersion = 0.0;
rep->last_update_interval = last_ref_update_interval; rep->last_update_interval = last_ref_update_interval;
rep->last_offset = last_offset; rep->last_offset = last_offset;
rep->rms_offset = sqrt(avg2_offset); rep->rms_offset = sqrt(avg2_offset);
if (synchronised) { if (are_we_synchronised) {
UTI_DiffTimevalsToDouble(&elapsed, &now_cooked, &our_ref_time);
extra_dispersion = (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
rep->ref_id = our_ref_id;
rep->ip_addr = our_ref_ip; rep->ip_addr = our_ref_ip;
rep->stratum = our_stratum;
rep->ref_time = our_ref_time;
rep->resid_freq_ppm = 1.0e6 * our_residual_freq; rep->resid_freq_ppm = 1.0e6 * our_residual_freq;
rep->skew_ppm = 1.0e6 * our_skew; rep->skew_ppm = 1.0e6 * our_skew;
rep->root_delay = our_root_delay;
rep->root_dispersion = our_root_dispersion + extra_dispersion;
} else if (enable_local_stratum) {
rep->ref_id = LOCAL_REFERENCE_ID;
rep->ip_addr.family = IPADDR_UNSPEC;
rep->stratum = local_stratum;
rep->ref_time = now_cooked;
rep->root_dispersion = LCL_GetSysPrecisionAsQuantum();
} }
} }

View File

@@ -35,14 +35,6 @@
#include "ntp.h" #include "ntp.h"
#include "reports.h" #include "reports.h"
/* Leap second handling modes */
typedef enum {
REF_LeapModeSystem,
REF_LeapModeSlew,
REF_LeapModeStep,
REF_LeapModeIgnore,
} REF_LeapMode;
/* Init function */ /* Init function */
extern void REF_Initialise(void); extern void REF_Initialise(void);
@@ -69,9 +61,6 @@ typedef void (*REF_ModeEndHandler)(int result);
/* Set the handler for being notified of mode ending */ /* Set the handler for being notified of mode ending */
extern void REF_SetModeEndHandler(REF_ModeEndHandler handler); extern void REF_SetModeEndHandler(REF_ModeEndHandler handler);
/* Get leap second handling mode */
extern REF_LeapMode REF_GetLeapMode(void);
/* Function which takes a local cooked time and returns the estimated /* Function which takes a local cooked time and returns the estimated
time of the reference. It also returns the other parameters time of the reference. It also returns the other parameters
required for forming the outgoing NTP packet. required for forming the outgoing NTP packet.
@@ -165,20 +154,12 @@ REF_SetUnsynchronised(void);
synchronised */ synchronised */
extern int REF_GetOurStratum(void); extern int REF_GetOurStratum(void);
/* Return stratum of the local reference if orphan mode is enabled */
extern int REF_GetOrphanStratum(void);
/* Return the current skew */
extern double REF_GetSkew(void);
/* Modify the setting for the maximum skew we are prepared to allow updates on (in ppm). */ /* Modify the setting for the maximum skew we are prepared to allow updates on (in ppm). */
extern void REF_ModifyMaxupdateskew(double new_max_update_skew); extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
/* Modify makestep settings */ extern void REF_EnableLocal(int stratum);
extern void REF_ModifyMakestep(int limit, double threshold);
extern void REF_EnableLocal(int stratum, double distance, int orphan);
extern void REF_DisableLocal(void); extern void REF_DisableLocal(void);
extern int REF_IsLocalActive(void);
/* Check if current raw or cooked time is close to a leap second /* Check if current raw or cooked time is close to a leap second
and is better to discard any measurements */ and is better to discard any measurements */

View File

@@ -29,7 +29,6 @@
#include "sysincl.h" #include "sysincl.h"
#include "addressing.h" #include "addressing.h"
#include "ntp.h"
#define REPORT_INVALID_OFFSET 0x80000000 #define REPORT_INVALID_OFFSET 0x80000000
@@ -39,7 +38,7 @@ typedef struct {
int poll; int poll;
enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode; enum {RPT_NTP_CLIENT, RPT_NTP_PEER, RPT_LOCAL_REFERENCE} mode;
enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE, RPT_OUTLIER} state; enum {RPT_SYNC, RPT_UNREACH, RPT_FALSETICKER, RPT_JITTERY, RPT_CANDIDATE, RPT_OUTLIER} state;
int sel_options; enum {RPT_NORMAL, RPT_PREFER, RPT_NOSELECT} sel_option;
int reachability; int reachability;
unsigned long latest_meas_ago; /* seconds */ unsigned long latest_meas_ago; /* seconds */
@@ -51,8 +50,8 @@ typedef struct {
typedef struct { typedef struct {
uint32_t ref_id; uint32_t ref_id;
IPAddr ip_addr; IPAddr ip_addr;
int stratum; unsigned long stratum;
NTP_Leap leap_status; unsigned long leap_status;
struct timeval ref_time; struct timeval ref_time;
double current_correction; double current_correction;
double last_offset; double last_offset;
@@ -88,25 +87,25 @@ typedef struct {
} RPT_RTC_Report; } RPT_RTC_Report;
typedef struct { typedef struct {
IPAddr ip_addr; unsigned long client_hits;
uint32_t ntp_hits; unsigned long peer_hits;
uint32_t cmd_hits; unsigned long cmd_hits_auth;
uint16_t ntp_drops; unsigned long cmd_hits_normal;
uint16_t cmd_drops; unsigned long cmd_hits_bad;
int8_t ntp_interval; unsigned long last_ntp_hit_ago;
int8_t cmd_interval; unsigned long last_cmd_hit_ago;
int8_t ntp_timeout_interval; } RPT_ClientAccess_Report;
uint32_t last_ntp_hit_ago;
uint32_t last_cmd_hit_ago;
} RPT_ClientAccessByIndex_Report;
typedef struct { typedef struct {
uint32_t ntp_hits; IPAddr ip_addr;
uint32_t cmd_hits; unsigned long client_hits;
uint32_t ntp_drops; unsigned long peer_hits;
uint32_t cmd_drops; unsigned long cmd_hits_auth;
uint32_t log_drops; unsigned long cmd_hits_normal;
} RPT_ServerStatsReport; unsigned long cmd_hits_bad;
unsigned long last_ntp_hit_ago;
unsigned long last_cmd_hit_ago;
} RPT_ClientAccessByIndex_Report;
typedef struct { typedef struct {
struct timeval when; struct timeval when;
@@ -123,14 +122,4 @@ typedef struct {
int unresolved; int unresolved;
} RPT_ActivityReport; } RPT_ActivityReport;
typedef struct {
int active;
int leap_only;
double offset;
double freq_ppm;
double wander_ppm;
double last_update_ago;
double remaining_time;
} RPT_SmoothingReport;
#endif /* GOT_REPORTS_H */ #endif /* GOT_REPORTS_H */

62
rtc.c
View File

@@ -39,12 +39,11 @@
/* ================================================== */ /* ================================================== */
static int driver_initialised = 0; static int driver_initialised = 0;
static int driver_preinit_ok = 0;
static struct { static struct {
int (*init)(void); int (*init)(void);
void (*fini)(void); void (*fini)(void);
int (*time_pre_init)(time_t driftfile_time); int (*time_pre_init)(void);
void (*time_init)(void (*after_hook)(void*), void *anything); void (*time_init)(void (*after_hook)(void*), void *anything);
void (*start_measurements)(void); void (*start_measurements)(void);
int (*write_parameters)(void); int (*write_parameters)(void);
@@ -74,37 +73,29 @@ static struct {
}; };
/* ================================================== */ /* ================================================== */
/* Get the last modification time of the driftfile */ /* Set the system clock to the time of last modification of driftfile
if it's in the future */
static time_t static void
get_driftfile_time(void) fallback_time_init(void)
{ {
struct timeval now;
struct stat buf; struct stat buf;
char *drift_file; char *drift_file;
drift_file = CNF_GetDriftFile(); drift_file = CNF_GetDriftFile();
if (!drift_file) if (!drift_file)
return 0; return;
if (stat(drift_file, &buf)) if (stat(drift_file, &buf))
return 0; return;
return buf.st_mtime;
}
/* ================================================== */
/* Set the system time to the driftfile time if it's in the future */
static void
apply_driftfile_time(time_t t)
{
struct timeval now;
LCL_ReadCookedTime(&now, NULL); LCL_ReadCookedTime(&now, NULL);
if (now.tv_sec < t) { if (now.tv_sec < buf.st_mtime) {
if (LCL_ApplyStepOffset(now.tv_sec - t)) LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime);
LOG(LOGS_INFO, LOGF_Rtc, "System time restored from driftfile"); LOG(LOGS_INFO, LOGF_Rtc,
"System clock set from driftfile %s", drift_file);
} }
} }
@@ -113,24 +104,15 @@ apply_driftfile_time(time_t t)
void void
RTC_Initialise(int initial_set) RTC_Initialise(int initial_set)
{ {
time_t driftfile_time;
char *file_name; char *file_name;
/* If the -s option was specified, try to do an initial read of the RTC and /* Do an initial read of the RTC and set the system time to it. This
set the system time to it. Also, read the last modification time of the is analogous to what /sbin/hwclock -s would do on Linux. If that fails
driftfile (i.e. system time when chronyd was previously stopped) and set or RTC is not supported, set the clock to the time of the last
the system time to it if it's in the future to bring the clock closer to modification of driftfile, so we at least get closer to the truth. */
the true time when the RTC is broken (e.g. it has no battery), is missing,
or there is no RTC driver. */
if (initial_set) { if (initial_set) {
driftfile_time = get_driftfile_time(); if (!driver.time_pre_init || !driver.time_pre_init()) {
fallback_time_init();
if (driver.time_pre_init && driver.time_pre_init(driftfile_time)) {
driver_preinit_ok = 1;
} else {
driver_preinit_ok = 0;
if (driftfile_time)
apply_driftfile_time(driftfile_time);
} }
} }
@@ -168,9 +150,9 @@ RTC_Finalise(void)
/* ================================================== */ /* ================================================== */
/* Start the processing to get a single measurement from the real time /* Start the processing to get a single measurement from the real time
clock, and use it to trim the system time, based on knowing the clock, and use it to trim the system time, based on knowing the
drift rate of the RTC and the error the last time we set it. If the drift rate of the RTC and the error the last time we set it. The
TimePreInit routine has succeeded, we can be sure that the trim required TimePreInit routine has already run, so we can be sure that the
is not *too* large. trim required is not *too* large.
We are called with a hook to a function to be called after the We are called with a hook to a function to be called after the
initialisation is complete. We also call this if we cannot do the initialisation is complete. We also call this if we cannot do the
@@ -179,7 +161,7 @@ RTC_Finalise(void)
void void
RTC_TimeInit(void (*after_hook)(void *), void *anything) RTC_TimeInit(void (*after_hook)(void *), void *anything)
{ {
if (driver_initialised && driver_preinit_ok) { if (driver_initialised) {
(driver.time_init)(after_hook, anything); (driver.time_init)(after_hook, anything);
} else { } else {
(after_hook)(anything); (after_hook)(anything);

View File

@@ -72,7 +72,8 @@ static int fd = -1;
static int measurement_period = LOWEST_MEASUREMENT_PERIOD; static int measurement_period = LOWEST_MEASUREMENT_PERIOD;
static SCH_TimeoutID timeout_id = 0; static int timeout_running = 0;
static SCH_TimeoutID timeout_id;
static int skip_interrupts; static int skip_interrupts;
@@ -85,8 +86,8 @@ static int skip_interrupts;
measured, together with a 'trim' that compensates these values for measured, together with a 'trim' that compensates these values for
any steps made to the RTC to bring it back into line any steps made to the RTC to bring it back into line
occasionally. The trim is in seconds. */ occasionally. The trim is in seconds. */
static time_t *rtc_sec = NULL; static time_t rtc_sec[MAX_SAMPLES];
static double *rtc_trim = NULL; static double rtc_trim[MAX_SAMPLES];
/* Reference time, against which delta times on the RTC scale are measured */ /* Reference time, against which delta times on the RTC scale are measured */
static time_t rtc_ref; static time_t rtc_ref;
@@ -94,7 +95,7 @@ static time_t rtc_ref;
/* System clock (gettimeofday) samples associated with the above /* System clock (gettimeofday) samples associated with the above
samples. */ samples. */
static struct timeval *system_times = NULL; static struct timeval system_times[MAX_SAMPLES];
/* Number of samples currently stored. */ /* Number of samples currently stored. */
static int n_samples; static int n_samples;
@@ -366,9 +367,6 @@ t_from_rtc(struct tm *stm) {
t2 = mktime(&temp2); t2 = mktime(&temp2);
diff = t2 - t1; diff = t2 - t1;
if (t1 - diff == -1)
DEBUG_LOG(LOGF_RtcLinux, "Could not convert RTC time");
return t1 - diff; return t1 - diff;
} }
@@ -381,13 +379,13 @@ read_hwclock_file(const char *hwclock_file)
char line[256]; char line[256];
int i; int i;
if (!hwclock_file || !hwclock_file[0]) if (!hwclock_file)
return; return;
in = fopen(hwclock_file, "r"); in = fopen(hwclock_file, "r");
if (!in) { if (!in) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open %s : %s", LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open hwclockfile %s",
hwclock_file, strerror(errno)); hwclock_file);
return; return;
} }
@@ -404,7 +402,7 @@ read_hwclock_file(const char *hwclock_file)
} else if (i == 3 && !strncmp(line, "UTC", 3)) { } else if (i == 3 && !strncmp(line, "UTC", 3)) {
rtc_on_utc = 1; rtc_on_utc = 1;
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read RTC LOCAL/UTC setting from %s", LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read LOCAL/UTC setting from hwclockfile %s",
hwclock_file); hwclock_file);
} }
} }
@@ -447,7 +445,7 @@ read_coefs_from_file(void)
&file_ref_offset, &file_ref_offset,
&file_rate_ppm) == 4) { &file_rate_ppm) == 4) {
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from %s", LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from RTC file %s",
coefs_file_name); coefs_file_name);
} }
fclose(in); fclose(in);
@@ -532,10 +530,6 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
int int
RTC_Linux_Initialise(void) RTC_Linux_Initialise(void)
{ {
rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES);
system_times = MallocArray(struct timeval, MAX_SAMPLES);
/* Setup details depending on configuration options */ /* Setup details depending on configuration options */
setup_config(); setup_config();
@@ -580,8 +574,10 @@ RTC_Linux_Initialise(void)
void void
RTC_Linux_Finalise(void) RTC_Linux_Finalise(void)
{ {
SCH_RemoveTimeout(timeout_id); if (timeout_running) {
timeout_id = 0; SCH_RemoveTimeout(timeout_id);
timeout_running = 0;
}
/* Remove input file handler */ /* Remove input file handler */
if (fd >= 0) { if (fd >= 0) {
@@ -592,9 +588,6 @@ RTC_Linux_Finalise(void)
(void) RTC_Linux_WriteParameters(); (void) RTC_Linux_WriteParameters();
} }
Free(rtc_sec);
Free(rtc_trim);
Free(system_times);
} }
/* ================================================== */ /* ================================================== */
@@ -607,16 +600,14 @@ switch_interrupts(int onoff)
if (onoff) { if (onoff) {
status = ioctl(fd, RTC_UIE_ON, 0); status = ioctl(fd, RTC_UIE_ON, 0);
if (status < 0) { if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s", LOG(LOGS_ERR, LOGF_RtcLinux, "Could not start measurement : %s", strerror(errno));
"enable", strerror(errno));
return; return;
} }
skip_interrupts = 1; skip_interrupts = 1;
} else { } else {
status = ioctl(fd, RTC_UIE_OFF, 0); status = ioctl(fd, RTC_UIE_OFF, 0);
if (status < 0) { if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s", LOG(LOGS_ERR, LOGF_RtcLinux, "Could not stop measurement : %s", strerror(errno));
"disable", strerror(errno));
return; return;
} }
} }
@@ -627,7 +618,7 @@ switch_interrupts(int onoff)
static void static void
measurement_timeout(void *any) measurement_timeout(void *any)
{ {
timeout_id = 0; timeout_running = 0;
switch_interrupts(1); switch_interrupts(1);
} }
@@ -696,11 +687,10 @@ handle_initial_trim(void)
/* sys_error_now is positive if the system clock is fast */ /* sys_error_now is positive if the system clock is fast */
sys_error_now = rtc_error_now - coef_seconds_fast; sys_error_now = rtc_error_now - coef_seconds_fast;
LOG(LOGS_INFO, LOGF_RtcLinux, "System trim from RTC = %f", sys_error_now);
LCL_AccumulateOffset(sys_error_now, 0.0); LCL_AccumulateOffset(sys_error_now, 0.0);
LOG(LOGS_INFO, LOGF_RtcLinux, "System clock off from RTC by %f seconds (slew)",
sys_error_now);
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid rtcfile coefficients"); LOG(LOGS_WARN, LOGF_RtcLinux, "No valid file coefficients, cannot trim system time");
} }
coefs_valid = 0; coefs_valid = 0;
@@ -725,7 +715,7 @@ handle_relock_after_trim(void)
if (valid) { if (valid) {
write_coefs_to_file(1,ref,fast,saved_coef_gain_rate); write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
} else { } else {
DEBUG_LOG(LOGF_RtcLinux, "Could not do regression after trim"); LOG(LOGS_WARN, LOGF_RtcLinux, "Could not do regression after trim");
} }
coefs_valid = 0; coefs_valid = 0;
@@ -860,6 +850,7 @@ read_from_device(void *any)
rtc_t = t_from_rtc(&rtc_tm); rtc_t = t_from_rtc(&rtc_tm);
if (rtc_t == (time_t)(-1)) { if (rtc_t == (time_t)(-1)) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not convert RTC time to timeval");
error = 1; error = 1;
goto turn_off_interrupt; goto turn_off_interrupt;
} }
@@ -885,12 +876,13 @@ turn_off_interrupt:
switch (operating_mode) { switch (operating_mode) {
case OM_INITIAL: case OM_INITIAL:
if (error) { if (error) {
DEBUG_LOG(LOGF_RtcLinux, "Could not complete initial step due to errors"); LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete initial step due to errors");
operating_mode = OM_NORMAL; operating_mode = OM_NORMAL;
(after_init_hook)(after_init_hook_arg); (after_init_hook)(after_init_hook_arg);
switch_interrupts(0); switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL); timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
} }
@@ -898,11 +890,12 @@ turn_off_interrupt:
case OM_AFTERTRIM: case OM_AFTERTRIM:
if (error) { if (error) {
DEBUG_LOG(LOGF_RtcLinux, "Could not complete after trim relock due to errors"); LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete after trim relock due to errors");
operating_mode = OM_NORMAL; operating_mode = OM_NORMAL;
switch_interrupts(0); switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL); timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
} }
@@ -911,6 +904,7 @@ turn_off_interrupt:
case OM_NORMAL: case OM_NORMAL:
switch_interrupts(0); switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL); timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
break; break;
@@ -930,8 +924,9 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
after_init_hook_arg = anything; after_init_hook_arg = anything;
operating_mode = OM_INITIAL; operating_mode = OM_INITIAL;
timeout_id = 0; timeout_running = 0;
switch_interrupts(1); switch_interrupts(1);
} }
/* ================================================== */ /* ================================================== */
@@ -939,6 +934,7 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
void void
RTC_Linux_StartMeasurements(void) RTC_Linux_StartMeasurements(void)
{ {
timeout_running = 0;
measurement_timeout(NULL); measurement_timeout(NULL);
} }
@@ -971,7 +967,7 @@ RTC_Linux_WriteParameters(void)
RTC behaviour than we do for the rest of the module. */ RTC behaviour than we do for the rest of the module. */
int int
RTC_Linux_TimePreInit(time_t driftfile_time) RTC_Linux_TimePreInit(void)
{ {
int fd, status; int fd, status;
struct rtc_time rtc_raw, rtc_raw_retry; struct rtc_time rtc_raw, rtc_raw_retry;
@@ -1036,23 +1032,18 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time); UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
return 0;
}
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &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 */ /* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) { if (fabs(sys_offset) >= 1.0) {
if (LCL_ApplyStepOffset(sys_offset)) LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
LOG(LOGS_INFO, LOGF_RtcLinux, "System time set from RTC"); accumulated_error);
LCL_ApplyStepOffset(sys_offset);
} }
} else { } else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");
return 0; return 0;
} }
} else {
return 0;
} }
return 1; return 1;
@@ -1092,8 +1083,7 @@ RTC_Linux_Trim(void)
if (fabs(coef_seconds_fast) > 1.0) { if (fabs(coef_seconds_fast) > 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "RTC wrong by %.3f seconds (step)", LOG(LOGS_INFO, LOGF_RtcLinux, "Trimming RTC, error = %.3f seconds", coef_seconds_fast);
coef_seconds_fast);
/* Do processing to set clock. Let R be the value we set the /* Do processing to set clock. Let R be the value we set the
RTC to, then in 500ms the RTC ticks (R+1) (see comments in RTC to, then in 500ms the RTC ticks (R+1) (see comments in
@@ -1118,8 +1108,10 @@ RTC_Linux_Trim(void)
coef_ref_time = now.tv_sec; coef_ref_time = now.tv_sec;
/* And start rapid sampling, interrupts on now */ /* And start rapid sampling, interrupts on now */
SCH_RemoveTimeout(timeout_id); if (timeout_running) {
timeout_id = 0; SCH_RemoveTimeout(timeout_id);
timeout_running = 0;
}
switch_interrupts(1); switch_interrupts(1);
} }

View File

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

72
sched.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2013-2015 * Copyright (C) Miroslav Lichvar 2011, 2013-2014
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,6 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "sched.h" #include "sched.h"
#include "memory.h" #include "memory.h"
#include "util.h" #include "util.h"
@@ -69,7 +68,7 @@ typedef struct {
SCH_ArbitraryArgument arg; SCH_ArbitraryArgument arg;
} FileHandlerEntry; } FileHandlerEntry;
static ARR_Instance file_handlers; static FileHandlerEntry file_handlers[FD_SETSIZE];
/* Timestamp when last select() returned */ /* Timestamp when last select() returned */
static struct timeval last_select_ts, last_select_ts_raw; static struct timeval last_select_ts, last_select_ts_raw;
@@ -135,8 +134,6 @@ SCH_Initialise(void)
FD_ZERO(&read_fds); FD_ZERO(&read_fds);
n_read_fds = 0; n_read_fds = 0;
file_handlers = ARR_CreateInstance(sizeof (FileHandlerEntry));
n_timer_queue_entries = 0; n_timer_queue_entries = 0;
next_tqe_id = 0; next_tqe_id = 0;
@@ -150,6 +147,8 @@ SCH_Initialise(void)
LCL_ReadRawTime(&last_select_ts_raw); LCL_ReadRawTime(&last_select_ts_raw);
last_select_ts = last_select_ts_raw; last_select_ts = last_select_ts_raw;
srandom(last_select_ts.tv_sec << 16 ^ last_select_ts.tv_usec);
initialised = 1; initialised = 1;
} }
@@ -158,8 +157,6 @@ SCH_Initialise(void)
void void
SCH_Finalise(void) { SCH_Finalise(void) {
ARR_DestroyInstance(file_handlers);
initialised = 0; initialised = 0;
} }
@@ -169,7 +166,6 @@ void
SCH_AddInputFileHandler SCH_AddInputFileHandler
(int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg) (int fd, SCH_FileHandler handler, SCH_ArbitraryArgument arg)
{ {
FileHandlerEntry *ptr;
assert(initialised); assert(initialised);
@@ -179,17 +175,12 @@ SCH_AddInputFileHandler
/* Don't want to allow the same fd to register a handler more than /* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests once without deleting a previous association - this suggests
a bug somewhere else in the program. */ a bug somewhere else in the program. */
if (FD_ISSET(fd, &read_fds)) assert(!FD_ISSET(fd, &read_fds));
assert(0);
++n_read_fds; ++n_read_fds;
if (ARR_GetSize(file_handlers) < fd + 1) file_handlers[fd].handler = handler;
ARR_SetSize(file_handlers, fd + 1); file_handlers[fd].arg = arg;
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
ptr->handler = handler;
ptr->arg = arg;
FD_SET(fd, &read_fds); FD_SET(fd, &read_fds);
@@ -209,8 +200,7 @@ SCH_RemoveInputFileHandler(int fd)
assert(initialised); assert(initialised);
/* Check that a handler was registered for the fd in question */ /* Check that a handler was registered for the fd in question */
if (!FD_ISSET(fd, &read_fds)) assert(FD_ISSET(fd, &read_fds));
assert(0);
--n_read_fds; --n_read_fds;
@@ -278,26 +268,6 @@ release_tqe(TimerQueueEntry *node)
/* ================================================== */ /* ================================================== */
static SCH_TimeoutID
get_new_tqe_id(void)
{
TimerQueueEntry *ptr;
try_again:
next_tqe_id++;
if (!next_tqe_id)
goto try_again;
/* Make sure the ID isn't already used */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next)
if (ptr->id == next_tqe_id)
goto try_again;
return next_tqe_id;
}
/* ================================================== */
SCH_TimeoutID SCH_TimeoutID
SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg) SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{ {
@@ -308,7 +278,7 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
new_tqe = allocate_tqe(); new_tqe = allocate_tqe();
new_tqe->id = get_new_tqe_id(); new_tqe->id = next_tqe_id++;
new_tqe->handler = handler; new_tqe->handler = handler;
new_tqe->arg = arg; new_tqe->arg = arg;
new_tqe->tv = *tv; new_tqe->tv = *tv;
@@ -350,10 +320,6 @@ SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArg
LCL_ReadRawTime(&now); LCL_ReadRawTime(&now);
UTI_AddDoubleToTimeval(&now, delay, &then); UTI_AddDoubleToTimeval(&now, delay, &then);
if (UTI_CompareTimevals(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
}
return SCH_AddTimeout(&then, handler, arg); return SCH_AddTimeout(&then, handler, arg);
} }
@@ -376,10 +342,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
assert(class < SCH_NumberOfClasses); assert(class < SCH_NumberOfClasses);
if (randomness > 0.0) { if (randomness > 0.0) {
uint16_t rnd; r = random() % 0xffff / (0xffff - 1.0) * randomness + 1.0;
UTI_GetRandomBytes(&rnd, sizeof (rnd));
r = rnd / (double)0xffff * randomness + 1.0;
min_delay *= r; min_delay *= r;
separation *= r; separation *= r;
} }
@@ -420,7 +383,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
/* We have located the insertion point */ /* We have located the insertion point */
new_tqe = allocate_tqe(); new_tqe = allocate_tqe();
new_tqe->id = get_new_tqe_id(); new_tqe->id = next_tqe_id++;
new_tqe->handler = handler; new_tqe->handler = handler;
new_tqe->arg = arg; new_tqe->arg = arg;
UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv); UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv);
@@ -444,9 +407,6 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
assert(initialised); assert(initialised);
if (!id)
return;
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) { for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (ptr->id == id) { if (ptr->id == id) {
@@ -462,12 +422,9 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
/* Release memory back to the operating system */ /* Release memory back to the operating system */
release_tqe(ptr); release_tqe(ptr);
return; break;
} }
} }
/* Catch calls with invalid non-zero ID */
assert(0);
} }
/* ================================================== */ /* ================================================== */
@@ -525,15 +482,13 @@ dispatch_timeouts(struct timeval *now) {
static void static void
dispatch_filehandlers(int nfh, fd_set *fhs) dispatch_filehandlers(int nfh, fd_set *fhs)
{ {
FileHandlerEntry *ptr;
int fh = 0; int fh = 0;
while (nfh > 0) { while (nfh > 0) {
if (FD_ISSET(fh, fhs)) { if (FD_ISSET(fh, fhs)) {
/* This descriptor can be read from, dispatch its handler */ /* This descriptor can be read from, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fh); (file_handlers[fh].handler)(file_handlers[fh].arg);
(ptr->handler)(ptr->arg);
/* Decrement number of readable files still to find */ /* Decrement number of readable files still to find */
--nfh; --nfh;
@@ -719,6 +674,7 @@ SCH_MainLoop(void)
void void
SCH_QuitProgram(void) SCH_QuitProgram(void)
{ {
assert(initialised);
need_to_exit = 1; need_to_exit = 1;
} }

View File

@@ -29,8 +29,7 @@
#include "sysincl.h" #include "sysincl.h"
/* Type for timeout IDs, valid IDs are always greater than zero */ typedef unsigned long SCH_TimeoutID;
typedef unsigned int SCH_TimeoutID;
typedef enum { typedef enum {
SCH_ReservedTimeoutValue = 0, SCH_ReservedTimeoutValue = 0,

342
smooth.c
View File

@@ -1,342 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Routines implementing time smoothing.
*/
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "reference.h"
#include "smooth.h"
#include "util.h"
/*
Time smoothing determines an offset that needs to be applied to the cooked
time to make it smooth for external observers. Observed offset and frequency
change slowly and there are no discontinuities. This can be used on an NTP
server to make it easier for the clients to track the time and keep their
clocks close together even when large offset or frequency corrections are
applied to the server's clock (e.g. after being offline for longer time).
Accumulated offset and frequency are smoothed out in three stages. In the
first stage, the frequency is changed at a constant rate (wander) up to a
maximum, in the second stage the frequency stays at the maximum for as long
as needed and in the third stage the frequency is brought back to zero.
|
max_freq +-------/--------\-------------
| /| |\
freq | / | | \
| / | | \
| / | | \
0 +--/----+--------+----\--------
| / | | | time
|/ | | |
stage 1 2 3
Integral of this function is the smoothed out offset. It's a continuous
piecewise polynomial with two quadratic parts and one linear.
*/
struct stage {
double wander;
double length;
};
#define NUM_STAGES 3
static struct stage stages[NUM_STAGES];
/* Enabled/disabled smoothing */
static int enabled;
/* Enabled/disabled mode where only leap seconds are smoothed out and normal
offset/frequency changes are ignored */
static int leap_only_mode;
/* Maximum skew/max_wander ratio to start updating offset and frequency */
#define UNLOCK_SKEW_WANDER_RATIO 10000
static int locked;
/* Maximum wander and frequency offset */
static double max_wander;
static double max_freq;
/* Frequency offset, time offset and the time of the last smoothing update */
static double smooth_freq;
static double smooth_offset;
static struct timeval last_update;
static void
get_smoothing(struct timeval *now, double *poffset, double *pfreq,
double *pwander)
{
double elapsed, length, offset, freq, wander;
int i;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
offset = smooth_offset;
freq = smooth_freq;
wander = 0.0;
for (i = 0; i < NUM_STAGES; i++) {
if (elapsed <= 0.0)
break;
length = stages[i].length;
if (length >= elapsed)
length = elapsed;
wander = stages[i].wander;
offset -= length * (2.0 * freq + wander * length) / 2.0;
freq += wander * length;
elapsed -= length;
}
if (elapsed > 0.0) {
wander = 0.0;
offset -= elapsed * freq;
}
*poffset = offset;
*pfreq = freq;
if (pwander)
*pwander = wander;
}
static void
update_stages(void)
{
double s1, s2, s, l1, l2, l3, lc, f, f2;
int i, dir;
/* Prepare the three stages so that the integral of the frequency offset
is equal to the offset that should be smoothed out */
s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
l1 = l2 = l3 = 0.0;
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. If length of the 1st stage comes out negative, switch
its direction. */
for (dir = -1; dir <= 1; dir += 2) {
s = dir * s1 + s2;
if (s >= 0.0) {
l3 = sqrt(s);
l1 = l3 - dir * smooth_freq / max_wander;
if (l1 >= 0.0)
break;
}
}
assert(dir <= 1 && l1 >= 0.0 && l3 >= 0.0);
/* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */
f = dir * smooth_freq + l1 * max_wander - max_freq;
if (f > 0.0) {
lc = f / max_wander;
/* No 1st stage if the frequency is already above the maximum */
if (lc > l1) {
lc = l1;
f2 = dir * smooth_freq;
} else {
f2 = max_freq;
}
l2 = lc * (2.0 + f / f2);
l1 -= lc;
l3 -= lc;
}
stages[0].wander = dir * max_wander;
stages[0].length = l1;
stages[1].wander = 0.0;
stages[1].length = l2;
stages[2].wander = -dir * max_wander;
stages[2].length = l3;
for (i = 0; i < NUM_STAGES; i++) {
DEBUG_LOG(LOGF_Smooth, "Smooth stage %d wander %e length %f",
i + 1, stages[i].wander, stages[i].length);
}
}
static void
update_smoothing(struct timeval *now, double offset, double freq)
{
/* Don't accept offset/frequency until the clock has stabilized */
if (locked) {
if (REF_GetSkew() / max_wander < UNLOCK_SKEW_WANDER_RATIO || leap_only_mode)
SMT_Activate(now);
return;
}
get_smoothing(now, &smooth_offset, &smooth_freq, NULL);
smooth_offset += offset;
smooth_freq = (smooth_freq - freq) / (1.0 - freq);
last_update = *now;
update_stages();
DEBUG_LOG(LOGF_Smooth, "Smooth offset %e freq %e", smooth_offset, smooth_freq);
}
static void
handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
double delta;
if (change_type == LCL_ChangeAdjust) {
if (leap_only_mode)
update_smoothing(cooked, 0.0, 0.0);
else
update_smoothing(cooked, doffset, dfreq);
}
UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset);
}
void SMT_Initialise(void)
{
CNF_GetSmooth(&max_freq, &max_wander, &leap_only_mode);
if (max_freq <= 0.0 || max_wander <= 0.0) {
enabled = 0;
return;
}
enabled = 1;
locked = 1;
/* Convert from ppm */
max_freq *= 1e-6;
max_wander *= 1e-6;
LCL_AddParameterChangeHandler(handle_slew, NULL);
}
void SMT_Finalise(void)
{
}
int SMT_IsEnabled(void)
{
return enabled;
}
double
SMT_GetOffset(struct timeval *now)
{
double offset, freq;
if (!enabled)
return 0.0;
get_smoothing(now, &offset, &freq, NULL);
return offset;
}
void
SMT_Activate(struct timeval *now)
{
if (!enabled || !locked)
return;
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ?
" (leap seconds only)" : "");
locked = 0;
last_update = *now;
}
void
SMT_Reset(struct timeval *now)
{
int i;
if (!enabled)
return;
smooth_offset = 0.0;
smooth_freq = 0.0;
last_update = *now;
for (i = 0; i < NUM_STAGES; i++)
stages[i].wander = stages[i].length = 0.0;
}
void
SMT_Leap(struct timeval *now, int leap)
{
/* When the leap-only mode is disabled, the leap second will be accumulated
in handle_slew() as a normal offset */
if (!enabled || !leap_only_mode)
return;
update_smoothing(now, leap, 0.0);
}
int
SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
{
double length, elapsed;
int i;
if (!enabled)
return 0;
report->active = !locked;
report->leap_only = leap_only_mode;
get_smoothing(now, &report->offset, &report->freq_ppm, &report->wander_ppm);
/* Convert to ppm and negate (positive values mean faster/speeding up) */
report->freq_ppm *= -1.0e6;
report->wander_ppm *= -1.0e6;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
if (!locked && elapsed >= 0.0) {
for (i = 0, length = 0.0; i < NUM_STAGES; i++)
length += stages[i].length;
report->last_update_ago = elapsed;
report->remaining_time = elapsed < length ? length - elapsed : 0.0;
} else {
report->last_update_ago = 0.0;
report->remaining_time = 0.0;
}
return 1;
}

1060
sources.c

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More