Compare commits

..

4 Commits

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

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

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

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

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

[1] https://www.eecis.udel.edu/~mills/onwire.html
2015-04-07 16:34:42 +02:00
198 changed files with 19461 additions and 23630 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 \
smooth.o sys.o sys_null.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)
@@ -63,18 +71,21 @@ chronyd : $(OBJS) $(EXTRA_OBJS)
chronyc : $(CLI_OBJS) chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS) $(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
client.o : client.c
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
$(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
$(CC) $(CFLAGS) $(CPPFLAGS) @HASH_COMPILE@ -c $<
distclean : clean distclean : clean
$(MAKE) -C doc distclean -rm -f Makefile
$(MAKE) -C test/unit distclean -rm -f chrony.conf.5 chrony.texi chronyc.1 chronyd.8
-rm -f .DS_Store
-rm -f Makefile config.h config.log
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
@@ -84,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
@@ -95,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 $<
@@ -109,19 +132,30 @@ install-docs :
%.s : %.c %.s : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -S $< $(CC) $(CFLAGS) $(CPPFLAGS) -S $<
quickcheck : chronyd chronyc check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run cd test/simulation && ./run
check : chronyd chronyc install-docs : docs
$(MAKE) -C test/unit check [ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
cd test/simulation && ./run -i 20 -m 2 cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt
chmod 644 $(DESTDIR)$(DOCDIR)/chrony.txt
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*
Makefile : Makefile.in configure docs : chrony.txt chrony.html chrony.info
@echo
@echo Makefile needs to be regenerated, run ./configure chrony.txt : chrony.texi
@echo makeinfo --no-headers --number-sections -o chrony.txt chrony.texi
@exit 1
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

255
NEWS
View File

@@ -1,263 +1,10 @@
New in version 3.2
==================
Enhancements
------------
* Improve stability with NTP sources and reference clocks
* Improve stability with hardware timestamping
* Improve support for NTP interleaved modes
* Control frequency of system clock on macOS 10.13 and later
* Set TAI-UTC offset of system clock with leapsectz directive
* Minimise data in client requests to improve privacy
* Allow transmit-only hardware timestamping
* Add support for new timestamping options introduced in Linux 4.13
* Add root delay, root dispersion and maximum error to tracking log
* Add mindelay and asymmetry options to server/peer/pool directive
* Add extpps option to PHC refclock to timestamp external PPS signal
* Add pps option to refclock directive to treat any refclock as PPS
* Add width option to refclock directive to filter wrong pulse edges
* Add rxfilter option to hwtimestamp directive
* Add -x option to disable control of system clock
* Add -l option to log to specified file instead of syslog
* Allow multiple command-line options to be specified together
* Allow starting without root privileges with -Q option
* Update seccomp filter for new glibc versions
* Dump history on exit by default with dumpdir directive
* Use hardening compiler options by default
Bug fixes
---------
* Don't drop PHC samples with low-resolution system clock
* Ignore outliers in PHC tracking, RTC tracking, manual input
* Increase polling interval when peer is not responding
* Exit with error message when include directive fails
* Don't allow slash after hostname in allow/deny directive/command
* Try to connect to all addresses in chronyc before giving up
New in version 3.1
==================
Enhancements
------------
* Add support for precise cross timestamping of PHC on Linux
* Add minpoll, precision, nocrossts options to hwtimestamp directive
* Add rawmeasurements option to log directive and modify measurements
option to log only valid measurements from synchronised sources
* Allow sub-second polling interval with NTP sources
Bug fixes
---------
* Fix time smoothing in interleaved mode
New in version 3.0
==================
Enhancements
------------
* Add support for software and hardware timestamping on Linux
* Add support for client/server and symmetric interleaved modes
* Add support for MS-SNTP authentication in Samba
* Add support for truncated MACs in NTPv4 packets
* Estimate and correct for asymmetric network jitter
* Increase default minsamples and polltarget to improve stability
with very low jitter
* Add maxjitter directive to limit source selection by jitter
* Add offset option to server/pool/peer directive
* Add maxlockage option to refclock directive
* Add -t option to chronyd to exit after specified time
* Add partial protection against replay attacks on symmetric mode
* Don't reset polling interval when switching sources to online state
* Allow rate limiting with very short intervals
* Improve maximum server throughput on Linux and NetBSD
* Remove dump files after start
* Add tab-completion to chronyc with libedit/readline
* Add ntpdata command to print details about NTP measurements
* Allow all source options to be set in add server/peer command
* Indicate truncated addresses/hostnames in chronyc output
* Print reference IDs as hexadecimal numbers to avoid confusion with
IPv4 addresses
Bug fixes
---------
* Fix crash with disabled asynchronous name resolving
New in version 2.4.1
====================
Bug fixes
---------
* Fix processing of kernel timestamps on non-Linux systems
* Fix crash with smoothtime directive
* Fix validation of refclock sample times
* Fix parsing of refclock directive
New in version 2.4
==================
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
--------------
* 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
===================== =====================
Security fixes Security fixes
-------------- --------------
* Protect authenticated symmetric NTP associations against DoS attacks * Protect authenticated symmetric NTP associations against DoS attacks
(CVE-2015-1853) (CVE-2015-1799)
* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821) * Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821)
* Fix initialization of reply slots for authenticated commands (CVE-2015-1822) * Fix initialization of reply slots for authenticated commands (CVE-2015-1822)

140
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 synchronise 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 synchronised over the Internet is chronyc is a command-line interface program which can be used to
within a few milliseconds; on a LAN, accuracy is typically in tens of monitor chronyd's performance and to change various operating
microseconds. With hardware timestamping, or a hardware reference clock, parameters whilst it is running.
sub-microsecond accuracy may be 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, macOS 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,36 +115,25 @@ 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 Author
======= ======
chrony is distributed under the GNU General Public License version 2.
Authors
=======
Richard P. Curnow <rc@rc0.org.uk> Richard P. Curnow <rc@rc0.org.uk>
Maintainers
===========
Miroslav Lichvar <mlichvar@redhat.com> 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 :
Lonnie Abelbeck <lonnie@abelbeck.com>
Patch to add tab-completion to chronyc
Benny Lyne Amorsen <benny@amorsen.dk> Benny Lyne Amorsen <benny@amorsen.dk>
Patch to add minstratum option Patch to add minstratum option
@@ -134,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 macOS
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.
@@ -153,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
@@ -194,6 +206,18 @@ Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi> Antti Jrvinen <costello@iki.fi>
Advice on configuring for BSD/386 Advice on configuring for BSD/386
Miroslav Lichvar <mlichvar@redhat.com>
Reference clock support
IPv6 support
Linux capabilities support
Leap second support
Improved source selection
Improved sample history trimming
Improved polling interval adjustment
Improved stability with temporary asymmetric delays
Temperature compensation
Many other bug fixes and improvements
Victor Moroz <vim@prv.adlum.ru> Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100 Patch to support Linux with HZ!=100
@@ -203,24 +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
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
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
Bill Unruh <unruh@physics.ubc.ca>
Advice on statistics
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
@@ -234,5 +246,5 @@ Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Doug Woodward <dougw@whistler.com> Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86 Advice on configuring for Solaris 2.8 on x86
Many other people have contributed bug reports and suggestions. We are sorry Many other people have contributed bug reports and suggestions. I'm
we cannot identify all of you individually. sorry I can't identify all of you individually.

View File

@@ -42,7 +42,6 @@ typedef struct {
uint8_t in6[16]; uint8_t in6[16];
} addr; } addr;
uint16_t family; uint16_t family;
uint16_t _pad;
} IPAddr; } IPAddr;
typedef struct { typedef struct {
@@ -50,11 +49,8 @@ typedef struct {
unsigned short port; unsigned short port;
} NTP_Remote_Address; } NTP_Remote_Address;
#define INVALID_IF_INDEX -1
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
int if_index;
int sock_fd; int sock_fd;
} NTP_Local_Address; } NTP_Local_Address;

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 (ip_len == 1)
printf("%d.%d.%d.%d",
((addr[0] >> 24) & 255),
((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');
}
}
printf("/%d : %s\n",
subnet_bits,
(node->state == ALLOW) ? "allow" :
(node->state == DENY) ? "deny" : "as parent");
if (node->extended) { if (node->extended) {
for (i = 0; i < TABLE_SIZE; i++) { for (i=0; i<16; i++) {
if (is_any_allowed(&node->extended[i], state)) sub_node = &(node->extended[i]);
return 1; 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);
} }
} else if (state == ALLOW) {
return 1;
} }
}
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:
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; 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 */

130
array.c
View File

@@ -1,130 +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)
{
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;
}
array->data = Realloc2(array->data, array->allocated, array->elem_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,9 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2001 * Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) J. Hannken-Illjes 2001
* Copyright (C) Bryan Christianson 2015
* *
* 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
@@ -23,16 +21,17 @@
======================================================================= =======================================================================
Header file for macOS driver Deal with broadcast server functions.
*/ */
#ifndef GOT_SYS_MACOSX_H #ifndef GOT_BROADCAST_H
#define GOT_SYS_MACOSX_H #define GOT_BROADCAST_H
void SYS_MacOSX_SetScheduler(int SchedPriority); #include "addressing.h"
void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid);
void SYS_MacOSX_Initialise(void); extern void BRD_Initialise(void);
void SYS_MacOSX_Finalise(void); extern void BRD_Finalise(void);
extern void BRD_AddDestination(IPAddr *addr, unsigned short port, int interval);
#endif /* GOT_BROADCAST_H */
#endif

263
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,26 +88,18 @@
#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
#define REQ_REFRESH 53
#define REQ_SERVER_STATS 54
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define REQ_LOCAL2 56
#define REQ_NTP_DATA 57
#define REQ_ADD_SERVER2 58
#define REQ_ADD_PEER2 59
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
#define N_REQUEST_TYPES 62
/* Structure used to exchange timespecs independent of time_t size */ /* Special utoken value used to log on with first exchange being the
password. (This time value has long since gone by) */
#define SPECIAL_UTOKEN 0x10101010
/* Structure used to exchange timevals independent on size of time_t */
typedef struct { typedef struct {
uint32_t tv_sec_high; uint32_t tv_sec_high;
uint32_t tv_sec_low; uint32_t tv_sec_low;
uint32_t tv_nsec; uint32_t tv_nsec;
} Timespec; } Timeval;
/* This is used in tv_sec_high for 32-bit timestamps */ /* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff #define TV_NOHIGHSEC 0x7fffffff
@@ -122,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;
@@ -199,26 +188,18 @@ typedef struct {
} REQ_Modify_Maxupdateskew; } REQ_Modify_Maxupdateskew;
typedef struct { typedef struct {
int32_t limit; Timeval ts;
Float threshold;
int32_t EOR;
} REQ_Modify_Makestep;
typedef struct {
Timespec ts;
int32_t EOR; int32_t EOR;
} REQ_Logon; } REQ_Logon;
typedef struct { typedef struct {
Timespec ts; Timeval ts;
int32_t EOR; int32_t EOR;
} REQ_Settime; } REQ_Settime;
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;
@@ -227,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;
@@ -249,9 +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
#define REQ_ADDSRC_INTERLEAVED 0x80
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -259,21 +245,10 @@ typedef struct {
int32_t minpoll; int32_t minpoll;
int32_t maxpoll; int32_t maxpoll;
int32_t presend_minpoll; int32_t presend_minpoll;
uint32_t min_stratum;
uint32_t poll_target;
uint32_t version;
uint32_t max_sources;
int32_t min_samples;
int32_t max_samples;
uint32_t authkey; uint32_t authkey;
Float max_delay; Float max_delay;
Float max_delay_ratio; Float max_delay_ratio;
Float max_delay_dev_ratio;
Float min_delay;
Float asymmetry;
Float offset;
uint32_t flags; uint32_t flags;
uint32_t reserved[4];
int32_t EOR; int32_t EOR;
} REQ_NTP_Source; } REQ_NTP_Source;
@@ -282,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;
@@ -293,44 +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;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPData;
/* ================================================== */ /* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1 #define PKT_TYPE_CMD_REQUEST 1
@@ -359,17 +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) and manual timestamp, new fields and flags
in NTP source request and report, new commands: ntpdata, refresh,
serverstats
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -383,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
/* ================================================== */ /* ================================================== */
@@ -398,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;
@@ -415,30 +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;
REQ_NTPData ntp_data;
} 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;
/* ================================================== */ /* ================================================== */
@@ -463,12 +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 RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
#define N_REPLY_TYPES 18
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -515,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;
@@ -538,7 +536,7 @@ typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
uint16_t stratum; uint16_t stratum;
uint16_t leap_status; uint16_t leap_status;
Timespec ref_time; Timeval ref_time;
Float current_correction; Float current_correction;
Float last_offset; Float last_offset;
Float rms_offset; Float rms_offset;
@@ -566,7 +564,7 @@ typedef struct {
} RPY_Sourcestats; } RPY_Sourcestats;
typedef struct { typedef struct {
Timespec ref_time; Timeval ref_time;
uint16_t n_samples; uint16_t n_samples;
uint16_t n_runs; uint16_t n_runs;
uint32_t span_seconds; uint32_t span_seconds;
@@ -576,7 +574,7 @@ typedef struct {
} RPY_Rtc; } RPY_Rtc;
typedef struct { typedef struct {
Float offset; uint32_t centiseconds;
Float dfreq_ppm; Float dfreq_ppm;
Float new_afreq_ppm; Float new_afreq_ppm;
int32_t EOR; int32_t EOR;
@@ -584,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;
@@ -604,19 +605,10 @@ 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 {
Timespec when; Timeval when;
Float slewed_offset; Float slewed_offset;
Float orig_offset; Float orig_offset;
Float residual; Float residual;
@@ -637,52 +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;
#define RPY_NTP_FLAGS_TESTS 0x3ff
#define RPY_NTP_FLAG_INTERLEAVED 0x4000
#define RPY_NTP_FLAG_AUTHENTICATED 0x8000
typedef struct {
IPAddr remote_addr;
IPAddr local_addr;
uint16_t remote_port;
uint8_t leap;
uint8_t version;
uint8_t mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
Float root_delay;
Float root_dispersion;
uint32_t ref_id;
Timespec ref_time;
Float offset;
Float peer_delay;
Float peer_dispersion;
Float response_time;
Float jitter_asymmetry;
uint16_t flags;
uint8_t tx_tss_char;
uint8_t rx_tss_char;
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
typedef struct { typedef struct {
uint8_t version; uint8_t version;
uint8_t pkt_type; uint8_t pkt_type;
@@ -695,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;
@@ -707,13 +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;
RPY_NTPData ntp_data;
} 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.

5154
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.

2345
client.c

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -31,20 +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_GetClientIndex(IPAddr *client); extern void CLG_LogNTPClientAccess(IPAddr *client, time_t now);
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now); extern void CLG_LogNTPPeerAccess(IPAddr *client, time_t now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index); /* When logging command packets, there are several subtypes */
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts); typedef enum {
extern int CLG_GetNtpMinPoll(void); 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 timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */ #endif /* GOT_CLIENTLOG_H */

1490
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,164 +34,164 @@
#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"
/* ================================================== */ /* ================================================== */
int 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; int ok, n, done;
CPS_Status result;
src->port = SRC_DEFAULT_PORT; src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL; src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL; src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
src->params.online = 1;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL; src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.version = 0;
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.authkey = INACTIVE_AUTHKEY; src->params.authkey = INACTIVE_AUTHKEY;
src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.min_delay = 0.0; src->params.online = 1;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY; src->params.auto_offline = 0;
src->params.offset = 0.0; src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
src->params.sel_option = SRC_SelectNormal;
result = CPS_Success;
hostname = line; hostname = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (!*hostname) if (!*hostname) {
return 0; result = CPS_BadHost;
ok = 0;
src->name = hostname; } else {
/* Parse subfields */
/* Parse options */ ok = 1;
for (; *line; line += n) { done = 0;
do {
cmd = line; cmd = line;
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
n = 0;
if (!strcasecmp(cmd, "auto_offline")) { if (*cmd) {
src->params.auto_offline = 1; if (!strcasecmp(cmd, "port")) {
} else if (!strcasecmp(cmd, "iburst")) { if (sscanf(line, "%hu%n", &src->port, &n) != 1) {
src->params.iburst = 1; result = CPS_BadPort;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1) {
result = CPS_BadMinpoll;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) {
result = CPS_BadMaxpoll;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1) {
result = CPS_BadPresend;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) {
result = CPS_BadMaxdelaydevratio;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) {
result = CPS_BadMaxdelayratio;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1) {
result = CPS_BadMaxdelay;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) {
result = CPS_BadKey;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "offline")) { } else if (!strcasecmp(cmd, "offline")) {
src->params.online = 0; src->params.online = 0;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT; } else if (!strcasecmp(cmd, "auto_offline")) {
} else if (!strcasecmp(cmd, "prefer")) { src->params.auto_offline = 1;
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) { } else if (!strcasecmp(cmd, "iburst")) {
src->params.sel_options |= SRC_SELECT_REQUIRE; src->params.iburst = 1;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
return 0;
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelayratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsamples")) {
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxsources")) {
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "mindelay")) {
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minpoll")) {
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "minsamples")) {
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
return 0;
} 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) {
return 0; result = CPS_BadMinstratum;
} else if (!strcasecmp(cmd, "offset")) { ok = 0;
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1) done = 1;
return 0;
} else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%hu%n", &src->port, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "presend")) {
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else { } else {
return 0;
}
}
return 1;
}
/* ================================================== */
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; line += n;
} }
return 1; } else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1) {
result = CPS_BadPolltarget;
ok = 0;
done = 1;
} else {
line += n;
}
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_option = SRC_SelectNoselect;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_option = SRC_SelectPrefer;
} else {
result = CPS_BadOption;
ok = 0;
done = 1;
}
} else {
done = 1;
}
} while (!done);
}
if (ok) {
src->name = strdup(hostname);
}
return result;
} }
/* ================================================== */ /* ================================================== */
@@ -254,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;
@@ -267,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

@@ -30,6 +30,22 @@
#include "srcparams.h" #include "srcparams.h"
#include "addressing.h" #include "addressing.h"
typedef enum {
CPS_Success,
CPS_BadOption,
CPS_BadHost,
CPS_BadPort,
CPS_BadMinpoll,
CPS_BadMaxpoll,
CPS_BadPresend,
CPS_BadMaxdelaydevratio,
CPS_BadMaxdelayratio,
CPS_BadMaxdelay,
CPS_BadKey,
CPS_BadMinstratum,
CPS_BadPolltarget
} CPS_Status;
typedef struct { typedef struct {
char *name; char *name;
unsigned short port; unsigned short port;
@@ -37,10 +53,7 @@ typedef struct {
} CPS_NTP_Source; } CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */ /* Parse a command to add an NTP server or peer */
extern int 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);
/* Remove extra white-space and comments */ /* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line); extern void CPS_NormalizeLine(char *line);
@@ -49,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 */

974
conf.c

File diff suppressed because it is too large Load Diff

47
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, int client_only); 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);
@@ -52,7 +48,7 @@ extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void); extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void); extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void); extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(int *raw); extern int CNF_GetLogMeasurements(void);
extern int CNF_GetLogStatistics(void); extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void); extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void); extern int CNF_GetLogRtc(void);
@@ -60,13 +56,16 @@ 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_GetManualEnabled(void); extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void); extern int CNF_GetCommandPort(void);
extern int CNF_GetRtcOnUtc(void); 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);
@@ -74,67 +73,37 @@ 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_GetNtpSigndSocket(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_GetMaxJitter(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);
extern int CNF_GetInitSources(void); extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void); extern double CNF_GetInitStepThreshold(void);
typedef enum {
CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter;
typedef struct {
char *name;
int minpoll;
int nocrossts;
CNF_HwTs_RxFilter rxfilter;
double precision;
double tx_comp;
double rx_comp;
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
#endif /* GOT_CONF_H */ #endif /* GOT_CONF_H */

544
configure vendored
View File

@@ -4,13 +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) Bryan Christianson 2016 # Copyright (C) Miroslav Lichvar 2009, 2012-2014
# Copyright (C) Miroslav Lichvar 2009, 2012-2016
# #
# ======================================================================= # =======================================================================
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
@@ -22,10 +43,9 @@ test_code () {
ldflags=$4 ldflags=$4
code=$5 code=$5
printf "%s" "Checking for $name : " echo -n "Checking for $name : "
( (
echo "#include \"config.h\""
for h in $headers; do for h in $headers; do
echo "#include <$h>" echo "#include <$h>"
done done
@@ -81,34 +101,22 @@ For better control, use the options below.
--disable-readline Disable line editing support --disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available --without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available --without-editline Don't use editline even if it is available
--with-readline-includes=DIR Specify where readline include directory is --readline-dir=DIR Specify parent of readline include and lib directories
--with-readline-library=DIR Specify where readline lib directory is --readline-inc-dir=DIR Specify where readline include 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
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
--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-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
--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
@@ -117,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]
--chronyrundir=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:
@@ -159,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
}
#}}}
# ====================================================================== # ======================================================================
@@ -190,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=""
@@ -219,23 +203,15 @@ try_setsched=0
try_lockmem=0 try_lockmem=0
feat_asyncdns=1 feat_asyncdns=1
feat_forcednsretry=1 feat_forcednsretry=1
try_clock_gettime=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
feat_ntp_signd=0
ntp_era_split="" ntp_era_split=""
default_user="root" default_user="root"
default_hwclockfile=""
default_pidfile="/var/run/chronyd.pid"
default_rtcdevice="/dev/rtc"
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
@@ -273,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/^.*=//;'`
;; ;;
@@ -282,21 +261,9 @@ do
--localstatedir=* ) --localstatedir=* )
SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'` SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--chronyrundir=* | --chronysockdir=* )
SETCHRONYRUNDIR=`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
;; ;;
@@ -309,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
@@ -330,36 +285,15 @@ do
--disable-forcednsretry) --disable-forcednsretry)
feat_forcednsretry=0 feat_forcednsretry=0
;; ;;
--without-clock-gettime)
try_clock_gettime=0
;;
--disable-timestamping)
feat_timestamping=0
;;
--enable-ntp-signd)
feat_ntp_signd=1
;;
--with-ntp-era=* ) --with-ntp-era=* )
ntp_era_split=`echo $option | sed -e 's/^.*=//;'` ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
;; ;;
--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-pidfile=* )
default_pidfile=`echo $option | sed -e 's/^.*=//;'`
;;
--with-rtcdevice=* )
default_rtcdevice=`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
;; ;;
@@ -384,162 +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_timestamping=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"
# recvmmsg() seems to be broken on FreeBSD 11.0 and it's just # FIXME: Should really test for GCC
# a wrapper around recvmsg() MYCFLAGS="$MYCFLAGS -mieee"
try_recvmmsg=0
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
add_def FREEBSD
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
major=`echo $VERSION | cut -d. -f1`
# ntp_adjtime is not available in macOS 10.12 (Darwin 16.x.x) and earlier
if [ $major -gt "16" ]; then
add_def HAVE_MACOS_SYS_TIMEX
EXTRA_OBJECTS="$EXTRA_OBJECTS sys_generic.o sys_netbsd.o sys_timex.o"
if [ $feat_droproot = "1" ]; then
priv_ops="$priv_ops ADJUSTTIMEX"
fi
fi
echo "Configuring for macOS (" $SYSTEM "macOS 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"
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
fi
else
feat_asyncdns=0
feat_timestamping=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
for cc in gcc clang cc ""; do
if [ "x$cc" = "x" ]; then
echo "error: no C compiler found"
exit 1
fi
MYCC=$cc
if test_code "$MYCC" '' '' '' ''; then
break
fi
done
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"
TESTCFLAGS="-D_FORTIFY_SOURCE=2 -fPIE"
TESTLDFLAGS="-pie -Wl,-z,relro,-z,now"
if test_code 'hardening compiler options' '' "$TESTCFLAGS" "$TESTLDFLAGS" ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
MYLDFLAGS="$MYLDFLAGS $TESTLDFLAGS"
fi
TESTCFLAGS="-fstack-protector-strong --param=ssp-buffer-size=4"
if test_code '-fstack-protector-strong' '' "$TESTCFLAGS" '' ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
else
TESTCFLAGS="-fstack-protector --param=ssp-buffer-size=4"
if test_code '-fstack-protector' '' "$TESTCFLAGS" '' ''; then
MYCFLAGS="$MYCFLAGS $TESTCFLAGS"
fi
fi
fi
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; 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];'
@@ -551,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))
@@ -581,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
@@ -623,22 +465,7 @@ then
fi fi
fi fi
if [ $try_clock_gettime = "1" ]; then if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' '' \
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
fi
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);' 'return getaddrinfo(0, 0, 0, 0);'
then then
add_def HAVE_GETADDRINFO add_def HAVE_GETADDRINFO
@@ -650,61 +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
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
add_def HAVE_GETRANDOM
fi
RECVMMSG_CODE='
struct mmsghdr hdr;
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
if [ $try_recvmmsg = "1" ]; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
add_def HAVE_RECVMMSG
else
if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
"$EXTRA_LIBS" "$RECVMMSG_CODE"
then
add_def _GNU_SOURCE
add_def HAVE_RECVMMSG
fi
fi
fi
if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
test_code 'SW/HW timestamping' 'sys/types.h sys/socket.h linux/net_tstamp.h
linux/errqueue.h linux/ptp_clock.h' '' '' '
int val = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_OPT_CMSG;
return sizeof (struct scm_timestamping) + SCM_TSTAMP_SND + PTP_SYS_OFFSET +
setsockopt(0, SOL_SOCKET, SO_SELECT_ERR_QUEUE + SO_TIMESTAMPING,
&val, sizeof (val));'
then
add_def HAVE_LINUX_TIMESTAMPING
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
if test_code 'other timestamping options' \
'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' '
struct scm_ts_pktinfo pktinfo;
pktinfo.if_index = pktinfo.pkt_length = 0;
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW 1
fi
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
@@ -723,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 RELOADDNS"
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);'
@@ -772,14 +522,20 @@ 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" ] && \
grep '#define HAVE_CLOCK_GETTIME' config.h > /dev/null && \
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 + PTP_SYS_OFFSET, 0);' 'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
then then
grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null || if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o"
add_def FEAT_PHC add_def FEAT_PHC
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(0, NULL);'
then
EXTRA_LIBS="$EXTRA_LIBS -lrt"
add_def FEAT_PHC
fi
fi
fi fi
if [ $try_setsched = "1" ] && \ if [ $try_setsched = "1" ] && \
@@ -809,6 +565,7 @@ then
add_def FORCE_DNSRETRY add_def FORCE_DNSRETRY
fi fi
READLINE_COMPILE=""
READLINE_LINK="" READLINE_LINK=""
if [ $feat_readline = "1" ]; then if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then if [ $try_editline = "1" ]; then
@@ -818,7 +575,7 @@ if [ $feat_readline = "1" ]; then
then then
add_def FEAT_READLINE add_def FEAT_READLINE
add_def USE_EDITLINE add_def USE_EDITLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc" READLINE_COMPILE="$readline_inc"
READLINE_LINK="$readline_lib -ledit" READLINE_LINK="$readline_lib -ledit"
fi fi
fi fi
@@ -829,7 +586,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));' 'add_history(readline("prompt"));'
then then
add_def FEAT_READLINE add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc" READLINE_COMPILE="$readline_inc"
READLINE_LINK="$readline_lib -lreadline" READLINE_LINK="$readline_lib -lreadline"
fi fi
fi fi
@@ -841,7 +598,7 @@ if [ $feat_readline = "1" ]; then
'add_history(readline("prompt"));' 'add_history(readline("prompt"));'
then then
add_def FEAT_READLINE add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc" READLINE_COMPILE="$readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses" READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi fi
fi fi
@@ -850,9 +607,10 @@ if [ $feat_readline = "1" ]; then
fi fi
HASH_OBJ="hash_intmd5.o" HASH_OBJ="hash_intmd5.o"
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' \
@@ -860,22 +618,22 @@ if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));' 'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
then then
HASH_OBJ="hash_nss.o" HASH_OBJ="hash_nss.o"
HASH_COMPILE="$test_cflags"
HASH_LINK="$test_link" HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags" add_def GENERATE_SHA1_KEY
add_def FEAT_SECHASH
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
HASH_OBJ="hash_tomcrypt.o" HASH_OBJ="hash_tomcrypt.o"
HASH_COMPILE="-I/usr/include/tomcrypt"
HASH_LINK="-ltomcrypt" HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt" add_def GENERATE_SHA1_KEY
add_def FEAT_SECHASH
fi fi
fi fi
@@ -909,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
@@ -924,40 +687,23 @@ if [ "x$SETLOCALSTATEDIR" != "x" ]; then
LOCALSTATEDIR=$SETLOCALSTATEDIR LOCALSTATEDIR=$SETLOCALSTATEDIR
fi fi
CHRONYRUNDIR=${LOCALSTATEDIR}/run/chrony
if [ "x$SETCHRONYRUNDIR" != "x" ]; then
CHRONYRUNDIR=$SETCHRONYRUNDIR
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_PID_FILE "\"$default_pidfile\""
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
add_def DEFAULT_USER "\"$default_user\"" add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/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 SIGND 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}%;\
@@ -968,20 +714,18 @@ do
s%@LDFLAGS@%${MYLDFLAGS}%;\ s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
s%@HASH_OBJ@%${HASH_OBJ}%;\ s%@HASH_OBJ@%${HASH_OBJ}%;\
s%@HASH_COMPILE@%${HASH_COMPILE}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\ s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\ s%@BINDIR@%${BINDIR}%;\
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%@CHRONYRUNDIR@%${CHRONYRUNDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\ s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\ s%@DEFAULT_USER@%${default_user}%;"\
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
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
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 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 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,76 +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@
CHRONYRUNDIR = @CHRONYRUNDIR@
CHRONYVARDIR = @CHRONYVARDIR@
CHRONY_VERSION = @CHRONY_VERSION@
DEFAULT_USER = @DEFAULT_USER@
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
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_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%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,184 +0,0 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2009-2017
//
// 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 written to the terminal instead of syslog. When
*chronyd* was compiled with debugging support, this option can be used twice to
print also debugging messages.
*-l* _file_::
This option specifies a file which should be used for logging instead of syslog
or terminal.
*-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 the *-q* option, except it only prints the offset
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
*-r*::
This option will try to reload and then delete files containing sample
histories for each of the servers and reference clocks 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,
Solaris, and macOS 10.13 or later).
*-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).
*-t* _timeout_::
This option sets a timeout (in seconds) after which *chronyd* will exit. If the
clock is not synchronised, it will exit with a non-zero status. This is useful
with the *-q* or *-Q* option to shorten the maximum time waiting for
measurements, or with the *-r* option to limit the time when *chronyd* is
running, but still allow it to adjust the frequency of the system clock.
*-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 macOS, 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 macOS, 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.
*-x*::
This option disables the control of the system clock. *chronyd* will not make
any adjustments of the clock, but it will still track its offset and frequency
relative to the estimated true time, and be able to operate as an NTP server.
This allows *chronyd* to run without the capability to adjust or set the system
clock (e.g. in some containers).
*-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,444 +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 stabilise 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 initialised 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
macOS.
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 access from 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 force `chronyd` to try to resolve the names 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.
If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the *hwtimestamp* directive in the _chrony.conf_ file. It
should make local receive and transmit timestamps of NTP packets much more
accurate.
There are also useful options which can be set in the `server` directive, they
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`,
`maxdelaydevratio`, and `xleave`.
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 8 (samples) for `polltarget`. The default values should be used
for general servers on the Internet. With your own NTP servers, or if you 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
----
If your server supports the interleaved mode, the `xleave` option should be
added to the `server` directive in order to allow the server to send the
client more accurate hardware or kernel transmit timestamps. When combined with
local hardware timestamping, sub-microsecond accuracy may be possible. An
example could be
----
server ntp.local minpoll 2 maxpoll 2 xleave
hwtimestamp eth0
----
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
With the `-Q` option it will print the measured offset without setting the
clock. If you don't want to use a configuration file, NTP servers can be
specified on the command line. For example:
----
# chronyd -q 'pool pool.ntp.org iburst'
----
=== 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. The `activity` command prints the number of sources that are
currently online and offline. For example:
----
200 OK
3 sources online
0 sources offline
0 sources doing burst (return to online)
0 sources doing burst (return to offline)
0 sources with unknown address
----
=== 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 in versions before 3.0 it was printed in
quad-dotted notation, even if the reference source did not actually 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.
Since version 3.0, the reference ID is printed as a hexadecimal number to avoid
confusion with IPv4 addresses.
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 initialise 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 initialised 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?
Yes. Starting from version 3.0, an offset can be specified by the `offset`
option for all time sources in the _chrony.conf_ file.
=== What happens if the network connection is dropped without using ``chronyc``'s `offline` command first?
`chronyd` will keep trying to access the sources that it thinks are online, and
it will take longer before new measurements are actually made and the clock is
corrected when the network is connected again. If the sources were set to
offline, `chronyd` would make new measurements immediately after issuing the
`online` command.
Unless the network connection lasts only few minutes (less than the maximum
polling interval), the delay is usually not a problem, and it may be acceptable
to keep all sources online all the time.
== 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

@@ -1,6 +1,5 @@
[Unit] [Unit]
Description=Wait for chrony to synchronize system clock Description=Wait for chrony to synchronize system clock
Documentation=man:chronyc(1)
After=chronyd.service After=chronyd.service
Requires=chronyd.service Requires=chronyd.service
Before=time-sync.target Before=time-sync.target
@@ -10,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 -h 127.0.0.1,::1 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,36 +43,44 @@
# 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
# 'offline' means that the server is not visible at boot time. Use
# chronyc's 'online' command to tell chronyd that these servers have
# become visible after you go on-line.
! server 0.pool.ntp.org offline
! server 1.pool.ntp.org offline
! server 2.pool.ntp.org offline
# 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
# have the 'front-line' interface to the public NTP servers, you can
# 'peer' these machines together to increase robustness.
! peer ntp0.my-company.com
# There are other options to the 'server' and 'peer' directives that you
# might want to use. For example, you can ignore measurements whose
# round-trip-time is too large (indicating that the measurement is
# probably useless, because you don't know which way the measurement
# message got held up.) Consult the full documentation for details.
####################################################################### #######################################################################
### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK ### AVOIDING POTENTIALLY BOGUS CHANGES TO YOUR CLOCK
# #
# To avoid changes being made to your computer's gain/loss compensation # To avoid changes being made to your computer's gain/loss compensation
# when the measurement history is too erratic, you might want to enable # when the measurement history is too erratic, you might want to enable
# one of the following lines. The first seems good with servers on the # one of the following lines. The first seems good for dial-up (or
# Internet, the second seems OK for a LAN environment. # other high-latency connections like slow leased lines), the second
# seems OK for a LAN environment.
! maxupdateskew 100 ! maxupdateskew 100
! maxupdateskew 5 ! maxupdateskew 5
# If you want to increase the minimum number of selectable sources
# required to update the system clock in order to make the
# synchronisation more reliable, uncomment (and edit) the following
# line.
! minsources 2
# If your computer has a good stable clock (e.g. it is not a virtual
# machine), you might also want to reduce the maximum assumed drift
# (frequency error) of the clock (the value is specified in ppm).
! maxdrift 100
####################################################################### #######################################################################
### FILENAMES ETC ### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files. # Chrony likes to keep information about your computer's clock in files.
@@ -67,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:
@@ -100,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
@@ -169,22 +206,16 @@ driftfile /var/lib/chrony/drift
# machine accesses it. The information can be accessed by the 'clients' # machine accesses it. The information can be accessed by the 'clients'
# command of chronyc. You can disable this facility by uncommenting the # command of chronyc. You can disable this facility by uncommenting the
# following line. This will save a bit of memory if you have many # following line. This will save a bit of memory if you have many
# clients and it will also disable support for the interleaved mode. # clients.
! noclientlog ! noclientlog
# The clientlog size is limited to 512KB by default. If you have many # The clientlog size is limited to 512KB by default. If you have many
# clients, you might want to increase the limit. # clients, especially in many different subnets, you might want to
# increase the limit.
! 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.
! ratelimit 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
@@ -202,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
@@ -227,20 +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 -4 burst 16 # default all you can do is view information about chronyd's operation.
#######################################################################
### HARDWARE TIMESTAMPING
# On Linux, if the network interface controller and its driver support
# hardware timestamping, it can significantly improve the accuracy of
# synchronisation. It can be enabled on specified interfaces only, or it
# can be enabled on all interfaces that support it.
! hwtimestamp eth0
! hwtimestamp *
####################################################################### #######################################################################
### REAL TIME CLOCK ### REAL TIME CLOCK
@@ -271,12 +286,6 @@ driftfile /var/lib/chrony/drift
! rtcdevice /dev/misc/rtc ! rtcdevice /dev/misc/rtc
# Alternatively, if not using the -s option, this directive can be used
# to enable a mode in which the RTC is periodically set to the system
# time, with no tracking of its drift.
! rtcsync
####################################################################### #######################################################################
### REAL TIME SCHEDULER ### REAL TIME SCHEDULER
# This directive tells chronyd to use the real-time FIFO scheduler with the # This directive tells chronyd to use the real-time FIFO scheduler with the

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
# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

View File

@@ -1,35 +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
# Allow the system clock to be stepped in the first three updates # Enable kernel RTC synchronization.
# if its offset is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync rtcsync
# Enable hardware timestamping on all interfaces that support it. # In first three updates step the system clock instead of slew
#hwtimestamp * # if the adjustment is larger than 10 seconds.
makestep 10 3
# Increase the minimum number of selectable sources required to adjust
# the system clock.
#minsources 2
# Allow NTP client access from local network. # Allow NTP client access from local network.
#allow 192.168.0.0/16 #allow 192.168/16
# Serve time even if not synchronized to a time source. # Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
# 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

@@ -1,35 +1,17 @@
#!/bin/sh #!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources # This is a NetworkManager dispatcher script for chronyd to set its NTP sources
# online or offline when a network interface is configured or removed # online/offline when a default route is configured/removed on the system.
export LC_ALL=C export LC_ALL=C
# Check if there is a default route if [ "$2" = "up" ]; then
/sbin/ip route list dev "$1" | grep -q '^default' &&
if /sbin/ip route list 2> /dev/null | grep -q '^default'; then /usr/bin/chronyc -a online > /dev/null 2>&1
chronyc online > /dev/null 2>&1
exit 0
fi fi
sources=$(chronyc -c -n sources 2> /dev/null) if [ "$2" = "down" ]; then
/sbin/ip route list | grep -q '^default' ||
[ $? -ne 0 ] && exit 0 /usr/bin/chronyc -a offline > /dev/null 2>&1
fi
# Check each configured source if it has a route
echo "$sources" | while IFS=, read mode state address rest; do
[ "$mode" != '^' ] && [ "$mode" != '=' ] && continue
/sbin/ip route get "$address" > /dev/null 2>&1 && command="online" || command="offline"
# Set priority of sources so that the selected source is set as
# last if offline to avoid unnecessary reselection
[ "$state" != '*' ] && priority=1 || priority=2
echo "$priority $command $address"
done | sort | while read priority command address; do
echo "$command $address"
done | chronyc > /dev/null 2>&1
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

View File

@@ -1,18 +1,13 @@
[Unit] [Unit]
Description=NTP client/server Description=NTP client/server
Documentation=man:chronyd(8) man:chrony.conf(5)
After=ntpdate.service sntp.service ntpd.service After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service Conflicts=ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service] [Service]
Type=forking Type=forking
PIDFile=/var/run/chronyd.pid PIDFile=/var/run/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS ExecStart=/usr/sbin/chronyd $OPTIONS
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

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)
{
}

213
hwclock.c
View File

@@ -1,213 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2017
*
* 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.
*
**********************************************************************
=======================================================================
Tracking of hardware clocks (e.g. RTC, PHC)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "regress.h"
#include "util.h"
/* Maximum number of samples per clock */
#define MAX_SAMPLES 16
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
struct timespec local_ref;
/* Samples stored as intervals (uncorrected for frequency error)
relative to local_ref and hw_ref */
double x_data[MAX_SAMPLES];
double y_data[MAX_SAMPLES];
/* Number of samples */
int n_samples;
/* Maximum error of the last sample */
double last_err;
/* Minimum interval between samples */
double min_separation;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
double frequency;
};
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
HCL_Instance clock;
double delta;
clock = anything;
if (clock->n_samples)
UTI_AdjustTimespec(&clock->local_ref, cooked, &clock->local_ref, &delta, dfreq, doffset);
if (clock->valid_coefs)
clock->frequency /= 1.0 - dfreq;
}
/* ================================================== */
HCL_Instance
HCL_CreateInstance(double min_separation)
{
HCL_Instance clock;
clock = MallocNew(struct HCL_Instance_Record);
clock->x_data[MAX_SAMPLES - 1] = 0.0;
clock->y_data[MAX_SAMPLES - 1] = 0.0;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
LCL_AddParameterChangeHandler(handle_slew, clock);
return clock;
}
/* ================================================== */
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
Free(clock);
}
/* ================================================== */
int
HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
{
if (!clock->n_samples ||
fabs(UTI_DiffTimespecsToDouble(now, &clock->local_ref)) >= clock->min_separation)
return 1;
return 0;
}
/* ================================================== */
void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err)
{
double hw_delta, local_delta, local_freq, raw_freq;
int i, n_runs, best_start;
local_freq = 1.0 - LCL_ReadAbsoluteFrequency() / 1.0e6;
/* Shift old samples */
if (clock->n_samples) {
if (clock->n_samples >= MAX_SAMPLES)
clock->n_samples--;
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
local_delta = UTI_DiffTimespecsToDouble(local_ts, &clock->local_ref) / local_freq;
if (hw_delta <= 0.0 || local_delta < clock->min_separation / 2.0) {
clock->n_samples = 0;
DEBUG_LOG("HW clock reset interval=%f", local_delta);
}
for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
}
}
clock->n_samples++;
clock->hw_ref = *hw_ts;
clock->local_ref = *local_ts;
clock->last_err = err;
/* Get new coefficients */
clock->valid_coefs =
RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
clock->y_data + MAX_SAMPLES - clock->n_samples,
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start);
if (!clock->valid_coefs) {
DEBUG_LOG("HW clock needs more samples");
return;
}
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
clock->n_samples -= best_start;
/* If the fit doesn't cross the error interval of the last sample,
or the frequency is not sane, drop all samples and start again */
if (fabs(clock->offset) > err ||
fabs(clock->frequency - 1.0) > MAX_FREQ_OFFSET) {
DEBUG_LOG("HW clock reset");
clock->n_samples = 0;
clock->valid_coefs = 0;
}
DEBUG_LOG("HW clock samples=%d offset=%e freq=%e raw_freq=%e err=%e ref_diff=%e",
clock->n_samples, clock->offset, clock->frequency - 1.0, raw_freq - 1.0, err,
UTI_DiffTimespecsToDouble(&clock->hw_ref, &clock->local_ref));
}
/* ================================================== */
int
HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked, double *err)
{
double offset, elapsed;
if (!clock->valid_coefs)
return 0;
elapsed = UTI_DiffTimespecsToDouble(raw, &clock->hw_ref);
offset = elapsed / clock->frequency - clock->offset;
UTI_AddDoubleToTimespec(&clock->local_ref, offset, cooked);
/* Fow now, just return the error of the last sample */
if (err)
*err = clock->last_err;
return 1;
}

View File

@@ -1,48 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* 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.
*
**********************************************************************
=======================================================================
Header for tracking of hardware clocks */
#ifndef GOT_HWCLOCK_H
#define GOT_HWCLOCK_H
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);
/* Convert raw hardware time to cooked local time */
extern int HCL_CookTime(HCL_Instance clock, struct timespec *raw, struct timespec *cooked,
double *err);
#endif

327
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,77 +137,37 @@ 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 timespec before, after; struct timeval before, after;
double diff, min_diff; unsigned long usecs, min_usecs=0;
int i, nsecs; int i;
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);
diff = UTI_DiffTimespecsToDouble(&after, &before); usecs = (after.tv_sec - before.tv_sec) * 1000000 + (after.tv_usec - before.tv_usec);
if (i == 0 || min_diff > diff) if (i == 0 || usecs < min_usecs) {
min_diff = diff; min_usecs = usecs;
}
} }
/* Add on a bit extra to allow for copying, conversions etc */ /* Add on a bit extra to allow for copying, conversions etc */
nsecs = 1.0625e9 * min_diff; min_usecs += min_usecs >> 4;
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs); DEBUG_LOG(LOGF_Keys, "authentication delay for key %lu: %ld useconds", key_id, min_usecs);
return nsecs; return min_usecs;
}
/* ================================================== */
/* Decode password encoded in ASCII or HEX */
static int
decode_password(char *key)
{
int i, j, len = strlen(key);
char buf[3], *p;
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
if ((len - 4) % 2)
return 0;
for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
key[i] = strtol(buf, &p, 16);
if (p != buf + 2)
return 0;
}
return i;
} else {
/* assume ASCII */
return len;
}
} }
/* ================================================== */ /* ================================================== */
@@ -183,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;
@@ -200,7 +216,7 @@ KEY_Reload(void)
in = fopen(key_file, "r"); in = fopen(key_file, "r");
if (!in) { if (!in) {
LOG(LOGS_WARN, "Could not open keyfile %s", key_file); LOG(LOGS_WARN, LOGF_Keys, "Could not open keyfile %s", key_file);
return; return;
} }
@@ -212,26 +228,26 @@ KEY_Reload(void)
continue; continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) { if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, "Could not parse key at line %d in file %s", line_number, key_file); LOG(LOGS_WARN, LOGF_Keys, "Could not parse key at line %d in file %s", line_number, key_file);
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, "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 = decode_password(keyval); keys[n_keys].len = UTI_DecodePasswordFromText(keyval);
if (!key.len) { if (!keys[n_keys].len) {
LOG(LOGS_WARN, "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);
@@ -239,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, "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);
@@ -290,122 +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_GetAuthLength(uint32_t key_id) KEY_GenerateAuth(unsigned long key_id, const unsigned char *data, int data_len,
{
unsigned char buf[MAX_HASH_LENGTH];
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
}
/* ================================================== */
int
KEY_CheckKeyLength(uint32_t key_id)
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return key->len >= MIN_SECURE_KEY_LENGTH;
}
/* ================================================== */
static int
generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
unsigned char *auth, int auth_len) unsigned char *auth, int auth_len)
{ {
return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len); int key_pos;
}
/* ================================================== */ key_pos = get_key_pos(key_id);
static int if (key_pos < 0) {
check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
/* ================================================== */
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0; return 0;
}
return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len, return UTI_GenerateNTPAuth(keys[key_pos].hash_id,
(unsigned char *)keys[key_pos].val, keys[key_pos].len,
data, data_len, auth, auth_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, int trunc_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 check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len, return UTI_CheckNTPAuth(keys[key_pos].hash_id,
data, data_len, auth, auth_len, trunc_len); (unsigned char *)keys[key_pos].val, keys[key_pos].len,
data, data_len, auth, auth_len);
} }

18
keys.h
View File

@@ -27,22 +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_GetAuthLength(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, int data_len, extern int KEY_CheckAuth(unsigned long key_id, const unsigned char *data,
const unsigned char *auth, int auth_len, int trunc_len); int data_len, const unsigned char *auth, int auth_len);
#endif /* GOT_KEYS_H */ #endif /* GOT_KEYS_H */

229
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;
/* ================================================== */ /* ================================================== */
@@ -106,47 +101,40 @@ static double max_clock_error;
under 1s of busy waiting. */ under 1s of busy waiting. */
#define NITERS 100 #define NITERS 100
#define NSEC_PER_SEC 1000000000
static void static void
calculate_sys_precision(void) calculate_sys_precision(void)
{ {
struct timespec ts, old_ts; struct timeval tv, old_tv;
int iters, diff, best; int dusec, best_dusec;
int iters;
LCL_ReadRawTime(&old_ts); gettimeofday(&old_tv, NULL);
best_dusec = 1000000; /* Assume we must be better than a second */
/* Assume we must be better than a second */
best = NSEC_PER_SEC;
iters = 0; iters = 0;
do { do {
LCL_ReadRawTime(&ts); gettimeofday(&tv, NULL);
dusec = 1000000*(tv.tv_sec - old_tv.tv_sec) + (tv.tv_usec - old_tv.tv_usec);
diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec); old_tv = tv;
if (dusec > 0) {
old_ts = ts; if (dusec < best_dusec) {
if (diff > 0) { best_dusec = dusec;
if (diff < best) }
best = diff;
iters++; iters++;
} }
} while (iters < NITERS); } while (iters < NITERS);
assert(best > 0); assert(best_dusec > 0);
precision_quantum = 1.0e-9 * best; precision_quantum = best_dusec * 1.0e-6;
/* Get rounded log2 value of the measured precision */ /* Get rounded log2 value of the measured precision */
precision_log = 0; precision_log = 0;
while (best < 707106781) { while (best_dusec < 707107) {
precision_log--; precision_log--;
best *= 2; best_dusec *= 2;
} }
assert(precision_log >= -30); DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
} }
/* ================================================== */ /* ================================================== */
@@ -172,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;
} }
@@ -185,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);
} }
/* ================================================== */ /* ================================================== */
@@ -271,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);
} }
/* ================================================== */ /* ================================================== */
@@ -285,7 +261,7 @@ LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
/* ================================================== */ /* ================================================== */
static void static void
invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked, invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
double dfreq, double doffset, double dfreq, double doffset,
LCL_ChangeType change_type) LCL_ChangeType change_type)
{ {
@@ -348,33 +324,27 @@ 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);
}
/* ================================================== */
/* At the moment, this is just gettimeofday(), because
I can't think of a Unix system where it would not be */
void
LCL_ReadRawTime(struct timeval *result)
{
if (gettimeofday(result, NULL) < 0) {
LOG_FATAL(LOGF_Local, "gettimeofday() failed");
}
} }
/* ================================================== */ /* ================================================== */
void void
LCL_ReadRawTime(struct timespec *ts) LCL_ReadCookedTime(struct timeval *result, double *err)
{ {
#if HAVE_CLOCK_GETTIME struct timeval raw;
if (clock_gettime(CLOCK_REALTIME, ts) < 0)
LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
#else
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0)
LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
UTI_TimevalToTimespec(&tv, ts);
#endif
}
/* ================================================== */
void
LCL_ReadCookedTime(struct timespec *result, double *err)
{
struct timespec raw;
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, result, err); LCL_CookTime(&raw, result, err);
@@ -383,18 +353,18 @@ LCL_ReadCookedTime(struct timespec *result, double *err)
/* ================================================== */ /* ================================================== */
void void
LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err) LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err)
{ {
double correction; double correction;
LCL_GetOffsetCorrection(raw, &correction, err); LCL_GetOffsetCorrection(raw, &correction, err);
UTI_AddDoubleToTimespec(raw, correction, cooked); UTI_AddDoubleToTimeval(raw, correction, cooked);
} }
/* ================================================== */ /* ================================================== */
void void
LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err) LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err)
{ {
/* Call system specific driver to get correction */ /* Call system specific driver to get correction */
(*drv_offset_convert)(raw, correction, err); (*drv_offset_convert)(raw, correction, err);
@@ -419,44 +389,15 @@ LCL_ReadAbsoluteFrequency(void)
} }
/* ================================================== */ /* ================================================== */
static double
clamp_freq(double freq)
{
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq;
LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
}
/* ================================================== */
static int
check_offset(struct timespec *now, double offset)
{
/* Check if the time will be still sane with accumulated offset */
if (UTI_IsTimeOffsetSane(now, -offset))
return 1;
LOG(LOGS_WARN, "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 */
void void
LCL_SetAbsoluteFrequency(double afreq_ppm) LCL_SetAbsoluteFrequency(double afreq_ppm)
{ {
struct timespec 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;
@@ -483,7 +424,7 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void void
LCL_AccumulateDeltaFrequency(double dfreq) LCL_AccumulateDeltaFrequency(double dfreq)
{ {
struct timespec raw, cooked; struct timeval raw, cooked;
double old_freq_ppm; double old_freq_ppm;
old_freq_ppm = current_freq_ppm; old_freq_ppm = current_freq_ppm;
@@ -494,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);
@@ -512,7 +451,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
void void
LCL_AccumulateOffset(double offset, double corr_rate) LCL_AccumulateOffset(double offset, double corr_rate)
{ {
struct timespec raw, cooked; struct timeval raw, cooked;
/* In this case, the cooked time to be passed to the notify clients /* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */ has to be the cooked time BEFORE the change was made */
@@ -520,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 */
@@ -531,10 +467,10 @@ LCL_AccumulateOffset(double offset, double corr_rate)
/* ================================================== */ /* ================================================== */
int void
LCL_ApplyStepOffset(double offset) LCL_ApplyStepOffset(double offset)
{ {
struct timespec raw, cooked; struct timeval raw, cooked;
/* In this case, the cooked time to be passed to the notify clients /* In this case, the cooked time to be passed to the notify clients
has to be the cooked time BEFORE the change was made */ has to be the cooked time BEFORE the change was made */
@@ -542,27 +478,16 @@ 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, "Could not step system 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;
} }
/* ================================================== */ /* ================================================== */
void void
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked, LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
double offset, double dispersion) double offset, double dispersion)
{ {
/* Dispatch to all handlers */ /* Dispatch to all handlers */
@@ -573,27 +498,10 @@ LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
/* ================================================== */ /* ================================================== */
void
LCL_NotifyLeap(int leap)
{
struct timespec 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)
{ {
struct timespec raw, cooked; struct timeval raw, cooked;
double old_freq_ppm; double old_freq_ppm;
LCL_ReadRawTime(&raw); LCL_ReadRawTime(&raw);
@@ -601,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
@@ -611,9 +516,7 @@ 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("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset); old_freq_ppm, current_freq_ppm, doffset);
/* Call the system-specific driver for setting the frequency */ /* Call the system-specific driver for setting the frequency */
@@ -647,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;
@@ -656,11 +558,10 @@ 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)();
DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm); DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
} }
/* ================================================== */ /* ================================================== */
@@ -670,40 +571,28 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
int int
LCL_MakeStep(void) LCL_MakeStep(void)
{ {
struct timespec raw; struct timeval raw;
double correction; double correction;
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, "System clock was stepped by %.6f seconds", correction); LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
return 1; return 1;
} }
/* ================================================== */ /* ================================================== */
int
LCL_CanSystemLeap(void)
{
return drv_set_leap ? 1 : 0;
}
/* ================================================== */
void void
LCL_SetSystemLeap(int leap, int tai_offset) LCL_SetLeap(int leap)
{ {
if (drv_set_leap) { if (drv_set_leap) {
(drv_set_leap)(leap, tai_offset); (drv_set_leap)(leap);
} }
} }
@@ -736,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);
}
}
/* ================================================== */

38
local.h
View File

@@ -31,8 +31,9 @@
#include "sysincl.h" #include "sysincl.h"
/* Read the system clock */ /* Read the system clock. This is analogous to gettimeofday(),
extern void LCL_ReadRawTime(struct timespec *ts); but with the timezone information ignored */
extern void LCL_ReadRawTime(struct timeval *);
/* Read the system clock, corrected according to all accumulated /* Read the system clock, corrected according to all accumulated
drifts and uncompensated offsets. drifts and uncompensated offsets.
@@ -43,15 +44,15 @@ extern void LCL_ReadRawTime(struct timespec *ts);
adjtime()-like interface to correct offsets, and to adjust the adjtime()-like interface to correct offsets, and to adjust the
frequency), we must correct the raw time to get this value */ frequency), we must correct the raw time to get this value */
extern void LCL_ReadCookedTime(struct timespec *ts, double *err); extern void LCL_ReadCookedTime(struct timeval *t, double *err);
/* Convert raw time to cooked. */ /* Convert raw time to cooked. */
extern void LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err); extern void LCL_CookTime(struct timeval *raw, struct timeval *cooked, double *err);
/* Read the current offset between the system clock and true time /* Read the current offset between the system clock and true time
(i.e. 'cooked' - 'raw') (in seconds). */ (i.e. 'cooked' - 'raw') (in seconds). */
extern void LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err); extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err);
/* Type of routines that may be invoked as callbacks when there is a /* Type of routines that may be invoked as callbacks when there is a
change to the frequency or offset. change to the frequency or offset.
@@ -78,7 +79,7 @@ typedef enum {
} LCL_ChangeType; } LCL_ChangeType;
typedef void (*LCL_ParameterChangeHandler) typedef void (*LCL_ParameterChangeHandler)
(struct timespec *raw, struct timespec *cooked, (struct timeval *raw, struct timeval *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -158,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 timespec *raw, struct timespec *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);
@@ -197,15 +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 and also
set its TAI-UTC offset. 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, int tai_offset);
/* 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, int tai_offset);
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,18 +47,15 @@ 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
raw time to get the corrected time */ raw time to get the corrected time */
typedef void (*lcl_OffsetCorrectionDriver)(struct timespec *raw, double *corr, double *err); typedef void (*lcl_OffsetCorrectionDriver)(struct timeval *raw, double *corr, double *err);
/* System driver to schedule leap seconds and set TAI-UTC offset */ /* System driver to schedule leap second */
typedef void (*lcl_SetLeapDriver)(int leap, int tai_offset); 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);
@@ -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 */
@@ -40,7 +41,6 @@ int log_debug_enabled = 0;
/* Flag indicating we have initialised */ /* Flag indicating we have initialised */
static int initialised = 0; static int initialised = 0;
static FILE *file_log;
static int system_log = 0; static int system_log = 0;
static int parent_fd = 0; static int parent_fd = 0;
@@ -49,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;
@@ -70,7 +74,10 @@ void
LOG_Initialise(void) LOG_Initialise(void)
{ {
initialised = 1; initialised = 1;
file_log = stderr;
#ifdef WINNT
logfile = fopen("./chronyd.err", "a");
#endif
} }
/* ================================================== */ /* ================================================== */
@@ -79,11 +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();
} else {
fclose(file_log);
} }
#endif
LOG_CycleLogFiles(); LOG_CycleLogFiles();
@@ -94,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) {
@@ -117,34 +133,34 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
} }
syslog(priority, fatal ? "Fatal error : %s" : "%s", message); syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else { } else {
fprintf(file_log, 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,
int line_number, const char *filename, const char *function_name, const char *function_name, const char *format, ...)
#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(file_log, "%s ", buf); fprintf(stderr, "%s ", buf);
#if DEBUG > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION) if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(file_log, "%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);
@@ -177,31 +193,16 @@ void LOG_Message(LOG_Severity severity,
} }
} }
/* ================================================== */
void
LOG_OpenFileLog(const char *log_file)
{
FILE *f;
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
/* Enable line buffering */
setvbuf(f, NULL, _IOLBF, BUFSIZ);
file_log = f;
}
/* ================================================== */ /* ================================================== */
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
} }
/* ================================================== */ /* ================================================== */
@@ -210,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;
} }
} }
@@ -259,18 +262,12 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return; return;
if (!logfiles[id].file) { if (!logfiles[id].file) {
char filename[512], *logdir = CNF_GetLogDir(); char filename[512];
if (logdir[0] == '\0') {
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;
}
if (snprintf(filename, sizeof(filename), "%s/%s.log", if (snprintf(filename, sizeof(filename), "%s/%s.log",
logdir, logfiles[id].name) >= sizeof (filename) || CNF_GetLogDir(), logfiles[id].name) >= sizeof(filename) ||
!(logfiles[id].file = fopen(filename, "a"))) { !(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, "Could not open log file %s", filename); LOG(LOGS_WARN, LOGF_Refclock, "Couldn't open logfile %s for update", filename);
logfiles[id].name = NULL; logfiles[id].name = NULL;
return; return;
} }
@@ -305,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 DEBUG_LOG(facility, ...) \
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#else
#define LOG_MESSAGE(severity, ...) \
LOG_Message(severity, __VA_ARGS__)
#endif
#define DEBUG_LOG(...) \
do { \ do { \
if (DEBUG && log_debug_enabled) \ if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, __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(...) \ #define LOG_FATAL(facility, ...) \
do { \ do { \
LOG_MESSAGE(LOGS_FATAL, __VA_ARGS__); \ LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
exit(1); \ exit(1); \
} while (0) } while (0)
#define LOG(severity, ...) LOG_MESSAGE(severity, __VA_ARGS__)
/* Definition of severity */ /* Definition of severity */
typedef enum { typedef enum {
LOGS_INFO, LOGS_INFO,
@@ -76,6 +66,42 @@ typedef enum {
LOGS_DEBUG LOGS_DEBUG
} LOG_Severity; } LOG_Severity;
/* Definition of facility. Each message is tagged with who generated
it, so that the user can customise what level of reporting he gets
for each area of the software */
typedef enum {
LOGF_Reference,
LOGF_NtpIO,
LOGF_NtpCore,
LOGF_NtpSources,
LOGF_Scheduler,
LOGF_SourceStats,
LOGF_Sources,
LOGF_Local,
LOGF_Util,
LOGF_Main,
LOGF_ClientLog,
LOGF_Configure,
LOGF_CmdMon,
LOGF_Acquire,
LOGF_Manual,
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
LOGF_SysGeneric,
LOGF_SysLinux,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysSunOS,
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock
} LOG_Facility;
/* Init function */ /* Init function */
extern void LOG_Initialise(void); extern void LOG_Initialise(void);
@@ -83,14 +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(5, 6) extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
extern void LOG_Message(LOG_Severity severity, 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
@@ -99,9 +121,6 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
*/ */
extern void LOG_SetDebugLevel(int level); extern void LOG_SetDebugLevel(int level);
/* Log messages to a file instead of stderr */
extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */ /* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void); extern void LOG_OpenSystemLog(void);
@@ -120,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 */

349
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-2017 * 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
@@ -35,7 +35,6 @@
#include "local.h" #include "local.h"
#include "sys.h" #include "sys.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_signd.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "sources.h" #include "sources.h"
@@ -49,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"
/* ================================================== */ /* ================================================== */
@@ -70,26 +67,10 @@ 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)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
if (!pidfile[0])
return;
/* Don't care if this fails, there's not a lot we can do */ /* Don't care if this fails, there's not a lot we can do */
unlink(pidfile); unlink(pidfile);
} }
@@ -101,37 +82,30 @@ MAI_CleanupAndExit(void)
{ {
if (!initialised) exit(exit_status); if (!initialised) exit(exit_status);
if (CNF_GetDumpDir()[0] != '\0') { if (CNF_GetDumpOnExit()) {
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();
NSD_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();
HSH_Finalise();
LOG_Finalise(); LOG_Finalise();
exit(exit_status); exit(exit_status);
@@ -148,16 +122,6 @@ signal_cleanup(int x)
/* ================================================== */ /* ================================================== */
static void
quit_timeout(void *arg)
{
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
}
/* ================================================== */
static void static void
ntp_source_resolving_end(void) ntp_source_resolving_end(void)
{ {
@@ -171,7 +135,6 @@ ntp_source_resolving_end(void)
SRC_ReloadSources(); SRC_ReloadSources();
} }
SRC_RemoveDumpFiles();
RTC_StartMeasurements(); RTC_StartMeasurements();
RCL_StartRefclocks(); RCL_StartRefclocks();
NSR_StartSources(); NSR_StartSources();
@@ -245,47 +208,57 @@ post_init_rtc_hook(void *anything)
} }
/* ================================================== */ /* ================================================== */
/* Return 1 if the process exists on the system. */
static void static int
check_pidfile(void) does_process_exist(int pid)
{
int status;
status = getsid(pid);
if (status >= 0) {
return 1;
} else {
return 0;
}
}
/* ================================================== */
static int
maybe_another_chronyd_running(int *other_pid)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
FILE *in; FILE *in;
int pid, count; int pid, count;
*other_pid = 0;
in = fopen(pidfile, "r"); in = fopen(pidfile, "r");
if (!in) if (!in) return 0;
return;
count = fscanf(in, "%d", &pid); count = fscanf(in, "%d", &pid);
fclose(in); fclose(in);
if (count != 1) if (count != 1) return 0;
return;
if (getsid(pid) < 0) *other_pid = pid;
return; return does_process_exist(pid);
LOG_FATAL("Another chronyd may already be running (pid=%d), check %s",
pid, pidfile);
} }
/* ================================================== */ /* ================================================== */
static void static void
write_pidfile(void) write_lockfile(void)
{ {
const char *pidfile = CNF_GetPidFile(); const char *pidfile = CNF_GetPidFile();
FILE *out; FILE *out;
if (!pidfile[0])
return;
out = fopen(pidfile, "w"); out = fopen(pidfile, "w");
if (!out) { if (!out) {
LOG_FATAL("Could not open %s : %s", pidfile, strerror(errno)); 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);
} }
} }
@@ -295,19 +268,24 @@ write_pidfile(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
when it's initialised or send an error message */ when it's initialised or send an error message */
if (pipe(pipefd)) { if (pipe(pipefd)) {
LOG_FATAL("pipe() failed : %s", strerror(errno)); LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno));
} }
/* Does this preserve existing signal handlers? */ /* Does this preserve existing signal handlers? */
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno)); LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
/* In the 'grandparent' */ /* In the 'grandparent' */
char message[1024]; char message[1024];
@@ -318,8 +296,7 @@ go_daemon(void)
if (r) { if (r) {
if (r > 0) { if (r > 0) {
/* Print the error message from the child */ /* Print the error message from the child */
message[sizeof (message) - 1] = '\0'; fprintf(stderr, "%.1024s\n", message);
fprintf(stderr, "%s\n", message);
} }
exit(1); exit(1);
} else } else
@@ -333,7 +310,7 @@ go_daemon(void)
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno)); LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
exit(0); /* In the 'parent' */ exit(0); /* In the 'parent' */
} else { } else {
@@ -341,7 +318,7 @@ go_daemon(void)
/* Change current directory to / */ /* Change current directory to / */
if (chdir("/") < 0) { if (chdir("/") < 0) {
LOG_FATAL("chdir() failed : %s", strerror(errno)); LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
} }
/* Don't keep stdin/out/err from before. But don't close /* Don't keep stdin/out/err from before. But don't close
@@ -354,35 +331,8 @@ go_daemon(void)
LOG_SetParentFd(pipefd[1]); LOG_SetParentFd(pipefd[1]);
} }
} }
}
/* ================================================== */ #endif
static void
print_help(const char *progname)
{
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
}
/* ================================================== */
static void
print_version(void)
{
printf("chronyd (chrony) version %s (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
}
/* ================================================== */
static int
parse_int_arg(const char *arg)
{
int i;
if (sscanf(arg, "%d", &i) != 1)
LOG_FATAL("Invalid argument %s", arg);
return i;
} }
/* ================================================== */ /* ================================================== */
@@ -391,154 +341,127 @@ 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, *log_file = NULL; int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
struct passwd *pw; int do_init_rtc = 0, restarted = 0;
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC; int other_pid;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0; int lock_memory = 0, sched_priority = 0;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0; int system_log = 1;
int clock_control = 1, system_log = 1;
int config_args = 0; int config_args = 0;
do_platform_checks();
LOG_Initialise(); LOG_Initialise();
/* Parse (undocumented) long command-line options */ /* Parse command line options */
for (optind = 1; optind < argc; optind++) { while (++argv, (--argc)>0) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
return 0;
} else if (!strcmp("--version", argv[optind])) {
print_version();
return 0;
}
}
optind = 1; if (!strcmp("-f", *argv)) {
++argv, --argc;
/* Parse short command-line options */ conf_file = *argv;
while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) { } else if (!strcmp("-P", *argv)) {
switch (opt) { ++argv, --argc;
case '4': if (argc == 0 || sscanf(*argv, "%d", &sched_priority) != 1) {
case '6': LOG_FATAL(LOGF_Main, "Bad scheduler priority");
address_family = opt == '4' ? IPADDR_INET4 : IPADDR_INET6; }
break; } else if (!strcmp("-m", *argv)) {
case 'd': lock_memory = 1;
} else if (!strcmp("-r", *argv)) {
reload = 1;
} else if (!strcmp("-R", *argv)) {
restarted = 1;
} else if (!strcmp("-u", *argv)) {
++argv, --argc;
if (argc == 0) {
LOG_FATAL(LOGF_Main, "Missing user name");
} else {
user = *argv;
}
} else if (!strcmp("-s", *argv)) {
do_init_rtc = 1;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
printf("chronyd (chrony) version %s\n", CHRONY_VERSION);
exit(0);
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
debug++; debug++;
nofork = 1; nofork = 1;
system_log = 0; system_log = 0;
break; } else if (!strcmp("-q", *argv)) {
case 'f':
conf_file = optarg;
break;
case 'F':
scfilter_level = parse_int_arg(optarg);
break;
case 'l':
log_file = optarg;
break;
case 'm':
lock_memory = 1;
break;
case 'n':
nofork = 1;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
case 'q':
ref_mode = REF_ModeUpdateOnce; ref_mode = REF_ModeUpdateOnce;
nofork = 1; nofork = 1;
client_only = 0;
system_log = 0; system_log = 0;
break; } else if (!strcmp("-Q", *argv)) {
case 'Q':
ref_mode = REF_ModePrintOnce; ref_mode = REF_ModePrintOnce;
nofork = 1; nofork = 1;
client_only = 1;
clock_control = 0;
system_log = 0; system_log = 0;
} else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
/* Process remaining arguments and configuration lines */
config_args = argc;
break; break;
case 'r':
reload = 1;
break;
case 'R':
restarted = 1;
break;
case 's':
do_init_rtc = 1;
break;
case 't':
timeout = parse_int_arg(optarg);
break;
case 'u':
user = optarg;
break;
case 'v':
print_version();
return 0;
case 'x':
clock_control = 0;
break;
default:
print_help(progname);
return opt != 'h';
} }
} }
if (getuid() && !client_only) if (getuid() != 0) {
LOG_FATAL("Not superuser"); /* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n");
exit(1);
}
/* Turn into a daemon */ /* Turn into a daemon */
if (!nofork) { if (!nofork) {
go_daemon(); go_daemon();
} }
if (log_file) { if (system_log) {
LOG_OpenFileLog(log_file);
} else if (system_log) {
LOG_OpenSystemLog(); LOG_OpenSystemLog();
} }
LOG_SetDebugLevel(debug); LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES); LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting", CHRONY_VERSION);
DNS_SetAddressFamily(address_family); DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted, client_only); CNF_SetRestarted(restarted);
/* Parse the config file or the remaining command line arguments */ /* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
if (!config_args) { if (!config_args) {
CNF_ReadFile(conf_file); CNF_ReadFile(conf_file);
} else { } else {
for (; optind < argc; optind++) do {
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]); CNF_ParseLine(NULL, config_args - argc + 1, *argv);
} while (++argv, --argc);
} }
/* Check whether another chronyd may already be running */ /* Check whether another chronyd may already be running. Do this after
check_pidfile(); * forking, so that message logging goes to the right place (i.e. syslog), in
* case this chronyd is being run from a boot script. */
if (maybe_another_chronyd_running(&other_pid)) {
LOG_FATAL(LOGF_Main, "Another chronyd may already be running (pid=%d), check lockfile (%s)",
other_pid, CNF_GetPidFile());
}
/* Write our pidfile to prevent other chronyds running */ /* Write our lockfile to prevent other chronyds running. This has *GOT* to
write_pidfile(); * be done *AFTER* the daemon-creation fork() */
write_lockfile();
PRV_Initialise();
LCL_Initialise(); LCL_Initialise();
SCH_Initialise(); SCH_Initialise();
SYS_Initialise(clock_control); 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();
@@ -554,35 +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("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 specified user has a non-zero UID */
if (!geteuid() && (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();
NSD_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;
@@ -591,20 +504,24 @@ int main
REF_SetModeEndHandler(reference_mode_end); REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode); REF_SetMode(ref_mode);
if (timeout > 0)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) { if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL); RTC_TimeInit(post_init_rtc_hook, NULL);
} else { } else {
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();
LOG(LOGS_INFO, "chronyd exiting"); LOG(LOGS_INFO, LOGF_Main, "chronyd exiting");
MAI_CleanupAndExit(); MAI_CleanupAndExit();

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

@@ -47,15 +47,13 @@ static int enabled = 0;
/* More recent samples at highest indices */ /* More recent samples at highest indices */
typedef struct { typedef struct {
struct timespec when; /* This is our 'cooked' time */ struct timeval when; /* This is our 'cooked' time */
double orig_offset; /*+ Not modified by slew samples */ double orig_offset; /*+ Not modified by slew samples */
double offset; /*+ if we are fast of the supplied reference */ double offset; /*+ if we are fast of the supplied reference */
double residual; /*+ regression residual (sign convention given by double residual; /*+ regression residual (sign convention given by
(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];
@@ -64,8 +62,8 @@ static int n_samples;
/* ================================================== */ /* ================================================== */
static void static void
slew_samples(struct timespec *raw, slew_samples(struct timeval *raw,
struct timespec *cooked, struct timeval *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -97,8 +95,7 @@ MNL_Finalise(void)
/* ================================================== */ /* ================================================== */
static void static void
estimate_and_set_system(struct timespec *now, int offset_provided, double offset, estimate_and_set_system(struct timeval *now, int offset_provided, double offset, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm)
{ {
double agos[MAX_SAMPLES], offsets[MAX_SAMPLES]; double agos[MAX_SAMPLES], offsets[MAX_SAMPLES];
double b0, b1; double b0, b1;
@@ -109,26 +106,32 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
int found_freq; int found_freq;
double slew_by; double slew_by;
b0 = offset_provided ? offset : 0.0;
b1 = freq = 0.0;
found_freq = 0;
if (n_samples > 1) { if (n_samples > 1) {
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
agos[i] = UTI_DiffTimespecsToDouble(&samples[n_samples - 1].when, &samples[i].when); UTI_DiffTimevalsToDouble(&agos[i], &samples[n_samples-1].when, &samples[i].when);
offsets[i] = samples[i].offset; offsets[i] = samples[i].offset;
} }
if (RGR_FindBestRobustRegression(agos, offsets, n_samples, 1.0e-8, RGR_FindBestRobustRegression(agos, offsets, n_samples,
&b0, &b1, &n_runs, &best_start)) { 1.0e-8, /* 0.01ppm easily good enough for this! */
&b0, &b1, &n_runs, &best_start);
/* Ignore b0 from regression; treat offset as being the most /* Ignore b0 from regression; treat offset as being the most
recently entered value. (If the administrator knows he's put recently entered value. (If the administrator knows he's put
an outlier in, he will rerun the settime operation.) However, an outlier in, he will rerun the settime operation.) However,
the frequency estimate comes from the regression. */ the frequency estimate comes from the regression. */
freq = -b1; freq = -b1;
found_freq = 1; found_freq = 1;
}
} else { } else {
if (offset_provided) {
b0 = offset;
} else {
b0 = 0.0;
}
b1 = freq = 0.0;
found_freq = 0;
agos[0] = 0.0; agos[0] = 0.0;
offsets[0] = b0; offsets[0] = b0;
} }
@@ -140,20 +143,21 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
} }
if (found_freq) { if (found_freq) {
LOG(LOGS_INFO, "Making a frequency change of %.3f ppm and a slew of %.6f", LOG(LOGS_INFO, LOGF_Manual,
"Making a frequency change of %.3f ppm and a slew of %.6f",
1.0e6 * freq, slew_by); 1.0e6 * freq, slew_by);
REF_SetManualReference(now, REF_SetManualReference(now,
slew_by, slew_by,
freq, skew); freq, skew);
} else { } else {
LOG(LOGS_INFO, "Making a slew of %.6f", slew_by); LOG(LOGS_INFO, LOGF_Manual, "Making a slew of %.6f", slew_by);
REF_SetManualReference(now, REF_SetManualReference(now,
slew_by, slew_by,
0.0, skew); 0.0, skew);
} }
if (reg_offset) *reg_offset = b0; if (offset_cs) *offset_cs = (long)(0.5 + 100.0 * b0);
if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq; if (dfreq_ppm) *dfreq_ppm = 1.0e6 * freq;
if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency(); if (new_afreq_ppm) *new_afreq_ppm = LCL_ReadAbsoluteFrequency();
@@ -167,28 +171,18 @@ estimate_and_set_system(struct timespec *now, int offset_provided, double offset
/* ================================================== */ /* ================================================== */
int int
MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm) MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm)
{ {
struct timespec 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 UTI_DiffTimevalsToDouble(&offset, &now, ts);
is not too close to the last one */
if (!UTI_IsTimeOffsetSane(ts, 0.0))
return 0;
if (n_samples) {
diff = UTI_DiffTimespecsToDouble(&now, &samples[n_samples - 1].when);
if (diff < MIN_SAMPLE_SEPARATION)
return 0;
}
offset = UTI_DiffTimespecsToDouble(&now, ts);
/* Check if buffer full up */ /* Check if buffer full up */
if (n_samples == MAX_SAMPLES) { if (n_samples == MAX_SAMPLES) {
@@ -204,7 +198,7 @@ MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm,
samples[n_samples].orig_offset = offset; samples[n_samples].orig_offset = offset;
++n_samples; ++n_samples;
estimate_and_set_system(&now, 1, offset, reg_offset, dfreq_ppm, new_afreq_ppm); estimate_and_set_system(&now, 1, offset, offset_cs, dfreq_ppm, new_afreq_ppm);
return 1; return 1;
@@ -218,8 +212,8 @@ MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm,
/* ================================================== */ /* ================================================== */
static void static void
slew_samples(struct timespec *raw, slew_samples(struct timeval *raw,
struct timespec *cooked, struct timeval *cooked,
double dfreq, double dfreq,
double doffset, double doffset,
LCL_ChangeType change_type, LCL_ChangeType change_type,
@@ -233,7 +227,7 @@ slew_samples(struct timespec *raw,
} }
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
UTI_AdjustTimespec(&samples[i].when, cooked, &samples[i].when, &delta_time, UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time,
dfreq, doffset); dfreq, doffset);
samples[i].offset += delta_time; samples[i].offset += delta_time;
} }
@@ -264,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 */
@@ -303,7 +289,7 @@ int
MNL_DeleteSample(int index) MNL_DeleteSample(int index)
{ {
int i; int i;
struct timespec now; struct timeval now;
if ((index < 0) || (index >= n_samples)) { if ((index < 0) || (index >= n_samples)) {
return 0; return 0;

View File

@@ -33,12 +33,11 @@
extern void MNL_Initialise(void); extern void MNL_Initialise(void);
extern void MNL_Finalise(void); extern void MNL_Finalise(void);
extern int MNL_AcceptTimestamp(struct timespec *ts, double *reg_offset, double *dfreq_ppm, double *new_afreq_ppm); extern int MNL_AcceptTimestamp(struct timeval *ts, long *offset_cs, double *dfreq_ppm, double *new_afreq_ppm);
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

@@ -1,93 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014, 2017
*
* 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.
*
**********************************************************************
=======================================================================
Utility functions for memory allocation.
*/
#include "config.h"
#include "logging.h"
#include "memory.h"
void *
Malloc(size_t size)
{
void *r;
r = malloc(size);
if (!r && size)
LOG_FATAL("Could not allocate memory");
return r;
}
void *
Realloc(void *ptr, size_t size)
{
void *r;
r = realloc(ptr, size);
if (!r && size)
LOG_FATAL("Could not allocate memory");
return r;
}
static size_t
get_array_size(size_t nmemb, size_t size)
{
size_t array_size;
array_size = nmemb * size;
/* Check for overflow */
if (nmemb > 0 && array_size / nmemb != size)
LOG_FATAL("Could not allocate memory");
return array_size;
}
void *
Malloc2(size_t nmemb, size_t size)
{
return Malloc(get_array_size(nmemb, size));
}
void *
Realloc2(void *ptr, size_t nmemb, size_t size)
{
return Realloc(ptr, get_array_size(nmemb, size));
}
char *
Strdup(const char *s)
{
void *r;
r = strdup(s);
if (!r)
LOG_FATAL("Could not allocate memory");
return r;
}

View File

@@ -27,17 +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 void *Malloc2(size_t nmemb, size_t size); #define Realloc(x,y) realloc(x,y)
extern void *Realloc2(void *ptr, size_t nmemb, size_t size); #define ReallocArray(T,n,x) ((T *) realloc((void *)(x), (n)*sizeof(T)))
extern char *Strdup(const char *s);
/* Convenient macros */
#define MallocNew(T) ((T *) Malloc(sizeof(T)))
#define MallocArray(T, n) ((T *) Malloc2(n, sizeof(T)))
#define ReallocArray(T, n, x) ((T *) Realloc2((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

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. 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
@@ -21,16 +21,11 @@
======================================================================= =======================================================================
This is the header file for the Linux-specific NTP socket I/O bits.
*/ */
extern void NIO_Linux_Initialise(void); #ifndef GOT_MKDIRPP_H
#define GOT_MKDIRPP_H
extern void NIO_Linux_Finalise(void); extern int mkdir_and_parents(const char *path);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events); #endif
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);

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:
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; 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)
@@ -72,26 +73,21 @@ start_resolving(void *anything)
/* ================================================== */ /* ================================================== */
static void static void
end_resolving(int fd, int event, 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("pthread_join() failed"); LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
} }
resolving_threads--; resolving_threads--;
SCH_RemoveFileHandler(inst->pipe[0]); SCH_RemoveInputFileHandler(inst->pipe[0]);
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);
} }
@@ -110,20 +106,17 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
inst->status = DNS_Failure; inst->status = DNS_Failure;
if (pipe(inst->pipe)) { if (pipe(inst->pipe)) {
LOG_FATAL("pipe() failed"); LOG_FATAL(LOGF_Nameserv, "pipe() failed");
} }
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
resolving_threads++; resolving_threads++;
assert(resolving_threads <= 1); assert(resolving_threads <= 1);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) { if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL("pthread_create() failed"); LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
} }
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, end_resolving, inst); SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst);
} }
/* ================================================== */ /* ================================================== */
@@ -131,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

60
ntp.h
View File

@@ -38,30 +38,7 @@ typedef struct {
typedef uint32_t NTP_int32; typedef uint32_t NTP_int32;
/* The UDP port number used by NTP */ #define MAX_NTP_AUTH_DATA_LEN MAX_HASH_LENGTH
#define NTP_PORT 123
/* The NTP protocol version that we support */
#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)
/* The maximum length of MAC in NTPv4 packets which allows deterministic
parsing of extension fields (RFC 7822) */
#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
/* Type definition for leap bits */ /* Type definition for leap bits */
typedef enum { typedef enum {
@@ -92,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 */

2214
ntp_core.c

File diff suppressed because it is too large Load Diff

View File

@@ -38,18 +38,6 @@ typedef enum {
NTP_SERVER, NTP_PEER NTP_SERVER, NTP_PEER
} NTP_Source_Type; } NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
NTP_Timestamp_Source source;
} NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for /* This is a private data type used for storing the instance record for
each source that we are chiming with */ each source that we are chiming with */
typedef struct NCR_Instance_Record *NCR_Instance; typedef struct NCR_Instance_Record *NCR_Instance;
@@ -70,34 +58,16 @@ 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);
/* Reset polling interval of an instance */
extern void NCR_ResetPoll(NCR_Instance instance);
/* 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_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr, extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int sock_fd, int length);
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, 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 */
extern void NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a source we have
an ongoing protocol exchange with */
extern void NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* This routine is called when a packet is sent to a destination we
do not recognize */
extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Slew receive and transmit times in instance records */ /* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset); extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);
/* Take a particular source online (i.e. start sampling it) */ /* Take a particular source online (i.e. start sampling it) */
extern void NCR_TakeSourceOnline(NCR_Instance inst); extern void NCR_TakeSourceOnline(NCR_Instance inst);
@@ -122,8 +92,7 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples); extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now); extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timeval *now);
extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all); extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
extern int NCR_CheckAccessRestriction(IPAddr *ip_addr); extern int NCR_CheckAccessRestriction(IPAddr *ip_addr);
@@ -133,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 */

530
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-2016 * 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
@@ -30,7 +30,6 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_sources.h" #include "ntp_sources.h"
@@ -38,80 +37,37 @@
#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"
#ifdef HAVE_LINUX_TIMESTAMPING
#include "ntp_io_linux.h"
#endif
#define INVALID_SOCK_FD -1 #define INVALID_SOCK_FD -1
#define CMSGBUF_SIZE 256
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;
}; };
struct Message {
union sockaddr_in46 name;
struct iovec iov;
NTP_Receive_Buffer buf;
/* Aligned buffer for control messages */
struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
};
#ifdef HAVE_RECVMMSG
#define MAX_RECV_MESSAGES 4
#define MessageHeader mmsghdr
#else
/* Compatible with mmsghdr */
struct MessageHeader {
struct msghdr msg_hdr;
unsigned int msg_len;
};
#define MAX_RECV_MESSAGES 1
#endif
/* Arrays of Message and MessageHeader */
static ARR_Instance recv_messages;
static ARR_Instance recv_headers;
/* 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;
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
static void read_from_socket(int sock_fd, int event, void *anything); static void read_from_socket(void *anything);
/* ================================================== */ /* ================================================== */
@@ -122,20 +78,15 @@ prepare_socket(int family, int port_number, int client_only)
socklen_t my_addr_len; socklen_t my_addr_len;
int sock_fd; int sock_fd;
IPAddr bind_address; IPAddr bind_address;
int events = SCH_FILE_INPUT, on_off = 1; int on_off = 1;
/* Open Internet domain UDP socket for NTP message transmissions */ /* Open Internet domain UDP socket for NTP message transmissions */
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, "Could not open %s NTP socket : %s", family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
UTI_SockaddrFamilyToString(family), strerror(errno));
} else {
DEBUG_LOG("Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
}
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
@@ -165,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);
@@ -193,80 +144,76 @@ 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, "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, "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 */
} }
/* Enable kernel/HW timestamping of packets */
#ifdef HAVE_LINUX_TIMESTAMPING
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif
#ifdef SO_TIMESTAMPNS
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
#endif
#ifdef SO_TIMESTAMP #ifdef SO_TIMESTAMP
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) /* Enable receiving of timestamp control messages */
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP"); if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set timestamp socket options");
/* Don't quit - we might survive anyway */
}
#endif #endif
;
#ifdef IP_FREEBIND #ifdef IP_FREEBIND
/* 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, "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, "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, "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, "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, "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, "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;
} }
/* Register handler for read and possibly exception events on the socket */ /* Register handler for read events on the socket */
SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL); SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
return sock_fd; return sock_fd;
} }
@@ -279,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
@@ -296,12 +243,30 @@ 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("Could not connect NTP socket to %s:%d : %s", DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno)); strerror(errno));
return 0; return 0;
@@ -318,38 +283,11 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD) if (sock_fd == INVALID_SOCK_FD)
return; return;
SCH_RemoveFileHandler(sock_fd); SCH_RemoveInputFileHandler(sock_fd);
close(sock_fd); close(sock_fd);
} }
/* ================================================== */ /* ================================================== */
static void
prepare_buffers(unsigned int n)
{
struct MessageHeader *hdr;
struct Message *msg;
unsigned int i;
for (i = 0; i < n; i++) {
msg = ARR_GetElement(recv_messages, i);
hdr = ARR_GetElement(recv_headers, i);
msg->iov.iov_base = &msg->buf;
msg->iov.iov_len = sizeof (msg->buf);
hdr->msg_hdr.msg_name = &msg->name;
hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov;
hdr->msg_hdr.msg_iovlen = 1;
hdr->msg_hdr.msg_control = &msg->cmsgbuf;
hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf);
hdr->msg_hdr.msg_flags = 0;
hdr->msg_len = 0;
}
}
/* ================================================== */
void void
NIO_Initialise(int family) NIO_Initialise(int family)
{ {
@@ -358,22 +296,6 @@ NIO_Initialise(int family)
assert(!initialised); assert(!initialised);
initialised = 1; initialised = 1;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise();
#else
if (1) {
CNF_HwTsInterface *conf_iface;
if (CNF_GetHwTsInterface(0, &conf_iface))
LOG_FATAL("HW timestamping not supported");
}
#endif
recv_messages = ARR_CreateInstance(sizeof (struct Message));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
prepare_buffers(MAX_RECV_MESSAGES);
server_port = CNF_GetNTPPort(); server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort(); client_port = CNF_GetAcquisitionPort();
@@ -382,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)
@@ -404,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)
@@ -417,17 +334,16 @@ 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
)) { )) {
LOG_FATAL("Could not open NTP sockets"); LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
} }
} }
@@ -440,26 +356,19 @@ 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);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif #endif
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise();
#endif
initialised = 0; initialised = 0;
} }
/* ================================================== */ /* ================================================== */
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);
@@ -477,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
@@ -490,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; return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD) #ifdef HAVE_IPV6
server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0);
if (server_sock_fd4 != INVALID_SOCK_FD)
server_sock_ref4++;
return server_sock_fd4;
#ifdef FEAT_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:
@@ -527,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
); );
@@ -568,59 +438,85 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */ /* ================================================== */
static void static void
process_message(struct msghdr *hdr, int length, int sock_fd) read_from_socket(void *anything)
{ {
/* This should only be called when there is something
to read, otherwise it will block. */
int status, sock_fd;
ReceiveBuffer message;
union sockaddr_in46 where_from;
unsigned int flags = 0;
struct timeval now;
double now_err;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts; char cmsgbuf[256];
struct timespec sched_ts; struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL); assert(initialised);
local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts;
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) { SCH_GetLastEventTime(&now, &now_err, NULL);
DEBUG_LOG("Truncated source address");
return;
}
if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) { iov.iov_base = message.arbitrary;
UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name, iov.iov_len = sizeof(message);
&remote_addr.ip_addr, &remote_addr.port); msg.msg_name = &where_from;
} else { msg.msg_namelen = sizeof(where_from);
remote_addr.ip_addr.family = IPADDR_UNSPEC; msg.msg_iov = &iov;
remote_addr.port = 0; msg.msg_iovlen = 1;
msg.msg_control = (void *) cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
sock_fd = (long)anything;
status = recvmsg(sock_fd, &msg, flags);
/* Don't bother checking if read failed or why if it did. More
likely than not, it will be connection refused, resulting from a
previous sendto() directing a datagram at a port that is not
listening (which appears to generate an ICMP response, and on
some architectures e.g. Linux this is translated into an error
reponse on a subsequent recvfrom). */
if (status > 0) {
if (msg.msg_namelen > sizeof (where_from))
LOG_FATAL(LOGF_NtpIO, "Truncated source address");
switch (where_from.u.sa_family) {
case AF_INET:
remote_addr.ip_addr.family = IPADDR_INET4;
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.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = sock_fd; local_addr.sock_fd = sock_fd;
if (hdr->msg_flags & MSG_TRUNC) { for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
DEBUG_LOG("Received truncated message from %s:%d", #ifdef IP_PKTINFO
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
return;
}
if (hdr->msg_flags & MSG_CTRUNC) {
DEBUG_LOG("Truncated control message");
/* Continue */
}
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_IN_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;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi)); memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr); local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
local_addr.ip_addr.family = IPADDR_INET4; local_addr.ip_addr.family = IPADDR_INET4;
local_addr.if_index = ipi.ipi_ifindex;
} }
#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;
@@ -628,128 +524,85 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr, memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6)); sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6; local_addr.ip_addr.family = IPADDR_INET6;
local_addr.if_index = ipi.ipi6_ifindex;
} }
#endif #endif
#ifdef SCM_TIMESTAMP #ifdef SO_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) {
struct timeval tv; struct timeval tv;
struct timespec ts;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv)); memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
UTI_TimevalToTimespec(&tv, &ts); LCL_CookTime(&tv, &now, &now_err);
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
#endif
#ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
struct timespec ts;
memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts));
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
} }
#endif #endif
} }
#ifdef HAVE_LINUX_TIMESTAMPING if (status > 0) {
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length)) DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
return; status,
#endif UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
}
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f", if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) {
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index, NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts)); &remote_addr, &local_addr, status);
} else {
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer))
return;
NSR_ProcessRx(&remote_addr, &local_addr, &local_ts, }
(NTP_Packet *)hdr->msg_iov[0].iov_base, length); }
} }
/* ================================================== */ /* ================================================== */
/* Send a packet to given address */
static void static int
read_from_socket(int sock_fd, int event, void *anything) send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
/* This should only be called when there is something
to read, otherwise it may block */
struct MessageHeader *hdr;
unsigned int i, n;
int status, flags = 0;
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= MSG_ERRQUEUE;
#else
assert(0);
#endif
}
#ifdef HAVE_RECVMMSG
status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL);
if (status >= 0)
n = status;
#else
n = 1;
status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags);
if (status >= 0)
hdr[0].msg_len = status;
#endif
if (status < 0) {
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return;
}
for (i = 0; i < n; i++) {
hdr = ARR_GetElement(recv_headers, i);
process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd);
}
/* Restore the buffers to their original state */
prepare_buffers(n);
}
/* ================================================== */
/* Send a packet to remote address from local address */
int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx)
{ {
union sockaddr_in46 remote; union sockaddr_in46 remote;
struct msghdr msg; struct msghdr msg;
struct iovec iov; struct iovec iov;
struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)]; char cmsgbuf[256];
int cmsglen; int cmsglen;
socklen_t addrlen = 0; socklen_t addrlen = 0;
assert(initialised); assert(initialised);
if (local_addr->sock_fd == INVALID_SOCK_FD) { if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG("No socket to send to %s:%d", DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port); UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return 0; return 0;
} }
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
/* Don't set address with connected socket */ /* Don't set address with connected socket */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) { if (local_addr->sock_fd != server_sock_fd4 && separate_client_sockets)
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, break;
&remote.u); memset(&remote.in4, 0, sizeof (remote.in4));
if (!addrlen) 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
default:
return 0; return 0;
} }
@@ -762,7 +615,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
} }
iov.iov_base = packet; iov.iov_base = packet;
iov.iov_len = length; iov.iov_len = packetlen;
msg.msg_iov = &iov; msg.msg_iov = &iov;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf; msg.msg_control = cmsgbuf;
@@ -770,8 +623,9 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
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 in_pktinfo *ipi; struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
@@ -787,8 +641,9 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
} }
#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 in6_pktinfo *ipi; struct in6_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
@@ -805,27 +660,40 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
} }
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING
if (process_tx)
cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd);
#endif
msg.msg_controllen = cmsglen; msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */ /* This is apparently required on some systems */
if (!cmsglen) if (!cmsglen)
msg.msg_control = NULL; msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) { if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s", DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&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,
strerror(errno)); strerror(errno));
return 0; return 0;
} }
DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length, 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);
return 1; return 1;
} }
/* ================================================== */
/* Send an unauthenticated packet to a given address */
int
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *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,22 +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, extern int NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
NTP_Local_Address *local_addr, int length, int process_tx);
/* 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

@@ -1,703 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2017
*
* 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 for NTP I/O specific to Linux
*/
#include "config.h"
#include "sysincl.h"
#include <ifaddrs.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
#include <net/if.h>
#include "array.h"
#include "conf.h"
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
#include "ntp_sources.h"
#include "sched.h"
#include "sys_linux.h"
#include "util.h"
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
struct Interface {
char name[IF_NAMESIZE];
int if_index;
int phc_fd;
int phc_mode;
int phc_nocrossts;
/* Link speed in mbit/s */
int link_speed;
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
/* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
/* Array of Interfaces */
static ARR_Instance interfaces;
/* RX/TX and TX-specific timestamping socket options */
static int ts_flags;
static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* ================================================== */
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags;
unsigned int i;
struct Interface *iface;
/* Check if the interface was not already added */
for (i = 0; i < ARR_GetSize(interfaces); i++) {
if (!strcmp(conf_iface->name, ((struct Interface *)ARR_GetElement(interfaces, i))->name))
return 1;
}
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
memset(&req, 0, sizeof (req));
memset(&ts_info, 0, sizeof (ts_info));
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
close(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
close(sock_fd);
return 0;
}
if_index = req.ifr_ifindex;
ts_info.cmd = ETHTOOL_GET_TS_INFO;
req.ifr_data = (char *)&ts_info;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return 0;
}
req_hwts_flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
close(sock_fd);
return 0;
}
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
switch (conf_iface->rxfilter) {
case CNF_HWTS_RXFILTER_ANY:
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_NTP_ALL))
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
else
#endif
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_ALL))
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
else
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
break;
case CNF_HWTS_RXFILTER_NONE:
ts_config.rx_filter = HWTSTAMP_FILTER_NONE;
break;
#ifdef HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP
case CNF_HWTS_RXFILTER_NTP:
ts_config.rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
#endif
default:
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
close(sock_fd);
return 0;
}
close(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
return 0;
iface = ARR_GetNewElement(interfaces);
snprintf(iface->name, sizeof (iface->name), "%s", conf_iface->name);
iface->if_index = if_index;
iface->phc_fd = phc_fd;
iface->phc_mode = 0;
iface->phc_nocrossts = conf_iface->nocrossts;
/* Start with 1 gbit and no VLANs or IPv4/IPv6 options */
iface->link_speed = 1000;
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance(UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
return 1;
}
/* ================================================== */
static int
add_all_interfaces(CNF_HwTsInterface *conf_iface_all)
{
CNF_HwTsInterface conf_iface;
struct ifaddrs *ifaddr, *ifa;
int r;
conf_iface = *conf_iface_all;
if (getifaddrs(&ifaddr)) {
DEBUG_LOG("getifaddrs() failed : %s", strerror(errno));
return 0;
}
for (r = 0, ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
conf_iface.name = ifa->ifa_name;
if (add_interface(&conf_iface))
r = 1;
}
freeifaddrs(ifaddr);
/* Return success if at least one interface was added */
return r;
}
/* ================================================== */
static void
update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return;
memset(&req, 0, sizeof (req));
memset(&cmd, 0, sizeof (cmd));
snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface->name);
cmd.cmd = ETHTOOL_GSET;
req.ifr_data = (char *)&cmd;
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd);
return;
}
close(sock_fd);
iface->link_speed = ethtool_cmd_speed(&cmd);
}
/* ================================================== */
#if defined(HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO) || defined(HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW)
static int
check_timestamping_option(int option)
{
int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
DEBUG_LOG("Could not enable timestamping option %x", option);
close(sock_fd);
return 0;
}
close(sock_fd);
return 1;
}
#endif
/* ================================================== */
void
NIO_Linux_Initialise(void)
{
CNF_HwTsInterface *conf_iface;
unsigned int i;
int hwts;
interfaces = ARR_CreateInstance(sizeof (struct Interface));
/* Enable HW timestamping on specified interfaces. If "*" was specified, try
all interfaces. If no interface was specified, enable SW timestamping. */
for (i = hwts = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (!strcmp("*", conf_iface->name))
continue;
if (!add_interface(conf_iface))
LOG_FATAL("Could not enable HW timestamping on %s", conf_iface->name);
hwts = 1;
}
for (i = 0; CNF_GetHwTsInterface(i, &conf_iface); i++) {
if (strcmp("*", conf_iface->name))
continue;
if (add_all_interfaces(conf_iface))
hwts = 1;
break;
}
ts_flags = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE;
ts_tx_flags = SOF_TIMESTAMPING_TX_SOFTWARE;
if (hwts) {
ts_flags |= SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE;
ts_tx_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_PKTINFO))
ts_flags |= SOF_TIMESTAMPING_OPT_PKTINFO;
#endif
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_TX_SWHW
if (check_timestamping_option(SOF_TIMESTAMPING_OPT_TX_SWHW))
ts_flags |= SOF_TIMESTAMPING_OPT_TX_SWHW;
#endif
}
/* Enable IP_PKTINFO in messages looped back to the error queue */
ts_flags |= SOF_TIMESTAMPING_OPT_CMSG;
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
}
/* ================================================== */
void
NIO_Linux_Finalise(void)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
ARR_DestroyInstance(interfaces);
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
int val, flags;
if (!ts_flags)
return 0;
/* Enable SCM_TIMESTAMPING control messages and the socket's error queue in
order to receive our transmitted packets with more accurate timestamps */
val = 1;
flags = ts_flags;
if (client_only || permanent_ts_options)
flags |= ts_tx_flags;
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
ts_flags = 0;
return 0;
}
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0;
return 0;
}
*events |= SCH_FILE_EXCEPTION;
return 1;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
struct Interface *iface;
unsigned int i;
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
if (iface->if_index != if_index)
continue;
return iface;
}
return NULL;
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, phc_err, local_err;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
}
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
timestamps. If we don't know the length of the packet at layer 2, we
make an assumption that UDP data start at the same position as in the
last transmitted packet which had a HW TX timestamp. */
if (rx_ntp_length && iface->link_speed) {
if (!l2_length)
l2_length = (family == IPADDR_INET4 ? iface->l2_udp4_ntp_start :
iface->l2_udp6_ntp_start) + rx_ntp_length + 4;
rx_correction = l2_length / (1.0e6 / 8 * iface->link_speed);
UTI_AddDoubleToTimespec(hw_ts, rx_correction, hw_ts);
}
if (!HCL_CookTime(iface->clock, hw_ts, &ts, &local_err))
return;
if (!rx_ntp_length && iface->tx_comp)
UTI_AddDoubleToTimespec(&ts, iface->tx_comp, &ts);
else if (rx_ntp_length && iface->rx_comp)
UTI_AddDoubleToTimespec(&ts, -iface->rx_comp, &ts);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE;
}
/* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */
static int
extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
{
unsigned char *msg_start = msg;
union sockaddr_in46 addr;
remote_addr->ip_addr.family = IPADDR_UNSPEC;
remote_addr->port = 0;
/* Skip MACs */
if (len < 12)
return 0;
len -= 12, msg += 12;
/* Skip VLAN tag(s) if present */
while (len >= 4 && msg[0] == 0x81 && msg[1] == 0x00)
len -= 4, msg += 4;
/* Skip IPv4 or IPv6 ethertype */
if (len < 2 || !((msg[0] == 0x08 && msg[1] == 0x00) ||
(msg[0] == 0x86 && msg[1] == 0xdd)))
return 0;
len -= 2, msg += 2;
/* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4;
if (len < ihl + 8 || msg[9] != 17)
return 0;
memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t));
addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2);
addr.in4.sin_family = AF_INET;
len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) {
int eh_len, next_header = msg[6];
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
len -= 40, msg += 40;
/* Skip IPv6 extension headers if present */
while (next_header != 17) {
switch (next_header) {
case 44: /* Fragment Header */
/* Process only the first fragment */
if (ntohs(*(uint16_t *)(msg + 2)) >> 3 != 0)
return 0;
eh_len = 8;
break;
case 0: /* Hop-by-Hop Options */
case 43: /* Routing Header */
case 60: /* Destination Options */
case 135: /* Mobility Header */
eh_len = 8 * (msg[1] + 1);
break;
case 51: /* Authentication Header */
eh_len = 4 * (msg[1] + 2);
break;
default:
return 0;
}
if (eh_len < 8 || len < eh_len + 8)
return 0;
next_header = msg[0];
len -= eh_len, msg += eh_len;
}
addr.in6.sin6_port = *(uint16_t *)(msg + 2);
addr.in6.sin6_family = AF_INET6;
len -= 8, msg += 8;
#endif
} else {
return 0;
}
UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
/* Move the message to fix alignment of its fields */
if (len > 0)
memmove(msg_start, msg, len);
return len;
}
/* ================================================== */
int
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
{
struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, ts_if_index, l2_length;
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
struct scm_ts_pktinfo ts_pktinfo;
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
ts_if_index = ts_pktinfo.if_index;
l2_length = ts_pktinfo.pkt_length;
DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
}
#endif
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(ts_if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family, l2_length);
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
(!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
}
}
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
DEBUG_LOG("Unknown extended error");
/* Drop the message */
return 1;
}
}
}
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
/* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
local_addr->sock_fd, local_addr->if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) {
if (remote_addr->ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - length;
else if (remote_addr->ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - length;
}
/* Drop the message if it has no timestamp or its processing failed */
if (local_ts->source == NTP_TS_DAEMON) {
DEBUG_LOG("Missing TX timestamp");
return 1;
}
if (length < NTP_NORMAL_PACKET_LENGTH)
return 1;
NSR_ProcessTx(remote_addr, local_addr, local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
return 1;
}
/* ================================================== */
int
NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen;
/* Add control message that will enable TX timestamping for this message.
Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
control messages. */
cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen);
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
return cmsglen;
}

View File

@@ -1,379 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* 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.
*
**********************************************************************
=======================================================================
Support for MS-SNTP authentication in Samba (ntp_signd)
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "conf.h"
#include "logging.h"
#include "ntp_io.h"
#include "ntp_signd.h"
#include "sched.h"
#include "util.h"
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
#define SIGND_VERSION 0
typedef enum {
SIGN_TO_CLIENT = 0,
ASK_SERVER_TO_SIGN = 1,
CHECK_SERVER_SIGNATURE = 2,
SIGNING_SUCCESS = 3,
SIGNING_FAILURE = 4,
} SigndOp;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint16_t packet_id;
uint16_t _pad;
uint32_t key_id;
NTP_Packet packet_to_sign;
} SigndRequest;
typedef struct {
uint32_t length;
uint32_t version;
uint32_t op;
uint32_t packet_id;
NTP_Packet signed_packet;
} SigndResponse;
typedef struct {
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
int sent;
int received;
int request_length;
struct timespec request_ts;
SigndRequest request;
SigndResponse response;
} SignInstance;
/* As the communication with ntp_signd is asynchronous, incoming packets are
saved in a queue in order to avoid loss when they come in bursts */
#define MAX_QUEUE_LENGTH 16U
#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
/* Fixed-size array of SignInstance */
static ARR_Instance queue;
static unsigned int queue_head;
static unsigned int queue_tail;
#define INVALID_SOCK_FD -1
/* Unix domain socket connected to ntp_signd */
static int sock_fd;
#define MIN_AUTH_DELAY 1.0e-5
#define MAX_AUTH_DELAY 1.0e-2
/* Average time needed for signing one packet. This is used to adjust the
transmit timestamp in NTP packets. The timestamp won't be very accurate as
the delay is variable, but it should be good enough for MS-SNTP clients. */
static double auth_delay;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static void
close_socket(void)
{
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
sock_fd = INVALID_SOCK_FD;
/* Empty the queue */
queue_head = queue_tail = 0;
}
/* ================================================== */
static int
open_socket(void)
{
struct sockaddr_un s;
if (sock_fd >= 0)
return 1;
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(sock_fd);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
DEBUG_LOG("signd socket path too long");
close_socket();
return 0;
}
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
close_socket();
return 0;
}
DEBUG_LOG("Connected to signd");
return 1;
}
/* ================================================== */
static void
process_response(SignInstance *inst)
{
struct timespec ts;
double delay;
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
DEBUG_LOG("Invalid response ID");
return;
}
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
DEBUG_LOG("Signing failed");
return;
}
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");
return;
}
SCH_GetLastEventTime(NULL, NULL, &ts);
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
DEBUG_LOG("Signing succeeded (delay %f)", delay);
/* Send the signed NTP packet */
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
ntohl(inst->response.length) + sizeof (inst->response.length) -
offsetof(SigndResponse, signed_packet), 0);
/* Update exponential moving average of the authentication delay */
delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
auth_delay += 0.1 * (delay - auth_delay);
}
/* ================================================== */
static void
read_write_socket(int sock_fd, int event, void *anything)
{
SignInstance *inst;
uint32_t response_length;
int s;
inst = ARR_GetElement(queue, queue_head);
if (event == SCH_FILE_OUTPUT) {
assert(!IS_QUEUE_EMPTY());
assert(inst->sent < inst->request_length);
if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
if (s < 0) {
DEBUG_LOG("signd socket error: %s", strerror(errno));
close_socket();
return;
}
DEBUG_LOG("Sent %d bytes to signd", s);
inst->sent += s;
/* Try again later if the request is not complete yet */
if (inst->sent < inst->request_length)
return;
/* Disable output and wait for a response */
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
}
if (event == SCH_FILE_INPUT) {
if (IS_QUEUE_EMPTY()) {
DEBUG_LOG("Unexpected signd response");
close_socket();
return;
}
assert(inst->received < sizeof (inst->response));
s = recv(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
if (s <= 0) {
if (s < 0)
DEBUG_LOG("signd socket error: %s", strerror(errno));
else
DEBUG_LOG("signd socket closed");
close_socket();
return;
}
DEBUG_LOG("Received %d bytes from signd", s);
inst->received += s;
if (inst->received < sizeof (inst->response.length))
return;
response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
if (response_length < offsetof(SigndResponse, signed_packet) ||
response_length > sizeof (SigndResponse)) {
DEBUG_LOG("Invalid response length");
close_socket();
return;
}
/* Wait for more data if not complete yet */
if (inst->received < response_length)
return;
process_response(inst);
/* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
}
}
/* ================================================== */
void
NSD_Initialise()
{
sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled)
return;
queue = ARR_CreateInstance(sizeof (SignInstance));
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
queue_head = queue_tail = 0;
LOG(LOGS_INFO, "MS-SNTP authentication enabled");
}
/* ================================================== */
void
NSD_Finalise()
{
if (!enabled)
return;
if (sock_fd != INVALID_SOCK_FD)
close_socket();
ARR_DestroyInstance(queue);
}
/* ================================================== */
extern int NSD_GetAuthDelay(uint32_t key_id)
{
return 1.0e9 * auth_delay;
}
/* ================================================== */
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
SignInstance *inst;
if (!enabled) {
DEBUG_LOG("signd disabled");
return 0;
}
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
DEBUG_LOG("signd queue full");
return 0;
}
if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG("Invalid packet length");
return 0;
}
if (!open_socket())
return 0;
inst = ARR_GetElement(queue, queue_tail);
inst->remote_addr = *remote_addr;
inst->local_addr = *local_addr;
inst->sent = 0;
inst->received = 0;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
/* The length field doesn't include itself */
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
inst->request.version = htonl(SIGND_VERSION);
inst->request.op = htonl(SIGN_TO_CLIENT);
inst->request.packet_id = htons(queue_tail);
inst->request._pad = 0;
inst->request.key_id = htonl(key_id);
memcpy(&inst->request.packet_to_sign, packet, length);
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
return 1;
}

View File

@@ -1,44 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* 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.
*
**********************************************************************
=======================================================================
Header for MS-SNTP authentication via Samba (ntp_signd) */
#ifndef GOT_NTP_SIGND_H
#define GOT_NTP_SIGND_H
#include "addressing.h"
#include "ntp.h"
/* Initialisation function */
extern void NSD_Initialise(void);
/* Finalisation function */
extern void NSD_Finalise(void);
/* Function to get an estimate of delay due to signing */
extern int NSD_GetAuthDelay(uint32_t key_id);
/* Function to sign an NTP packet and send it */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
#endif

File diff suppressed because it is too large Load Diff

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,23 +77,8 @@ 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_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, 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);
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
/* This routine is called by ntp_io when a packet was sent to the network and
an accurate transmit timestamp was captured */
extern void NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length);
/* Initialisation function */ /* Initialisation function */
extern void NSR_Initialise(void); extern void NSR_Initialise(void);
@@ -127,9 +112,7 @@ extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address); extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now); extern void NSR_ReportSource(RPT_SourceReport *report, struct timeval *now);
extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report); extern void NSR_GetActivityReport(RPT_ActivityReport *report);

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,128 +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);
{ 0, 0 }, /* ADD_SERVER */ case REQ_MANUAL :
{ 0, 0 }, /* 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 :
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */ return offsetof(CMD_Request, data.ntp_source.EOR);
{ 0, 0 }, /* ADD_SERVER2 */ case REQ_DEL_SOURCE :
{ 0, 0 }, /* ADD_PEER2 */ return offsetof(CMD_Request, data.del_source.EOR);
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */ case REQ_WRITERTC :
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */ 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 */
0, /* 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 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
};
/* ================================================== */ /* ================================================== */
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;
@@ -164,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;
}
} }
/* ================================================== */ /* ================================================== */
@@ -185,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 {
/* Length of MANUAL_LIST depends on number of samples stored in it */ switch (type) {
if (type == RPY_MANUAL_LIST) { case RPY_NULL:
uint32_t ns; return offsetof(CMD_Reply, data.null.EOR);
case RPY_N_SOURCES:
if (r->status != htons(STT_SUCCESS)) 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); return offsetof(CMD_Reply, data);
}
ns = ntohl(r->data.manual_list.n_samples); }
case RPY_MANUAL_LIST:
{
unsigned long ns = ntohl(r->data.manual_list.n_samples);
if (ns > MAX_MANUAL_LIST_SAMPLES) if (ns > MAX_MANUAL_LIST_SAMPLES)
return 0; 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);
return offsetof(CMD_Reply, data.manual_list.samples) + default:
ns * sizeof (RPY_ManualListSample); assert(0);
}
} }
return reply_lengths[type]; return 0;
} }
/* ================================================== */ /* ================================================== */

730
privops.c
View File

@@ -1,730 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Bryan Christianson 2015
* Copyright (C) Miroslav Lichvar 2017
*
* 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_RELOADDNS 1029
#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';
res->rc = DNS_Name2IPAddress(req->name, res->data.name_to_ipaddress.addresses,
DNS_MAX_ADDRESSES);
}
#endif
/* ======================================================================= */
/* HELPER - perform DNS_Reload() */
#ifdef PRIVOPS_RELOADDNS
static void
do_reload_dns(PrvResponse *res)
{
DNS_Reload();
res->rc = 0;
}
#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
#ifdef PRIVOPS_RELOADDNS
case OP_RELOADDNS:
do_reload_dns(&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("Could not read from helper : %s", strerror(errno));
if (resp_len != sizeof (*res))
LOG_FATAL("Invalid helper response");
if (res->fatal_error)
LOG_FATAL("Error in helper : %s", res->data.fatal_msg.msg);
DEBUG_LOG("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("Could not send to helper : %s", strerror(errno));
}
DEBUG_LOG("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("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
/* ======================================================================= */
/* DAEMON - request res_init() */
#ifdef PRIVOPS_RELOADDNS
void
PRV_ReloadDNS(void)
{
PrvRequest req;
PrvResponse res;
if (!have_helper()) {
DNS_Reload();
return;
}
memset(&req, 0, sizeof (req));
req.op = OP_RELOADDNS;
submit_request(&req, &res);
assert(!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("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("socketpair() failed : %s", strerror(errno));
UTI_FdSetCloexec(sock_pair[0]);
UTI_FdSetCloexec(sock_pair[1]);
pid = fork();
if (pid < 0)
LOG_FATAL("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,77 +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_RELOADDNS
void PRV_ReloadDNS(void);
#else
#define PRV_ReloadDNS DNS_Reload
#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

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2017 * Copyright (C) Miroslav Lichvar 2009-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
@@ -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"
@@ -48,7 +47,7 @@ extern RefclockDriver RCL_PHC_driver;
struct FilterSample { struct FilterSample {
double offset; double offset;
double dispersion; double dispersion;
struct timespec sample_time; struct timeval sample_time;
}; };
struct MedianFilter { struct MedianFilter {
@@ -75,60 +74,50 @@ struct RCL_Instance_Record {
int driver_polled; int driver_polled;
int poll; int poll;
int leap_status; int leap_status;
int pps_forced;
int pps_rate; int pps_rate;
int pps_active; int pps_active;
int max_lock_age;
struct MedianFilter filter; struct MedianFilter filter;
uint32_t ref_id; uint32_t ref_id;
uint32_t lock_ref; uint32_t lock_ref;
double offset; double offset;
double delay; double delay;
double precision; double precision;
double pulse_width;
SCH_TimeoutID timeout_id; SCH_TimeoutID timeout_id;
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;
static int valid_sample_time(RCL_Instance instance, struct timespec *sample_time); static int valid_sample_time(RCL_Instance instance, struct timeval *tv);
static int pps_stratum(RCL_Instance instance, struct timespec *ts); static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static void poll_timeout(void *arg); static void poll_timeout(void *arg);
static void slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq, static void 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);
static void add_dispersion(double dispersion, void *anything); static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion); static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion); static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_fini(struct MedianFilter *filter); static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter); static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter); static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion); static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion); static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static int filter_get_samples(struct MedianFilter *filter);
static int filter_select_samples(struct MedianFilter *filter); static int filter_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion); static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timespec *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);
} }
@@ -141,51 +130,55 @@ 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)
{ {
RCL_Instance inst; int pps_source = 0;
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;
inst->precision = 1e-6;
} else if (strcmp(params->driver_name, "SOCK") == 0) { } else if (strcmp(params->driver_name, "SOCK") == 0) {
inst->driver = &RCL_SOCK_driver; inst->driver = &RCL_SOCK_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PPS") == 0) { } else if (strcmp(params->driver_name, "PPS") == 0) {
inst->driver = &RCL_PPS_driver; inst->driver = &RCL_PPS_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PHC") == 0) { } else if (strcmp(params->driver_name, "PHC") == 0) {
inst->driver = &RCL_PHC_driver; inst->driver = &RCL_PHC_driver;
inst->precision = 1e-9;
} else { } else {
LOG_FATAL("unknown refclock driver %s", params->driver_name); LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
return 0; return 0;
} }
if (!inst->driver->init && !inst->driver->poll) { if (!inst->driver->init && !inst->driver->poll) {
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name); LOG_FATAL(LOGF_Refclock, "refclock driver %s is not compiled in", params->driver_name);
return 0; return 0;
} }
@@ -196,16 +189,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll; inst->poll = params->poll;
inst->driver_polled = 0; inst->driver_polled = 0;
inst->leap_status = LEAP_Normal; inst->leap_status = LEAP_Normal;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate; inst->pps_rate = params->pps_rate;
inst->pps_active = 0; inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age;
inst->lock_ref = params->lock_ref_id; inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset; inst->offset = params->offset;
inst->delay = params->delay; inst->delay = params->delay;
inst->precision = LCL_GetSysPrecisionAsQuantum(); if (params->precision > 0.0)
inst->precision = MAX(inst->precision, params->precision); inst->precision = params->precision;
inst->pulse_width = params->pulse_width;
inst->timeout_id = -1; inst->timeout_id = -1;
inst->source = NULL; inst->source = NULL;
@@ -218,21 +208,20 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_parameter[i] = '\0'; inst->driver_parameter[i] = '\0';
} }
if (pps_source) {
if (inst->pps_rate < 1) if (inst->pps_rate < 1)
inst->pps_rate = 1; inst->pps_rate = 1;
} else {
inst->pps_rate = 0;
}
if (params->ref_id) if (params->ref_id)
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) {
@@ -244,7 +233,7 @@ RCL_AddRefclock(RefclockParameters *params)
max_samples = 1 << (inst->poll - inst->driver_poll); max_samples = 1 << (inst->poll - inst->driver_poll);
if (max_samples < params->filter_length) { if (max_samples < params->filter_length) {
if (max_samples < 4) { if (max_samples < 4) {
LOG(LOGS_WARN, "Setting filter length for %s to %d", LOG(LOGS_WARN, LOGF_Refclock, "Setting filter length for %s to %d",
UTI_RefidToString(inst->ref_id), max_samples); UTI_RefidToString(inst->ref_id), max_samples);
} }
params->filter_length = max_samples; params->filter_length = max_samples;
@@ -253,18 +242,17 @@ RCL_AddRefclock(RefclockParameters *params)
if (inst->driver->init) if (inst->driver->init)
if (!inst->driver->init(inst)) { if (!inst->driver->init(inst)) {
LOG_FATAL("refclock %s initialisation failed", params->driver_name); LOG_FATAL(LOGF_Refclock, "refclock %s initialisation failed", params->driver_name);
return 0; return 0;
} }
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, 0.0, 0.0);
DEBUG_LOG("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);
@@ -274,37 +262,36 @@ 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;
} }
} }
void void
RCL_ReportSource(RPT_SourceReport *report, struct timespec *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;
@@ -357,23 +344,20 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
} }
int int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap) RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap)
{ {
double correction, dispersion; double correction, dispersion;
struct timespec cooked_time; struct timeval cooked_time;
if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion); LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(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, &cooked_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:
@@ -381,11 +365,10 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
instance->leap_status = leap; instance->leap_status = leap;
break; break;
default: default:
DEBUG_LOG("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);
@@ -398,59 +381,25 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
} }
int int
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second) RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
{ {
double correction, dispersion; double correction, dispersion, offset;
struct timespec cooked_time; struct timeval cooked_time;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(pulse_time, correction, &cooked_time);
second += correction;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
return 0;
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
}
static int
check_pulse_edge(RCL_Instance instance, double offset, double distance)
{
double max_error;
if (instance->pulse_width <= 0.0)
return 1;
max_error = 1.0 / instance->pps_rate - instance->pulse_width;
max_error = MIN(instance->pulse_width, max_error);
max_error *= 0.5;
if (fabs(offset) > max_error || distance > max_error) {
DEBUG_LOG("refclock pulse ignored offset=%.9f distance=%.9f max_error=%.9f",
offset, distance, max_error);
return 0;
}
return 1;
}
int
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction)
{
double offset;
int rate; int rate;
NTP_Leap leap; NTP_Leap leap;
if (!UTI_IsTimeOffsetSane(cooked_time, second) || leap = LEAP_Normal;
!valid_sample_time(instance, cooked_time)) LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
dispersion += instance->precision;
if (!valid_sample_time(instance, pulse_time))
return 0; return 0;
leap = LEAP_Normal;
dispersion += instance->precision;
rate = instance->pps_rate; rate = instance->pps_rate;
assert(rate > 0);
offset = -second + instance->offset; offset = -second - correction + instance->offset;
/* Adjust the offset to [-0.5/rate, 0.5/rate) interval */ /* Adjust the offset to [-0.5/rate, 0.5/rate) interval */
offset -= (long)(offset * rate) / (double)rate; offset -= (long)(offset * rate) / (double)rate;
@@ -460,23 +409,20 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
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 timespec 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("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);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample_time); UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) { if (fabs(sample_diff) >= 2.0 / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
sample_diff); sample_diff);
return 0; return 0;
} }
@@ -490,49 +436,41 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
offset += shift; offset += shift;
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) { if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_offset - offset, ref_dispersion, dispersion); ref_offset - offset, ref_dispersion, dispersion);
return 0; return 0;
} }
if (!check_pulse_edge(instance, ref_offset - offset, 0.0)) leap = refclocks[instance->lock_ref].leap_status;
return 0;
leap = lock_refclock->leap_status; DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff);
DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
offset, ref_offset - offset, sample_diff);
} else { } else {
struct timespec ref_time; struct timeval ref_time;
int is_synchronised, stratum; int is_synchronised, stratum;
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("refclock pulse ignored offset=%.9f sync=%d dist=%.9f", DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
offset, 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;
} }
if (!check_pulse_edge(instance, offset, distance))
return 0;
} }
filter_add_sample(&instance->filter, cooked_time, offset, dispersion); filter_add_sample(&instance->filter, &cooked_time, offset, dispersion);
instance->leap_status = leap; instance->leap_status = leap;
instance->pps_active = 1; instance->pps_active = 1;
log_sample(instance, cooked_time, 0, 1, offset + raw_correction - instance->offset, log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion);
offset, dispersion);
/* for logging purposes */ /* for logging purposes */
if (!instance->driver->poll) if (!instance->driver->poll)
@@ -541,66 +479,52 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 1; return 1;
} }
double static double
RCL_GetPrecision(RCL_Instance instance) poll_interval(int poll)
{ {
return instance->precision; if (poll >= 0)
} return 1 << poll;
else
int return 1.0 / (1 << -poll);
RCL_GetDriverPoll(RCL_Instance instance)
{
return instance->driver_poll;
} }
static int static int
valid_sample_time(RCL_Instance instance, struct timespec *sample_time) valid_sample_time(RCL_Instance instance, struct timeval *tv)
{ {
struct timespec now, last_sample_time; struct timeval raw_time;
double diff, last_offset, last_dispersion; double diff;
LCL_ReadCookedTime(&now, NULL); LCL_ReadRawTime(&raw_time);
diff = UTI_DiffTimespecsToDouble(&now, sample_time); UTI_DiffTimevalsToDouble(&diff, &raw_time, tv);
if (diff < 0.0 || diff > poll_interval(instance->poll + 1)) {
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) || DEBUG_LOG(LOGF_Refclock, "refclock sample not valid age=%.6f tv=%s",
(filter_get_samples(&instance->filter) > 0 && diff, UTI_TimevalToString(tv));
filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) &&
UTI_CompareTimespecs(&last_sample_time, sample_time) >= 0)) {
DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
UTI_RefidToString(instance->ref_id),
UTI_TimespecToString(sample_time), diff);
return 0; return 0;
} }
return 1; return 1;
} }
static int static int
pps_stratum(RCL_Instance instance, struct timespec *ts) pps_stratum(RCL_Instance instance, struct timeval *tv)
{ {
struct timespec 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(ts, &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;
} }
@@ -624,7 +548,7 @@ poll_timeout(void *arg)
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) { if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
double offset, dispersion; double offset, dispersion;
struct timespec sample_time; struct timeval sample_time;
int sample_ok, stratum; int sample_ok, stratum;
sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion); sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
@@ -648,34 +572,34 @@ 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 timespec *raw, struct timespec *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
log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion) log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion)
{ {
char sync_stats[4] = {'N', '+', '-', '?'}; char sync_stats[4] = {'N', '+', '-', '?'};
@@ -685,7 +609,7 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
if (!filtered) { if (!filtered) {
LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e", LOG_FileWrite(logfileid, "%s.%06d %-5s %3d %1c %1d %13.6e %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec), UTI_TimeToLogForm(sample_time->tv_sec),
(int)sample_time->tv_nsec / 1000, (int)sample_time->tv_usec,
UTI_RefidToString(instance->ref_id), UTI_RefidToString(instance->ref_id),
instance->driver_polled, instance->driver_polled,
sync_stats[instance->leap_status], sync_stats[instance->leap_status],
@@ -696,7 +620,7 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
} else { } else {
LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e", LOG_FileWrite(logfileid, "%s.%06d %-5s - %1c - - %13.6e %10.3e",
UTI_TimeToLogForm(sample_time->tv_sec), UTI_TimeToLogForm(sample_time->tv_sec),
(int)sample_time->tv_nsec / 1000, (int)sample_time->tv_usec,
UTI_RefidToString(instance->ref_id), UTI_RefidToString(instance->ref_id),
sync_stats[instance->leap_status], sync_stats[instance->leap_status],
cooked_offset, cooked_offset,
@@ -749,7 +673,7 @@ filter_get_avg_sample_dispersion(struct MedianFilter *filter)
} }
static void static void
filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion) filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion)
{ {
filter->index++; filter->index++;
filter->index %= filter->length; filter->index %= filter->length;
@@ -761,12 +685,12 @@ filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
filter->samples[filter->index].offset = offset; filter->samples[filter->index].offset = offset;
filter->samples[filter->index].dispersion = dispersion; filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG("filter sample %d t=%s offset=%.9f dispersion=%.9f", DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimespecToString(sample_time), offset, dispersion); filter->index, UTI_TimevalToString(sample_time), offset, dispersion);
} }
static int static int
filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion) filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
{ {
if (filter->last < 0) if (filter->last < 0)
return 0; return 0;
@@ -777,12 +701,6 @@ filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time
return 1; return 1;
} }
static int
filter_get_samples(struct MedianFilter *filter)
{
return filter->used;
}
static const struct FilterSample *tmp_sorted_array; static const struct FilterSample *tmp_sorted_array;
static int static int
@@ -885,7 +803,7 @@ filter_select_samples(struct MedianFilter *filter)
} }
static int static int
filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion) filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
{ {
struct FilterSample *s, *ls; struct FilterSample *s, *ls;
int i, n, dof; int i, n, dof;
@@ -902,7 +820,7 @@ filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
s = &filter->samples[filter->selected[i]]; s = &filter->samples[filter->selected[i]];
filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time); UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time);
filter->y_data[i] = s->offset; filter->y_data[i] = s->offset;
filter->w_data[i] = s->dispersion; filter->w_data[i] = s->dispersion;
} }
@@ -951,7 +869,7 @@ filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
/* drop the sample if variance is larger than allowed maximum */ /* drop the sample if variance is larger than allowed maximum */
if (filter->max_var > 0.0 && var > filter->max_var) { if (filter->max_var > 0.0 && var > filter->max_var) {
DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f", DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f",
sqrt(var), sqrt(filter->max_var)); sqrt(var), sqrt(filter->max_var));
return 0; return 0;
} }
@@ -977,7 +895,7 @@ filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
if (d < e) if (d < e)
d = e; d = e;
UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time); UTI_AddDoubleToTimeval(&ls->sample_time, x, sample_time);
*offset = y; *offset = y;
*dispersion = d; *dispersion = d;
@@ -987,26 +905,15 @@ filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, dou
} }
static void static void
filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset) filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset)
{ {
int i, first, last; int i;
double delta_time; double delta_time;
struct timespec *sample; struct timeval *sample;
if (filter->last < 0) for (i = 0; i < filter->used; i++) {
return;
/* always slew the last sample as it may be needed by PPS refclocks */
if (filter->used > 0) {
first = 0;
last = filter->used - 1;
} else {
first = last = filter->last;
}
for (i = first; i <= last; i++) {
sample = &filter->samples[i].sample_time; sample = &filter->samples[i].sample_time;
UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset); UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
filter->samples[i].offset -= delta_time; filter->samples[i].offset -= delta_time;
} }
} }

View File

@@ -37,19 +37,14 @@ typedef struct {
int driver_poll; int driver_poll;
int poll; int poll;
int filter_length; int filter_length;
int pps_forced;
int pps_rate; int pps_rate;
int min_samples;
int max_samples;
int sel_options;
int max_lock_age;
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;
double pulse_width; SRC_SelectOption sel_option;
} RefclockParameters; } RefclockParameters;
typedef struct RCL_Instance_Record *RCL_Instance; typedef struct RCL_Instance_Record *RCL_Instance;
@@ -64,18 +59,14 @@ extern void RCL_Initialise(void);
extern void RCL_Finalise(void); extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params); extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void); extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now); extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now);
/* functions used by drivers */ /* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data); extern void RCL_SetDriverData(RCL_Instance instance, void *data);
extern void *RCL_GetDriverData(RCL_Instance instance); extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance); extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name); extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap); extern int RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second); extern int RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);
extern double RCL_GetPrecision(RCL_Instance instance);
extern int RCL_GetDriverPoll(RCL_Instance instance);
#endif #endif

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 2013, 2017 * 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
@@ -33,134 +33,151 @@
#include "sysincl.h" #include "sysincl.h"
#include "refclock.h" #include <linux/ptp_clock.h>
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
#include "sched.h"
#include "sys_linux.h"
struct phc_instance { #include "refclock.h"
int fd; #include "logging.h"
int mode; #include "util.h"
int nocrossts;
int extpps; /* From linux/include/linux/posix-timers.h */
int pin; #define CPUCLOCK_MAX 3
int channel; #define CLOCKFD CPUCLOCK_MAX
HCL_Instance clock; #define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
#define NUM_READINGS 10
static int no_sys_offset_ioctl = 0;
struct phc_reading {
struct timespec sys_ts1;
struct timespec phc_ts;;
struct timespec sys_ts2;
}; };
static void read_ext_pulse(int sockfd, int event, void *anything); static double diff_ts(struct timespec *ts1, struct timespec *ts2)
static int phc_initialise(RCL_Instance instance)
{ {
struct phc_instance *phc; return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
int phc_fd, rising_edge; }
char *path, *s;
path = RCL_GetDriverParameter(instance); static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
struct ptp_sys_offset sys_off;
int i;
phc_fd = SYS_Linux_OpenPHC(path, 0); /* Silence valgrind */
if (phc_fd < 0) { memset(&sys_off, 0, sizeof (sys_off));
LOG_FATAL("Could not open PHC");
sys_off.n_samples = n;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno));
return 0; return 0;
} }
phc = MallocNew(struct phc_instance); for (i = 0; i < n; i++) {
phc->fd = phc_fd; readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec;
phc->mode = 0; readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0; readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0; readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec;
if (phc->extpps) { readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
s = RCL_GetDriverOption(instance, "pin");
phc->pin = s ? atoi(s) : 0;
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
} }
RCL_SetDriverData(instance, phc); return 1;
#else
/* Not available */
return 0;
#endif
}
static int read_phc_user(struct phc_reading *readings, int phc_fd, int n)
{
clockid_t phc_id;
int i;
phc_id = FD_TO_CLOCKID(phc_fd);
for (i = 0; i < n; i++) {
if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) ||
clock_gettime(phc_id, &readings[i].phc_ts) ||
clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) {
LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno));
return 0;
}
}
return 1;
}
static int phc_initialise(RCL_Instance instance)
{
struct ptp_clock_caps caps;
int phc_fd;
char *path;
path = RCL_GetDriverParameter(instance);
phc_fd = open(path, O_RDONLY);
if (phc_fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
return 0;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(phc_fd);
RCL_SetDriverData(instance, (void *)(long)phc_fd);
return 1; return 1;
} }
static void phc_finalise(RCL_Instance instance) static void phc_finalise(RCL_Instance instance)
{ {
struct phc_instance *phc; close((long)RCL_GetDriverData(instance));
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
}
close(phc->fd);
Free(phc);
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
int channel;
instance = anything;
phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
}
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
} }
static int phc_poll(RCL_Instance instance) static int phc_poll(RCL_Instance instance)
{ {
struct phc_instance *phc; struct phc_reading readings[NUM_READINGS];
struct timespec phc_ts, sys_ts, local_ts; struct timeval tv;
double offset, phc_err, local_err; double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best;
phc = (struct phc_instance *)RCL_GetDriverData(instance); phc_fd = (long)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance), if (!no_sys_offset_ioctl) {
&phc->mode, &phc_ts, &sys_ts, &phc_err)) if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) {
no_sys_offset_ioctl = 1;
return 0; return 0;
}
if (phc->extpps) { } else {
LCL_CookTime(&sys_ts, &local_ts, &local_err); if (!read_phc_user(readings, phc_fd, NUM_READINGS))
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0; return 0;
} }
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts); /* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) {
delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1);
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err); if (!i || best_delay > delay) {
best = i;
best_delay = delay;
}
}
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal); offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0;
tv.tv_sec = readings[best].sys_ts2.tv_sec;
tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &tv, offset, LEAP_Normal);
} }
RefclockDriver RCL_PHC_driver = { RefclockDriver RCL_PHC_driver = {

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>
@@ -59,37 +59,37 @@ static int pps_initialise(RCL_Instance instance) {
fd = open(path, O_RDWR); fd = open(path, O_RDWR);
if (fd < 0) { if (fd < 0) {
LOG_FATAL("open() failed on %s", path); LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
return 0; return 0;
} }
UTI_FdSetCloexec(fd); UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) { if (time_pps_create(fd, &handle) < 0) {
LOG_FATAL("time_pps_create() failed on %s", path); LOG_FATAL(LOGF_Refclock, "time_pps_create() failed on %s", path);
return 0; return 0;
} }
if (time_pps_getcap(handle, &mode) < 0) { if (time_pps_getcap(handle, &mode) < 0) {
LOG_FATAL("time_pps_getcap() failed on %s", path); LOG_FATAL(LOGF_Refclock, "time_pps_getcap() failed on %s", path);
return 0; return 0;
} }
if (time_pps_getparams(handle, &params) < 0) { if (time_pps_getparams(handle, &params) < 0) {
LOG_FATAL("time_pps_getparams() failed on %s", path); LOG_FATAL(LOGF_Refclock, "time_pps_getparams() failed on %s", path);
return 0; return 0;
} }
if (!edge_clear) { if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) { if (!(mode & PPS_CAPTUREASSERT)) {
LOG_FATAL("CAPTUREASSERT not supported on %s", path); LOG_FATAL(LOGF_Refclock, "CAPTUREASSERT not supported on %s", path);
return 0; return 0;
} }
params.mode |= PPS_CAPTUREASSERT; params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR; params.mode &= ~PPS_CAPTURECLEAR;
} else { } else {
if (!(mode & PPS_CAPTURECLEAR)) { if (!(mode & PPS_CAPTURECLEAR)) {
LOG_FATAL("CAPTURECLEAR not supported on %s", path); LOG_FATAL(LOGF_Refclock, "CAPTURECLEAR not supported on %s", path);
return 0; return 0;
} }
params.mode |= PPS_CAPTURECLEAR; params.mode |= PPS_CAPTURECLEAR;
@@ -97,7 +97,7 @@ static int pps_initialise(RCL_Instance instance) {
} }
if (time_pps_setparams(handle, &params) < 0) { if (time_pps_setparams(handle, &params) < 0) {
LOG_FATAL("time_pps_setparams() failed on %s", path); LOG_FATAL(LOGF_Refclock, "time_pps_setparams() failed on %s", path);
return 0; return 0;
} }
@@ -124,6 +124,7 @@ static int pps_poll(RCL_Instance instance)
{ {
struct pps_instance *pps; struct pps_instance *pps;
struct timespec ts; struct timespec ts;
struct timeval tv;
pps_info_t pps_info; pps_info_t pps_info;
pps_seq_t seq; pps_seq_t seq;
@@ -133,7 +134,7 @@ static int pps_poll(RCL_Instance instance)
ts.tv_nsec = 0; ts.tv_nsec = 0;
if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) { if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) {
LOG(LOGS_ERR, "time_pps_fetch() failed : %s", strerror(errno)); LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno));
return 0; return 0;
} }
@@ -145,15 +146,17 @@ static int pps_poll(RCL_Instance instance)
ts = pps_info.clear_timestamp; ts = pps_info.clear_timestamp;
} }
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) { if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
DEBUG_LOG("PPS sample ignored seq=%lu ts=%s", DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu",
seq, UTI_TimespecToString(&ts)); seq, ts.tv_sec, ts.tv_nsec);
return 0; return 0;
} }
pps->last_seq = seq; pps->last_seq = seq;
tv.tv_sec = ts.tv_sec;
tv.tv_usec = ts.tv_nsec / 1000;
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec); return RCL_AddPulse(instance, &tv, ts.tv_nsec / 1e9);
} }
RefclockDriver RCL_PPS_driver = { RefclockDriver RCL_PPS_driver = {

View File

@@ -69,13 +69,13 @@ static int shm_initialise(RCL_Instance instance) {
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm); id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
if (id == -1) { if (id == -1) {
LOG_FATAL("shmget() failed"); LOG_FATAL(LOGF_Refclock, "shmget() failed");
return 0; return 0;
} }
shm = (struct shmTime *)shmat(id, 0, 0); shm = (struct shmTime *)shmat(id, 0, 0);
if ((long)shm == -1) { if ((long)shm == -1) {
LOG_FATAL("shmat() failed"); LOG_FATAL(LOGF_Refclock, "shmat() failed");
return 0; return 0;
} }
@@ -90,7 +90,7 @@ static void shm_finalise(RCL_Instance instance)
static int shm_poll(RCL_Instance instance) static int shm_poll(RCL_Instance instance)
{ {
struct timespec receive_ts, clock_ts; struct timeval tv;
struct shmTime t, *shm; struct shmTime t, *shm;
double offset; double offset;
@@ -100,30 +100,24 @@ static int shm_poll(RCL_Instance instance)
if ((t.mode == 1 && t.count != shm->count) || if ((t.mode == 1 && t.count != shm->count) ||
!(t.mode == 0 || t.mode == 1) || !t.valid) { !(t.mode == 0 || t.mode == 1) || !t.valid) {
DEBUG_LOG("SHM sample ignored mode=%d count=%d valid=%d", DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d",
t.mode, t.count, t.valid); t.mode, t.count, t.valid);
return 0; return 0;
} }
shm->valid = 0; shm->valid = 0;
receive_ts.tv_sec = t.receiveTimeStampSec; tv.tv_sec = t.receiveTimeStampSec;
clock_ts.tv_sec = t.clockTimeStampSec; tv.tv_usec = t.receiveTimeStampUSec;
offset = t.clockTimeStampSec - t.receiveTimeStampSec;
if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec && if (t.clockTimeStampNSec / 1000 == t.clockTimeStampUSec &&
t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec) { t.receiveTimeStampNSec / 1000 == t.receiveTimeStampUSec)
receive_ts.tv_nsec = t.receiveTimeStampNSec; offset += (t.clockTimeStampNSec - t.receiveTimeStampNSec) * 1e-9;
clock_ts.tv_nsec = t.clockTimeStampNSec; else
} else { offset += (t.clockTimeStampUSec - t.receiveTimeStampUSec) * 1e-6;
receive_ts.tv_nsec = 1000 * t.receiveTimeStampUSec;
clock_ts.tv_nsec = 1000 * t.clockTimeStampUSec;
}
UTI_NormaliseTimespec(&clock_ts); return RCL_AddSample(instance, &tv, offset, t.leap);
UTI_NormaliseTimespec(&receive_ts);
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
} }
RefclockDriver RCL_SHM_driver = { RefclockDriver RCL_SHM_driver = {

View File

@@ -37,62 +37,47 @@
#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;
}; };
static void read_sample(int sockfd, int event, void *anything) static void read_sample(void *anything)
{ {
struct sock_sample sample; struct sock_sample sample;
struct timespec ts;
RCL_Instance instance; RCL_Instance instance;
int s; int sockfd, s;
instance = (RCL_Instance)anything; instance = (RCL_Instance)anything;
sockfd = (long)RCL_GetDriverData(instance);
s = recv(sockfd, &sample, sizeof (sample), 0); s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 0) { if (s < 0) {
LOG(LOGS_ERR, "Could not read SOCK sample : %s", LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s",
strerror(errno)); strerror(errno));
return; return;
} }
if (s != sizeof (sample)) { if (s != sizeof (sample)) {
LOG(LOGS_WARN, "Unexpected length of SOCK sample : %d != %ld", LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample)); s, (long)sizeof (sample));
return; return;
} }
if (sample.magic != SOCK_MAGIC) { if (sample.magic != SOCK_MAGIC) {
LOG(LOGS_WARN, "Unexpected magic number in SOCK sample : %x != %x", LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x",
sample.magic, SOCK_MAGIC); sample.magic, SOCK_MAGIC);
return; return;
} }
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
if (sample.pulse) { if (sample.pulse) {
RCL_AddPulse(instance, &ts, sample.offset); RCL_AddPulse(instance, &sample.tv, sample.offset);
} else { } else {
RCL_AddSample(instance, &ts, sample.offset, sample.leap); RCL_AddSample(instance, &sample.tv, sample.offset, sample.leap);
} }
} }
@@ -106,13 +91,13 @@ static int sock_initialise(RCL_Instance instance)
s.sun_family = AF_UNIX; s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) { if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
LOG_FATAL("path %s is too long", path); LOG_FATAL(LOGF_Refclock, "path %s is too long", path);
return 0; return 0;
} }
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0) { if (sockfd < 0) {
LOG_FATAL("socket() failed"); LOG_FATAL(LOGF_Refclock, "socket() failed");
return 0; return 0;
} }
@@ -120,12 +105,12 @@ static int sock_initialise(RCL_Instance instance)
unlink(path); unlink(path);
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) { if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
LOG_FATAL("bind() failed"); LOG_FATAL(LOGF_Refclock, "bind() failed");
return 0; return 0;
} }
RCL_SetDriverData(instance, (void *)(long)sockfd); RCL_SetDriverData(instance, (void *)(long)sockfd);
SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance); SCH_AddInputFileHandler(sockfd, read_sample, instance);
return 1; return 1;
} }
@@ -134,7 +119,7 @@ static void sock_finalise(RCL_Instance instance)
int sockfd; int sockfd;
sockfd = (long)RCL_GetDriverData(instance); sockfd = (long)RCL_GetDriverData(instance);
SCH_RemoveFileHandler(sockfd); SCH_RemoveInputFileHandler(sockfd);
close(sockfd); close(sockfd);
} }

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