Compare commits

...

384 Commits

Author SHA1 Message Date
Miroslav Lichvar
b703bc32c9 doc: update NEWS 2016-06-07 11:20:59 +02:00
Miroslav Lichvar
09afdd4b36 doc: update README 2016-06-07 11:20:59 +02:00
Miroslav Lichvar
5cadaf8d55 doc: add question about reference ID to FAQ 2016-06-07 11:20:59 +02:00
Stephen Wadeley
89ac745184 doc: improve chronyc man page
- fix redundant words, word order, articles, consistency, typos
- avoid slashes, contractions, `may`, dashes in running text
- add Oxford commas
- use colon before examples
2016-06-07 09:36:55 +02:00
Stephen Wadeley
e2422023c4 doc: improve chrony.conf man page
- fix word order, articles, consistency, and some typos
- avoid slashes, contractions, `may`, dashes in running text
- use colons before example and code blocks
- add Oxford commas
2016-06-07 09:36:55 +02:00
Miroslav Lichvar
1ec0813663 client: fix compiler warnings on NetBSD 2016-06-07 09:36:55 +02:00
Miroslav Lichvar
6ba7fad2a7 reference: suppress orphan option in special reference modes
This allows a server that will become the orphan source to initialize
its time with the initstepslew directive from the current orphan source
or its clients.
2016-05-30 14:12:42 +02:00
Miroslav Lichvar
962ca91574 doc: improve answer in FAQ for firewall issue 2016-05-23 09:01:10 +02:00
Miroslav Lichvar
91cbebb629 examples: update chrony.spec 2016-05-17 17:57:33 +02:00
Miroslav Lichvar
722f038f1f test: extend 105-ntpauth 2016-05-17 13:03:53 +02:00
Miroslav Lichvar
5e61c002a6 ntp: fix definition of minimum and maximum MAC length
The NTP_*_MAC_LENGTH macros didn't include the key ID, which caused the
NTP authentication check to ignore MACs with 512-bit hashes (SHA512,
WHIRLPOOL).

This was broken since update to NTPv4.
2016-05-17 12:57:28 +02:00
Miroslav Lichvar
46e1e79921 doc: update NEWS 2016-05-16 11:08:00 +02:00
Miroslav Lichvar
546e5e236c make_release: update for changes in documentation 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
16a1a89bf4 makefile: remove config.h and config.log in distclean 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
8a996572d2 update copyright years 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
5f082b9a4d doc: update examples of configuration in isolated networks 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
4622173135 doc: fix typo in chronyc man page 2016-05-13 16:58:07 +02:00
Miroslav Lichvar
99e1c44c25 doc: update FAQ 2016-05-13 16:58:04 +02:00
Miroslav Lichvar
b48e4421de ntp: don't check for synchronization loop in special reference modes
If a special reference mode is enabled, always pass the test for
synchronization loop. This allows chronyd using the initstepslew
directive (or the -q/-Q option) to accept time from its own clients
after restart as is documented in the chrony.conf man page.

This was broken since update to NTPv4.
2016-05-13 13:47:12 +02:00
Miroslav Lichvar
d1f4e5876b refclock: avoid reallocation of refclock instances
Change the array with refclock instances to store just pointers and
avoid reallocation of the instances. This fixes a bug with the SOCK
refclock, which uses the pointer to the instance in a file handler and
which was invalid when the instance was reallocated (after adding
another refclock).

The bug is from commit d92583ed33.
2016-05-12 13:27:46 +02:00
Miroslav Lichvar
71b7e689c0 sched: fix handling of signals after finalization
Don't require the scheduler to be initialized in SCH_QuitProgram().
This fixes a crash when a signal is received between scheduler
finalization and chronyd exit.
2016-04-15 14:49:03 +02:00
Miroslav Lichvar
26b87b844d sources: consider only reachable orphans for selection
Ignore orphan sources that are unreachable (but still have usable stats)
to have a quick and consistent source selection between orphans.

This also fixes the "Unknown local refid in orphan mode" error appearing
when a selected orphan source is removed, as the source is marked as
unreachable and the selection runs with disabled NTP instance before the
source instance is actually removed.
2016-04-13 11:43:36 +02:00
Miroslav Lichvar
1834ee05e5 doc: fix typos in man pages 2016-04-12 12:43:10 +02:00
Miroslav Lichvar
7d7bf915ac doc: improve answer in FAQ for error 501 Not authorised 2016-04-12 12:43:10 +02:00
Miroslav Lichvar
d86e9f4aa3 doc: use https in links to chrony website 2016-04-12 12:24:41 +02:00
Miroslav Lichvar
942b52a3ca client: initialize variables in new local command 2016-04-11 08:28:45 +02:00
Miroslav Lichvar
b252c57a22 reference: rework activation of local reference
Instead of using a timer for switching the reference to the
unsynchronised state (which activates the local reference), check
if it should be active when returning the reference parameters.
2016-04-08 16:47:41 +02:00
Miroslav Lichvar
2aab6a85a4 reference: return real sync status in REF_GetReferenceParams()
If local reference is active, return normal leap, but unsynchronised
status. Update the callers of the function to work with the leap
directly and not change their behaviour.

REF_IsLocalActive() is no longer needed.
2016-04-08 16:29:09 +02:00
Miroslav Lichvar
10719d6d35 reference: report same values in tracking command as in NTP
Use REF_GetReferenceParams() in the tracking command to simplify the
code and report the same values as what NTP clients of the server see.

When the local reference mode is active, this changes the leap status to
synchronised and reference time to one second behind current time. When
not synchronised, the root delay and root dispersion are now 1 second.
2016-04-08 16:21:19 +02:00
Miroslav Lichvar
59938efd23 stubs: add NSR_GetLocalRefid() 2016-04-08 16:21:19 +02:00
Miroslav Lichvar
53b15bd5c7 cmdmon: extend local command to match local directive 2016-04-08 16:21:19 +02:00
Miroslav Lichvar
5084a8b342 reference: clamp local stratum set from cmdmon 2016-04-08 16:21:15 +02:00
Miroslav Lichvar
4d1c795804 cmdparse: check if stratum in local directive is valid 2016-04-06 16:38:14 +02:00
Miroslav Lichvar
a9049569af cmdmon: remove obsolete definition 2016-04-06 15:56:12 +02:00
Miroslav Lichvar
dec1d2bfb2 ntp: ignore order of resolved addresses when replacing tentative source
If the replaced source never had a valid reply (e.g. because it was a
bad replacement), ignore the order of addresses from the resolver to not
get stuck to a pair of addresses if the order doesn't change, or a group
of IPv4/IPv6 addresses if the resolver prefers inaccessible IP family.
2016-04-05 18:03:46 +02:00
Miroslav Lichvar
62e66bda60 ntp: mark all new sources and replacements as tentative 2016-04-05 18:03:44 +02:00
Miroslav Lichvar
3abaa92926 doc: update description of local directive 2016-04-01 18:42:43 +02:00
Miroslav Lichvar
37e6357c02 ntp: don't check reference timestamp in received packets
When ntpd as an NTP server has active orphan mode, it doesn't update
its reference time and the reference timestamp may fail the NTP test
3 and 7. (https://bugs.ntp.org/show_bug.cgi?id=1098)

Remove both checks of the timestamp to allow chronyd to operate as
a client of ntpd server in the orphan mode. When ntpd is fixed and
old versions are no longer used, this may be reverted.
2016-04-01 15:13:28 +02:00
Miroslav Lichvar
6accd19eb3 sources: log error when local refid is unknown in orphan mode 2016-04-01 09:37:49 +02:00
Miroslav Lichvar
da96d334ab test: add 121-orphan 2016-03-31 16:12:14 +02:00
Miroslav Lichvar
5a92dbe784 sources: add support for orphan sources
When the local reference is configured with the orphan option, NTP
sources that have stratum equal to the configured local stratum are
considered to be orphans (i.e. serving local time while not being
synchronised with real time) and are excluded from the normal source
selection. Sources with stratum larger than the local stratum are
considered to be directly on indirectly synchronised to an orphan and
are always ignored.

If no selectable source is available and all orphan sources have
reference IDs larger than the local ID, no source will be selected and
the local reference mode will be activated at some point, i.e. this host
will become an orphan. Otherwise, the orphan source with the smallest
reference ID will be selected. This ensures a group of servers polling
each other (with the same orphan configuration) which have no external
source can settle down to a state where only one server is serving its
local unsychronised time and others are synchronised to it.
2016-03-31 16:08:49 +02:00
Miroslav Lichvar
8fe5e9cf1e reference: add orphan mode to local reference
Add orphan option to the local directive. It will enable an orphan mode
compatible with ntpd.
2016-03-31 16:08:49 +02:00
Miroslav Lichvar
81f440a882 reference: activate local reference with large root distance
Since the update to NTPv4, when the clock is in the synchronised state
and the clock updates stop (e.g. sources become unreachable), it doesn't
switch to the unsynchronised state and the local reference is never
activate. This can be a problem for clients that rely on the server to
always have root distance below some value (e.g. chronyd's maxdistance).

Add a timer that will activate the local reference when the root
distance reaches a specified threshold. It can be configured with the
distance option in the local directive (by default 1.0 second).
2016-03-31 16:08:45 +02:00
Miroslav Lichvar
981f897c96 conf: rework local directive to have default stratum
Allow the local directive to be specified without the stratum field.
It's an option now, with default value 10. Also, move the parsing code
to cmdparse.c to make it available to the client.
2016-03-31 16:01:06 +02:00
Miroslav Lichvar
eb75ce7d07 ntp: add function to get local reference ID
When a valid NTP reply is received, save the local address (e.g. from
IP_PKTINFO), so the reference ID which would the source use for this
host can be calculated when needed.
2016-03-31 16:01:02 +02:00
Miroslav Lichvar
5645e57ce0 sys_linux: include <termios.h> for TCGETS 2016-03-29 10:06:21 +02:00
Miroslav Lichvar
a12c7c422b local: make maximum frequency offset configurable
Add maxdrift directive to set the maximum assumed drift of the clock,
which sets the maximum frequency offset chronyd is allowed to use to
to correct the drift.
2016-03-22 17:12:27 +01:00
Miroslav Lichvar
d70e815e9f sources: try to replace NTP sources with bad distance
Similarly to unreachable sources and falsetickers, try to replace
sources with distance larger than the limit set by the maxdistance
directive with a newly resolved address of the hostname.
2016-03-22 17:12:27 +01:00
Miroslav Lichvar
eb329e9f52 client: ignore -v option in csv mode 2016-03-22 16:34:35 +01:00
Miroslav Lichvar
5833be6ccf util: fix UTI_FloatNetworkToHost() with very small exponents
Fix conversion of floating point numbers from the cmdmon format with
very small exponents, as for instance could be in the smoothing report
when the smoothing process ends.

This was broken in commit 8e71a46173.
2016-03-17 16:18:28 +01:00
Miroslav Lichvar
ea3950d57e client: add CSV output mode
Add a new option (-c) to chronyc to enable printing of reports in a
column-separated values (CSV) format. IP addresses will not be resolved
to hostnames, time will be printed as number of seconds since the epoch
and values in seconds will not be converted to other units.
2016-03-17 16:01:58 +01:00
Miroslav Lichvar
3f51805e62 client: rework printing of reports
Add a new printf-like function to allow printing of all fields at once
and rework all commands which print a report to use it. Add functions
for printing of headers and information fields, and formatting of IP
addresses and reference IDs.
2016-03-17 15:50:39 +01:00
Miroslav Lichvar
b45f53dd20 util: randomize hashing of IP addresses
Include a random (constant) value in the hash in UTI_IPToHash() to
randomize the order in which NTP sources are stored in the hash table
and polled on start. This change also randomizes the order of clientlog
records.
2016-03-15 14:29:42 +01:00
Miroslav Lichvar
9749a1c6fc test: make 105-ntpauth more reliable 2016-03-14 17:42:49 +01:00
Miroslav Lichvar
5ca5d279d7 makefile: add distclean target to test/unit/Makefile 2016-03-14 15:55:39 +01:00
Miroslav Lichvar
7b52c1578f makefile: remove Makefile in doc/Makefile on distclean 2016-03-14 15:55:39 +01:00
Miroslav Lichvar
72975ce1f0 ntp: improve error messages for socket options 2016-03-14 15:55:39 +01:00
Miroslav Lichvar
9a4c22db03 cmdmon: extend initialization checks
Move the message size checks to a separate function and check also
header size in the command request and reply to catch incompatible
changes.
2016-03-14 15:34:52 +01:00
Miroslav Lichvar
e7af875b68 rewrite assertions with very long messages 2016-03-14 15:15:51 +01:00
Miroslav Lichvar
4acca9b727 client: add reselectdist to help text 2016-03-11 17:29:10 +01:00
Miroslav Lichvar
b2d93b2e38 git: update .gitignore 2016-03-11 17:29:10 +01:00
Miroslav Lichvar
74afffed0c doc: convert manual from Texinfo to AsciiDoc
Split and convert the manual into four AsciiDoc documents, a document
about installation and three documents in the manpage type for
chrony.conf, chronyd and chronyc. The minimal man pages that were
maintained separately from the manual are replaced by full man pages
generated from AsciiDoc. Info files will no longer be provided.

Some parts of the manual are rewritten, updated or trimmed. The
introduction chapter is partially merged with README. The chapter about
typical operating scenarios is included in the chrony.conf man page.
2016-03-11 17:29:03 +01:00
Miroslav Lichvar
5828426977 doc: update installation instructions 2016-02-16 14:25:38 +01:00
Miroslav Lichvar
d04fb4b7fa doc: improve description of trust option 2016-02-16 13:43:33 +01:00
Miroslav Lichvar
f5fe3ab4a1 test/unit: add sources unit test 2016-02-16 13:43:33 +01:00
Miroslav Lichvar
6b6b097fe8 test/unit: include microseconds in default random seed 2016-02-16 13:43:28 +01:00
Miroslav Lichvar
4998afc9bb test/unit: add more helper functions 2016-02-16 13:43:07 +01:00
Miroslav Lichvar
80f4d75968 test/unit: follow chrony function naming convention 2016-02-15 16:09:05 +01:00
Miroslav Lichvar
910663c37b test: add ntp_sources unit test 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
34a4695e81 test: add clientlog unit test 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
fe00319f45 addrfilt: remove TEST code
A test of the address filter is now included in unit tests.
2016-02-05 15:20:40 +01:00
Miroslav Lichvar
4c77d18416 test: add addrfilt unit test 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
a63e18edb8 test: specify files with path in source commands
This should prevent sourcing of an unrelated file found in $PATH.
2016-02-05 15:20:40 +01:00
Miroslav Lichvar
8b676502de test: don't download files in tests
Remove automatic download and compilation of clknetsim. If clknetsim is
not found, skip all simulation tests, but don't fail "make check".
Also, respect the CLKNETSIM_PATH environment variable.
2016-02-05 15:20:40 +01:00
Miroslav Lichvar
cf5b344ea8 git: update .gitignore 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
4ab98f62e9 test: add support for unit testing 2016-02-05 15:20:40 +01:00
Miroslav Lichvar
e6cc682f86 update NEWS 2016-02-02 16:49:05 +01:00
Miroslav Lichvar
ff541e24fb update README 2016-02-02 12:05:51 +01:00
Miroslav Lichvar
008615370a update copyright years 2016-02-02 12:02:16 +01:00
Miroslav Lichvar
beaf275222 ntp: optimize resizing of hash table with sources 2016-02-02 12:02:16 +01:00
Miroslav Lichvar
400820d3f3 sys_generic: use privops for settimeofday()
This is needed on FreeBSD and Solaris when running without root
privileges.
2016-02-01 16:54:08 +01:00
Miroslav Lichvar
4eabc84a0c clientlog: fix warning reported by static analyzer 2016-02-01 14:37:10 +01:00
Miroslav Lichvar
cf636a969e client: fix format specifiers in client report
This was missing in commit 861ac013bc.
2016-02-01 10:30:31 +01:00
Miroslav Lichvar
e3191e372b cmdmon: update protocol changelog 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
705e32acdc cmdmon: define new types for CLIENT_ACCESSES_BY_INDEX command
There was an incompatible change in the client access report. To avoid
bumping the protocol version drop support for the original request/reply
types and define new CLIENT_ACCESSES_BY_INDEX2 types as a newer version
of the command.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
6e4dd9302d cmdmon: allow unhandled commands
Replace the assert() with a debug message to not crash if someone
forgets to implement a newly defined command.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
ea002130d7 cmdmon: reply to invalid commands
If an unknown command is received (e.g. from a future client), it should
get a reply and print an error code instead of timing out.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
7ba5ffa706 cmdmon: update debug messages 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
861ac013bc cmdmon: use 32-bit fields in client access report
The clientlog record still uses 16-bit integers to count dropped
packets, but this will avoid an incompatible change in the command
reply if there will be a need to count more than 2^16 drops.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
a6da963f45 clientlog: don't allow rate limiting with noclientlog 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
55ba7ee2a1 doc: update description of clients command 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
3121f31ced doc: describe rate limiting directives 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
da296db91d examples: update for recent changes 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
d36ca9288a doc: update keyfile description 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
8549043a3f conf: set logchange to 1 second by default
logchange is now always enabled, with 1 second threshold by default.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
e0ae2b4bb5 client: generate key 1 by default in keygen command 2016-01-29 17:55:58 +01:00
Miroslav Lichvar
aad42ceaec keys: warn about short key only if used by source
After restricting authentication of servers and peers to the specified
key, a short key in the key file is a security problem from the client's
point of view only if it's specified for a source.
2016-01-29 17:55:58 +01:00
Miroslav Lichvar
f225469e6e pktlength: fix compiler warning on Mac OS X 2016-01-25 12:33:42 +01:00
Miroslav Lichvar
7cc432ff7e cmdmon: extend initialisation tests 2016-01-22 17:30:55 +01:00
Miroslav Lichvar
0a9d75bfb8 pktlength: rework code to use tables 2016-01-22 17:30:51 +01:00
Miroslav Lichvar
070f2706b7 client: add serverstats command 2016-01-22 14:40:29 +01:00
Miroslav Lichvar
9b019a03e7 cmdmon: add serverstats command
Add a new command to obtain a server report with the new clientlog
statistics.
2016-01-22 13:26:38 +01:00
Miroslav Lichvar
f52a738660 clientlog: count total number of hits and drops
Count total number of NTP and command hits. Count also number of log
records that were replaced when the hash table couldn't be resized due
to the memory limit.
2016-01-22 13:26:04 +01:00
Miroslav Lichvar
b80df5152a Merge branch '2.2-security' 2016-01-20 12:18:42 +01:00
Miroslav Lichvar
beb275a769 doc: update NEWS 2016-01-18 17:37:21 +01:00
Miroslav Lichvar
86c21a3a85 test: extend 105-ntpauth to test symmetric mode 2016-01-18 17:37:21 +01:00
Miroslav Lichvar
05236a4f23 test: allow setting options for each peer side separately 2016-01-18 17:37:21 +01:00
Miroslav Lichvar
a78bf9725a ntp: restrict authentication of server/peer to specified key
When a server/peer was specified with a key number to enable
authentication with a symmetric key, packets received from the
server/peer were accepted if they were authenticated with any of
the keys contained in the key file and not just the specified key.

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

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

This issue was discovered by Matt Street of Cisco ASIG.
2016-01-18 17:28:47 +01:00
Miroslav Lichvar
82fbb5c2f5 privops: reload DNS configuration
The helper process needs to call res_init() before DNS_Name2IPAddress()
in order to see changes in resolv.conf.
2016-01-15 16:58:12 +01:00
Miroslav Lichvar
a592d82ad9 client: improve waitsync help text 2016-01-14 14:45:52 +01:00
Miroslav Lichvar
7fcf69ce5f client: add keygen command
Add a new command that will generate a random key from /dev/urandom with
given ID, hash function and length.
2016-01-14 14:45:52 +01:00
Miroslav Lichvar
32ac6ffa26 util: add UTI_GetRandomBytesUrandom()
This function always uses /dev/urandom, even if arc4random() is
available, and is intended for generating long-term keys.
2016-01-14 14:45:52 +01:00
Miroslav Lichvar
0d12410eaa keys: warn when loaded key is shorter than 80 bits
Consider 80 bits as the absolute minimum for a secure symmetric key.  If
a loaded key is shorter, send a warning to the system log to encourage
the admin to replace it with a longer key.
2016-01-14 14:45:52 +01:00
Miroslav Lichvar
54c8732c46 sys_linux: use privops helper when running with seccomp filter
Enable the PRV_Name2IPAddress() function with seccomp support and start
the helper process before loading the seccomp filter (but after dropping
root privileges). This will move the getaddrinfo() call outside the
seccomp filter and should make it more reliable as the list of required
system calls won't depend on what glibc NSS modules are used on the
system.
2016-01-14 14:45:48 +01:00
Miroslav Lichvar
9b9d6ab150 privops: add support for privileged DNS_Name2IPAddress() 2016-01-13 11:25:45 +01:00
Miroslav Lichvar
c6554bfd30 nameserv: return at most 16 addresses from DNS_Name2IPAddress()
This is the same limit as in the asynchronous resolver. Use common macro
for all buffers storing IP addresses.
2016-01-13 11:25:26 +01:00
Miroslav Lichvar
83cd8ae39b test: don't check packet intervals in 009-sourceselection
Since commit 8b235297, which changed address hashing, the first packet
is not sent to the first server and doesn't have the extra delay. If the
last packet is sent to the first server, the mean outgoing interval will
be significantly longer than the incoming interval and the check will
fail.
2016-01-08 14:30:23 +01:00
Miroslav Lichvar
23b9d80897 test: add 120-selectoptions 2016-01-08 14:30:23 +01:00
Miroslav Lichvar
e98f76e084 sources: add require option
Require that at least one of the sources specified with this option is
selectable (i.e. recently reachable and not a falseticker) before
updating the clock. Together with the trust option this may be useful to
allow a trusted, but not very precise, reference clock or a trusted
authenticated NTP source to be safely combined with unauthenticated NTP
sources in order to improve the accuracy of the clock. They can be
selected and used for synchronization only if they agree with the
trusted and required source.
2016-01-08 14:30:17 +01:00
Miroslav Lichvar
936f5cb0f1 sources: add trust option
Assume time from a source that is specified with the trust option is
always true.  It can't be rejected as falseticker in the source
selection if sources that are specified without this option don't agree
with it.
2016-01-07 16:20:27 +01:00
Miroslav Lichvar
fa15fb3d53 sources: turn select options into flags
This will allow adding new options for source selection which can be
combined with others.
2015-12-18 16:29:47 +01:00
Miroslav Lichvar
62d61de93d sources: fix formatting of selection intervals in comment
It was mangled in commit 6f84d2fac1.
2015-12-18 16:25:26 +01:00
Miroslav Lichvar
ba81d68b07 refclock: ignore samples with unsynchronised leap status 2015-12-18 16:25:23 +01:00
Miroslav Lichvar
ba25fb1bcc refclock: describe fields in SOCK sample 2015-12-18 12:44:15 +01:00
Miroslav Lichvar
69642dd440 fix undefined shift operations on signed integers 2015-12-17 12:09:45 +01:00
Miroslav Lichvar
e5a593f013 conf: update default ratelimit configuration 2015-12-17 09:44:21 +01:00
Miroslav Lichvar
acec7d0e28 clientlog: use token buckets for response rate limiting
Replace thresholds that activated rate limiting with token buckets.
Response rate limiting is now not just active or inactive, the interval
and burst options directly control the response rate.
2015-12-17 09:42:48 +01:00
Miroslav Lichvar
a4c89e5bbe clientlog: refactor updating of record data 2015-12-14 17:05:02 +01:00
Miroslav Lichvar
c5265f6070 doc: update description of -u option and user directive 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
0a10df1cf5 sys_linux: keep CAP_NET_BIND_SERVICE only if NTP port can be opened
If port is set to 0 in the config file, the server port cannot be opened
and there is no point in keeping the binding capability.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
30f2a2003c sys: remove unused code 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
67b108d1ce sys_solaris: add support for dropping root privileges
On Solaris, use the privops helper for the ntp_adjtime(),
settimeofday(), and bind() system calls.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
8a95631e39 sys_solaris: fix building with current timex driver
The SYS_Timex_InitialiseWithFunctions() call in the Solaris driver
wasn't updated in commit d6fdae5f1d.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
82510e6b1f sys_netbsd: add support for dropping root privileges on FreeBSD
On FreeBSD, use the privops helper for the adjtime(), ntp_adjtime(),
settimeofday(), and bind() system calls.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
12ee4bf6ac sys_timex: add support for ntp_adjtime() via privops 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
3cb0351aff privops: add support for privileged ntp_adjtime() 2015-12-10 16:30:38 +01:00
Miroslav Lichvar
d5bc4e92e6 sys_timex: move inclusion of sys/timex.h to sysincl.h
It will be needed by privops.
2015-12-10 16:30:38 +01:00
Miroslav Lichvar
8e327bb0a3 privops: ignore signals in helper
If the whole process group receives a signal (e.g. CTRL-C in terminal),
the helper process needs to keep running until it gets the QUIT request,
so the system drivers can still use it in their finalisation, e.g. to
cancel remaining slew.
2015-12-10 16:30:31 +01:00
Miroslav Lichvar
fbf170a6c2 privops: compile only required helper functions 2015-12-10 15:31:55 +01:00
Miroslav Lichvar
cd472e6aaf privops: return from PRV functions with helper response code
In receive_reponse() don't interpret return codes in helper responses as
a non-zero value may not necessarily mean an error. Just copy errno if
it's not zero and let PRV_* functions deal with the return code.
2015-12-10 15:30:45 +01:00
Miroslav Lichvar
e9487b1a1a privops: make naming of fields and functions more consistent 2015-12-10 15:25:56 +01:00
Miroslav Lichvar
3cf6acdf24 util: add function for dropping root privileges
Share the code for dropping supplementary groups and setting effective,
saved, and real user UID/GID between system drivers.
2015-12-10 15:25:56 +01:00
Miroslav Lichvar
334ac06102 main: initialise privops sooner
System drivers may need it in their initialisation.
2015-12-10 15:25:56 +01:00
Bryan Christianson
2d9486ec7c sys_macosx: fix adjustment correction after step
The desired offset was being added to the current time instead of being
subtracted.
2015-12-10 15:25:24 +01:00
Miroslav Lichvar
fe502128b8 main: fix compiler warning 2015-12-08 18:02:05 +01:00
Miroslav Lichvar
46f0ad6b53 sys_netbsd: use privileged helper for socket binding
When dropping root privileges, start the helper to allow binding
of server sockets later.
2015-12-08 18:02:05 +01:00
Miroslav Lichvar
fedc605956 configure: rework setting of privops macros
Prepare a list of required privileged operations first and from that
define the PRIVOPS macros. This will reduce the amount of code that will
be needed when the privileged helper is used on other platforms.
2015-12-08 18:02:05 +01:00
Miroslav Lichvar
d44e26ba22 configure: fix check of date output 2015-12-08 18:02:05 +01:00
Miroslav Lichvar
610f234043 privops: refactor initialisation/finalisation
Rename PRV_Initialise() to PRV_StartHelper() and add a new
initialisation function, which just sets the helper fd to -1. Move
the initialision/finalisation calls from the system drivers to main.c.
If privops is not included in the build, define empty macros for the
function names, so their calls don't have to be wrapped in #ifdefs.
2015-12-08 18:02:05 +01:00
Miroslav Lichvar
aa9a4c697c privops: wait for helper pid
Save the pid of the helper process and replace wait() with waitpid().
2015-12-08 18:01:59 +01:00
Miroslav Lichvar
1b8ee3259e privops: stop helper on exit
With SOCK_DGRAM sockets, the helper doesn't stop as there is no error
received when the socket is closed on the daemon side.

Add a QUIT operation to the protocol which is requested when the daemon
is exiting. It has no response. Register the stopping function with
atexit() to stop the helper even when the daemon is not exiting cleanly,
e.g. due to a fatal error.
2015-12-08 17:50:09 +01:00
Miroslav Lichvar
c7ae4940c3 privops: split send_to_helper()
Split out the sending part of the function into send_request() and
rename it to submit_request(). This will be useful to send a request
without waiting for a response.

Also, remove the fd parameter from the functions and just use helper_fd
directly.
2015-12-08 17:50:07 +01:00
Miroslav Lichvar
aa4bf41400 privops: use SOCK_SEQPACKET sockets when supported
SOCK_SEQPACKET is preferred over SOCK_DGRAM for communication with the
helper as the process will get an error when the other end of the socket
is closed. It's not supported on all platforms.

If SOCK_SEQPACKET is defined, try creating the pair of sockets with this
type first and if that fails, fall back to SOCK_DGRAM.
2015-12-08 17:38:56 +01:00
Miroslav Lichvar
4e32de09a2 sys_linux: allow mremap in seccomp filter 2015-12-07 11:35:27 +01:00
Bryan Christianson
86e0399ea9 sys_macosx: synchronise RTC from system time
When the rtcsync directive is specified in the chronyd config file,
chronyd will update the RTC via settimeofday() every 60 minutes if
the system time is synchronised to NTP.
2015-12-03 12:20:54 +01:00
Miroslav Lichvar
302abf8480 client: print invalid intervals as dash
Instead of printing some large arbitrary values use dash in the LastRx
column of the sources output and the Last/Int columns in the clients
output when no sample or hit is recorded.
2015-12-03 11:43:06 +01:00
Miroslav Lichvar
f50b520557 sourcestats: use maximum value as invalid age in source report 2015-12-03 11:43:06 +01:00
Miroslav Lichvar
d4074c7993 clientlog: fix counting of command drops 2015-12-03 11:43:06 +01:00
Miroslav Lichvar
d3096c3b5e clientlog: save time of last hit with sub-second resolution
Instead of time_t use a 32-bit fixed point representation with 4-bit
fraction to save the time of the last hit. The rate can now be measured
up to 16 packets per second. Maximum interval between hits is about 4
years.
2015-12-03 11:43:06 +01:00
Miroslav Lichvar
98947a4e52 conf: inline one-line parse_* functions 2015-12-03 11:43:06 +01:00
Miroslav Lichvar
bafb434f06 main: assert supported integer size, representation and conversion
Abort immediately on start if chronyd is compiled on a platform with int
shorter than 32 bits, using other representation than two's complement,
or unexpected conversion of large unsigned integers to signed.
2015-12-03 11:43:06 +01:00
Miroslav Lichvar
8e71a46173 fix undefined shift operations on signed integers 2015-12-02 12:06:01 +01:00
Miroslav Lichvar
934df19c52 array: always return non-NULL pointer from ARR_GetElements()
Some libc calls like memcpy() expect the pointer to be valid even when
the size is zero and there is nothing to do. Instead of checking the
size before all such calls, modify ARR_GetElements() to return a pointer
to the array instance itself if data was not allocated yet.
2015-12-02 12:04:09 +01:00
Bryan Christianson
024842a38b contrib: update chronylogrotate.sh script
1. Remove obsolete options when running chronyc
2. Add copyright/licence notice
3. Use logger utility to print/store error messages
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
657929f8ec cmdmon: update CLIENT_ACCESSES_BY_INDEX command
Add new fields from clientlog to the report and print them in chronyc.
Rework the code to skip empty records in the hash table. The reply no
longer has variable length, all client fields are filled even if some
are empty. Reply with RPY_NULL when the facility is disabled.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
b506594c2d clientlog: limit response rate
When the measured NTP or command request rate of a client exceeds
a threshold, reply only to a small fraction of the requests to reduce
the network traffic. Clients are allowed to send a burst of requests.
Try to detect broken clients which increase the request rate when not
getting replies and suppress the rate limiting for them.

Add ratelimit and cmdratelimit directives to configure the thresholds,
bursts and leak rates independently for NTP and command response rate
limiting. Both are disabled by default. Commands from localhost are
never limited.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
830135edea clientlog: measure request rates
Extend the record with estimates of the current client's NTP and command
request rates. Store them as 8-bit scaled log2 values to save memory.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
464cdbbb6e clientlog: store records in hash table instead of tree
This simplifies the code and allows older records to be reused when no
more memory can be allocated for new addresses. Each slot of the hash
table has 16 records and there is no chaining between different slots.
Reused records may be newer than records in other slots, but the search
time remains constant.
2015-11-30 17:50:55 +01:00
Miroslav Lichvar
086e886d1e clientlog: reduce amount of logged information
Don't log NTP peer access and auth/bad command access. Also, change
types for logging number of hits from long to uint32_t. This reduces the
size of the node and allows more clients to be monitored in the same
amount of memory.
2015-11-30 17:50:44 +01:00
Miroslav Lichvar
f2b82c1e1d conf: don't allow disabling clientloglimit
Don't treat zero as a special value disabling clientloglimit. It's not
useful, the amount of available memory is never unlimited.
2015-11-30 17:34:53 +01:00
Miroslav Lichvar
801830df57 util: add macros for maximum, minimum and clamp
If MAX/MIN are defined in system headers, undefine them first.
2015-11-30 17:34:53 +01:00
Miroslav Lichvar
8b235297a5 util: add function for IP address hashing
Move the hashing function from find_slot() in ntp_sources to make it
available to clientlog and improve the hashing a bit.
2015-11-30 17:34:50 +01:00
Miroslav Lichvar
59a3140621 cmdmon: tidy up declarations in read_from_cmd_socket() 2015-11-26 10:10:24 +01:00
Bryan Christianson
16bd56ae7e sys_macosx: tidy up includes
Use "sysincl.h" in place of the common system include files
2015-11-24 10:15:47 +01:00
Bryan Christianson
750d82f1d1 sys_macosx: drop root privileges
Run chronyd as a non-privileged user, using the privops helper to
perform adjtime(), settimeofday() and bind() functions on its behalf.
2015-11-24 09:29:22 +01:00
Bryan Christianson
139fc667aa add support for privilege separation
Privileged helper that will perform adjtime(), settimeofday(), bind() on
behalf of chronyd when running as non-root user.
2015-11-20 18:01:22 +01:00
Miroslav Lichvar
f21e5f6cc5 sys_linux: allow ioctl(TCGETS) in seccomp filter
This seems to be needed to allow fopen() called on /dev/urandom to check
if it's a terminal.
2015-11-18 12:49:11 +01:00
Miroslav Lichvar
f660aa9d7a conf: don't allow invalid last refclock option 2015-11-18 12:49:05 +01:00
Miroslav Lichvar
d28d644b04 ntp: ignore poll in KoD RATE packets
The meaning of the poll value in KoD RATE packets is not currently
defined in the NTP specification (RFC 5905). In the reference NTP
implementation it signals the minimum acceptable polling interval to the
clients. In chrony the minimum poll is set to the KoD RATE poll if it's
larger, but not to a larger value than 10.

The problem is that ntpd as a server sets the KoD RATE poll to the
maximum of the client's poll and the configured rate limiting interval.
An attacker can send a burst of spoofed packets to the server to trigger
the client's request rate limit. When the client sends its next request
and the server responds with a KoD RATE packet, the client will set its
minimum poll to the current poll and it will no longer be able to switch
to a shorter poll when needed.

ntpd could be fixed to always set the KoD RATE poll to the rate limiting
interval. Unfortunately, ntpd as a client seems to depend on the current
behavior. It tries to follow the server poll and if the KoD RATE poll
was shorter than the current poll, the polling interval would be
reduced, defeating the purpose of KoD RATE. The server fix will probably
need to wait until clients are fixed and that could take a very long
time.

For now, ignore the poll value in KoD RATE packets. Just add an extra
delay based on the current poll to the next transmit timeout and stop an
ongoing burst.
2015-11-16 17:28:30 +01:00
Miroslav Lichvar
a634fd3a2d doc: update description of offline command
Reachability and online/offline mode has no effect on source selection
since version 2.0.
2015-11-16 14:52:05 +01:00
Miroslav Lichvar
045794df4c ntp: adjust initial delay for polling interval
First packet after setting a source to online was sent with constant
delay (0.2s). If the period in which the source was offline was shorter
than the current polling interval, the new packet was sent sooner than
it would be if the source wasn't switched to offline and back.

Don't reset the local tx timestamp when mode is changed. When starting
the initial transmit timeout, adjust the delay to make the interval
between the two packets at least as long as the current polling
interval.
2015-11-16 14:48:02 +01:00
Miroslav Lichvar
dfc96e4702 sched: update timeout randomization
Use UTI_GetRandomBytes() instead of random() to calculate the random
part of the timeout. This was the only remaining use of random() in the
code and the srandom() call can be removed.
2015-11-16 10:30:59 +01:00
Miroslav Lichvar
8225bf01f7 ntp: don't reveal local clock in client packets
In client packets set the leap, stratum, reference ID, reference time,
root delay and root dispersion to constant values to not reveal the
state of the synchronization. Use precision 32 to make the receive and
transmit timestamps completely random and not reveal the local time.
2015-11-16 10:30:52 +01:00
Miroslav Lichvar
116c697282 util: rework timestamp fuzzing
Use UTI_GetRandomBytes() instead of random() to generate random bits
below precision. Save the result in NTP_int64 in the network order and
allow precision in the full range from -32 to 32. With precision 32
the fuzzing now makes the timestamp completely random and can be used to
hide the time.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
6199a89170 util: add function to generate random bytes
Add a function to fill a buffer with random bytes which uses a better
PRNG than random(). Use arc4random() if it's available on the system.
Fall back to reading from /dev/urandom, which should be available on
all currently supported systems.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
cbd77c9752 ntp: don't keep client sockets open for longer than necessary
After sending a client packet, schedule a timeout to close the socket
at the time when all server replies would fail the delay test, so the
socket is not open for longer than necessary (e.g. when the server is
unreachable). With the default maxdelay of 3 seconds the timeout is 7
seconds.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
df9b5d8c22 ntp: check remote interval in client mode
For testA in the client mode require also that the time the server
needed to process the client request is not longer than 4 seconds.
With maximum peer delay this limits the interval in which the client can
accept a server reply.
2015-11-16 10:26:14 +01:00
Miroslav Lichvar
66d534417b sched: use shorter data type for timeout IDs 2015-11-16 10:26:14 +01:00
Miroslav Lichvar
8803ab27c6 sched: don't allow SCH_RemoveTimeout() with invalid non-zero ID 2015-11-16 10:26:14 +01:00
Miroslav Lichvar
38910424f2 sched: don't return currently used timeout ID
To avoid problems in the very unlikely case where a timeout is so long
and new IDs are allocated so frequently that they would have a chance
to overflow and catch up with it, make sure before returning new ID that
it's currently not in use.
2015-11-16 10:25:33 +01:00
Miroslav Lichvar
0076458e9d sched: always return non-zero timeout ID
Timeout ID of zero can be now safely used to indicate that the timer is
not running. Remove the extra timer_running variables that were
necessary to track that.
2015-11-10 14:52:52 +01:00
Miroslav Lichvar
bdb1650ed8 sys_linux: allow more syscalls in seccomp filter
These seem to be needed by getaddrinfo() in default NSS configuration
on recent Fedora.
2015-11-04 15:17:16 +01:00
Miroslav Lichvar
a030ed4f39 doc: update NEWS 2015-10-19 11:18:37 +02:00
Miroslav Lichvar
9fc15394de configure: disable scfilter by default
As an experimental feature it should be explicitly enabled.
2015-10-19 11:18:17 +02:00
Miroslav Lichvar
34ea8770d0 client: add debug message for recv() error 2015-10-15 11:59:13 +02:00
Miroslav Lichvar
a5897840a0 doc: add minimum recommended configuration to FAQ 2015-10-14 16:53:37 +02:00
Miroslav Lichvar
59087dd0ff doc: include chrony version in manual title 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
1924481077 doc: update comparison with ntpd 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
da1f7563e9 doc: remove obsolete section on contributing 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
7496a14d2d doc: improve maxdistance description 2015-10-14 15:03:45 +02:00
Miroslav Lichvar
6e6dead680 logging: don't ignore message severity with debug support
The severity was fixed for all messages to LOGS_DEBUG. This was broken
in commit 7b2430fc3c.
2015-10-12 13:41:41 +02:00
Miroslav Lichvar
55dbbab5eb configure: check for struct in_pktinfo with ipi_spec_dst
On NetBSD there is a struct in_pktinfo, but it doesn't have the
ipi_spec_dst field and it breaks compilation.
2015-10-12 13:41:35 +02:00
Miroslav Lichvar
d6b6461658 configure: improve description of struct in6_pktinfo check 2015-10-12 13:41:18 +02:00
Miroslav Lichvar
85f7a4054d configure: include IPV6_PKTINFO in struct in6_pktinfo check 2015-10-12 13:40:02 +02:00
Miroslav Lichvar
01965d147a doc: update NEWS 2015-10-09 13:39:44 +02:00
Miroslav Lichvar
6a84126c28 examples: use one-second check interval in chrony-wait.service 2015-10-09 13:39:37 +02:00
Miroslav Lichvar
32f8bec92d configure: make default hwclockfile configurable 2015-10-08 15:20:32 +02:00
Miroslav Lichvar
00a6394b48 rtc: improve logging
Improve, shorten, or convert to debug log messages.
2015-10-08 15:20:28 +02:00
Miroslav Lichvar
ca5a791d09 client: make waitsync check interval configurable 2015-10-07 15:52:37 +02:00
Miroslav Lichvar
6a9c756cf0 rtc: restore time from driftfile if later than RTC time
This is useful on computers that have an RTC, but there is no battery to
keep the time when they are turned off and start with the same time on
each boot.
2015-10-06 15:52:36 +02:00
Miroslav Lichvar
1714d3e8ae rtc: don't run time_init function if pre_init failed 2015-10-06 13:23:14 +02:00
Miroslav Lichvar
25b7d47b34 doc: reduce makestep threshold in examples to 1 second 2015-10-05 10:15:02 +02:00
Miroslav Lichvar
9e8b4bae11 sys_linux: abort when loading seccomp rules fails 2015-10-05 09:56:58 +02:00
Miroslav Lichvar
a466395a19 doc: update NEWS 2015-10-02 11:50:08 +02:00
Miroslav Lichvar
a3cb3fc490 doc: update README 2015-10-01 18:09:44 +02:00
Miroslav Lichvar
3396778061 update copyright years 2015-10-01 18:07:10 +02:00
Miroslav Lichvar
01cef64070 client: remove unreachable code 2015-10-01 09:28:55 +02:00
Miroslav Lichvar
a9bfaf9e54 client: don't try sending request with invalid socket 2015-09-30 14:58:17 +02:00
Miroslav Lichvar
cec7c44f61 client: don't shorten default timeout with ASYNCDNS
With connected sockets recv() should fail immediately if chronyd is not
listening on localhost and with the Unix socket connecting should fail.
2015-09-30 14:35:05 +02:00
Miroslav Lichvar
38ac2b39ce stubs: add NSR_RefreshAddresses() 2015-09-30 13:33:27 +02:00
Miroslav Lichvar
967e358dbc stubs: don't call DNS_Name2IPAddress handler directly
Instead of calling the handler directly schedule a timeout with zero
delay for resolving to make the function behave similarly to the real
asynchronous resolver. This should prevent problems with code that
inadvertently depends on this behavior and which would break only when
compiled without support for asynchronous resolving.
2015-09-29 18:06:33 +02:00
Miroslav Lichvar
60721d2cc1 client: improve signal handling
After receiving a signal, don't process new command from readline() and
break from waitsync command.
2015-09-29 18:05:50 +02:00
Miroslav Lichvar
b698184939 doc: document refresh command 2015-09-29 18:05:45 +02:00
Miroslav Lichvar
c6c833fb9c client: update help text 2015-09-29 16:42:21 +02:00
Gautier PHILIPPON
3eb43f4619 cmdmon: add refresh command
This command can be used to resolve the names of configured sources to
IP addresses again.
2015-09-29 16:42:18 +02:00
Miroslav Lichvar
440c159217 client: fix compiler warning on extra printf argument 2015-09-29 16:28:28 +02:00
Miroslav Lichvar
b49dcfbef7 doc: update for recent changes 2015-09-25 19:08:01 +02:00
Miroslav Lichvar
a4d9cfaaeb client: update help text
Update the text for recent changes, add missing commands and indent the
description in the output.
2015-09-25 19:07:58 +02:00
Miroslav Lichvar
7b2430fc3c logging: don't save debugging arguments when debug is disabled
Don't save the facility number, line number, function name and filename
in the compiled binary unless the debugging support is enabled.
2015-09-24 18:32:23 +02:00
Miroslav Lichvar
bd8be7133d sys: use NetBSD driver on FreeBSD
The NetBSD driver now provides fast slewing using adjtime(), which
can be used on FreeBSD too.
2015-09-23 11:19:34 +02:00
Miroslav Lichvar
692ef0549b sys_netbsd: add fast slewing based on adjtime()
Implement slewing based on adjtime() that the generic driver can use to
correct offsets larger than 1 second with 5000 ppm slewing rate.
2015-09-23 11:19:34 +02:00
Miroslav Lichvar
d6fdae5f1d sys_generic: allow fast slewing with system driver
The system drivers may implement their own slewing which the generic
driver can use to slew faster than the maximum frequency the driver is
allowed to set directly.
2015-09-23 11:19:09 +02:00
Miroslav Lichvar
8feb37df2b sys_solaris: use timex driver
Remove driver functions based on adjtime() and switch to the new timex
driver. The kernel allows the timex frequency to be set in the full
range of int32_t, which gives a maximum frequency of 32768 ppm. Round
the limit to 32500 ppm.
2015-09-18 16:42:40 +02:00
Miroslav Lichvar
1d2b481069 sys_timex: set timex constant on Solaris
The kernel apparently checks the constant even when it's not being set
with MOD_TIMECONST and may return EINVAL on an uninitialized value.
2015-09-18 16:42:40 +02:00
Miroslav Lichvar
c062fa2fa9 client: fix binding of Unix socket on Solaris
bind() needs to be called before connect(), otherwise it fails with
EINVAL.
2015-09-18 16:42:40 +02:00
Miroslav Lichvar
f444561a10 fix building on Solaris
- a feature test macro is needed to get msg_control in struct msghdr
- variables must not be named sun to avoid conflict with a macro
- res_init() needs -lresolv
- configure tests for IPv6 and getaddrinfo need -lsocket -lnsl
- pid_t is defined as long and needs to be cast for %d format
2015-09-18 16:42:28 +02:00
Miroslav Lichvar
046f219a0e clean up sysincl.h more
Define feature test macros in config.h if needed.
2015-09-18 10:07:56 +02:00
Miroslav Lichvar
3cd32ed660 configure: check if C compiler works
Check if the C compiler works to get a useful error message when it
doesn't or it's missing. If the CC environment variable is not set, try
gcc and then cc.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
4f172f6f9f configure: prefix error messages 2015-09-17 15:57:48 +02:00
Miroslav Lichvar
22fc0a6846 configure: don't set any arch-specific CFLAGS 2015-09-17 15:57:48 +02:00
Miroslav Lichvar
71e596b443 configure: ignore architecture in system selection
Assume chrony can be compiled and work on all architectures supported by
the operating systems.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
98c245ed7b sys: drop SunOS driver
On FreeBSD is used the new timex driver and SunOS 4 is not supported
anymore.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
bf57222e96 sys: use timex driver on FreeBSD
Switch from the SunOS adjtime() based driver to the timex driver.
There is no FreeBSD-specific code, so call SYS_Timex_Initialise()
and SYS_Timex_Finalise() directly from sys.c.
2015-09-17 15:57:48 +02:00
Miroslav Lichvar
c075c070f0 clean up sysincl.h 2015-09-17 15:57:44 +02:00
Miroslav Lichvar
4bc6950632 drop WINNT-specific code
This was never really supported and it would probably require a lot of
work to get a usable chronyd in Cygwin. Remove all WINNT-specific code.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
bde279c093 sys: don't allow empty SYS_Initialise()/SYS_Finalise()
Require one system-specific macro to be defined to always call an
initialization/finalization function.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
4f6ab8ac93 sys: move DRIFT_REMOVAL_INTERVAL definition
In the SunOS and Solaris drivers DRIFT_REMOVAL_INTERVAL needs to be
defined before it's used. This was broken in commit
b6a27df5b9.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
d2d82e2e5f sys_netbsd: use timex driver
Remove the driver functions based on adjtime() and switch to the new
timex driver, which is based on ntp_adjtime(). This allows chronyd to
control the kernel frequency, adjust the offset with sub-microsecond
accuracy, and set the kernel leap and sync status. A drawback is that
the maximum slew rate is now limited by the 500 ppm maximum frequency
offset, while adjtime() on NetBSD slewed by up to 5000 ppm.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
1b2510e4b2 sys_linux: use timex driver
Remove functions that are included in the new timex driver. Keep only
functions that have extended functionality, i.e. read and set the
frequency using the timex tick field and apply step offset with
ADJ_SETOFFSET.

Merge the code from wrap_adjtimex.c that is still needed with
sys_linux.c and remove the file.
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
e735be59a7 sys: add generic timex driver
This is based on sys_linux.c and wrap_adjtimex.c. It's intended for all
systems that support the adjtimex() or ntp_adjtime() system call. The
driver functions can be replaced with extended system-specific versions
(e.g. to control the frequency with the tick field on Linux).
2015-09-17 15:52:49 +02:00
Miroslav Lichvar
5190539ce1 test: add tests for system adjtime() and ntp_adjtime()
Include a test program to determine how the adjtime() implementation
behaves. Check the range of supported offset, support for readonly
operation, and slew rate with different update intervals and offsets.

Also, add a test for ntp_adjtime() to check what frequency range it
supports.
2015-09-17 10:56:51 +02:00
Miroslav Lichvar
5776eb35b6 git: use absolute paths in .gitignore 2015-09-14 16:54:04 +02:00
Miroslav Lichvar
f102acd423 sys_linux: allow uname in seccomp filter
It may be called from res_init() apparently.
2015-09-14 16:53:25 +02:00
Miroslav Lichvar
06486f3162 util: print expected uid/gid in UTI_CheckDirPermissions() 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
1619453b2b sys_linux: allow setting IP_FREEBIND option in seccomp filter
This is needed when chronyd is started with no allow directive, but the
NTP server socket is opened by the allow command later.
2015-09-09 17:19:07 +02:00
Miroslav Lichvar
5a40950ffd test: extend compilation/001-features 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
16eb18e797 stubs: add CAM_OpenUnixSocket()
It is needed to build with disabled cmdmon.
2015-09-09 17:19:07 +02:00
Miroslav Lichvar
7bf0684557 configure: add --disable-scfilter option 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
961c490436 configure: update chronyc feature list 2015-09-09 17:19:07 +02:00
Miroslav Lichvar
d8b0a4a288 doc: update section on isolated networks
Since the NTPv4 update, the detection of synchronization loops based on
the refid prevents a server to initialize its clock from its clients
after restart. Remove that part from the recommended configuration.
Also, mention the time smoothing feature.
2015-09-09 16:46:09 +02:00
Miroslav Lichvar
76d12ac136 doc: update for recent changes 2015-09-09 13:33:34 +02:00
Miroslav Lichvar
434faeecb8 sys_linux: add support for seccomp filters
The Linux secure computing (seccomp) facility allows a process to
install a filter in the kernel that will allow only specific system
calls to be made. The process is killed when trying to make other system
calls. This is useful to reduce the kernel attack surface and possibly
prevent kernel exploits when the process is compromised.

Use the libseccomp library to add rules and load the filter into the
kernel. Keep a list of system calls that are always allowed after
chronyd is initialized. Restrict arguments that may be passed to the
socket(), setsockopt(), fcntl(), and ioctl() system calls. Arguments
to socketcall(), which is used on some architectures as a multiplexer
instead of separate socket system calls, are not restricted for now.
The mailonchange directive is not allowed as it calls sendmail.

Calls made by the libraries that chronyd is using have to be covered
too. It's difficult to determine which system calls they need as it may
change after an upgrade and it may depend on their configuration (e.g.
resolver in libc). There are also differences between architectures. It
can all break very easily and is therefore disabled by default. It can
be enabled with the new -F option.

This is based on a patch from Andrew Griffiths <agriffit@redhat.com>.
2015-09-04 17:56:51 +02:00
Miroslav Lichvar
ea2858b323 main: install signal handler sooner 2015-09-04 17:03:00 +02:00
Miroslav Lichvar
3391d5f846 doc: fix typo in chronyd man page 2015-09-04 17:03:00 +02:00
Miroslav Lichvar
7d6de7afe6 rtc: fix setting time from driftfile when RTC reading fails
Fix RTC_Linux_TimePreInit() to return 0 when the RTC device can be
opened, but reading its time fails to at least have the time restored
from the driftfile.
2015-09-03 11:42:58 +02:00
Bryan Christianson
67ce6bd279 sys_macosx: reset drift removal timer after spike in offset_sd
When a large spike occurs in offset_sd the drift removal interval can be
set to an excessively long time, although what ever event caused the
perturbation has passed. At the next set_sync_status() we now compare
the expected drift removal interval with that currently in effect. If
they are significantly different, the current timer is cancelled and new
cycle started using the new drift removal interval.
2015-08-31 16:29:24 +02:00
Miroslav Lichvar
770db1fe02 sys_linux: always call TMX_SetLeap() in set_leap()
The optimization avoiding unnecessary setting of the kernel leap status
can cause a problem when something outside chronyd sets the status to
the new expected value. There will be no TMX_SetLeap() call which would
update the saved status and the kernel status will be overwritten with
the old (incorrect) value in a later TMX_*() call.

Always call TMX_SetLeap() to save the new value and for the log message
selection just check if a leap second has been applied.
2015-08-27 13:26:12 +02:00
Miroslav Lichvar
d73394dde1 reference: call LCL_SetSystemLeap() only on leap changes 2015-08-26 14:42:14 +02:00
Miroslav Lichvar
eb0c7e33d2 examples: update for removed cmdmon authentication 2015-08-26 10:19:33 +02:00
Bryan Christianson
b9cfdaf666 sys_macosx: add option to run chronyd as real-time process
Adds option -P to chronyd on MacOS X which can be used to enable the
thread time constraint scheduling policy. This near real-time scheduling
policy removes a 1usec bias from the 'System time' offset.
2015-08-25 17:43:57 +02:00
Miroslav Lichvar
5039f959e0 sources: add option to limit selection by root distance
Add maxdistance directive to set the maximum root distance the sources
are allowed to have to be selected. This is useful to reject NTPv4
sources that are no longer synchronized and report large dispersion.
The default value is 3 seconds.
2015-08-25 17:35:34 +02:00
Miroslav Lichvar
b7a54f8cd8 configure: add new options to disable dropping root privileges 2015-08-25 17:09:55 +02:00
Miroslav Lichvar
7b6435b2b8 sys_netbsd: allow running without root privileges
On NetBSD programs with write access to /dev/clockctl can adjust or set
the system clock without the root privileges. Add a function to drop the
privileges and check if the process has write access to the device to
get a more descriptive error message when the chrony uid/gid doesn't
match the owner of the device.
2015-08-25 17:09:55 +02:00
Miroslav Lichvar
8854c00d48 main: open cmdmon and NTP internet sockets before dropping root
Call the CAM, NIO, NCR initialization functions and setup the access
restrictions before root is dropped. This will be needed on NetBSD,
where it's not possible to bind sockets to privileged ports without the
root privileges. Split the creation of the Unix domain command socket
from the CAM initialization to keep the chrony user as the owner of the
socket.
2015-08-25 17:09:18 +02:00
Miroslav Lichvar
c0867b58f5 conf: allow wildcard patterns in include directive
Use glob() to match and read multiple configuration files with one
include directive.
2015-08-24 15:25:02 +02:00
Miroslav Lichvar
05183748a8 conf: extend logging in CNF_ReadFile() 2015-08-24 14:57:39 +02:00
Miroslav Lichvar
e56154a687 sys_linux: remove unused variables 2015-08-24 13:25:39 +02:00
Miroslav Lichvar
e5784c1ca8 cmdmon: update candm.h
Remove the auth fields in the command request/reply and replace the
token and utoken fields with padding.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
282a9c7d7c keys: remove support for command key
Without the cmdmon authentication, there is no need for command keys.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
b11ca92ca6 client: remove authentication support
Follow the removal of the server authentication support and remove also
the client support. The -a and -f options are now silently ignored to
not break scripts. The authhash and password commands print a warning,
but they don't return an error.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
49846b3e68 cmdmon: remove authentication support
With the new support for cmdmon over Unix domain sockets, authentication
is no longer necessary to authorize a client running on localhost with
the permissions of the root or chrony user/group. Remove the cmdmon
authentication support to simplify the code and significantly reduce the
attack surface of the protocol.

Only monitoring commands are now allowed remotely. Users that need to
configure chronyd remotely or locally without root/chrony permissions
are advised to use ssh and/or sudo.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
0887824324 cmdmon: allow unauthenticated commands from Unix domain socket
Allow all commands received from the Unix domain command socket (which
is accessible only by the root and chrony user/group), even when they
are not authenticated with the command key.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
fbe65f2c71 client: connect to Unix domain socket by default
The default value of the -h option is now
/var/run/chrony/chronyd.sock,127.0.0.1,::1.
2015-08-21 13:26:46 +02:00
Miroslav Lichvar
eb5a412bed configure: add option to set default location of Unix domain sockets 2015-08-21 13:26:46 +02:00
Miroslav Lichvar
0cc8f68754 client: reconnect with multiple addresses
Allow multiple hostnames/addresses separated by comma to be specified
with the -h option. Hostnames are resolved to up to 16 addresses. When
connecting to an address fails or no reply is received, try the next
address in the list.

Set the default value for the -h option to 127.0.0.1,::1.
2015-08-21 13:26:45 +02:00
Miroslav Lichvar
7079ca2718 client: allow connecting to Unix domain sockets
If the specified hostname starts with /, consider it to be the path of
the chronyd Unix domain command socket. Create the client socket in the
same directory as the server socket (which is not accessible by others)
and change its permission to 0666 to allow chronyd running without root
privileges to send a reply. Remove the socket on exit.
2015-08-21 13:26:45 +02:00
Miroslav Lichvar
70ad0bc573 client: connect socket
Call connect() on the socket to set the remote address and switch from
sendto()/recvfrom() to send()/recv(). Setting the IP_RECVERR option no
longer seems to be necessary in order to get ECONNREFUSED errors.
2015-08-21 13:26:45 +02:00
Miroslav Lichvar
22345c5ddf client: add -d option to print debug messages 2015-08-21 13:26:45 +02:00
Miroslav Lichvar
28b0a23949 client: convert disabled printf() calls to debug messages 2015-08-21 13:26:45 +02:00
Miroslav Lichvar
1b57a796b1 client: use LOG macro for error messages 2015-08-21 13:26:45 +02:00
Miroslav Lichvar
0abb470022 cmdmon: print path of Unix command socket in debug messages 2015-08-20 14:35:40 +02:00
Miroslav Lichvar
b7a4b84f0a cmdmon: fix handling of packets from unbound Unix sockets
When a packet is received from an unbound Unix domain socket, recvfrom()
may return with zero addrlen.
2015-08-20 14:35:40 +02:00
Bryan Christianson
794a1e6cfe contrib: add Mac OS X support files
launchd plist files for chronyd and logrotation.
shell script for logrotation
README file with detailed installation instructions
2015-08-20 14:35:26 +02:00
Bryan Christianson
7c4db99d44 sys_macosx: make drift removal interval dynamic
Adjust the drift removal interval based on the observed offset_sd.
A newly calculated interval goes into effect after the current drift
removal has completed. When offset_sd is high, the interval is increased
resulting in fewer wakeups to adjust the drift offset. At lower values
of offset_sd the drift removal adjustment interval is pinned to 0.5
seconds. The predicted error applied at the start of an adjustment is
based on the remaining time of the drift removal that is currently in
effect. Default drift removal adjustment interval is 4.0 seconds (was
1.0). If not synchronised set interval to  maximum of default interval
and current interval. Clamp elapsed drift removal time to
[0, current_drift_removal_interval] to cover clock stepping.
2015-08-18 10:43:20 +02:00
Miroslav Lichvar
30b6213910 util: set uid/gid of created directory even when zero
Call chown() in create_dir() even when the specified uid/gid is zero.
This is needed on BSD systems, where directories are created with gid
of the parent directory.
2015-08-13 17:15:50 +02:00
Miroslav Lichvar
b6a27df5b9 sys: include predicted drift in adjtime() offset
In drivers with periodic drift removal include in the adjustment also a
prediction of the error gained in half of the interval to move the mean
offset of the clock closer to zero. E.g. offset of a stable clock
drifting by 10 ppm should now be closer to 0 +/- 5 microseconds instead
of 5 +/- 5 microseconds.
2015-08-12 16:09:24 +02:00
Miroslav Lichvar
18d514d552 sys: define NETBSD macro on NetBSD 2015-08-12 14:45:23 +02:00
Miroslav Lichvar
f1ed08abf0 conf: create directory for Unix domain command socket
Try to create the directory where will be the Unix domain command socket
bound to allow starting with empty /var/run. Check the permissions and
owner/group in case the directory already existed. It MUST NOT be
accessible by others as permissions on Unix domain sockets are ignored
on some systems (e.g. Solaris).
2015-08-12 14:45:23 +02:00
Miroslav Lichvar
6d42dd8603 conf: create directories before dropping root
Create logdir and dumpdir before dropping root. Set their uid/gid to the
user chronyd will switch to. This allows chronyd to create the
directories in a directory where the user won't have write permissions
(e.g. /var/lib).
2015-08-12 14:45:20 +02:00
Miroslav Lichvar
e7100e106d main: always call getpwnam()
Don't hardcode root as the user with zero uid/gid.
2015-08-12 14:34:28 +02:00
Miroslav Lichvar
6402350c83 sys: move getpwnam() call to main.c
Pass uid/gid instead of user name to the root dropping function.
2015-08-10 16:06:39 +02:00
Miroslav Lichvar
236576c124 util: add mode, uid, gid parameters to UTI_CreateDirAndParents() 2015-08-10 14:56:17 +02:00
Miroslav Lichvar
9a83cab2f8 util: don't try to create current directory
This prevents error messages when running chronyd -d/-q/-Q with default
logdir in a directory chronyd is not allowed do access after dropping
the root privileges.
2015-08-05 18:07:39 +02:00
Miroslav Lichvar
92706b158e move mkdirpp code to util.c 2015-08-05 18:07:39 +02:00
Miroslav Lichvar
ad34b26955 client: check if memory allocation fails 2015-08-05 18:07:39 +02:00
Miroslav Lichvar
12c434fdc0 client: add logging function to allow linking with memory.o 2015-08-05 18:07:39 +02:00
Miroslav Lichvar
9ceaef6479 doc: update FAQ 2015-08-05 11:26:26 +02:00
Bryan Christianson
abb56bded2 sys: add drift removal to Mac OS X driver
The darwin kernel implementation of adjtime() does not require the
adjustment to be aligned to a tickadj boundary, and we can apply
adjustments to the nearest microsecond. Rounding is accounted for by
adding any rounding errors back into the offset.
2015-08-03 17:28:19 +02:00
Miroslav Lichvar
0bcd10560a cmdmon: listen on Unix domain socket
In addition to the IPv4/IPv6 command sockets, create also a Unix domain
socket to process cmdmon requests. For now, there is no difference for
authorized commands, packets from all sockets need to be authenticated.

The default path of the socket is /var/run/chrony/chronyd.sock. It can
be configured with the bindcmdaddress directive with an address starting
with /.
2015-07-28 15:29:30 +02:00
Miroslav Lichvar
46b7148f3b clientlog: refactor CLG_Log*Access functions a bit 2015-07-28 14:46:03 +02:00
Miroslav Lichvar
37732130e1 clientlog: allow unspecified address in CLG_Log*Access functions 2015-07-28 14:32:54 +02:00
Miroslav Lichvar
7a3b1414cd util: add function to get sockaddr family name 2015-07-28 13:11:00 +02:00
Miroslav Lichvar
a4a21c1dca client: handle signals
Add a signal handler and rework the code to go through close_io() even
when terminated by a signal. This will allow chronyc to remove Unix
domain sockets on exit.
2015-07-28 11:57:57 +02:00
Miroslav Lichvar
206e597b04 util: use sigaction() to set signal handler 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
ceef8ad2d8 main: move signal handler setting to util.c 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
2d581a6a86 cmdmon: add debug messages for receiving/sending packets 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
82f7fa3887 util: remove INLINE_UTILITIES support 2015-07-28 11:57:57 +02:00
Miroslav Lichvar
f88a01e8c7 remove getdate.c from repository
Building from repository now requires installed bison, but released
tarballs will still include a generated getdate.c.
2015-07-28 11:57:57 +02:00
Miroslav Lichvar
ca8e03b785 include config.h in all compiled files
After running configure script (new config.h written), all objects
should be recompiled.
2015-07-28 11:57:57 +02:00
Miroslav Lichvar
15932c9d7b sys: add new log message for kernel status reset after leap second
When a leap second is applied by the kernel, it doesn't actually clear
the STA_INS|STA_DEL bits from the status word, but the state returned
by ntp_adjtime()/adjtimex() is TIME_WAIT until the application clears
the bits.

Add "System clock status reset after leap second" log message for this
case.
2015-07-27 12:35:21 +02:00
Bryan Christianson
0fc0f906e1 util: fix rounding of negative numbers in UTI_DoubleToTimeval() 2015-07-23 14:53:00 +02:00
Miroslav Lichvar
7f58852ec0 util: fix UTI_Log2ToDouble() for maximum/minimum exponent 2015-07-23 12:04:27 +02:00
Miroslav Lichvar
85a9a53e69 configure: replace echo -n with printf
POSIX doesn't require echo to support -n.
2015-07-23 11:27:16 +02:00
Miroslav Lichvar
aa0c0fc401 make_release: don't package chrony.txt 2015-07-22 18:14:40 +02:00
Miroslav Lichvar
0e694e08fc makefile: install chrony.txt in install-doc only
Don't install chrony.txt in make install to avoid dependency on makeinfo
since chrony.texi is prepared by configure to set the default paths in
the documentation.
2015-07-22 17:42:42 +02:00
Miroslav Lichvar
c2ddcc9f36 makefile: don't install COPYING and README 2015-07-22 17:38:00 +02:00
Miroslav Lichvar
7a7cf6a5ce doc: update NEWS 2015-06-23 16:02:17 +02:00
Miroslav Lichvar
c2f83bd8a4 sys: fix clock stepping by integer number of seconds on Linux
The kernel requires in the ADJ_SETOFFSET | ADJ_NANO mode that the
timex.time.tv_usec value is smaller than 10^9 nanosecond, which wasn't
the case with a negative integer offset (e.g. inserted leap second).
2015-06-23 15:08:42 +02:00
Miroslav Lichvar
1f0e6296c6 doc: update NEWS 2015-06-22 12:54:52 +02:00
Miroslav Lichvar
ab1f01bacd ntp: use specific reference ID when smoothing served time
Set refid in server/broadcast packets to 127.127.1.255 when a time
smoothing offset is applied to the timestamps. This allows the clients
and administrators to detect that the server is not serving its best
estimate of the true time.
2015-06-22 12:54:50 +02:00
Miroslav Lichvar
b9b896d8e7 ntp: remove unnecessary casting 2015-06-22 12:54:33 +02:00
Miroslav Lichvar
6be54f366c reference: move definition of special refids to ntp.h 2015-06-22 12:54:22 +02:00
Miroslav Lichvar
802cdb3230 test: require latest clknetsim 2015-06-19 14:20:58 +02:00
Miroslav Lichvar
7e27880cb6 doc: update leapsecmode and smoothtime descriptions 2015-06-19 14:20:58 +02:00
Miroslav Lichvar
d3ad85aa43 doc: add Mac OS X to supported platforms 2015-06-18 16:35:26 +02:00
Miroslav Lichvar
59192fc695 update copyright years 2015-06-18 15:30:22 +02:00
Miroslav Lichvar
9095b80c5b doc: refer to authhash command in password command description 2015-06-17 18:31:16 +02:00
Miroslav Lichvar
ed5b78bf09 doc: convert FAQ to AsciiDoc and update it
It's now in a separate file again.
2015-06-17 18:05:14 +02:00
Bryan Christianson
d6aafa3f64 sys: MacOS X driver ported from NetBSD 2015-06-15 14:40:54 +02:00
Miroslav Lichvar
8de04a808d ntp: add debug message to print number of resolved addresses 2015-06-15 13:03:11 +02:00
Miroslav Lichvar
2a299233b3 update NEWS 2015-06-10 14:44:54 +02:00
Miroslav Lichvar
64f83c8861 cmdmon: reply with STT_INVALID on invalid option in handle_manual() 2015-06-09 17:05:45 +02:00
Miroslav Lichvar
1009fe3d9c makefile: warn when Makefile needs to be regenerated 2015-06-09 16:43:56 +02:00
Miroslav Lichvar
ba341fe81a sources: remove unused code in SRC_SelectSource() 2015-06-09 16:15:30 +02:00
Miroslav Lichvar
36e8cb6530 client: add smoothtime command 2015-06-09 16:15:30 +02:00
Miroslav Lichvar
273da62aec cmdmon: add smoothtime command
This adds a command to reset or activate the time smoothing process.
2015-06-09 16:15:30 +02:00
Miroslav Lichvar
41788184a7 client: add smoothing command 2015-06-09 16:15:30 +02:00
Miroslav Lichvar
fb9c2c7dc8 cmdmon: add smoothing command
This adds a new request to get a current report on time smoothing.
2015-06-09 16:15:30 +02:00
Miroslav Lichvar
43116be122 smooth: fix resetting 2015-06-08 17:22:01 +02:00
Miroslav Lichvar
ee038d5de5 cmdmon: use SCH_GetLastEventTime() to get current time
It's cheaper and accurate enough.
2015-06-08 15:07:18 +02:00
Miroslav Lichvar
ea7fae5277 sched: detect timeout overflow in SCH_AddTimeoutByDelay()
Abort when the system time gets so close to the end of 32-bit time_t
that timeouts added by delay start to overflow. This is an addition to
the loop detector in dispatch_timeouts().
2015-06-08 14:49:52 +02:00
Miroslav Lichvar
70b108ab69 array: allow arrays larger than 4 GB
It's not expected we will work with such large arrays anytime soon, but
better be safe than sorry.

Also, limit the number of elements to 2^31-1 to prevent infinite loop in
the calculation of allocated elements.
2015-06-08 14:43:16 +02:00
Miroslav Lichvar
08b152d6a2 test: add 202-prefer 2015-06-08 12:33:46 +02:00
Miroslav Lichvar
83c6213c67 test: add option to override generated server directives on client 2015-06-08 12:33:34 +02:00
Miroslav Lichvar
4253075a97 sources: fix marking of non-preferred selectable sources
When reducing the list of selectable sources to sources with the prefer
option, sources before the first preferred source were left with the
SRC_OK status, which triggered an assertion failure in the next
selection.
2015-06-08 11:54:43 +02:00
Miroslav Lichvar
0abdc2a350 smooth: add option to smooth out only leap seconds
The leaponly option can be used to enable a mode where only leap seconds
are smoothed out and normal offset/frequency changes are ignored. This
is useful to make the interval in which a leap second is smoothed out
constant and allow an NTP client to use multiple leap smearing servers
safely.
2015-06-02 15:24:01 +02:00
Miroslav Lichvar
31669f343a cmdmon: replace definitions of empty requests with null request 2015-06-01 15:00:14 +02:00
Miroslav Lichvar
438b881ab4 cmdmon: set only non-success status in command handling functions 2015-05-28 15:21:25 +02:00
Miroslav Lichvar
27863146a3 cmdmon: refactor allow/deny functions 2015-05-28 14:32:45 +02:00
Miroslav Lichvar
cd4b73612b ntp: include message precision in peer dispersion 2015-05-28 13:49:43 +02:00
Miroslav Lichvar
3c217a9e37 util: add UTI_Log2ToDouble() 2015-05-28 12:51:54 +02:00
Miroslav Lichvar
cde3a003ea util: handle NaN in UTI_FloatHostToNetwork() 2015-05-18 15:51:32 +02:00
Miroslav Lichvar
2c35f56612 client: handle empty hostname before slash in allow/deny commands 2015-05-18 15:36:52 +02:00
Miroslav Lichvar
4295db25d7 doc: remove chrony(1) man page
It's a copy of README, chrony(1) is not a program itself.
2015-05-18 13:15:43 +02:00
Miroslav Lichvar
3c06e57f24 ntp: increase minimum replacement interval to 30 minutes 2015-05-18 13:15:43 +02:00
Miroslav Lichvar
e949cf5967 ntp: replace non-pool sources when unreachable or falsetickers
Sources that are not specified as a pool and have a name (i.e. not
specified by an IP address or added from chronyc) will be replaced with
a newly resolved address of the name when they become unreachable or
falseticker too.
2015-05-18 13:15:42 +02:00
Miroslav Lichvar
4eeaf34295 ntp: add sources specified by IP directly without name resolving 2015-05-18 13:12:00 +02:00
Miroslav Lichvar
2212a90698 ntp: improve alignment of columns in banner for measurements log 2015-05-04 10:23:17 +02:00
Miroslav Lichvar
dc52b61dad doc: update NEWS 2015-04-27 12:58:19 +02:00
Miroslav Lichvar
bbf4c3186b doc: update chrony description 2015-04-27 12:58:19 +02:00
Miroslav Lichvar
f72016a78e doc: document when smoothtime function is activated 2015-04-27 12:27:55 +02:00
Miroslav Lichvar
29b587a9c5 sys: fix TMX_ResetOffset() to set status back correctly 2015-04-27 12:27:55 +02:00
Miroslav Lichvar
cec4f2b140 reference: use 2012 leap second in leapsectz test 2015-04-24 12:16:47 +02:00
Miroslav Lichvar
05278c3b4c sources: ignore reselect distance when combining with refclock 2015-04-20 12:59:12 +02:00
Miroslav Lichvar
1769b8ea0f use return to exit from main function 2015-04-17 17:34:02 +02:00
Miroslav Lichvar
5686bd87d7 client: improve usage line 2015-04-17 17:33:38 +02:00
Miroslav Lichvar
1cda2db45d main: print usage with -h option 2015-04-17 17:30:38 +02:00
Miroslav Lichvar
fdf9640349 ntp: don't log error when socket() fails for client only socket 2015-04-14 15:59:55 +02:00
Miroslav Lichvar
8f2d5d99f1 doc: don't mention ancient systems
Also, don't try to track working versions of supported systems, assume
current versions are ok.
2015-04-13 17:18:19 +02:00
Miroslav Lichvar
61272e7ce8 update copyright years 2015-04-10 11:06:32 +02:00
Miroslav Lichvar
88b76f49cc doc: warn that unauthenticated peers are vulnerable to DoS attack 2015-04-10 10:52:30 +02:00
Miroslav Lichvar
ad942e352d sys: clamp frequency set in generic driver on exit 2015-04-10 10:22:28 +02:00
Miroslav Lichvar
39c2bcd462 util: don't allow time too close to 32-bit time_t overflow
In UTI_IsTimeOffsetSane() consider time in one year interval before
32-bit time_t overflow (in 2038) as invalid. Hopefully everything will
be using 64-bit time_t when that time comes.
2015-04-10 10:05:15 +02:00
Miroslav Lichvar
ae10664b24 doc: fix CVE-ID in NEWS
CVE-2015-1853 is for chrony, CVE-2015-1799 is for ntp.
2015-04-08 08:44:42 +02:00
149 changed files with 11889 additions and 14401 deletions

32
.gitignore vendored
View File

@@ -2,20 +2,24 @@
.vimrc
*.o
*.swp
RELEASES
Makefile
chrony.conf.5
chrony.info
chrony.html
chrony.texi
chrony.txt
chronyc
chronyc.1
chronyd
chronyd.8
config.h
config.log
*.dSYM
*.DS_Store
tags
version.h
/RELEASES
/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/tmp
/test/unit/Makefile
/test/unit/*.test
/test/unit/*.o

View File

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

129
NEWS
View File

@@ -1,3 +1,127 @@
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
==================
@@ -15,7 +139,7 @@ Enhancements
* Improve source selection
* Handle offline sources as unreachable
* Open NTP server port only when necessary (client access is allowed by
allow directive/command, peer or broadcast is configured)
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
@@ -30,6 +154,7 @@ 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
@@ -39,7 +164,7 @@ New in version 1.31.1
Security fixes
--------------
* Protect authenticated symmetric NTP associations against DoS attacks
(CVE-2015-1799)
(CVE-2015-1853)
* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821)
* Fix initialization of reply slots for authenticated commands (CVE-2015-1822)

107
README
View File

@@ -3,67 +3,35 @@ This is the README for chrony.
What is chrony?
===============
Chrony is a pair of programs for maintaining the accuracy of computer
clocks.
chrony is a versatile implementation of the Network Time Protocol (NTP).
It can synchronize the system clock with NTP servers, reference 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.
chronyd is a (background) daemon program that can be started at boot
time. This does most of the work.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
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.
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 NTPv4 (RFC 5905) server, peer and broadcast
server.
Typical accuracy between two machines on a LAN is in tens, or a few
hundreds, of microseconds; over the Internet, accuracy is typically
within a few milliseconds. With a good hardware reference clock
sub-microsecond accuracy is possible.
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?
========================
Chrony can be successfully built and run on
1. Linux 2.2.x, 2.3.x, 2.4.x, 2.6.x, 3.x
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.
The software is known to work on Linux, FreeBSD, NetBSD, Mac OS X and
Solaris. Closely related systems may work too. Any other system will
likely 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?
===================
@@ -85,7 +53,7 @@ ready-formatted plain text (chrony.txt) in the distribution.
There is also information available on the chrony web pages, accessible
through the URL
http://chrony.tuxfamily.org/
https://chrony.tuxfamily.org/
Where are new versions announced?
=================================
@@ -116,6 +84,17 @@ chrony-dev-request@chrony.tuxfamily.org
as applicable.
When you are reporting a bug, please send us all the information you can.
Unfortunately, chrony has proven to be one of those programs where it is very
difficult to reproduce bugs in a different environment. So we may have to
interact with you quite a lot to obtain enough extra logging and tracing to
pin-point the problem in some cases. Please be patient and plan for this!
License
=======
chrony is distributed under the GNU General Public License version 2.
Author
======
@@ -132,6 +111,10 @@ Miroslav Lichvar <mlichvar@redhat.com>
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
to the program :
@@ -155,6 +138,11 @@ Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Erik Bryer <ebryer@spots.ab.ca>
Entries in contrib directory
Bryan Christianson <bryan@whatroute.net>
Support for Mac OS X
Support for privilege separation
Entries in contrib directory
Juliusz Chroboczek <jch@pps.jussieu.fr>
Fix install rule in Makefile if chronyd file is in use.
@@ -169,6 +157,9 @@ Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for
package builders.
Andrew Griffiths <agriffit@redhat.com>
Patch to add support for seccomp filter
Walter Haidinger <walter.haidinger@gmx.at>
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
@@ -228,12 +219,18 @@ Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc
Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available
Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts
Stephen Wadeley <swadeley@redhat.com>
Improvements to man pages
Wolfgang Weisselberg <weissel@netcologne.de>
Entries in contrib directory

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997,1998,1999,2000,2001,2002,2005
* Copyright (C) Miroslav Lichvar 2009
* Copyright (C) Miroslav Lichvar 2009, 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
@@ -62,7 +62,7 @@ split_ip6(IPAddr *ip, uint32_t *dst)
int i;
for (i = 0; i < 4; i++)
dst[i] = ip->addr.in6[i * 4 + 0] << 24 |
dst[i] = (uint32_t)ip->addr.in6[i * 4 + 0] << 24 |
ip->addr.in6[i * 4 + 1] << 16 |
ip->addr.in6[i * 4 + 2] << 8 |
ip->addr.in6[i * 4 + 3];
@@ -401,117 +401,3 @@ ADF_IsAnyAllowed(ADF_AuthTable table, int family)
return 0;
}
}
/* ================================================== */
#if defined TEST
static void print_node(TableNode *node, uint32_t *addr, int ip_len, int shift, int subnet_bits)
{
uint32_t new_addr[4];
int i;
TableNode *sub_node;
for (i=0; i<subnet_bits; i++) putchar(' ');
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) {
for (i=0; i<16; i++) {
sub_node = &(node->extended[i]);
new_addr[0] = addr[0];
new_addr[1] = addr[1];
new_addr[2] = addr[2];
new_addr[3] = addr[3];
new_addr[ip_len - 1 - shift / 32] |= ((uint32_t)i << (shift % 32));
print_node(sub_node, new_addr, ip_len, shift - 4, subnet_bits + 4);
}
}
}
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 main (int argc, char **argv)
{
IPAddr ip;
ADF_AuthTable table;
table = ADF_CreateTable();
ip.family = IPADDR_INET4;
ip.addr.in4 = 0x7e800000;
ADF_Allow(table, &ip, 9);
ip.addr.in4 = 0x7ecc0000;
ADF_Deny(table, &ip, 14);
#if 0
ip.addr.in4 = 0x7f000001;
ADF_Deny(table, &ip, 32);
ip.addr.in4 = 0x7f000000;
ADF_Allow(table, &ip, 8);
#endif
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.addr.in4 ^= 1;
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.family = IPADDR_INET6;
memcpy(ip.addr.in6, "abcdefghijklmnop", 16);
ADF_Deny(table, &ip, 66);
ADF_Allow(table, &ip, 59);
memcpy(ip.addr.in6, "xbcdefghijklmnop", 16);
ADF_Deny(table, &ip, 128);
ip.addr.in6[15] ^= 3;
ADF_Allow(table, &ip, 127);
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
ip.addr.in4 ^= 1;
printf("allowed: %d\n", ADF_IsAllowed(table, &ip));
print_table(table);
ADF_DestroyTable(table);
return 0;
}
#endif /* defined TEST */

18
array.c
View File

@@ -44,6 +44,8 @@ ARR_CreateInstance(unsigned int elem_size)
{
ARR_Instance array;
assert(elem_size > 0);
array = MallocNew(struct ARR_Instance_Record);
array->data = NULL;
@@ -64,6 +66,9 @@ ARR_DestroyInstance(ARR_Instance array)
static void
realloc_array(ARR_Instance array, unsigned int min_size)
{
size_t data_size;
assert(min_size <= 2 * min_size);
if (array->allocated >= min_size && array->allocated <= 2 * min_size)
return;
@@ -74,7 +79,10 @@ realloc_array(ARR_Instance array, unsigned int min_size)
array->allocated = min_size;
}
array->data = Realloc(array->data, array->elem_size * array->allocated);
data_size = (size_t)array->elem_size * array->allocated;
assert(data_size / array->elem_size == array->allocated);
array->data = Realloc(array->data, data_size);
}
void *
@@ -89,12 +97,18 @@ void *
ARR_GetElement(ARR_Instance array, unsigned int index)
{
assert(index < array->used);
return (void *)((char *)array->data + index * array->elem_size);
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;
}

167
candm.h
View File

@@ -31,7 +31,6 @@
#include "sysincl.h"
#include "addressing.h"
#include "hash.h"
/* This is the default port to use for CANDM, if no alternative is
defined */
@@ -89,11 +88,13 @@
#define REQ_RESELECT 48
#define REQ_RESELECTDISTANCE 49
#define REQ_MODIFY_MAKESTEP 50
#define N_REQUEST_TYPES 51
/* 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
#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 N_REQUEST_TYPES 57
/* Structure used to exchange timevals independent on size of time_t */
typedef struct {
@@ -116,6 +117,10 @@ typedef struct {
pktlength.c, to get the number of bytes that ought to be
transmitted for each packet type. */
typedef struct {
int32_t EOR;
} REQ_Null;
typedef struct {
IPAddr mask;
IPAddr address;
@@ -207,6 +212,8 @@ typedef struct {
typedef struct {
int32_t on_off;
int32_t stratum;
Float distance;
int32_t orphan;
int32_t EOR;
} REQ_Local;
@@ -215,19 +222,11 @@ typedef struct {
int32_t EOR;
} REQ_Manual;
typedef struct {
int32_t EOR;
} REQ_N_Sources;
typedef struct {
int32_t index;
int32_t EOR;
} REQ_Source_Data;
typedef struct {
int32_t EOR;
} REQ_Rekey;
typedef struct {
IPAddr ip;
int32_t subnet_bits;
@@ -245,6 +244,8 @@ typedef struct {
#define REQ_ADDSRC_IBURST 0x4
#define REQ_ADDSRC_PREFER 0x8
#define REQ_ADDSRC_NOSELECT 0x10
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
typedef struct {
IPAddr ip_addr;
@@ -264,10 +265,6 @@ typedef struct {
int32_t EOR;
} REQ_Del_Source;
typedef struct {
int32_t EOR;
} REQ_WriteRtc;
typedef struct {
Float dfreq;
int32_t EOR;
@@ -279,63 +276,39 @@ typedef struct {
int32_t EOR;
} REQ_Doffset;
typedef struct {
int32_t EOR;
} REQ_Tracking;
typedef struct {
uint32_t index;
int32_t EOR;
} REQ_Sourcestats;
typedef struct {
int32_t EOR;
} REQ_RTCReport;
typedef struct {
int32_t EOR;
} REQ_TrimRTC;
typedef struct {
int32_t EOR;
} REQ_CycleLogs;
/* This is based on the response size rather than the
request size */
#define MAX_CLIENT_ACCESSES 8
typedef struct {
uint32_t first_index;
uint32_t n_indices;
uint32_t n_clients;
int32_t EOR;
} REQ_ClientAccessesByIndex;
typedef struct {
int32_t EOR;
} REQ_ManualList;
typedef struct {
int32_t index;
int32_t EOR;
} 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 {
Float distance;
int32_t EOR;
} REQ_ReselectDistance;
#define REQ_SMOOTHTIME_RESET 0
#define REQ_SMOOTHTIME_ACTIVATE 1
typedef struct {
int32_t option;
int32_t EOR;
} REQ_SmoothTime;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -365,7 +338,15 @@ typedef struct {
Version 6 : added padding to requests to prevent amplification attack,
changed maximum number of samples in manual list to 16, new commands: modify
makestep
makestep, smoothing, smoothtime
Support for authentication was removed later in version 6 of the protocol
and commands that required authentication are allowed only locally over Unix
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types), new flags in NTP source request and report,
new commands: refresh, serverstats
*/
#define PROTO_VERSION_NUMBER 6
@@ -379,7 +360,7 @@ typedef struct {
#define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently
defined by CLIENT_ACCESSES_BY_INDEX and MANUAL_LIST */
defined by MANUAL_LIST */
#define MAX_PADDING_LENGTH 396
/* ================================================== */
@@ -394,10 +375,11 @@ typedef struct {
(count up from zero for same sequence
number) */
uint32_t sequence; /* Client's sequence number */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* Command token (to prevent replay attack) */
uint32_t pad1;
uint32_t pad2;
union {
REQ_Null null;
REQ_Online online;
REQ_Offline offline;
REQ_Burst burst;
@@ -415,39 +397,24 @@ typedef struct {
REQ_Settime settime;
REQ_Local local;
REQ_Manual manual;
REQ_N_Sources n_sources;
REQ_Source_Data source_data;
REQ_Rekey rekey;
REQ_Allow_Deny allow_deny;
REQ_Ac_Check ac_check;
REQ_NTP_Source ntp_source;
REQ_Del_Source del_source;
REQ_WriteRtc writertc;
REQ_Dfreq dfreq;
REQ_Doffset doffset;
REQ_Tracking tracking;
REQ_Sourcestats sourcestats;
REQ_RTCReport rtcreport;
REQ_TrimRTC trimrtc;
REQ_CycleLogs cyclelogs;
REQ_ClientAccessesByIndex client_accesses_by_index;
REQ_ManualList manual_list;
REQ_ManualDelete manual_delete;
REQ_MakeStep make_step;
REQ_Activity activity;
REQ_Reselect reselect;
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
} data; /* Command specific parameters */
/* The following fields only set the maximum size of the packet.
There are no holes between them and the actual data. */
/* Padding used to prevent traffic amplification */
/* Padding used to prevent traffic amplification. It only defines the
maximum size of the packet, there is no hole after the data field. */
uint8_t padding[MAX_PADDING_LENGTH];
/* Authentication data */
uint8_t auth[MAX_HASH_LENGTH];
} CMD_Request;
/* ================================================== */
@@ -472,7 +439,10 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX 10
#define RPY_MANUAL_LIST 11
#define RPY_ACTIVITY 12
#define N_REPLY_TYPES 13
#define RPY_SMOOTHING 13
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define N_REPLY_TYPES 16
/* Status codes */
#define STT_SUCCESS 0
@@ -519,6 +489,8 @@ typedef struct {
#define RPY_SD_FLAG_NOSELECT 0x1
#define RPY_SD_FLAG_PREFER 0x2
#define RPY_SD_FLAG_TRUST 0x4
#define RPY_SD_FLAG_REQUIRE 0x8
typedef struct {
IPAddr ip_addr;
@@ -586,11 +558,14 @@ typedef struct {
typedef struct {
IPAddr ip;
uint32_t client_hits;
uint32_t peer_hits;
uint32_t cmd_hits_auth;
uint32_t cmd_hits_normal;
uint32_t cmd_hits_bad;
uint32_t ntp_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t cmd_drops;
int8_t ntp_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago;
uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client;
@@ -603,6 +578,15 @@ typedef struct {
int32_t EOR;
} 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
typedef struct {
@@ -627,6 +611,19 @@ typedef struct {
int32_t EOR;
} RPY_Activity;
#define RPY_SMT_FLAG_ACTIVE 0x1
#define RPY_SMT_FLAG_LEAPONLY 0x2
typedef struct {
uint32_t flags;
Float offset;
Float freq_ppm;
Float wander_ppm;
Float last_update_ago;
Float remaining_time;
int32_t EOR;
} RPY_Smoothing;
typedef struct {
uint8_t version;
uint8_t pkt_type;
@@ -639,9 +636,9 @@ typedef struct {
uint16_t pad2;
uint16_t pad3;
uint32_t sequence; /* Echo of client's sequence number */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* New command token (only if command was successfully
authenticated) */
uint32_t pad4;
uint32_t pad5;
union {
RPY_Null null;
RPY_N_Sources n_sources;
@@ -651,14 +648,12 @@ typedef struct {
RPY_Sourcestats sourcestats;
RPY_Rtc rtc;
RPY_ClientAccessesByIndex client_accesses_by_index;
RPY_ServerStats server_stats;
RPY_ManualList manual_list;
RPY_Activity activity;
RPY_Smoothing smoothing;
} 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;
/* ================================================== */

View File

@@ -1,72 +0,0 @@
.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
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 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 NTPv4 (RFC 5905) server, peer and
broadcast server.
.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.

View File

@@ -1,69 +0,0 @@
.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 `foo.example.net', `bar.example.net' and `baz.example.net',
your \fBchrony.conf\fR file could contain as a minimum
.EX
server foo.example.net
server bar.example.net
server baz.example.net
.EE
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', `makestep', `rtcsync'. Also, the `iburst'
server option is useful to speed up the initial synchronization. The smallest
useful configuration file would look something like
.EX
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
driftfile @CHRONYVARDIR@/drift
makestep 10 3
rtcsync
.EE
When using a pool of NTP servers (one name is used for multiple servers which
may change over time), it's better to specify them with the `pool' directive
instead of multiple `server' directives in order to allow \fIchronyd\fR to
replace unreachable or bad servers automatically. The configuration file could
in this case look like
.EX
pool pool.ntp.org iburst
driftfile @CHRONYVARDIR@/drift
makestep 10 3
rtcsync
.EE
.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.

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +0,0 @@
.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.

View File

@@ -1,154 +0,0 @@
.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.

1962
client.c

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -33,32 +33,15 @@
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern void CLG_LogNTPClientAccess(IPAddr *client, time_t now);
extern void CLG_LogNTPPeerAccess(IPAddr *client, time_t now);
/* When logging command packets, there are several subtypes */
typedef enum {
CLG_CMD_AUTH, /* authenticated */
CLG_CMD_NORMAL, /* normal */
CLG_CMD_BAD_PKT /* bad version or packet length */
} CLG_Command_Type;
extern void CLG_LogCommandAccess(IPAddr *client, CLG_Command_Type type, time_t now);
extern int CLG_LogNTPAccess(IPAddr *client, struct timeval *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timeval *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
/* 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;
CLG_Status
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report,
time_t now, unsigned long *n_indices);
extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timeval *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */

1336
cmdmon.c

File diff suppressed because it is too large Load Diff

View File

@@ -33,6 +33,7 @@ extern void CAM_Initialise(int family);
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_CheckAccessRestriction(IPAddr *ip_addr);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013
* Copyright (C) Miroslav Lichvar 2013-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
@@ -63,7 +63,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
src->params.sel_option = SRC_SelectNormal;
src->params.sel_options = 0;
result = CPS_Success;
@@ -165,11 +165,17 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
}
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_option = SRC_SelectNoselect;
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_option = SRC_SelectPrefer;
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "version")) {
if (sscanf(line, "%d%n", &src->params.version, &n) != 1) {
result = CPS_BadVersion;
@@ -217,6 +223,41 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
{
int n;
char *cmd;
*stratum = 10;
*distance = 1.0;
*orphan = 0;
while (*line) {
cmd = line;
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
return 0;
} else if (!strcasecmp(cmd, "orphan")) {
*orphan = 1;
n = 0;
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
} else {
return 0;
}
line += n;
}
return 1;
}
/* ================================================== */
void
CPS_StatusToString(CPS_Status status, char *dest, int len)
{

View File

@@ -59,6 +59,9 @@ typedef struct {
/* Parse a command to add an NTP server or peer */
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
/* Get a string describing error status */
extern void CPS_StatusToString(CPS_Status status, char *dest, int len);

351
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2014
* 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
@@ -47,19 +47,15 @@
static int parse_string(char *line, char **result);
static int parse_int(char *line, int *result);
static int parse_uint32(char *, uint32_t *result);
static int parse_double(char *line, double *result);
static int parse_null(char *line);
static void parse_allow(char *);
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
static void parse_bindacqaddress(char *);
static void parse_bindaddress(char *);
static void parse_bindcmdaddress(char *);
static void parse_broadcast(char *);
static void parse_clientloglimit(char *);
static void parse_cmdallow(char *);
static void parse_cmddeny(char *);
static void parse_deny(char *);
static void parse_fallbackdrift(char *);
static void parse_include(char *);
static void parse_initstepslew(char *);
@@ -69,30 +65,30 @@ static void parse_log(char *);
static void parse_mailonchange(char *);
static void parse_makestep(char *);
static void parse_maxchange(char *);
static void parse_peer(char *);
static void parse_pool(char *);
static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak);
static void parse_refclock(char *);
static void parse_server(char *);
static void parse_smoothtime(char *);
static void parse_source(char *line, NTP_Source_Type type, int pool);
static void parse_tempcomp(char *);
/* ================================================== */
/* Configuration variables */
static int restarted = 0;
static int generate_command_key = 0;
static char *rtc_device;
static int acquisition_port = -1;
static int ntp_port = 123;
static char *keys_file = NULL;
static char *drift_file = NULL;
static char *rtc_file = NULL;
static uint32_t command_key_id;
static double max_update_skew = 1000.0;
static double correction_time_ratio = 3.0;
static double max_clock_error = 1.0; /* in ppm */
static double max_drift = 500000.0; /* in ppm */
static double max_slew_rate = 1e6 / 12.0; /* in ppm */
static double max_distance = 3.0;
static double reselect_distance = 1e-4;
static double stratum_weight = 1e-3;
static double combine_limit = 3.0;
@@ -112,6 +108,8 @@ static char *dumpdir;
static int enable_local=0;
static int local_stratum;
static int local_orphan;
static double local_distance;
/* Threshold (in seconds) - if absolute value of initial error is less
than this, slew instead of stepping */
@@ -126,7 +124,7 @@ static int enable_manual=0;
static int rtc_on_utc = 0;
/* Filename used to read the hwclock(8) LOCAL/UTC setting */
static char *hwclock_file = NULL;
static char *hwclock_file;
/* Flag set if the RTC should be automatically synchronised by kernel */
static int rtc_sync = 0;
@@ -151,10 +149,8 @@ static double max_offset;
static int max_samples = 0; /* no limit */
static int min_samples = 0;
/* Flag set if we should log to syslog when a time adjustment
exceeding the threshold is initiated */
static int do_log_change = 0;
static double log_change_threshold = 0.0;
/* Threshold for a time adjustment to be logged to syslog */
static double log_change_threshold = 1.0;
static char *mail_user_on_change = NULL;
static double mail_change_threshold = 0.0;
@@ -182,13 +178,27 @@ static IPAddr bind_acq_address4, bind_acq_address6;
the loopback address will be used */
static IPAddr bind_cmd_address4, bind_cmd_address6;
/* Path to the Unix domain command socket. */
static char *bind_cmd_path;
/* Filename to use for storing pid of running chronyd, to prevent multiple
* chronyds being started. */
static char *pidfile;
/* Rate limiting parameters */
static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 3;
static int cmd_ratelimit_enabled = 0;
static int cmd_ratelimit_interval = 1;
static int cmd_ratelimit_burst = 16;
static int cmd_ratelimit_leak = 2;
/* Smoothing constants */
static double smooth_max_freq = 0.0; /* in ppm */
static double smooth_max_wander = 0.0; /* in ppm/s */
static int smooth_leap_only = 0;
/* Temperature sensor, update interval and compensation coefficients */
static char *tempcomp_sensor_file = NULL;
@@ -319,8 +329,10 @@ CNF_Initialise(int r)
dumpdir = Strdup(".");
logdir = Strdup(".");
bind_cmd_path = Strdup(DEFAULT_COMMAND_SOCKET);
pidfile = Strdup("/var/run/chronyd.pid");
rtc_device = Strdup("/dev/rtc");
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER);
}
@@ -348,6 +360,7 @@ CNF_Finalise(void)
Free(keys_file);
Free(leapsec_tz);
Free(logdir);
Free(bind_cmd_path);
Free(pidfile);
Free(rtc_device);
Free(rtc_file);
@@ -369,10 +382,13 @@ CNF_ReadFile(const char *filename)
in = fopen(filename, "r");
if (!in) {
LOG_FATAL(LOGF_Configure, "Could not open configuration file %s", filename);
LOG_FATAL(LOGF_Configure, "Could not open configuration file %s : %s",
filename, strerror(errno));
return;
}
DEBUG_LOG(LOGF_Configure, "Reading %s", filename);
for (i = 1; fgets(line, sizeof(line), in); i++) {
CNF_ParseLine(filename, i, line);
}
@@ -406,7 +422,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
if (!strcasecmp(command, "acquisitionport")) {
parse_int(p, &acquisition_port);
} else if (!strcasecmp(command, "allow")) {
parse_allow(p);
parse_allow_deny(p, ntp_restrictions, 1);
} else if (!strcasecmp(command, "bindacqaddress")) {
parse_bindacqaddress(p);
} else if (!strcasecmp(command, "bindaddress")) {
@@ -418,19 +434,20 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "clientloglimit")) {
parse_clientloglimit(p);
} else if (!strcasecmp(command, "cmdallow")) {
parse_cmdallow(p);
parse_allow_deny(p, cmd_restrictions, 1);
} else if (!strcasecmp(command, "cmddeny")) {
parse_cmddeny(p);
parse_allow_deny(p, cmd_restrictions, 0);
} else if (!strcasecmp(command, "cmdport")) {
parse_int(p, &cmd_port);
} else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
&cmd_ratelimit_burst, &cmd_ratelimit_leak);
} else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "commandkey")) {
parse_uint32(p, &command_key_id);
} else if (!strcasecmp(command, "corrtimeratio")) {
parse_double(p, &correction_time_ratio);
} else if (!strcasecmp(command, "deny")) {
parse_deny(p);
parse_allow_deny(p, ntp_restrictions, 0);
} else if (!strcasecmp(command, "driftfile")) {
parse_string(p, &drift_file);
} else if (!strcasecmp(command, "dumpdir")) {
@@ -439,8 +456,6 @@ CNF_ParseLine(const char *filename, int number, char *line)
do_dump_on_exit = parse_null(p);
} else if (!strcasecmp(command, "fallbackdrift")) {
parse_fallbackdrift(p);
} else if (!strcasecmp(command, "generatecommandkey")) {
generate_command_key = parse_null(p);
} else if (!strcasecmp(command, "hwclockfile")) {
parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "include")) {
@@ -453,10 +468,6 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_leapsecmode(p);
} else if (!strcasecmp(command, "leapsectz")) {
parse_string(p, &leapsec_tz);
} else if (!strcasecmp(command, "linux_freq_scale")) {
LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command);
} else if (!strcasecmp(command, "linux_hz")) {
LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command);
} else if (!strcasecmp(command, "local")) {
parse_local(p);
} else if (!strcasecmp(command, "lock_all")) {
@@ -466,7 +477,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "logbanner")) {
parse_int(p, &log_banner);
} else if (!strcasecmp(command, "logchange")) {
do_log_change = parse_double(p, &log_change_threshold);
parse_double(p, &log_change_threshold);
} else if (!strcasecmp(command, "logdir")) {
parse_string(p, &logdir);
} else if (!strcasecmp(command, "mailonchange")) {
@@ -479,6 +490,10 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_maxchange(p);
} else if (!strcasecmp(command, "maxclockerror")) {
parse_double(p, &max_clock_error);
} else if (!strcasecmp(command, "maxdistance")) {
parse_double(p, &max_distance);
} else if (!strcasecmp(command, "maxdrift")) {
parse_double(p, &max_drift);
} else if (!strcasecmp(command, "maxsamples")) {
parse_int(p, &max_samples);
} else if (!strcasecmp(command, "maxslewrate")) {
@@ -492,13 +507,16 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "noclientlog")) {
no_client_log = parse_null(p);
} else if (!strcasecmp(command, "peer")) {
parse_peer(p);
parse_source(p, NTP_PEER, 0);
} else if (!strcasecmp(command, "pidfile")) {
parse_string(p, &pidfile);
} else if (!strcasecmp(command, "pool")) {
parse_pool(p);
parse_source(p, NTP_SERVER, 1);
} else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port);
} else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
} else if (!strcasecmp(command, "reselectdist")) {
@@ -516,7 +534,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "sched_priority")) {
parse_int(p, &sched_priority);
} else if (!strcasecmp(command, "server")) {
parse_server(p);
parse_source(p, NTP_SERVER, 0);
} else if (!strcasecmp(command, "smoothtime")) {
parse_smoothtime(p);
} else if (!strcasecmp(command, "stratumweight")) {
@@ -525,6 +543,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_tempcomp(p);
} else if (!strcasecmp(command, "user")) {
parse_string(p, &user);
} else if (!strcasecmp(command, "commandkey") ||
!strcasecmp(command, "generatecommandkey") ||
!strcasecmp(command, "linux_freq_scale") ||
!strcasecmp(command, "linux_hz")) {
LOG(LOGS_WARN, LOGF_Configure, "%s directive is no longer supported", command);
} else {
other_parse_error("Invalid command");
}
@@ -556,19 +579,6 @@ parse_int(char *line, int *result)
/* ================================================== */
static int
parse_uint32(char *line, uint32_t *result)
{
check_number_of_args(line, 1);
if (sscanf(line, "%"SCNu32, result) != 1) {
command_parse_error();
return 0;
}
return 1;
}
/* ================================================== */
static int
parse_double(char *line, double *result)
{
@@ -615,25 +625,30 @@ parse_source(char *line, NTP_Source_Type type, int pool)
/* ================================================== */
static void
parse_server(char *line)
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
{
parse_source(line, NTP_SERVER, 0);
}
int n, val;
char *opt;
/* ================================================== */
*enabled = 1;
static void
parse_peer(char *line)
{
parse_source(line, NTP_PEER, 0);
}
/* ================================================== */
static void
parse_pool(char *line)
{
parse_source(line, NTP_SERVER, 1);
while (*line) {
opt = line;
line = CPS_SplitWord(line);
if (sscanf(line, "%d%n", &val, &n) != 1) {
command_parse_error();
return;
}
line += n;
if (!strcasecmp(opt, "interval"))
*interval = val;
else if (!strcasecmp(opt, "burst"))
*burst = val;
else if (!strcasecmp(opt, "leak"))
*leak = val;
else
command_parse_error();
}
}
/* ================================================== */
@@ -641,12 +656,11 @@ parse_pool(char *line)
static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples;
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion;
char *p, *cmd, *name, *param;
unsigned char ref[5];
SRC_SelectOption sel_option;
RefclockParameters *refclock;
poll = 4;
@@ -655,13 +669,13 @@ parse_refclock(char *line)
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
max_samples = SRC_DEFAULT_MAXSAMPLES;
sel_options = 0;
offset = 0.0;
delay = 1e-9;
precision = 0.0;
max_dispersion = 0.0;
ref_id = 0;
lock_ref_id = 0;
sel_option = SRC_SelectNormal;
if (!*line) {
command_parse_error();
@@ -688,11 +702,11 @@ parse_refclock(char *line)
if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "lock")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
lock_ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
break;
@@ -728,18 +742,25 @@ parse_refclock(char *line)
break;
} else if (!strcasecmp(cmd, "noselect")) {
n = 0;
sel_option = SRC_SelectNoselect;
sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
n = 0;
sel_option = SRC_SelectPrefer;
sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "trust")) {
n = 0;
sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
n = 0;
sel_options |= SRC_SELECT_REQUIRE;
} else {
break;
other_parse_error("Invalid refclock option");
return;
}
line += n;
}
if (*line) {
other_parse_error("Invalid/unreadable refclock parameter");
command_parse_error();
return;
}
@@ -752,11 +773,11 @@ parse_refclock(char *line)
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
refclock->sel_options = sel_options;
refclock->offset = offset;
refclock->delay = delay;
refclock->precision = precision;
refclock->max_dispersion = max_dispersion;
refclock->sel_option = sel_option;
refclock->ref_id = ref_id;
refclock->lock_ref_id = lock_ref_id;
}
@@ -798,13 +819,9 @@ parse_log(char *line)
static void
parse_local(char *line)
{
int stratum;
if (sscanf(line, "stratum%d", &stratum) == 1) {
local_stratum = stratum;
enable_local = 1;
} else {
if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance))
command_parse_error();
}
enable_local = 1;
}
/* ================================================== */
@@ -867,11 +884,6 @@ parse_clientloglimit(char *line)
if (sscanf(line, "%lu", &client_log_limit) != 1) {
command_parse_error();
}
if (client_log_limit == 0) {
/* unlimited */
client_log_limit = (unsigned long)-1;
}
}
/* ================================================== */
@@ -1033,41 +1045,6 @@ parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
}
}
/* ================================================== */
static void
parse_allow(char *line)
{
parse_allow_deny(line, ntp_restrictions, 1);
}
/* ================================================== */
static void
parse_deny(char *line)
{
parse_allow_deny(line, ntp_restrictions, 0);
}
/* ================================================== */
static void
parse_cmdallow(char *line)
{
parse_allow_deny(line, cmd_restrictions, 1);
}
/* ================================================== */
static void
parse_cmddeny(char *line)
{
parse_allow_deny(line, cmd_restrictions, 0);
}
/* ================================================== */
static void
@@ -1112,7 +1089,14 @@ parse_bindcmdaddress(char *line)
IPAddr ip;
check_number_of_args(line, 1);
if (UTI_StringToIP(line, &ip)) {
/* Address starting with / is for the Unix domain socket */
if (line[0] == '/') {
parse_string(line, &bind_cmd_path);
/* / disables the socket */
if (!strcmp(bind_cmd_path, "/"))
bind_cmd_path[0] = '\0';
} else if (UTI_StringToIP(line, &ip)) {
if (ip.family == IPADDR_INET4)
bind_cmd_address4 = ip;
else if (ip.family == IPADDR_INET6)
@@ -1174,11 +1158,23 @@ parse_broadcast(char *line)
static void
parse_smoothtime(char *line)
{
check_number_of_args(line, 2);
if (get_number_of_args(line) != 3)
check_number_of_args(line, 2);
if (sscanf(line, "%lf %lf", &smooth_max_freq, &smooth_max_wander) != 2) {
smooth_max_freq = 0.0;
command_parse_error();
}
line = CPS_SplitWord(CPS_SplitWord(line));
smooth_leap_only = 0;
if (*line) {
if (!strcasecmp(line, "leaponly"))
smooth_leap_only = 1;
else
command_parse_error();
}
}
/* ================================================== */
@@ -1227,8 +1223,47 @@ parse_tempcomp(char *line)
static void
parse_include(char *line)
{
glob_t gl;
size_t i;
check_number_of_args(line, 1);
CNF_ReadFile(line);
if (glob(line, 0, NULL, &gl)) {
DEBUG_LOG(LOGF_Configure, "glob of %s failed", line);
return;
}
for (i = 0; i < gl.gl_pathc; i++)
CNF_ReadFile(gl.gl_pathv[i]);
globfree(&gl);
}
/* ================================================== */
void
CNF_CreateDirs(uid_t uid, gid_t gid)
{
char *dir;
UTI_CreateDirAndParents(logdir, 0755, uid, gid);
UTI_CreateDirAndParents(dumpdir, 0755, uid, gid);
/* Create a directory for the Unix domain command socket */
if (bind_cmd_path[0]) {
dir = UTI_PathToDir(bind_cmd_path);
UTI_CreateDirAndParents(dir, 0770, uid, gid);
/* Check the permissions and owner/group in case the directory already
existed. It MUST NOT be accessible by others as permissions on Unix
domain sockets are ignored on some systems (e.g. Solaris). */
if (!UTI_CheckDirPermissions(dir, 0770, uid, gid)) {
LOG(LOGS_WARN, LOGF_Configure, "Disabled command socket %s", bind_cmd_path);
bind_cmd_path[0] = '\0';
}
Free(dir);
}
}
/* ================================================== */
@@ -1436,22 +1471,6 @@ CNF_GetRtcDevice(void)
/* ================================================== */
uint32_t
CNF_GetCommandKey(void)
{
return command_key_id;
}
/* ================================================== */
int
CNF_GetGenerateCommandKey(void)
{
return generate_command_key;
}
/* ================================================== */
int
CNF_GetDumpOnExit(void)
{
@@ -1468,6 +1487,14 @@ CNF_GetMaxUpdateSkew(void)
/* ================================================== */
double
CNF_GetMaxDrift(void)
{
return max_drift;
}
/* ================================================== */
double
CNF_GetMaxClockError(void)
{
@@ -1492,6 +1519,14 @@ CNF_GetMaxSlewRate(void)
/* ================================================== */
double
CNF_GetMaxDistance(void)
{
return max_distance;
}
/* ================================================== */
double
CNF_GetReselectDistance(void)
{
@@ -1532,10 +1567,12 @@ CNF_GetCommandPort(void) {
/* ================================================== */
int
CNF_AllowLocalReference(int *stratum)
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance)
{
if (enable_local) {
*stratum = local_stratum;
*orphan = local_orphan;
*distance = local_distance;
return 1;
} else {
return 0;
@@ -1579,11 +1616,10 @@ CNF_GetMaxChange(int *delay, int *ignore, double *offset)
/* ================================================== */
void
CNF_GetLogChange(int *enabled, double *threshold)
double
CNF_GetLogChange(void)
{
*enabled = do_log_change;
*threshold = log_change_threshold;
return log_change_threshold;
}
/* ================================================== */
@@ -1684,6 +1720,14 @@ CNF_GetBindAcquisitionAddress(int family, IPAddr *addr)
/* ================================================== */
char *
CNF_GetBindCommandPath(void)
{
return bind_cmd_path;
}
/* ================================================== */
void
CNF_GetBindCommandAddress(int family, IPAddr *addr)
{
@@ -1737,11 +1781,32 @@ CNF_GetLockMemory(void)
/* ================================================== */
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
{
*interval = ntp_ratelimit_interval;
*burst = ntp_ratelimit_burst;
*leak = ntp_ratelimit_leak;
return ntp_ratelimit_enabled;
}
/* ================================================== */
int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak)
{
*interval = cmd_ratelimit_interval;
*burst = cmd_ratelimit_burst;
*leak = cmd_ratelimit_leak;
return cmd_ratelimit_enabled;
}
/* ================================================== */
void
CNF_GetSmooth(double *max_freq, double *max_wander)
CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only)
{
*max_freq = smooth_max_freq;
*max_wander = smooth_max_wander;
*leap_only = smooth_leap_only;
}
/* ================================================== */

15
conf.h
View File

@@ -39,6 +39,8 @@ extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
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_AddSources(void);
extern void CNF_AddBroadcasts(void);
@@ -58,8 +60,6 @@ extern int CNF_GetLogRefclocks(void);
extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void);
extern uint32_t CNF_GetCommandKey(void);
extern int CNF_GetGenerateCommandKey(void);
extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void);
@@ -67,7 +67,7 @@ extern int CNF_GetRtcOnUtc(void);
extern int CNF_GetRtcSync(void);
extern void CNF_GetMakeStep(int *limit, double *threshold);
extern void CNF_GetMaxChange(int *delay, int *ignore, double *offset);
extern void CNF_GetLogChange(int *enabled, double *threshold);
extern double CNF_GetLogChange(void);
extern void CNF_GetMailOnChange(int *enabled, double *threshold, char **user);
extern int CNF_GetNoClientLog(void);
extern unsigned long CNF_GetClientLogLimit(void);
@@ -75,6 +75,7 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindCommandPath(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
@@ -82,21 +83,25 @@ extern char *CNF_GetLeapSecTimezone(void);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance);
extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
extern void CNF_GetSmooth(double *max_freq, double *max_wander);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
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);

277
configure vendored
View File

@@ -4,32 +4,12 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate.
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Miroslav Lichvar 2009, 2012-2014
# Copyright (C) Miroslav Lichvar 2009, 2012-2015
#
# =======================================================================
# 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
@@ -41,7 +21,7 @@ test_code () {
ldflags=$4
code=$5
echo -n "Checking for $name : "
printf "%s" "Checking for $name : "
(
for h in $headers; do
@@ -113,12 +93,16 @@ For better control, use the options below.
--disable-pps Disable PPS refclock driver
--disable-ipv6 Disable IPv6 support
--disable-rtc Don't include RTC even on Linux
--disable-linuxcaps Disable libcap (Linux capabilities) support
--disable-privdrop Disable support for dropping root privileges
--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-forcednsretry Don't retry on permanent DNS error
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support
@@ -127,10 +111,10 @@ Fine tuning of the installation directories:
--bindir=DIR user executables [EPREFIX/bin]
--sbindir=DIR system admin executables [EPREFIX/sbin]
--datarootdir=DIR data root [PREFIX/share]
--infodir=DIR info documentation [DATAROOTDIR/info]
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root [DATAROOTDIR/doc/chrony]
--localstatedir=DIR modifiable single-machine data [/var]
--chronysockdir=DIR location for chrony sockets [LOCALSTATEDIR/run/chrony]
--chronyvardir=DIR location for chrony data [LOCALSTATEDIR/lib/chrony]
Overriding system detection when cross-compiling:
@@ -174,12 +158,12 @@ get_features () {
ff=1
for f; do
if [ "$ff" = "0" ]; then
echo -n " "
printf " "
fi
if grep "define FEAT_$f" config.h > /dev/null; then
echo -n "+$f"
printf "%s" "+$f"
else
echo -n "-$f"
printf "%s" "-$f"
fi
ff=0
done
@@ -213,7 +197,11 @@ try_tomcrypt=1
feat_rtc=1
try_rtc=0
feat_droproot=1
try_libcap=0
try_libcap=-1
try_clockctl=0
feat_scfilter=0
try_seccomp=-1
priv_ops=""
readline_lib=""
readline_inc=""
ncurses_lib=""
@@ -227,6 +215,7 @@ feat_asyncdns=1
feat_forcednsretry=1
ntp_era_split=""
default_user="root"
default_hwclockfile=""
mail_program="/usr/lib/sendmail"
for option
@@ -271,9 +260,6 @@ do
--datarootdir=* )
SETDATAROOTDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--infodir=* )
SETINFODIR=`echo $option | sed -e 's/^.*=//;'`
;;
--mandir=* )
SETMANDIR=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -283,6 +269,9 @@ do
--localstatedir=* )
SETLOCALSTATEDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--chronysockdir=* )
SETCHRONYSOCKDIR=`echo $option | sed -e 's/^.*=//;'`
;;
--chronyvardir=* )
SETCHRONYVARDIR=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -307,9 +296,21 @@ do
--disable-pps)
feat_pps=0
;;
--disable-linuxcaps)
--disable-privdrop)
feat_droproot=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)
feat_asyncdns=0
;;
@@ -322,6 +323,9 @@ do
--with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-hwclockfile=* )
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
;;
--with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -356,71 +360,61 @@ rm -f config.h config.log
SYSTEM=${OPERATINGSYSTEM}-${MACHINE}
case $SYSTEM in
SunOS-sun4* )
case $VERSION in
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_libcap=1
case $OPERATINGSYSTEM in
Linux)
EXTRA_OBJECTS="sys_generic.o sys_linux.o sys_timex.o"
[ $try_libcap != "0" ] && try_libcap=1
try_rtc=1
[ $try_seccomp != "0" ] && try_seccomp=1
try_setsched=1
try_lockmem=1
try_phc=1
add_def LINUX
echo "Configuring for " $SYSTEM
if [ "${MACHINE}" = "alpha" ]; then
echo "Enabling -mieee"
# FIXME: Should really test for GCC
MYCFLAGS="$MYCFLAGS -mieee"
;;
FreeBSD)
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
;;
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"
;;
SunOS-i86pc* )
# Doug Woodward <dougw@whistler.com> reported that this configuration
# works for Solaris 2.8 / SunOS 5.8 on x86 platforms
EXTRA_OBJECTS="sys_solaris.o"
EXTRA_LIBS="-lsocket -lnsl -lkvm -lelf"
EXTRA_CLI_LIBS="-lsocket -lnsl"
NetBSD)
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
try_clockctl=1
add_def NETBSD
echo "Configuring for $SYSTEM"
;;
Darwin)
EXTRA_OBJECTS="sys_macosx.o"
EXTRA_LIBS="-lresolv"
EXTRA_CLI_LIBS="-lresolv"
add_def MACOSX
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIME SETTIME BINDSOCKET"
fi
echo "Configuring for MacOS X (" $SYSTEM "MacOS X version" $VERSION ")"
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o"
EXTRA_LIBS="-lsocket -lnsl -lresolv"
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
add_def SOLARIS
# 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 ")"
;;
CYGWIN32_NT-i[3456]86 )
EXTRA_OBJECTS="sys_winnt.o"
EXTRA_LIBS=""
add_def WINNT
echo "Configuring for Windows NT (Cygwin32)"
;;
* )
echo "Sorry, I don't know how to build this software on your system."
echo "error: $SYSTEM is not supported (yet?)"
exit 1
;;
esac
@@ -453,6 +447,35 @@ if [ $feat_refclock = "1" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o"
fi
MYCC="$CC"
MYCFLAGS="$CFLAGS"
MYCPPFLAGS="$CPPFLAGS"
MYLDFLAGS="$LDFLAGS"
if [ "x$MYCC" = "x" ]; then
MYCC=gcc
if ! test_code "$MYCC" '' '' '' ''; then
MYCC=cc
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: no C compiler found"
exit 1
fi
fi
else
if ! test_code "$MYCC" '' '' '' ''; then
echo "error: C compiler $MYCC cannot create executables"
exit 1
fi
fi
if [ "x$MYCFLAGS" = "x" ]; then
MYCFLAGS="-O2 -g"
fi
if [ "x$MYCC" = "xgcc" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
if test_code '64-bit time_t' 'time.h' '' '' '
char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
return x[0];'
@@ -464,8 +487,8 @@ then
split_days=0
else
split_seconds=`date '+%s'`
if [ "x$split_seconds" = "" ]; then
echo "Could not get current time, --with-ntp-era option is needed"
if [ "x$split_seconds" = "x" ]; then
echo "error: could not get current time, --with-ntp-era option is needed"
exit 1
fi
split_days=$((50 * 365))
@@ -494,7 +517,7 @@ else
if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then
LIBS="-lm"
else
echo "Can't compile/link a program which uses sqrt(), log(), pow(), bailing out"
echo "error: could not compile/link a program which uses sqrt(), log(), pow()"
exit 1
fi
fi
@@ -507,21 +530,28 @@ if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
add_def HAVE_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
if [ $feat_ipv6 = "1" ] && \
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' '' '
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" '
struct sockaddr_in6 n;
char p[100];
n.sin6_addr = in6addr_any;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then
add_def FEAT_IPV6
if test_code 'in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
return sizeof(struct in6_pktinfo);'
if test_code 'struct in6_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;'
then
add_def HAVE_IN6_PKTINFO
else
if test_code 'in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \
'-D_GNU_SOURCE' '' 'return sizeof(struct in6_pktinfo);'
if test_code 'struct in6_pktinfo with _GNU_SOURCE' 'sys/socket.h netinet/in.h' \
'-D_GNU_SOURCE' '' 'return sizeof (struct in6_pktinfo) + IPV6_PKTINFO;'
then
add_def _GNU_SOURCE
add_def HAVE_IN6_PKTINFO
@@ -529,7 +559,7 @@ then
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' '' \
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);'
then
add_def HAVE_GETADDRINFO
@@ -545,6 +575,10 @@ then
MYCFLAGS="$MYCFLAGS -pthread"
fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
add_def HAVE_ARC4RANDOM
fi
timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
@@ -579,6 +613,33 @@ then
EXTRA_LIBS="$EXTRA_LIBS -lcap"
fi
if [ $feat_droproot = "1" ] && [ $try_clockctl = "1" ] && \
test_code '<sys/clockctl.h>' 'sys/clockctl.h' '' '' ''
then
add_def FEAT_PRIVDROP
priv_ops="BINDSOCKET"
fi
if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
test_code seccomp 'seccomp.h' '' '-lseccomp' \
'seccomp_init(SCMP_ACT_KILL);'
then
add_def FEAT_SCFILTER
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could
# block the main thread
priv_ops="NAME2IPADDRESS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi
if [ "x$priv_ops" != "x" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS privops.o"
add_def PRIVOPS_HELPER
for o in $priv_ops; do
add_def PRIVOPS_$o
done
fi
if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \
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);'
@@ -732,11 +793,6 @@ if [ "x$SETDATAROOTDIR" != "x" ]; then
DATAROOTDIR=$SETDATAROOTDIR
fi
INFODIR=${DATAROOTDIR}/info
if [ "x$SETINFODIR" != "x" ]; then
INFODIR=$SETINFODIR
fi
MANDIR=${DATAROOTDIR}/man
if [ "x$SETMANDIR" != "x" ]; then
MANDIR=$SETMANDIR
@@ -752,29 +808,38 @@ if [ "x$SETLOCALSTATEDIR" != "x" ]; then
LOCALSTATEDIR=$SETLOCALSTATEDIR
fi
CHRONYSOCKDIR=${LOCALSTATEDIR}/run/chrony
if [ "x$SETCHRONYSOCKDIR" != "x" ]; then
CHRONYSOCKDIR=$SETCHRONYSOCKDIR
fi
CHRONYVARDIR=${LOCALSTATEDIR}/lib/chrony
if [ "x$SETCHRONYVARDIR" != "x" ]; then
CHRONYVARDIR=$SETCHRONYVARDIR
fi
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYSOCKDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features ASYNCDNS IPV6 SECHASH`"
common_features="`get_features IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP DEBUG`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
if [ -f version.txt ]; then
add_def CHRONY_VERSION "\"`cat version.txt`\""
CHRONY_VERSION="`cat version.txt`"
else
add_def CHRONY_VERSION "\"DEVELOPMENT\""
CHRONY_VERSION="DEVELOPMENT"
fi
for f in Makefile chrony.conf.5 chrony.texi chronyc.1 chronyd.8
add_def CHRONY_VERSION "\"${CHRONY_VERSION}\""
for f in Makefile doc/Makefile test/unit/Makefile
do
echo Creating $f
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
@@ -793,10 +858,12 @@ do
s%@SBINDIR@%${SBINDIR}%;\
s%@DOCDIR@%${DOCDIR}%;\
s%@MANDIR@%${MANDIR}%;\
s%@INFODIR@%${INFODIR}%;\
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
s%@CHRONYSOCKDIR@%${CHRONYSOCKDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_USER@%${default_user}%;"\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\
s%@DEFAULT_USER@%${default_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f
done

View File

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

View File

@@ -0,0 +1,58 @@
#!/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

@@ -0,0 +1,22 @@
<?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

@@ -0,0 +1,19 @@
<?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>

72
doc/Makefile.in Normal file
View File

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

2108
doc/chrony.conf.adoc Normal file

File diff suppressed because it is too large Load Diff

1126
doc/chronyc.adoc Normal file

File diff suppressed because it is too large Load Diff

164
doc/chronyd.adoc Normal file
View File

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

404
doc/faq.adoc Normal file
View File

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

189
doc/installation.adoc Normal file
View File

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

View File

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

View File

@@ -5,8 +5,8 @@ pool pool.ntp.org iburst
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
# if the adjustment is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

View File

@@ -6,8 +6,8 @@ pool pool.ntp.org iburst
driftfile /var/lib/chrony/drift
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
# if the adjustment is larger than 1 second.
makestep 1.0 3
# Enable kernel synchronization of the real-time clock (RTC).
rtcsync
@@ -18,20 +18,8 @@ rtcsync
# Serve time even if not synchronized to any NTP server.
#local stratum 10
# Specify file containing keys for NTP and command authentication.
keyfile /etc/chrony.keys
# Specify key number for command authentication.
commandkey 1
# Generate new command key on start if missing.
generatecommandkey
# Disable logging of client accesses.
noclientlog
# Send message to syslog when clock adjustment is larger than 0.5 seconds.
logchange 0.5
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -5,22 +5,6 @@
# want to enable. The more obscure options are not included. Refer
# 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
# Any of the following lines are comments (you have a choice of
@@ -95,24 +79,10 @@
driftfile /var/lib/chrony/drift
# If you want to use the program called chronyc to configure aspects of
# 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.
# If you want to enable NTP authentication with symmetric keys, you will need
# to uncomment the following line and edit the file to set up the 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
! keyfile /etc/chrony.keys
# chronyd can save the measurement history for the servers to files when
# it it exits. This is useful in 2 situations:
@@ -142,15 +112,15 @@ generatecommandkey
#######################################################################
### INITIAL CLOCK CORRECTION
# This option is useful to quickly correct the clock on start if it's
# off by a large amount. The value '10' means that if the error is less
# than 10 seconds, it will be gradually removed by speeding up or
# slowing down your computer's clock until it is correct. If the error
# is above 10 seconds, an immediate time jump will be applied to correct
# it. The value '1' means the step is allowed only on the first update
# of the clock. Some software can get upset if the system clock jumps
# off by a large amount. The value '1.0' means that if the error is less
# than 1 second, it will be gradually removed by speeding up or slowing
# down your computer's clock until it is correct. If the error is above
# 1 second, an immediate time jump will be applied to correct it. The
# value '3' means the step is allowed only in the first three updates of
# the clock. Some software can get upset if the system clock jumps
# (especially backwards), so be careful!
! makestep 10 1
! makestep 1.0 3
#######################################################################
### LOGGING
@@ -221,6 +191,13 @@ generatecommandkey
! clientloglimit 4194304
# By default, chronyd tries to respond to all valid NTP requests from
# allowed addresses. If you want to limit the response rate for NTP
# clients that are sending requests too frequently, uncomment and edit
# the following line.
! limitrate interval 3 burst 8
#######################################################################
### REPORTING BIG CLOCK CHANGES
# Perhaps you want to know if chronyd suddenly detects any large error
@@ -247,6 +224,7 @@ generatecommandkey
# 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 ::
@@ -262,10 +240,10 @@ generatecommandkey
# syntax and meaning is the same as for 'allow' and 'deny', except that
# 'cmdallow' and 'cmddeny' control access to the chronyd's command port.
# NOTE, even if the host where you run chronyc is granted access, you
# 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
# default all you can do is view information about chronyd's operation.
# Rate limiting can be enabled also for command packets. (Note,
# commands from localhost are never limited.)
! cmdratelimit interval 1 burst 16
#######################################################################
### REAL TIME CLOCK

View File

@@ -1,29 +1,12 @@
#######################################################################
# 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.
#
# This is an example chrony keys file. You should copy it to /etc/chrony.keys
# 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
#
######################################################################
# Don't use the example keys! It's recommended to generate random keys using
# the chronyc keygen command.
# Examples of valid keys:
#1 ALongAndRandomPassword
#2 MD5 HEX:B028F91EA5C38D06C2E140B26C7F41EC
#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.
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995

View File

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

View File

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

View File

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

2870
getdate.c

File diff suppressed because it is too large Load Diff

View File

@@ -8,12 +8,7 @@
** This code is in the public domain and has no copyright.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# endif
#endif
#include "config.h"
/* 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

View File

@@ -25,11 +25,12 @@
*/
#include "config.h"
#include <nss.h>
#include <hasht.h>
#include <nsslowhash.h>
/* #include "config.h" */
#include "hash.h"
static NSSLOWInitContext *ictx;

98
keys.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2014
* Copyright (C) Miroslav Lichvar 2012-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
@@ -39,6 +39,8 @@
#include "local.h"
#include "logging.h"
/* Consider 80 bits as the absolute minimum for a secure key */
#define MIN_SECURE_KEY_LENGTH 10
typedef struct {
uint32_t id;
@@ -50,72 +52,12 @@ typedef struct {
static ARR_Instance keys;
static int command_key_valid;
static uint32_t command_key_id;
static int cache_valid;
static uint32_t cache_key_id;
static int cache_key_pos;
/* ================================================== */
static int
generate_key(uint32_t key_id)
{
#ifdef FEAT_SECHASH
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;
key_file = CNF_GetKeysFile();
if (!key_file)
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%"PRIu32" %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 %"PRIu32, key_id);
return 1;
}
/* ================================================== */
static void
free_keys(void)
{
@@ -125,7 +67,6 @@ free_keys(void)
Free(((Key *)ARR_GetElement(keys, i))->val);
ARR_SetSize(keys, 0);
command_key_valid = 0;
cache_valid = 0;
}
@@ -135,14 +76,8 @@ void
KEY_Initialise(void)
{
keys = ARR_CreateInstance(sizeof (Key));
command_key_valid = 0;
cache_valid = 0;
KEY_Reload();
if (CNF_GetGenerateCommandKey() && !KEY_KeyKnown(KEY_GetCommandKey())) {
if (generate_key(KEY_GetCommandKey()))
KEY_Reload();
}
}
/* ================================================== */
@@ -334,18 +269,6 @@ get_key_by_id(uint32_t key_id)
/* ================================================== */
uint32_t
KEY_GetCommandKey(void)
{
if (!command_key_valid) {
command_key_id = CNF_GetCommandKey();
}
return command_key_id;
}
/* ================================================== */
int
KEY_KeyKnown(uint32_t key_id)
{
@@ -369,6 +292,21 @@ KEY_GetAuthDelay(uint32_t key_id)
/* ================================================== */
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;
}
/* ================================================== */
int
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)

3
keys.h
View File

@@ -34,11 +34,10 @@ extern void KEY_Finalise(void);
extern void KEY_Reload(void);
extern uint32_t KEY_GetCommandKey(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data,
int data_len, unsigned char *auth, int auth_len);

21
local.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2014
* Copyright (C) Miroslav Lichvar 2011, 2014-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
@@ -42,13 +42,12 @@
/* ================================================== */
/* Maximum allowed frequency offset in ppm, the time must not stop
or run backwards */
#define MAX_FREQ 500000.0
/* Variable to store the current frequency, in ppm */
static double current_freq_ppm;
/* Maximum allowed frequency, in ppm */
static double max_freq_ppm;
/* Temperature compensation, in ppm */
static double temp_comp_ppm;
@@ -166,6 +165,11 @@ LCL_Initialise(void)
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;
}
@@ -406,12 +410,12 @@ LCL_ReadAbsoluteFrequency(void)
static double
clamp_freq(double freq)
{
if (freq <= MAX_FREQ && freq >= -MAX_FREQ)
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
return freq;
LOG(LOGS_WARN, LOGF_Local, "Frequency %.1f ppm exceeds allowed maximum", freq);
return freq >= MAX_FREQ ? MAX_FREQ : -MAX_FREQ;
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
}
/* ================================================== */
@@ -564,6 +568,9 @@ LCL_NotifyLeap(int leap)
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);
}

View File

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

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2013-2014
* Copyright (C) Miroslav Lichvar 2013-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
@@ -45,18 +45,28 @@ extern int log_debug_enabled;
#define FORMAT_ATTRIBUTE_PRINTF(str, first)
#endif
#if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__);
#else
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, __VA_ARGS__);
#endif
#define DEBUG_LOG(facility, ...) \
do { \
if (DEBUG && log_debug_enabled) \
LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
LOG_MESSAGE(LOGS_DEBUG, facility, __VA_ARGS__); \
} while (0)
#define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_FATAL(facility, ...) \
do { \
LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
LOG_MESSAGE(LOGS_FATAL, facility, __VA_ARGS__); \
exit(1); \
} while (0)
#define LOG(severity, facility, ...) LOG_MESSAGE(severity, facility, __VA_ARGS__)
/* Definition of severity */
typedef enum {
LOGS_INFO,
@@ -81,6 +91,7 @@ typedef enum {
LOGF_Util,
LOGF_Main,
LOGF_Memory,
LOGF_Client,
LOGF_ClientLog,
LOGF_Configure,
LOGF_CmdMon,
@@ -89,14 +100,16 @@ typedef enum {
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_PrivOps,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
LOGF_SysGeneric,
LOGF_SysLinux,
LOGF_SysMacOSX,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysSunOS,
LOGF_SysTimex,
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
@@ -111,10 +124,15 @@ extern void LOG_Initialise(void);
extern void LOG_Finalise(void);
/* Line logging function */
#if DEBUG > 0
FORMAT_ATTRIBUTE_PRINTF(6, 7)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
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:
0, 1 - only non-debug messages are logged
@@ -141,7 +159,6 @@ extern LOG_FileID LOG_FileOpen(const char *name, const char *banner);
FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_FileWrite(LOG_FileID id, const char *format, ...);
extern void LOG_CreateLogFileDir(void);
extern void LOG_CycleLogFiles(void);
#endif /* GOT_LOGGING_H */

87
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2014
* Copyright (C) Miroslav Lichvar 2012-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
@@ -49,8 +49,10 @@
#include "refclock.h"
#include "clientlog.h"
#include "nameserv.h"
#include "privops.h"
#include "smooth.h"
#include "tempcomp.h"
#include "util.h"
/* ================================================== */
@@ -67,6 +69,18 @@ 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
delete_pidfile(void)
{
@@ -94,10 +108,10 @@ MAI_CleanupAndExit(void)
MNL_Finalise();
CLG_Finalise();
NSR_Finalise();
NCR_Finalise();
CAM_Finalise();
NIO_Finalise();
SST_Finalise();
NCR_Finalise();
NIO_Finalise();
CAM_Finalise();
KEY_Finalise();
RCL_Finalise();
SRC_Finalise();
@@ -106,6 +120,7 @@ MAI_CleanupAndExit(void)
SYS_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
delete_pidfile();
@@ -264,7 +279,7 @@ write_lockfile(void)
if (!out) {
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
} else {
fprintf(out, "%d\n", getpid());
fprintf(out, "%d\n", (int)getpid());
fclose(out);
}
}
@@ -274,11 +289,6 @@ write_lockfile(void)
static void
go_daemon(void)
{
#ifdef WINNT
#else
int pid, fd, pipefd[2];
/* Create pipe which will the daemon use to notify the grandparent
@@ -337,8 +347,6 @@ go_daemon(void)
LOG_SetParentFd(pipefd[1]);
}
}
#endif
}
/* ================================================== */
@@ -347,14 +355,18 @@ int main
(int argc, char **argv)
{
const char *conf_file = DEFAULT_CONF_FILE;
const char *progname = argv[0];
char *user = NULL;
struct passwd *pw;
int debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0;
int other_pid;
int lock_memory = 0, sched_priority = 0;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int system_log = 1;
int config_args = 0;
do_platform_checks();
LOG_Initialise();
/* Parse command line options */
@@ -381,12 +393,16 @@ int main
} else {
user = *argv;
}
} else if (!strcmp("-F", *argv)) {
++argv, --argc;
if (argc == 0 || sscanf(*argv, "%d", &scfilter_level) != 1)
LOG_FATAL(LOGF_Main, "Bad syscall filter level");
} else if (!strcmp("-s", *argv)) {
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 (%s)\n", CHRONY_VERSION, CHRONYD_FEATURES);
exit(0);
return 0;
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
@@ -405,6 +421,10 @@ int main
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else if (!strcmp("-h", *argv) || !strcmp("--help", *argv)) {
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-f FILE|COMMAND...]\n",
progname);
return 0;
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
@@ -417,7 +437,7 @@ int main
if (getuid() != 0) {
/* This write to the terminal is OK, it comes before we turn into a daemon */
fprintf(stderr,"Not superuser\n");
exit(1);
return 1;
}
/* Turn into a daemon */
@@ -459,6 +479,7 @@ int main
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();
@@ -467,6 +488,12 @@ int main
RCL_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 */
if (!sched_priority) {
sched_priority = CNF_GetSchedPriority();
@@ -482,17 +509,19 @@ int main
if (!user) {
user = CNF_GetUser();
}
if (user && strcmp(user, "root")) {
SYS_DropRoot(user);
}
LOG_CreateLogFileDir();
if ((pw = getpwnam(user)) == NULL)
LOG_FATAL(LOGF_Main, "Could not get %s uid/gid", user);
/* Create all directories before dropping root */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Drop root privileges if the user has non-zero uid or gid */
if (pw->pw_uid || pw->pw_gid)
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise();
SST_Initialise();
NIO_Initialise(address_family);
CAM_Initialise(address_family);
NCR_Initialise();
NSR_Initialise();
CLG_Initialise();
MNL_Initialise();
@@ -502,7 +531,12 @@ int main
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
CNF_SetupAccessRestrictions();
UTI_SetQuitSignalsHandler(signal_cleanup);
CAM_OpenUnixSocket();
if (scfilter_level)
SYS_EnableSystemCallFilter(scfilter_level);
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew;
@@ -517,13 +551,6 @@ int main
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 scheduler. */
SCH_MainLoop();

View File

@@ -1,6 +1,6 @@
#!/bin/sh
LANG=C
LANG=C.UTF-8
export LANG
if [ $# -ne 1 ]; then
@@ -11,7 +11,6 @@ fi
version=$1
tag=$version
subdir=chrony-${version}
mandate=$(date +'%B %Y')
umask 022
@@ -39,37 +38,13 @@ echo $version > version.txt
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
for m in chrony.1 chronyc.1.in chrony.conf.5.in chronyd.8.in; do
sed -e "s%@VERSION@%${version}%;s%@MAN_DATE@%${mandate}%" \
< $m > ${m}_
mv -f ${m}_ $m
done
./configure && make -C doc man txt || exit 1
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL
iconv -f utf-8 -t ascii//TRANSLIT < doc/faq.txt > FAQ
./configure && make chrony.txt || exit 1
mv chrony.txt chrony.txt_
make distclean
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
rm -f make_release .gitignore
cd ..
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz

135
mkdirpp.c
View File

@@ -1,135 +0,0 @@
/*
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 "memory.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

@@ -49,7 +49,9 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai;
int i, result;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
@@ -99,6 +101,8 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
return DNS_Failure;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
host = gethostbyname(name);
if (host == NULL) {

View File

@@ -39,6 +39,9 @@ typedef enum {
/* Resolve names only to selected address family */
extern void DNS_SetAddressFamily(int family);
/* Maximum number of addresses returned by DNS_Name2IPAddress */
#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);

View File

@@ -31,20 +31,19 @@
#include "nameserv_async.h"
#include "logging.h"
#include "memory.h"
#include "privops.h"
#include "sched.h"
#include "util.h"
#ifdef USE_PTHREAD_ASYNCDNS
#include <pthread.h>
#define MAX_ADDRESSES 16
/* ================================================== */
struct DNS_Async_Instance {
const char *name;
DNS_Status status;
IPAddr addresses[MAX_ADDRESSES];
IPAddr addresses[DNS_MAX_ADDRESSES];
DNS_NameResolveHandler handler;
void *arg;
@@ -61,7 +60,7 @@ start_resolving(void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
inst->status = DNS_Name2IPAddress(inst->name, inst->addresses, MAX_ADDRESSES);
inst->status = PRV_Name2IPAddress(inst->name, inst->addresses, DNS_MAX_ADDRESSES);
/* Notify the main thread that the result is ready */
if (write(inst->pipe[1], "", 1) < 0)
@@ -88,7 +87,7 @@ end_resolving(void *anything)
close(inst->pipe[0]);
close(inst->pipe[1]);
for (i = 0; inst->status == DNS_Success && i < MAX_ADDRESSES &&
for (i = 0; inst->status == DNS_Success && i < DNS_MAX_ADDRESSES &&
inst->addresses[i].family != IPADDR_UNSPEC; i++)
;

View File

@@ -34,8 +34,7 @@
typedef void (*DNS_NameResolveHandler)(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *anything);
/* Request resolving of a name to IP address. The handler will be
called when the result is available, but it may be also called
directly from this function call. */
called when the result is available. */
extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything);
#endif

11
ntp.h
View File

@@ -53,8 +53,8 @@ typedef uint32_t NTP_int32;
#define NTP_MAX_EXTENSIONS_LENGTH 1024
/* The minimum and maximum supported length of MAC */
#define NTP_MIN_MAC_LENGTH 16
#define NTP_MAX_MAC_LENGTH MAX_HASH_LENGTH
#define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* Type definition for leap bits */
typedef enum {
@@ -91,7 +91,7 @@ typedef struct {
/* Optional message authentication code (MAC) */
NTP_int32 auth_keyid;
uint8_t auth_data[NTP_MAX_MAC_LENGTH];
uint8_t auth_data[NTP_MAX_MAC_LENGTH - 4];
} NTP_Packet;
#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid)
@@ -109,4 +109,9 @@ typedef struct {
#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 */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2014
* 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
@@ -71,10 +71,8 @@ struct NCR_Instance_Record {
(client/server or symmetric active peer) */
OperatingMode opmode; /* Whether we are sampling this source
or not and in what way */
int timer_running; /* Boolean indicating whether we have a timeout
pending to transmit to the source */
SCH_TimeoutID timeout_id; /* Scheduler's timeout ID, if we are
running on a timer. */
SCH_TimeoutID rx_timeout_id; /* Timeout ID for latest received response */
SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */
int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_offline; /* If 1, automatically go offline if server/peer
@@ -217,6 +215,9 @@ static ARR_Instance broadcasts;
/* Invalid stratum number */
#define NTP_INVALID_STRATUM 0
/* Maximum allowed time for server to process client packet */
#define MAX_SERVER_INTERVAL 4.0
/* Minimum and maximum allowed poll interval */
#define MIN_POLL 0
#define MAX_POLL 24
@@ -285,8 +286,8 @@ do_time_checks(void)
NTP_int64 ntv1, ntv2;
int r;
UTI_TimevalToInt64(&tv1, &ntv1, 0);
UTI_TimevalToInt64(&tv2, &ntv2, 0);
UTI_TimevalToInt64(&tv1, &ntv1, NULL);
UTI_TimevalToInt64(&tv2, &ntv2, NULL);
UTI_Int64ToTimeval(&ntv1, &tv1);
UTI_Int64ToTimeval(&ntv2, &tv2);
@@ -316,7 +317,7 @@ NCR_Initialise(void)
do_time_checks();
logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements",
" Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp.")
" Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp.")
: -1;
access_auth_table = ADF_CreateTable();
@@ -353,20 +354,20 @@ restart_timeout(NCR_Instance inst, double delay)
{
/* Check if we can transmit */
if (inst->tx_suspended) {
assert(!inst->timer_running);
assert(!inst->tx_timeout_id);
return;
}
/* Stop old timer if running */
if (inst->timer_running)
SCH_RemoveTimeout(inst->timeout_id);
/* Stop both rx and tx timers if running */
SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0;
SCH_RemoveTimeout(inst->tx_timeout_id);
/* Start new timer for transmission */
inst->timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
inst->timer_running = 1;
inst->tx_timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
}
/* ================================================== */
@@ -374,14 +375,28 @@ restart_timeout(NCR_Instance inst, double delay)
static void
start_initial_timeout(NCR_Instance inst)
{
if (!inst->timer_running) {
double delay, last_tx;
struct timeval now;
if (!inst->tx_timeout_id) {
/* This will be the first transmission after mode change */
/* Mark source active */
SRC_SetActive(inst->source);
}
restart_timeout(inst, INITIAL_DELAY);
/* In case the offline period was too short, adjust the delay to keep
the interval between packets at least as long as the current polling
interval */
SCH_GetLastEventTime(&now, NULL, NULL);
UTI_DiffTimevalsToDouble(&last_tx, &now, &inst->local_tx);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
if (delay < INITIAL_DELAY)
delay = INITIAL_DELAY;
restart_timeout(inst, delay);
}
/* ================================================== */
@@ -393,6 +408,9 @@ close_client_socket(NCR_Instance inst)
NIO_CloseClientSocket(inst->local_addr.sock_fd);
inst->local_addr.sock_fd = INVALID_SOCK_FD;
}
SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0;
}
/* ================================================== */
@@ -401,10 +419,9 @@ static void
take_offline(NCR_Instance inst)
{
inst->opmode = MD_OFFLINE;
if (inst->timer_running) {
SCH_RemoveTimeout(inst->timeout_id);
inst->timer_running = 0;
}
SCH_RemoveTimeout(inst->tx_timeout_id);
inst->tx_timeout_id = 0;
/* Mark source unreachable */
SRC_ResetReachability(inst->source);
@@ -480,22 +497,29 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->do_auth = 1;
result->auth_key_id = params->authkey;
if (!KEY_KeyKnown(result->auth_key_id)) {
LOG(LOGS_WARN, LOGF_NtpCore, "Source %s added with unknown key %"PRIu32,
UTI_IPToString(&result->remote_addr.ip_addr), result->auth_key_id);
LOG(LOGS_WARN, LOGF_NtpCore, "Key %"PRIu32" used by source %s is %s",
result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
"missing");
} else if (!KEY_CheckKeyLength(result->auth_key_id)) {
LOG(LOGS_WARN, LOGF_NtpCore, "Key %"PRIu32" used by source %s is %s",
result->auth_key_id, UTI_IPToString(&result->remote_addr.ip_addr),
"too short");
}
}
/* Create a source instance for this NTP source */
result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr),
SRC_NTP, params->sel_option,
SRC_NTP, params->sel_options,
&result->remote_addr.ip_addr,
params->min_samples, params->max_samples);
result->timer_running = 0;
result->timeout_id = 0;
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll;
result->local_tx.tv_sec = 0;
result->local_tx.tv_usec = 0;
NCR_ResetInstance(result);
@@ -553,8 +577,6 @@ NCR_ResetInstance(NCR_Instance instance)
instance->remote_orig.lo = 0;
instance->local_rx.tv_sec = 0;
instance->local_rx.tv_usec = 0;
instance->local_tx.tv_sec = 0;
instance->local_tx.tv_usec = 0;
instance->local_ntp_tx.hi = 0;
instance->local_ntp_tx.lo = 0;
@@ -562,7 +584,7 @@ NCR_ResetInstance(NCR_Instance instance)
instance->local_poll = instance->minpoll;
/* The timer was set with a longer poll interval, restart it */
if (instance->timer_running)
if (instance->tx_timeout_id)
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
}
}
@@ -580,6 +602,7 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr)
close_client_socket(inst);
else {
NIO_CloseServerSocket(inst->local_addr.sock_fd);
inst->local_addr.ip_addr.family = IPADDR_UNSPEC;
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
}
@@ -738,6 +761,22 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
return delay_time;
}
/* ================================================== */
/* Timeout handler for closing the client socket when no acceptable
reply can be received from the server */
static void
receive_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance)arg;
DEBUG_LOG(LOGF_NtpCore, "Receive timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
inst->rx_timeout_id = 0;
close_client_socket(inst);
}
/* ================================================== */
static int
@@ -762,13 +801,14 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
)
{
NTP_Packet message;
int leap, auth_len, length, ret;
int auth_len, length, ret, precision;
struct timeval local_receive, local_transmit;
NTP_int64 ts_fuzz;
/* Parameters read from reference module */
int are_we_synchronised, our_stratum, smooth_time;
NTP_Leap leap_status;
uint32_t our_ref_id, ts_fuzz;
uint32_t our_ref_id;
struct timeval our_ref_time;
double our_root_delay, our_root_dispersion, smooth_offset;
@@ -777,38 +817,50 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
version = NTP_VERSION;
}
/* This is accurate enough and cheaper than calling LCL_ReadCookedTime.
A more accurate time stamp will be taken later in this function. */
SCH_GetLastEventTime(&local_transmit, NULL, NULL);
smooth_time = 0;
smooth_offset = 0.0;
REF_GetReferenceParams(&local_transmit,
&are_we_synchronised, &leap_status,
&our_stratum,
&our_ref_id, &our_ref_time,
&our_root_delay, &our_root_dispersion);
/* Get current smoothing offset when sending packet to a client */
if (SMT_IsEnabled() && (my_mode == MODE_SERVER || my_mode == MODE_BROADCAST)) {
smooth_time = 1;
smooth_offset = SMT_GetOffset(&local_transmit);
/* Suppress leap second when smoothing and slew mode are enabled */
if (REF_GetLeapMode() == REF_LeapModeSlew &&
(leap_status == LEAP_InsertSecond || leap_status == LEAP_DeleteSecond))
leap_status = LEAP_Normal;
if (my_mode == MODE_CLIENT) {
/* Don't reveal local time or state of the clock in client packets */
precision = 32;
leap_status = our_stratum = our_ref_id = 0;
our_ref_time.tv_sec = our_ref_time.tv_usec = 0;
our_root_delay = our_root_dispersion = 0.0;
} else {
smooth_time = 0;
smooth_offset = 0.0;
/* This is accurate enough and cheaper than calling LCL_ReadCookedTime.
A more accurate timestamp will be taken later in this function. */
SCH_GetLastEventTime(&local_transmit, NULL, NULL);
REF_GetReferenceParams(&local_transmit,
&are_we_synchronised, &leap_status,
&our_stratum,
&our_ref_id, &our_ref_time,
&our_root_delay, &our_root_dispersion);
/* Get current smoothing offset when sending packet to a client */
if (SMT_IsEnabled() && (my_mode == MODE_SERVER || my_mode == MODE_BROADCAST)) {
smooth_offset = SMT_GetOffset(&local_transmit);
smooth_time = fabs(smooth_offset) > LCL_GetSysPrecisionAsQuantum();
/* Suppress leap second when smoothing and slew mode are enabled */
if (REF_GetLeapMode() == REF_LeapModeSlew &&
(leap_status == LEAP_InsertSecond || leap_status == LEAP_DeleteSecond))
leap_status = LEAP_Normal;
}
precision = LCL_GetSysPrecisionAsLog();
}
if (are_we_synchronised) {
leap = (int) leap_status;
if (smooth_time) {
our_ref_id = NTP_REFID_SMOOTH;
UTI_AddDoubleToTimeval(&our_ref_time, smooth_offset, &our_ref_time);
UTI_AddDoubleToTimeval(local_rx, smooth_offset, &local_receive);
} else {
leap = LEAP_Unsynchronised;
local_receive = *local_rx;
}
/* Generate transmit packet */
message.lvm = NTP_LVM(leap, version, my_mode);
message.lvm = NTP_LVM(leap_status, version, my_mode);
/* Stratum 16 and larger are invalid */
if (our_stratum < NTP_MAX_STRATUM) {
message.stratum = our_stratum;
@@ -817,37 +869,33 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
}
message.poll = my_poll;
message.precision = LCL_GetSysPrecisionAsLog();
message.precision = precision;
/* 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);
message.reference_id = htonl(our_ref_id);
/* Now fill in timestamps */
if (smooth_time) {
UTI_AddDoubleToTimeval(&our_ref_time, smooth_offset, &our_ref_time);
UTI_AddDoubleToTimeval(local_rx, smooth_offset, &local_receive);
} else {
local_receive = *local_rx;
}
UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, 0);
UTI_TimevalToInt64(&our_ref_time, &message.reference_ts, NULL);
/* Originate - this comes from the last packet the source sent us */
message.originate_ts = *orig_ts;
/* Prepare random bits which will be added to the receive timestamp */
UTI_GetInt64Fuzz(&ts_fuzz, precision);
/* Receive - this is when we received the last packet from the source.
This timestamp will have been adjusted so that it will now look to
the source like we have been running on our latest estimate of
frequency all along */
UTI_TimevalToInt64(&local_receive, &message.receive_ts, 0);
UTI_TimevalToInt64(&local_receive, &message.receive_ts, &ts_fuzz);
/* Prepare random bits which will be added to the transmit timestamp. */
ts_fuzz = UTI_GetNTPTsFuzz(message.precision);
UTI_GetInt64Fuzz(&ts_fuzz, precision);
/* Transmit - this our local time right now! Also, we might need to
store this for our own use later, next time we receive a message
@@ -865,7 +913,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
take to generate the authentication data. */
local_transmit.tv_usec += KEY_GetAuthDelay(key_id);
UTI_NormaliseTimeval(&local_transmit);
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, &ts_fuzz);
auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
offsetof(NTP_Packet, auth_keyid),
@@ -885,7 +933,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
message.auth_keyid = 0;
length += sizeof (message.auth_keyid);
}
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, &ts_fuzz);
}
ret = NIO_SendPacket(&message, where_to, from, length);
@@ -908,9 +956,10 @@ static void
transmit_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance) arg;
NTP_Local_Address local_addr;
int sent;
inst->timer_running = 0;
inst->tx_timeout_id = 0;
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
@@ -943,6 +992,10 @@ transmit_timeout(void *arg)
inst->local_addr.sock_fd = NIO_OpenClientSocket(&inst->remote_addr);
}
/* Don't require the packet to be sent from the same address as before */
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = inst->local_addr.sock_fd;
/* Check whether we need to 'warm up' the link to the other end by
sending an NTP exchange to ensure both ends' ARP caches are
primed. On loaded systems this might also help ensure that bits
@@ -956,7 +1009,7 @@ transmit_timeout(void *arg)
as the reply will be ignored */
transmit_packet(MODE_CLIENT, inst->local_poll, inst->version, 0, 0,
&inst->remote_orig, &inst->local_rx, NULL, NULL,
&inst->remote_addr, &inst->local_addr);
&inst->remote_addr, &local_addr);
inst->presend_done = 1;
@@ -974,7 +1027,7 @@ transmit_timeout(void *arg)
&inst->remote_orig,
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
&inst->remote_addr,
&inst->local_addr);
&local_addr);
++inst->tx_count;
@@ -1007,8 +1060,14 @@ transmit_timeout(void *arg)
/* Restart timer for this message */
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
}
/* If a client packet was just sent, schedule a timeout to close the socket
at the time when all server replies would fail the delay test, so the
socket is not open for longer than necessary */
if (inst->mode == MODE_CLIENT)
inst->rx_timeout_id = SCH_AddTimeoutByDelay(inst->max_delay + MAX_SERVER_INTERVAL,
receive_timeout, (void *)inst);
}
/* ================================================== */
@@ -1098,7 +1157,7 @@ static int
receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance inst, NTP_Local_Address *local_addr, int length)
{
int pkt_leap;
uint32_t pkt_refid;
uint32_t pkt_refid, pkt_key_id;
double pkt_root_delay;
double pkt_root_dispersion;
@@ -1125,7 +1184,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* These are the timeval equivalents of the remote epochs */
struct timeval remote_receive_tv, remote_transmit_tv;
struct timeval remote_reference_tv;
struct timeval local_average, remote_average;
double local_interval, remote_interval;
@@ -1164,7 +1222,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
UTI_Int64ToTimeval(&message->receive_ts, &remote_receive_tv);
UTI_Int64ToTimeval(&message->transmit_ts, &remote_transmit_tv);
UTI_Int64ToTimeval(&message->reference_ts, &remote_reference_tv);
/* Check if the packet is valid per RFC 5905, section 8.
The test values are 1 when passed and 0 when failed. */
@@ -1182,18 +1239,19 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
association if not properly 'up'. */
test3 = (message->originate_ts.hi || message->originate_ts.lo) &&
(message->receive_ts.hi || message->receive_ts.lo) &&
(message->reference_ts.hi || message->reference_ts.lo) &&
(message->transmit_ts.hi || message->transmit_ts.lo);
/* Test 4 would check for denied access. It would always pass as this
function is called only for known sources. */
/* Test 5 checks for authentication failure. If we expect authenticated info
from this peer/server and the packet doesn't have it or the authentication
is bad, it's got to fail. If the peer or server sends us an authenticated
frame, but we're not bothered about whether he authenticates or not, just
ignore the test. */
test5 = inst->do_auth ? check_packet_auth(message, length, NULL, NULL) : 1;
from this peer/server and the packet doesn't have it, the authentication
is bad, or it's authenticated with a different key than expected, it's got
to fail. If we don't expect the packet to be authenticated, just ignore
the test. */
test5 = !inst->do_auth ||
(check_packet_auth(message, length, NULL, &pkt_key_id) &&
pkt_key_id == inst->auth_key_id);
/* Test 6 checks for unsynchronised server */
test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1201,10 +1259,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
message->stratum != NTP_INVALID_STRATUM;
/* Test 7 checks for bad data. The root distance must be smaller than a
defined maximum and the transmit time must not be before the time of
the last synchronisation update. */
test7 = pkt_root_delay / 2.0 + pkt_root_dispersion < NTP_MAX_DISPERSION &&
UTI_CompareTimevals(&remote_reference_tv, &remote_transmit_tv) < 1;
defined maximum. */
test7 = pkt_root_delay / 2.0 + pkt_root_dispersion < NTP_MAX_DISPERSION;
/* The packet is considered valid if the tests above passed */
valid_packet = test1 && test2 && test3 && test5 && test6 && test7;
@@ -1230,7 +1286,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
inst->local_ntp_tx.hi = inst->local_ntp_tx.lo = 0;
if (valid_packet) {
precision = LCL_GetSysPrecisionAsQuantum();
precision = LCL_GetSysPrecisionAsQuantum() +
UTI_Log2ToDouble(message->precision);
SRC_GetFrequencyRange(inst->source, &source_freq_lo, &source_freq_hi);
@@ -1269,9 +1326,10 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Additional tests required to pass before accumulating the sample */
/* Test A requires that the round trip delay is less than an
administrator-defined value */
testA = delay <= inst->max_delay;
/* Test A requires that the peer delay is not larger than the configured
maximum and in client mode also that the server processing time is sane */
testA = delay <= inst->max_delay &&
(inst->mode != MODE_CLIENT || remote_interval <= MAX_SERVER_INTERVAL);
/* Test B requires that the ratio of the round trip delay to the
minimum one currently in the stats data register is less than an
@@ -1290,7 +1348,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Test D requires that the remote peer is not synchronised to us to
prevent a synchronisation loop */
testD = message->stratum <= 1 || pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
testD = message->stratum <= 1 || REF_GetMode() != REF_ModeNormal ||
pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
} else {
offset = delay = dispersion = 0.0;
sample_time = *now;
@@ -1324,24 +1383,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Reduce polling rate if KoD RATE was received */
if (kod_rate) {
if (message->poll > inst->minpoll) {
/* Set our minpoll to message poll, but use a reasonable maximum */
if (message->poll <= MAX_KOD_RATE_POLL)
inst->minpoll = message->poll;
else if (inst->minpoll < MAX_KOD_RATE_POLL)
inst->minpoll = MAX_KOD_RATE_POLL;
if (inst->minpoll > inst->maxpoll)
inst->maxpoll = inst->minpoll;
if (inst->minpoll > inst->local_poll)
inst->local_poll = inst->minpoll;
LOG(LOGS_WARN, LOGF_NtpCore,
"Received KoD RATE with poll %d from %s, minpoll set to %d",
message->poll, UTI_IPToString(&inst->remote_addr.ip_addr),
inst->minpoll);
}
/* Stop ongoing burst */
if (inst->opmode == MD_BURST_WAS_OFFLINE || inst->opmode == MD_BURST_WAS_ONLINE) {
inst->burst_good_samples_to_go = 0;
@@ -1366,8 +1407,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
&sample_time,
offset, delay, dispersion,
root_delay, root_dispersion,
message->stratum > inst->min_stratum ?
message->stratum : inst->min_stratum,
MAX(message->stratum, inst->min_stratum),
(NTP_Leap) pkt_leap);
SRC_SelectSource(inst->source);
@@ -1406,6 +1446,9 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
server and the socket can be closed */
close_client_socket(inst);
/* Update the local address */
inst->local_addr.ip_addr = local_addr->ip_addr;
requeue_transmit = 1;
}
@@ -1419,7 +1462,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
}
/* Get rid of old timeout and start a new one */
assert(inst->timer_running);
assert(inst->tx_timeout_id);
restart_timeout(inst, delay_time);
}
@@ -1479,7 +1522,7 @@ NCR_ProcessKnown
int length /* the length of the received packet */
)
{
int pkt_mode, proc_packet, proc_as_unknown, log_peer_access;
int pkt_mode, proc_packet, proc_as_unknown;
if (!check_packet_format(message, length))
return 0;
@@ -1487,7 +1530,6 @@ NCR_ProcessKnown
pkt_mode = NTP_LVM_TO_MODE(message->lvm);
proc_packet = 0;
proc_as_unknown = 0;
log_peer_access = 0;
/* Now, depending on the mode we decide what to do */
switch (pkt_mode) {
@@ -1495,7 +1537,6 @@ NCR_ProcessKnown
switch (inst->mode) {
case MODE_ACTIVE:
/* Ordinary symmetric peering */
log_peer_access = 1;
proc_packet = 1;
break;
case MODE_PASSIVE:
@@ -1518,7 +1559,6 @@ NCR_ProcessKnown
case MODE_ACTIVE:
/* This would arise if we have the remote configured as a peer and
he does not have us configured */
log_peer_access = 1;
proc_packet = 1;
break;
case MODE_PASSIVE:
@@ -1572,9 +1612,6 @@ NCR_ProcessKnown
break;
}
if (log_peer_access)
CLG_LogNTPPeerAccess(&inst->remote_addr.ip_addr, now->tv_sec);
if (proc_packet) {
/* Check if the reply was received by the socket that sent the request */
if (local_addr->sock_fd != inst->local_addr.sock_fd) {
@@ -1618,7 +1655,7 @@ NCR_ProcessUnknown
)
{
NTP_Mode pkt_mode, my_mode;
int has_auth, valid_auth;
int has_auth, valid_auth, log_index;
uint32_t key_id;
/* Ignore the packet if it wasn't received by server socket */
@@ -1644,12 +1681,10 @@ NCR_ProcessUnknown
case MODE_ACTIVE:
/* We are symmetric passive, even though we don't ever lock to him */
my_mode = MODE_PASSIVE;
CLG_LogNTPPeerAccess(&remote_addr->ip_addr, now->tv_sec);
break;
case MODE_CLIENT:
/* Reply with server packet */
my_mode = MODE_SERVER;
CLG_LogNTPClientAccess(&remote_addr->ip_addr, now->tv_sec);
break;
default:
/* Discard */
@@ -1657,6 +1692,14 @@ NCR_ProcessUnknown
return;
}
log_index = CLG_LogNTPAccess(&remote_addr->ip_addr, now);
/* Don't reply to all requests if the rate is excessive */
if (log_index >= 0 && CLG_LimitNTPResponseRate(log_index)) {
DEBUG_LOG(LOGF_NtpCore, "NTP packet discarded to limit response rate");
return;
}
/* Check if the packet includes MAC that authenticates properly */
valid_auth = check_packet_auth(message, length, &has_auth, &key_id);
@@ -1964,6 +2007,14 @@ NCR_GetRemoteAddress(NCR_Instance inst)
/* ================================================== */
uint32_t
NCR_GetLocalRefid(NCR_Instance inst)
{
return UTI_IPToRefid(&inst->local_addr.ip_addr);
}
/* ================================================== */
int NCR_IsSyncPeer(NCR_Instance inst)
{
return SRC_IsSyncPeer(inst->source);

View File

@@ -105,6 +105,8 @@ extern void NCR_IncrementActivityCounters(NCR_Instance inst, int *online, int *o
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 void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2014
* Copyright (C) Miroslav Lichvar 2009, 2013-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
@@ -37,6 +37,7 @@
#include "local.h"
#include "logging.h"
#include "conf.h"
#include "privops.h"
#include "util.h"
#define INVALID_SOCK_FD -1
@@ -97,8 +98,13 @@ prepare_socket(int family, int port_number, int client_only)
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
if (!client_only) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
} else {
DEBUG_LOG(LOGF_NtpIO, "Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
}
return INVALID_SOCK_FD;
}
@@ -156,21 +162,21 @@ prepare_socket(int family, int port_number, int client_only)
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_REUSEADDR");
/* Don't quit - we might survive anyway */
}
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_BROADCAST");
/* Don't quit - we might survive anyway */
}
#ifdef SO_TIMESTAMP
/* Enable receiving of timestamp control messages */
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");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "SO_TIMESTAMP");
/* Don't quit - we might survive anyway */
}
#endif
@@ -179,15 +185,15 @@ prepare_socket(int family, int port_number, int client_only)
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_FREEBIND");
}
#endif
if (family == AF_INET) {
#ifdef IP_PKTINFO
#ifdef HAVE_IN_PKTINFO
/* We want the local IP info on server sockets */
if (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");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IP_PKTINFO");
/* Don't quit - we might survive anyway */
}
#endif
@@ -197,26 +203,28 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_V6ONLY");
}
#endif
#ifdef HAVE_IN6_PKTINFO
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_RECVPKTINFO");
}
#elif defined(IPV6_PKTINFO)
#else
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPv6 packet info socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set %s socket option", "IPV6_PKTINFO");
}
#endif
#endif
}
#endif
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
return INVALID_SOCK_FD;
}
@@ -526,7 +534,7 @@ read_from_socket(void *anything)
local_addr.sock_fd = sock_fd;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
#ifdef IP_PKTINFO
#ifdef HAVE_IN_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi;
@@ -536,7 +544,7 @@ read_from_socket(void *anything)
}
#endif
#if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
#ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi;
@@ -624,7 +632,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
msg.msg_flags = 0;
cmsglen = 0;
#ifdef IP_PKTINFO
#ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
struct cmsghdr *cmsg;
struct in_pktinfo *ipi;
@@ -642,7 +650,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Lo
}
#endif
#if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
#ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) {
struct cmsghdr *cmsg;
struct in6_pktinfo *ipi;

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014
* Copyright (C) Miroslav Lichvar 2011-2012, 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
@@ -49,14 +49,15 @@ typedef struct {
NTP_Remote_Address *remote_addr; /* The address of this source, non-NULL
means this slot in table is in use */
NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source, may be NULL */
int pool; /* Number of the pool from which was this source
added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response
yet and the source may be removed if other
sources from the pool respond first */
received from the source yet */
} SourceRecord;
/* Hash table of SourceRecord, the size should be a power of two */
/* Hash table of SourceRecord, its size is a power of two and it's never
more than half full */
static ARR_Instance records;
/* Number of sources in the hash table */
@@ -69,6 +70,7 @@ static int auto_start_sources = 0;
struct UnresolvedSource {
char *name;
int port;
int random_order;
int replacement;
union {
struct {
@@ -85,6 +87,7 @@ struct UnresolvedSource {
#define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9
#define MIN_REPLACEMENT_INTERVAL 8
static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0;
@@ -92,15 +95,11 @@ static SCH_TimeoutID resolving_id;
static struct UnresolvedSource *resolving_source = NULL;
static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
#define MIN_POOL_RESOLVE_INTERVAL 5
#define MAX_POOL_SOURCES 16
#define INVALID_POOL (-1)
/* Pool of sources, the name is expected to resolve to multiple addresses
which change over time */
/* Pool of sources with the same name */
struct SourcePool {
char *name;
int port;
/* Number of sources added from this pool (ignoring tentative sources) */
int sources;
/* Maximum number of sources */
@@ -115,6 +114,7 @@ static ARR_Instance pools;
static void resolve_sources(void *arg);
static void rehash_records(void);
static void clean_source_record(SourceRecord *record);
static void
slew_sources(struct timeval *raw,
@@ -162,16 +162,12 @@ NSR_Finalise(void)
struct UnresolvedSource *us;
unsigned int i;
for (i = 0; i < ARR_GetSize(pools); i++)
Free(((struct SourcePool *)ARR_GetElement(pools, i))->name);
ARR_DestroyInstance(pools);
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr)
continue;
record->remote_addr = NULL;
NCR_DestroyInstance(record->data);
if (record->remote_addr)
clean_source_record(record);
}
ARR_DestroyInstance(records);
@@ -207,26 +203,16 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
uint32_t hash;
unsigned int i, size;
unsigned short port;
uint8_t *ip6;
size = ARR_GetSize(records);
switch (remote_addr->ip_addr.family) {
case IPADDR_INET6:
ip6 = remote_addr->ip_addr.addr.in6;
hash = (ip6[0] ^ ip6[4] ^ ip6[8] ^ ip6[12]) |
(ip6[1] ^ ip6[5] ^ ip6[9] ^ ip6[13]) << 8 |
(ip6[2] ^ ip6[6] ^ ip6[10] ^ ip6[14]) << 16 |
(ip6[3] ^ ip6[7] ^ ip6[11] ^ ip6[15]) << 24;
break;
case IPADDR_INET4:
hash = remote_addr->ip_addr.addr.in4;
break;
default:
*found = *slot = 0;
return;
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6) {
*found = *slot = 0;
return;
}
hash = UTI_IPToHash(&remote_addr->ip_addr);
port = remote_addr->port;
for (i = 0; i < size / 2; i++) {
@@ -248,12 +234,12 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
}
/* ================================================== */
/* Check if hash table of given size is sufficient to contain sources */
static int
check_hashtable_size(unsigned int sources, unsigned int size)
{
return sources * 2 + 1 < size;
return sources * 2 <= size;
}
/* ================================================== */
@@ -271,7 +257,7 @@ rehash_records(void)
memcpy(temp_records, ARR_GetElements(records), old_size * sizeof (SourceRecord));
/* The size of the hash table is always a power of two */
for (new_size = 4; !check_hashtable_size(n_sources, new_size); new_size *= 2)
for (new_size = 1; !check_hashtable_size(n_sources, new_size); new_size *= 2)
;
ARR_SetSize(records, new_size);
@@ -296,7 +282,7 @@ rehash_records(void)
/* Procedure to add a new source */
static NSR_Status
add_source(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params, int pool)
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, SourceParameters *params, int pool)
{
SourceRecord *record;
int slot, found;
@@ -323,8 +309,9 @@ add_source(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParamete
record = get_record(slot);
record->data = NCR_GetInstance(remote_addr, type, params);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->name = name ? Strdup(name) : NULL;
record->pool = pool;
record->tentative = pool != INVALID_POOL ? 1 : 0;
record->tentative = 1;
if (auto_start_sources)
NCR_StartInstance(record->data);
@@ -341,6 +328,7 @@ replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{
int slot1, slot2, found;
SourceRecord *record;
struct SourcePool *pool;
find_slot(old_addr, &slot1, &found);
if (!found)
@@ -354,6 +342,15 @@ replace_source(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
NCR_ChangeRemoteAddress(record->data, new_addr);
record->remote_addr = NCR_GetRemoteAddress(record->data);
if (!record->tentative) {
record->tentative = 1;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources--;
}
}
/* The hash table must be rebuilt for the new address */
rehash_records();
@@ -371,18 +368,22 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
{
NTP_Remote_Address address;
int i, added;
unsigned short first = 0;
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
for (i = added = 0; i < n_addrs; i++) {
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %s", us->name, UTI_IPToString(&ip_addrs[i]));
address.ip_addr = ip_addrs[i];
address.ip_addr = ip_addrs[((unsigned int)i + first) % n_addrs];
address.port = us->port;
DEBUG_LOG(LOGF_NtpSources, "(%d) %s", i + 1, UTI_IPToString(&address.ip_addr));
if (us->replacement) {
if (replace_source(&us->replace_source, &address) != NSR_AlreadyInUse)
break;
} else {
if (add_source(&address, us->new_source.type, &us->new_source.params,
if (add_source(&address, us->name, us->new_source.type, &us->new_source.params,
us->new_source.pool) == NSR_Success)
added++;
@@ -403,6 +404,8 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
assert(us == resolving_source);
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %d addrs", us->name, n_addrs);
switch (status) {
case DNS_TryAgain:
break;
@@ -495,7 +498,7 @@ append_unresolved_source(struct UnresolvedSource *us)
NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params)
{
return add_source(remote_addr, type, params, INVALID_POOL);
return add_source(remote_addr, NULL, type, params, INVALID_POOL);
}
/* ================================================== */
@@ -505,10 +508,20 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, Source
{
struct UnresolvedSource *us;
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
/* If the name is an IP address, don't bother with full resolving now
or later when trying to replace the source */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
NSR_AddSource(&remote_addr, type, params);
return;
}
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->port = port;
us->random_order = 0;
us->replacement = 0;
us->new_source.type = type;
us->new_source.params = *params;
@@ -518,8 +531,6 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, Source
us->new_source.max_new_sources = 1;
} else {
sp = (struct SourcePool *)ARR_GetNewElement(pools);
sp->name = Strdup(name);
sp->port = port;
sp->sources = 0;
sp->max_sources = params->max_sources;
us->new_source.pool = ARR_GetSize(pools) - 1;
@@ -587,6 +598,8 @@ clean_source_record(SourceRecord *record)
assert(record->remote_addr);
record->remote_addr = NULL;
NCR_DestroyInstance(record->data);
if (record->name)
Free(record->name);
n_sources--;
}
@@ -640,15 +653,23 @@ NSR_RemoveAllSources(void)
/* ================================================== */
static void
resolve_pool_replacement(struct SourcePool *sp, NTP_Remote_Address *addr)
resolve_source_replacement(SourceRecord *record)
{
struct UnresolvedSource *us;
DEBUG_LOG(LOGF_NtpSources, "trying to replace %s",
UTI_IPToString(&record->remote_addr->ip_addr));
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(sp->name);
us->port = sp->port;
us->name = Strdup(record->name);
us->port = record->remote_addr->port;
/* If there never was a valid reply from this source (e.g. it was a bad
replacement), ignore the order of addresses from the resolver to not get
stuck to a pair of addresses if the order doesn't change, or a group of
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
us->random_order = record->tentative;
us->replacement = 1;
us->replace_source = *addr;
us->replace_source = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
@@ -662,32 +683,50 @@ NSR_HandleBadSource(IPAddr *address)
static struct timeval last_replacement;
struct timeval now;
NTP_Remote_Address remote_addr;
struct SourcePool *pool;
int pool_index, slot, found;
SourceRecord *record;
int slot, found;
double diff;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
/* Only sources from a pool can be replaced */
find_slot(&remote_addr, &slot, &found);
if (!found || (pool_index = get_record(slot)->pool) == INVALID_POOL)
if (!found)
return;
pool = (struct SourcePool *)ARR_GetElement(pools, pool_index);
record = get_record(slot);
/* Don't resolve the pool name too frequently */
/* Only sources with a name can be replaced */
if (!record->name)
return;
/* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&diff, &now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_POOL_RESOLVE_INTERVAL)) {
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG(LOGF_NtpSources, "replacement postponed");
return;
}
last_replacement = now;
DEBUG_LOG(LOGF_NtpSources, "pool replacement for %s", UTI_IPToString(address));
resolve_source_replacement(record);
}
resolve_pool_replacement(pool, &remote_addr);
/* ================================================== */
void
NSR_RefreshAddresses(void)
{
SourceRecord *record;
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || !record->name)
continue;
resolve_source_replacement(record);
}
}
/* ================================================== */
@@ -714,6 +753,26 @@ static void remove_tentative_pool_sources(int pool)
rehash_records();
}
/* ================================================== */
uint32_t
NSR_GetLocalRefid(IPAddr *address)
{
NTP_Remote_Address remote_addr;
int slot, found;
remote_addr.ip_addr = *address;
remote_addr.port = 0;
find_slot(&remote_addr, &slot, &found);
if (!found)
return 0;
return NCR_GetLocalRefid(get_record(slot)->data);
}
/* ================================================== */
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void
@@ -733,20 +792,21 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
return;
if (record->tentative) {
/* First reply from a pool source */
/* This was the first valid reply from the source */
record->tentative = 0;
assert(record->pool != INVALID_POOL);
pool = (struct SourcePool *)ARR_GetElement(pools, record->pool);
pool->sources++;
if (record->pool != INVALID_POOL) {
pool = ARR_GetElement(pools, record->pool);
pool->sources++;
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
pool->name, pool->sources);
DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
record->name, pool->sources);
/* If the number of sources reached the configured maximum, remove
the tentative sources added from this pool */
if (pool->sources >= pool->max_sources)
remove_tentative_pool_sources(record->pool);
/* If the number of sources from the pool reached the configured
maximum, remove the remaining tentative sources */
if (pool->sources >= pool->max_sources)
remove_tentative_pool_sources(record->pool);
}
}
} else {
NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);

View File

@@ -80,6 +80,12 @@ 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 */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);

View File

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

689
privops.c Normal file
View File

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

71
privops.h Normal file
View File

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

View File

@@ -87,7 +87,7 @@ struct RCL_Instance_Record {
SRC_Instance source;
};
/* Array of RCL_Instance_Record */
/* Array of pointers to RCL_Instance_Record */
static ARR_Instance refclocks;
static LOG_FileID logfileid;
@@ -114,13 +114,13 @@ 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);
return *(RCL_Instance *)ARR_GetElement(refclocks, index);
}
void
RCL_Initialise(void)
{
refclocks = ARR_CreateInstance(sizeof (struct RCL_Instance_Record));
refclocks = ARR_CreateInstance(sizeof (RCL_Instance));
CNF_AddRefclocks();
@@ -148,6 +148,7 @@ RCL_Finalise(void)
filter_fini(&inst->filter);
Free(inst->driver_parameter);
SRC_DestroyInstance(inst->source);
Free(inst);
}
if (ARR_GetSize(refclocks) > 0) {
@@ -162,8 +163,10 @@ int
RCL_AddRefclock(RefclockParameters *params)
{
int pps_source = 0;
RCL_Instance inst;
RCL_Instance inst = ARR_GetNewElement(refclocks);
inst = MallocNew(struct RCL_Instance_Record);
*(RCL_Instance *)ARR_GetNewElement(refclocks) = inst;
if (strcmp(params->driver_name, "SHM") == 0) {
inst->driver = &RCL_SHM_driver;
@@ -233,7 +236,7 @@ RCL_AddRefclock(RefclockParameters *params)
if (index >= 10)
ref[2] = (index / 10) % 10 + '0';
inst->ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
}
if (inst->driver->poll) {
@@ -260,7 +263,7 @@ RCL_AddRefclock(RefclockParameters *params)
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_option, NULL,
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples);
DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d",
@@ -372,8 +375,6 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
!valid_sample_time(instance, sample_time))
return 0;
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
switch (leap) {
case LEAP_Normal:
case LEAP_InsertSecond:
@@ -381,10 +382,11 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
instance->leap_status = leap;
break;
default:
instance->leap_status = LEAP_Unsynchronised;
break;
DEBUG_LOG(LOGF_Refclock, "refclock sample ignored bad leap %d", leap);
return 0;
}
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
@@ -471,15 +473,16 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
double root_delay, root_dispersion, distance;
uint32_t ref_id;
/* Ignore the pulse if we are not well synchronized */
/* Ignore the pulse if we are not well synchronized and the local
reference is not active */
REF_GetReferenceParams(&cooked_time, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
distance = fabs(root_delay) / 2 + root_dispersion;
if (!is_synchronised || distance >= 0.5 / rate) {
if (leap == LEAP_Unsynchronised || distance >= 0.5 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
second, is_synchronised, distance);
second, leap != LEAP_Unsynchronised, distance);
/* Drop also all stored samples */
filter_reset(&instance->filter);
return 0;
@@ -499,15 +502,6 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
return 1;
}
static double
poll_interval(int poll)
{
if (poll >= 0)
return 1 << poll;
else
return 1.0 / (1 << -poll);
}
static int
valid_sample_time(RCL_Instance instance, struct timeval *tv)
{
@@ -516,7 +510,7 @@ valid_sample_time(RCL_Instance instance, struct timeval *tv)
LCL_ReadRawTime(&raw_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, "%s refclock sample not valid age=%.6f tv=%s",
UTI_RefidToString(instance->ref_id), diff, UTI_TimevalToString(tv));
return 0;
@@ -538,9 +532,10 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
REF_GetReferenceParams(tv, &is_synchronised, &leap, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
/* Don't change our stratum if local stratum is active
/* Don't change our stratum if the local reference is active
or this is the current source */
if (ref_id == instance->ref_id || REF_IsLocalActive())
if (ref_id == instance->ref_id ||
(!is_synchronised && leap != LEAP_Unsynchronised))
return stratum - 1;
/* Or the current source is another PPS refclock */
@@ -595,7 +590,7 @@ poll_timeout(void *arg)
}
}
inst->timeout_id = SCH_AddTimeoutByDelay(poll_interval(poll), poll_timeout, arg);
inst->timeout_id = SCH_AddTimeoutByDelay(UTI_Log2ToDouble(poll), poll_timeout, arg);
}
static void

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2014
* 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
@@ -45,6 +45,8 @@
static int are_we_synchronised;
static int enable_local_stratum;
static int local_stratum;
static int local_orphan;
static double local_distance;
static NTP_Leap our_leap_status;
static int our_leap_sec;
static int our_stratum;
@@ -80,8 +82,7 @@ static int max_offset_delay;
static int max_offset_ignore;
static double max_offset;
/* Flag and threshold for logging clock changes to syslog */
static int do_log_change;
/* Threshold for logging clock changes to syslog */
static double log_change_threshold;
/* Flag, threshold and user for sending mail notification on large clock changes */
@@ -106,7 +107,6 @@ static REF_LeapMode leap_mode;
static int leap_in_progress;
/* Timer for the leap second handler */
static int leap_timer_running;
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
@@ -120,11 +120,6 @@ static LOG_FileID logfileid;
/* ================================================== */
/* Reference ID supplied when we are locally referenced */
#define LOCAL_REFERENCE_ID 0x7f7f0101UL
/* ================================================== */
/* Exponential moving averages of absolute clock frequencies
used as a fallback when synchronisation is lost. */
@@ -237,9 +232,9 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
leap_timer_running = 0;
leap_timeout_id = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
/* Switch to step mode if the system driver doesn't support leap */
@@ -248,9 +243,9 @@ REF_Initialise(void)
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2008 and Dec 31 2008 */
if (get_tz_leap(1214784000) == LEAP_Normal &&
get_tz_leap(1230681600) == LEAP_InsertSecond) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400) == LEAP_InsertSecond &&
get_tz_leap(1356912000) == LEAP_Normal) {
LOG(LOGS_INFO, LOGF_Reference, "Using %s timezone to obtain leap second data", leap_tzname);
} else {
LOG(LOGS_WARN, LOGF_Reference, "Timezone %s failed leap second check, ignoring", leap_tzname);
@@ -260,8 +255,8 @@ REF_Initialise(void)
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
CNF_GetLogChange(&do_log_change, &log_change_threshold);
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
log_change_threshold = CNF_GetLogChange();
CNF_GetFallbackDrifts(&fb_drift_min, &fb_drift_max);
@@ -269,7 +264,7 @@ REF_Initialise(void)
fb_drifts = MallocArray(struct fb_drift, fb_drift_max - fb_drift_min + 1);
memset(fb_drifts, 0, sizeof (struct fb_drift) * (fb_drift_max - fb_drift_min + 1));
next_fb_drift = 0;
fb_drift_timeout_id = -1;
fb_drift_timeout_id = 0;
}
last_ref_update.tv_sec = 0;
@@ -278,11 +273,6 @@ REF_Initialise(void)
LCL_AddParameterChangeHandler(handle_slew, NULL);
/* And just to prevent anything wierd ... */
if (do_log_change) {
log_change_threshold = fabs(log_change_threshold);
}
/* Make first entry in tracking log */
REF_SetUnsynchronised();
}
@@ -433,10 +423,8 @@ update_fb_drifts(double freq_ppm, double update_interval)
next_fb_drift = 0;
}
if (fb_drift_timeout_id != -1) {
SCH_RemoveTimeout(fb_drift_timeout_id);
fb_drift_timeout_id = -1;
}
SCH_RemoveTimeout(fb_drift_timeout_id);
fb_drift_timeout_id = 0;
if (update_interval < 1.0 || update_interval > last_ref_update_interval * 4.0)
return;
@@ -469,7 +457,7 @@ fb_drift_timeout(void *arg)
{
assert(next_fb_drift >= fb_drift_min && next_fb_drift <= fb_drift_max);
fb_drift_timeout_id = -1;
fb_drift_timeout_id = 0;
DEBUG_LOG(LOGF_Reference, "Fallback drift %d active: %f ppm",
next_fb_drift, fb_drifts[next_fb_drift - fb_drift_min].freq);
@@ -486,7 +474,7 @@ schedule_fb_drift(struct timeval *now)
double unsynchronised;
struct timeval when;
if (fb_drift_timeout_id != -1)
if (fb_drift_timeout_id)
return; /* already scheduled */
UTI_DiffTimevalsToDouble(&unsynchronised, now, &last_ref_update);
@@ -544,8 +532,7 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset);
if (do_log_change &&
(abs_offset > log_change_threshold)) {
if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, LOGF_Reference,
"System clock wrong by %.6f seconds, adjustment started",
-offset);
@@ -691,7 +678,7 @@ get_tz_leap(time_t when)
static void
leap_end_timeout(void *arg)
{
leap_timer_running = 0;
leap_timeout_id = 0;
leap_in_progress = 0;
our_leap_sec = 0;
@@ -743,11 +730,9 @@ set_leap_timeout(time_t now)
struct timeval when;
/* Stop old timer if there is one */
if (leap_timer_running) {
SCH_RemoveTimeout(leap_timeout_id);
leap_timer_running = 0;
leap_in_progress = 0;
}
SCH_RemoveTimeout(leap_timeout_id);
leap_timeout_id = 0;
leap_in_progress = 0;
if (!our_leap_sec)
return;
@@ -765,7 +750,6 @@ set_leap_timeout(time_t now)
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
leap_timer_running = 1;
}
/* ================================================== */
@@ -794,7 +778,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
}
if (reset || (leap_sec != our_leap_sec && !REF_IsLeapSecondClose())) {
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
our_leap_sec = leap_sec;
switch (leap_mode) {
@@ -810,6 +794,8 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
assert(0);
break;
}
} else if (reset) {
set_leap_timeout(now);
}
our_leap_status = leap;
@@ -1109,12 +1095,10 @@ REF_SetManualReference
double skew
)
{
uint32_t manual_refid = 0x4D414E55; /* MANU */
/* We are not synchronised to an external source, as such. This is
only supposed to be used with the local source option, really
... */
REF_SetReference(0, LEAP_Unsynchronised, 1, manual_refid, NULL,
only supposed to be used with the local source option, really.
Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */
REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL,
ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
}
@@ -1175,32 +1159,40 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double elapsed;
double extra_dispersion;
double elapsed, dispersion;
assert(initialised);
if (are_we_synchronised) {
UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
dispersion = our_root_dispersion +
(our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
} else {
dispersion = 0.0;
}
/* Local reference is active when enabled and the clock is not synchronised
or the root distance exceeds the threshold */
if (are_we_synchronised &&
!(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
*is_synchronised = 1;
*stratum = our_stratum;
UTI_DiffTimevalsToDouble(&elapsed, local_time, &our_ref_time);
extra_dispersion = (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
*leap_status = !leap_in_progress ? our_leap_status : LEAP_Unsynchronised;
*ref_id = our_ref_id;
*ref_time = our_ref_time;
*root_delay = our_root_delay;
*root_dispersion = our_root_dispersion + extra_dispersion;
*root_dispersion = dispersion;
} else if (enable_local_stratum) {
*is_synchronised = 1;
*is_synchronised = 0;
*stratum = local_stratum;
*ref_id = LOCAL_REFERENCE_ID;
*ref_id = NTP_REFID_LOCAL;
/* Make the reference time be now less a second - this will
scarcely affect the client, but will ensure that the transmit
timestamp cannot come before this (which would cause test 7 to
@@ -1222,7 +1214,7 @@ REF_GetReferenceParams
*leap_status = LEAP_Unsynchronised;
*stratum = NTP_MAX_STRATUM;
*ref_id = 0;
*ref_id = NTP_REFID_UNSYNC;
ref_time->tv_sec = ref_time->tv_usec = 0;
/* These values seem to be standard for a client, and
any peer or client of ours will ignore them anyway because
@@ -1238,13 +1230,27 @@ REF_GetReferenceParams
int
REF_GetOurStratum(void)
{
if (are_we_synchronised) {
return our_stratum;
} else if (enable_local_stratum) {
return local_stratum;
} else {
struct timeval now_cooked, ref_time;
int synchronised, stratum;
NTP_Leap leap_status;
uint32_t ref_id;
double root_delay, root_dispersion;
SCH_GetLastEventTime(&now_cooked, NULL, NULL);
REF_GetReferenceParams(&now_cooked, &synchronised, &leap_status, &stratum,
&ref_id, &ref_time, &root_delay, &root_dispersion);
return stratum;
}
/* ================================================== */
int
REF_GetOrphanStratum(void)
{
if (!enable_local_stratum || !local_orphan || mode != REF_ModeNormal)
return NTP_MAX_STRATUM;
}
return local_stratum;
}
/* ================================================== */
@@ -1275,10 +1281,12 @@ REF_ModifyMakestep(int limit, double threshold)
/* ================================================== */
void
REF_EnableLocal(int stratum)
REF_EnableLocal(int stratum, double distance, int orphan)
{
enable_local_stratum = 1;
local_stratum = stratum;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
}
/* ================================================== */
@@ -1291,14 +1299,6 @@ REF_DisableLocal(void)
/* ================================================== */
int
REF_IsLocalActive(void)
{
return !are_we_synchronised && enable_local_stratum;
}
/* ================================================== */
#define LEAP_SECOND_CLOSE 5
int REF_IsLeapSecondClose(void)
@@ -1327,52 +1327,34 @@ int REF_IsLeapSecondClose(void)
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
double elapsed;
double extra_dispersion;
struct timeval now_raw, now_cooked;
double correction;
int synchronised;
LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &correction, NULL);
UTI_AddDoubleToTimeval(&now_raw, correction, &now_cooked);
rep->ref_id = 0;
REF_GetReferenceParams(&now_cooked, &synchronised,
&rep->leap_status, &rep->stratum,
&rep->ref_id, &rep->ref_time,
&rep->root_delay, &rep->root_dispersion);
if (rep->stratum == NTP_MAX_STRATUM)
rep->stratum = 0;
rep->ip_addr.family = IPADDR_UNSPEC;
rep->stratum = 0;
rep->leap_status = our_leap_status;
rep->ref_time.tv_sec = 0;
rep->ref_time.tv_usec = 0;
rep->current_correction = correction;
rep->freq_ppm = LCL_ReadAbsoluteFrequency();
rep->resid_freq_ppm = 0.0;
rep->skew_ppm = 0.0;
rep->root_delay = 0.0;
rep->root_dispersion = 0.0;
rep->last_update_interval = last_ref_update_interval;
rep->last_offset = last_offset;
rep->rms_offset = sqrt(avg2_offset);
if (are_we_synchronised) {
UTI_DiffTimevalsToDouble(&elapsed, &now_cooked, &our_ref_time);
extra_dispersion = (our_skew + fabs(our_residual_freq) + LCL_GetMaxClockError()) * elapsed;
rep->ref_id = our_ref_id;
if (synchronised) {
rep->ip_addr = our_ref_ip;
rep->stratum = our_stratum;
rep->ref_time = our_ref_time;
rep->resid_freq_ppm = 1.0e6 * our_residual_freq;
rep->skew_ppm = 1.0e6 * our_skew;
rep->root_delay = our_root_delay;
rep->root_dispersion = our_root_dispersion + extra_dispersion;
} else if (enable_local_stratum) {
rep->ref_id = LOCAL_REFERENCE_ID;
rep->ip_addr.family = IPADDR_UNSPEC;
rep->stratum = local_stratum;
rep->ref_time = now_cooked;
rep->root_dispersion = LCL_GetSysPrecisionAsQuantum();
}
}

View File

@@ -165,6 +165,9 @@ REF_SetUnsynchronised(void);
synchronised */
extern int REF_GetOurStratum(void);
/* Return stratum of the local reference if orphan mode is enabled */
extern int REF_GetOrphanStratum(void);
/* Return the current skew */
extern double REF_GetSkew(void);
@@ -174,9 +177,8 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
/* Modify makestep settings */
extern void REF_ModifyMakestep(int limit, double threshold);
extern void REF_EnableLocal(int stratum);
extern void REF_EnableLocal(int stratum, double distance, int orphan);
extern void REF_DisableLocal(void);
extern int REF_IsLocalActive(void);
/* Check if current raw or cooked time is close to a leap second
and is better to discard any measurements */

View File

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

62
rtc.c
View File

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

View File

@@ -72,8 +72,7 @@ static int fd = -1;
static int measurement_period = LOWEST_MEASUREMENT_PERIOD;
static int timeout_running = 0;
static SCH_TimeoutID timeout_id;
static SCH_TimeoutID timeout_id = 0;
static int skip_interrupts;
@@ -367,6 +366,9 @@ t_from_rtc(struct tm *stm) {
t2 = mktime(&temp2);
diff = t2 - t1;
if (t1 - diff == -1)
DEBUG_LOG(LOGF_RtcLinux, "Could not convert RTC time");
return t1 - diff;
}
@@ -379,13 +381,13 @@ read_hwclock_file(const char *hwclock_file)
char line[256];
int i;
if (!hwclock_file)
if (!hwclock_file || !hwclock_file[0])
return;
in = fopen(hwclock_file, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open hwclockfile %s",
hwclock_file);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open %s : %s",
hwclock_file, strerror(errno));
return;
}
@@ -402,7 +404,7 @@ read_hwclock_file(const char *hwclock_file)
} else if (i == 3 && !strncmp(line, "UTC", 3)) {
rtc_on_utc = 1;
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read LOCAL/UTC setting from hwclockfile %s",
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read RTC LOCAL/UTC setting from %s",
hwclock_file);
}
}
@@ -445,7 +447,7 @@ read_coefs_from_file(void)
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from RTC file %s",
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from %s",
coefs_file_name);
}
fclose(in);
@@ -578,10 +580,8 @@ RTC_Linux_Initialise(void)
void
RTC_Linux_Finalise(void)
{
if (timeout_running) {
SCH_RemoveTimeout(timeout_id);
timeout_running = 0;
}
SCH_RemoveTimeout(timeout_id);
timeout_id = 0;
/* Remove input file handler */
if (fd >= 0) {
@@ -607,14 +607,16 @@ switch_interrupts(int onoff)
if (onoff) {
status = ioctl(fd, RTC_UIE_ON, 0);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not start measurement : %s", strerror(errno));
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
"enable", strerror(errno));
return;
}
skip_interrupts = 1;
} else {
status = ioctl(fd, RTC_UIE_OFF, 0);
if (status < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not stop measurement : %s", strerror(errno));
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not %s RTC interrupt : %s",
"disable", strerror(errno));
return;
}
}
@@ -625,7 +627,7 @@ switch_interrupts(int onoff)
static void
measurement_timeout(void *any)
{
timeout_running = 0;
timeout_id = 0;
switch_interrupts(1);
}
@@ -694,10 +696,11 @@ handle_initial_trim(void)
/* sys_error_now is positive if the system clock is fast */
sys_error_now = rtc_error_now - coef_seconds_fast;
LOG(LOGS_INFO, LOGF_RtcLinux, "System trim from RTC = %f", sys_error_now);
LCL_AccumulateOffset(sys_error_now, 0.0);
LOG(LOGS_INFO, LOGF_RtcLinux, "System clock off from RTC by %f seconds (slew)",
sys_error_now);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid file coefficients, cannot trim system time");
LOG(LOGS_WARN, LOGF_RtcLinux, "No valid rtcfile coefficients");
}
coefs_valid = 0;
@@ -722,7 +725,7 @@ handle_relock_after_trim(void)
if (valid) {
write_coefs_to_file(1,ref,fast,saved_coef_gain_rate);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not do regression after trim");
DEBUG_LOG(LOGF_RtcLinux, "Could not do regression after trim");
}
coefs_valid = 0;
@@ -857,7 +860,6 @@ read_from_device(void *any)
rtc_t = t_from_rtc(&rtc_tm);
if (rtc_t == (time_t)(-1)) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not convert RTC time to timeval");
error = 1;
goto turn_off_interrupt;
}
@@ -883,13 +885,12 @@ turn_off_interrupt:
switch (operating_mode) {
case OM_INITIAL:
if (error) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete initial step due to errors");
DEBUG_LOG(LOGF_RtcLinux, "Could not complete initial step due to errors");
operating_mode = OM_NORMAL;
(after_init_hook)(after_init_hook_arg);
switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
}
@@ -897,12 +898,11 @@ turn_off_interrupt:
case OM_AFTERTRIM:
if (error) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not complete after trim relock due to errors");
DEBUG_LOG(LOGF_RtcLinux, "Could not complete after trim relock due to errors");
operating_mode = OM_NORMAL;
switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
}
@@ -911,7 +911,6 @@ turn_off_interrupt:
case OM_NORMAL:
switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
break;
@@ -931,9 +930,8 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
after_init_hook_arg = anything;
operating_mode = OM_INITIAL;
timeout_running = 0;
timeout_id = 0;
switch_interrupts(1);
}
/* ================================================== */
@@ -941,7 +939,6 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
void
RTC_Linux_StartMeasurements(void)
{
timeout_running = 0;
measurement_timeout(NULL);
}
@@ -974,7 +971,7 @@ RTC_Linux_WriteParameters(void)
RTC behaviour than we do for the rest of the module. */
int
RTC_Linux_TimePreInit(void)
RTC_Linux_TimePreInit(time_t driftfile_time)
{
int fd, status;
struct rtc_time rtc_raw, rtc_raw_retry;
@@ -1039,18 +1036,23 @@ RTC_Linux_TimePreInit(void)
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
if (new_sys_time.tv_sec < driftfile_time) {
LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)");
return 0;
}
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (fabs(sys_offset) >= 1.0) {
if (LCL_ApplyStepOffset(sys_offset))
LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
accumulated_error);
LOG(LOGS_INFO, LOGF_RtcLinux, "System time set from RTC");
}
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");
return 0;
}
} else {
return 0;
}
return 1;
@@ -1090,7 +1092,8 @@ RTC_Linux_Trim(void)
if (fabs(coef_seconds_fast) > 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "Trimming RTC, error = %.3f seconds", coef_seconds_fast);
LOG(LOGS_INFO, LOGF_RtcLinux, "RTC wrong by %.3f seconds (step)",
coef_seconds_fast);
/* Do processing to set clock. Let R be the value we set the
RTC to, then in 500ms the RTC ticks (R+1) (see comments in
@@ -1115,10 +1118,8 @@ RTC_Linux_Trim(void)
coef_ref_time = now.tv_sec;
/* And start rapid sampling, interrupts on now */
if (timeout_running) {
SCH_RemoveTimeout(timeout_id);
timeout_running = 0;
}
SCH_RemoveTimeout(timeout_id);
timeout_id = 0;
switch_interrupts(1);
}

View File

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

52
sched.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011, 2013-2014
* Copyright (C) Miroslav Lichvar 2011, 2013-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
@@ -150,8 +150,6 @@ SCH_Initialise(void)
LCL_ReadRawTime(&last_select_ts_raw);
last_select_ts = last_select_ts_raw;
srandom(last_select_ts.tv_sec << 16 ^ last_select_ts.tv_usec);
initialised = 1;
}
@@ -181,7 +179,8 @@ SCH_AddInputFileHandler
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
assert(!FD_ISSET(fd, &read_fds));
if (FD_ISSET(fd, &read_fds))
assert(0);
++n_read_fds;
@@ -210,7 +209,8 @@ SCH_RemoveInputFileHandler(int fd)
assert(initialised);
/* Check that a handler was registered for the fd in question */
assert(FD_ISSET(fd, &read_fds));
if (!FD_ISSET(fd, &read_fds))
assert(0);
--n_read_fds;
@@ -278,6 +278,26 @@ release_tqe(TimerQueueEntry *node)
/* ================================================== */
static SCH_TimeoutID
get_new_tqe_id(void)
{
TimerQueueEntry *ptr;
try_again:
next_tqe_id++;
if (!next_tqe_id)
goto try_again;
/* Make sure the ID isn't already used */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next)
if (ptr->id == next_tqe_id)
goto try_again;
return next_tqe_id;
}
/* ================================================== */
SCH_TimeoutID
SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgument arg)
{
@@ -288,7 +308,7 @@ SCH_AddTimeout(struct timeval *tv, SCH_TimeoutHandler handler, SCH_ArbitraryArgu
new_tqe = allocate_tqe();
new_tqe->id = next_tqe_id++;
new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler;
new_tqe->arg = arg;
new_tqe->tv = *tv;
@@ -330,6 +350,10 @@ SCH_AddTimeoutByDelay(double delay, SCH_TimeoutHandler handler, SCH_ArbitraryArg
LCL_ReadRawTime(&now);
UTI_AddDoubleToTimeval(&now, delay, &then);
if (UTI_CompareTimevals(&now, &then) > 0) {
LOG_FATAL(LOGF_Scheduler, "Timeout overflow");
}
return SCH_AddTimeout(&then, handler, arg);
}
@@ -352,7 +376,10 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
assert(class < SCH_NumberOfClasses);
if (randomness > 0.0) {
r = random() % 0xffff / (0xffff - 1.0) * randomness + 1.0;
uint16_t rnd;
UTI_GetRandomBytes(&rnd, sizeof (rnd));
r = rnd / (double)0xffff * randomness + 1.0;
min_delay *= r;
separation *= r;
}
@@ -393,7 +420,7 @@ SCH_AddTimeoutInClass(double min_delay, double separation, double randomness,
/* We have located the insertion point */
new_tqe = allocate_tqe();
new_tqe->id = next_tqe_id++;
new_tqe->id = get_new_tqe_id();
new_tqe->handler = handler;
new_tqe->arg = arg;
UTI_AddDoubleToTimeval(&now, new_min_delay, &new_tqe->tv);
@@ -417,6 +444,9 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
assert(initialised);
if (!id)
return;
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
if (ptr->id == id) {
@@ -432,9 +462,12 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
/* Release memory back to the operating system */
release_tqe(ptr);
break;
return;
}
}
/* Catch calls with invalid non-zero ID */
assert(0);
}
/* ================================================== */
@@ -686,7 +719,6 @@ SCH_MainLoop(void)
void
SCH_QuitProgram(void)
{
assert(initialised);
need_to_exit = 1;
}

View File

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

111
smooth.c
View File

@@ -77,6 +77,10 @@ static struct stage stages[NUM_STAGES];
/* Enabled/disabled smoothing */
static int enabled;
/* Enabled/disabled mode where only leap seconds are smoothed out and normal
offset/frequency changes are ignored */
static int leap_only_mode;
/* Maximum skew/max_wander ratio to start updating offset and frequency */
#define UNLOCK_SKEW_WANDER_RATIO 10000
@@ -93,15 +97,17 @@ static struct timeval last_update;
static void
get_offset_freq(struct timeval *now, double *offset, double *freq)
get_smoothing(struct timeval *now, double *poffset, double *pfreq,
double *pwander)
{
double elapsed, length;
double elapsed, length, offset, freq, wander;
int i;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
*offset = smooth_offset;
*freq = smooth_freq;
offset = smooth_offset;
freq = smooth_freq;
wander = 0.0;
for (i = 0; i < NUM_STAGES; i++) {
if (elapsed <= 0.0)
@@ -111,13 +117,21 @@ get_offset_freq(struct timeval *now, double *offset, double *freq)
if (length >= elapsed)
length = elapsed;
*offset -= length * (2.0 * *freq + stages[i].wander * length) / 2.0;
*freq += stages[i].wander * length;
wander = stages[i].wander;
offset -= length * (2.0 * freq + wander * length) / 2.0;
freq += wander * length;
elapsed -= length;
}
if (elapsed > 0.0)
*offset -= elapsed * *freq;
if (elapsed > 0.0) {
wander = 0.0;
offset -= elapsed * freq;
}
*poffset = offset;
*pfreq = freq;
if (pwander)
*pwander = wander;
}
static void
@@ -185,14 +199,12 @@ update_smoothing(struct timeval *now, double offset, double freq)
{
/* Don't accept offset/frequency until the clock has stabilized */
if (locked) {
if (REF_GetSkew() / max_wander < UNLOCK_SKEW_WANDER_RATIO) {
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated");
locked = 0;
}
if (REF_GetSkew() / max_wander < UNLOCK_SKEW_WANDER_RATIO || leap_only_mode)
SMT_Activate(now);
return;
}
get_offset_freq(now, &smooth_offset, &smooth_freq);
get_smoothing(now, &smooth_offset, &smooth_freq, NULL);
smooth_offset += offset;
smooth_freq = (smooth_freq - freq) / (1.0 - freq);
last_update = *now;
@@ -208,15 +220,19 @@ handle_slew(struct timeval *raw, struct timeval *cooked, double dfreq,
{
double delta;
if (change_type == LCL_ChangeAdjust)
update_smoothing(cooked, doffset, dfreq);
if (change_type == LCL_ChangeAdjust) {
if (leap_only_mode)
update_smoothing(cooked, 0.0, 0.0);
else
update_smoothing(cooked, doffset, dfreq);
}
UTI_AdjustTimeval(&last_update, cooked, &last_update, &delta, dfreq, doffset);
}
void SMT_Initialise(void)
{
CNF_GetSmooth(&max_freq, &max_wander);
CNF_GetSmooth(&max_freq, &max_wander, &leap_only_mode);
if (max_freq <= 0.0 || max_wander <= 0.0) {
enabled = 0;
return;
@@ -249,19 +265,78 @@ SMT_GetOffset(struct timeval *now)
if (!enabled)
return 0.0;
get_offset_freq(now, &offset, &freq);
get_smoothing(now, &offset, &freq, NULL);
return offset;
}
void
SMT_Activate(struct timeval *now)
{
if (!enabled || !locked)
return;
LOG(LOGS_INFO, LOGF_Smooth, "Time smoothing activated%s", leap_only_mode ?
" (leap seconds only)" : "");
locked = 0;
last_update = *now;
}
void
SMT_Reset(struct timeval *now)
{
int i;
if (!enabled)
return;
locked = 1;
smooth_offset = 0.0;
smooth_freq = 0.0;
last_update = *now;
for (i = 0; i < NUM_STAGES; i++)
stages[i].wander = stages[i].length = 0.0;
}
void
SMT_Leap(struct timeval *now, int leap)
{
/* When the leap-only mode is disabled, the leap second will be accumulated
in handle_slew() as a normal offset */
if (!enabled || !leap_only_mode)
return;
update_smoothing(now, leap, 0.0);
}
int
SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now)
{
double length, elapsed;
int i;
if (!enabled)
return 0;
report->active = !locked;
report->leap_only = leap_only_mode;
get_smoothing(now, &report->offset, &report->freq_ppm, &report->wander_ppm);
/* Convert to ppm and negate (positive values mean faster/speeding up) */
report->freq_ppm *= -1.0e6;
report->wander_ppm *= -1.0e6;
UTI_DiffTimevalsToDouble(&elapsed, now, &last_update);
if (!locked && elapsed >= 0.0) {
for (i = 0, length = 0.0; i < NUM_STAGES; i++)
length += stages[i].length;
report->last_update_ago = elapsed;
report->remaining_time = elapsed < length ? length - elapsed : 0.0;
} else {
report->last_update_ago = 0.0;
report->remaining_time = 0.0;
}
return 1;
}

View File

@@ -27,6 +27,8 @@
#ifndef GOT_SMOOTH_H
#define GOT_SMOOTH_H
#include "reports.h"
extern void SMT_Initialise(void);
extern void SMT_Finalise(void);
@@ -35,6 +37,12 @@ extern int SMT_IsEnabled(void);
extern double SMT_GetOffset(struct timeval *now);
extern void SMT_Activate(struct timeval *now);
extern void SMT_Reset(struct timeval *now);
extern void SMT_Leap(struct timeval *now, int leap);
extern int SMT_GetSmoothingReport(RPT_SmoothingReport *report, struct timeval *now);
#endif

264
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014
* Copyright (C) Miroslav Lichvar 2011-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
@@ -44,7 +44,6 @@
#include "logging.h"
#include "reports.h"
#include "nameserv.h"
#include "mkdirpp.h"
#include "sched.h"
#include "regress.h"
@@ -71,8 +70,10 @@ typedef enum {
SRC_OK, /* OK so far, not a final status! */
SRC_UNSELECTABLE, /* Has noselect option set */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
SRC_STALE, /* Has older samples than others */
SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */
SRC_FALSETICKER, /* Doesn't agree with others */
SRC_JITTERY, /* Scatter worse than other's dispersion (not used) */
SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */
@@ -118,7 +119,7 @@ struct SRC_Instance_Record {
SRC_Type type;
/* Options used when selecting sources */
SRC_SelectOption sel_option;
int sel_options;
/* Score against currently selected source */
double sel_score;
@@ -156,6 +157,7 @@ static int selected_source_index; /* Which source index is currently
/* Number of updates needed to reset the distant status */
#define DISTANT_PENALTY 32
static double max_distance;
static double reselect_distance;
static double stratum_weight;
static double combine_limit;
@@ -180,6 +182,7 @@ void SRC_Initialise(void) {
n_sources = 0;
max_n_sources = 0;
selected_source_index = INVALID_SOURCE;
max_distance = CNF_GetMaxDistance();
reselect_distance = CNF_GetReselectDistance();
stratum_weight = CNF_GetStratumWeight();
combine_limit = CNF_GetCombineLimit();
@@ -207,7 +210,7 @@ void SRC_Finalise(void)
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, SRC_SelectOption sel_option, IPAddr *addr, int min_samples, int max_samples)
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, IPAddr *addr, int min_samples, int max_samples)
{
SRC_Instance result;
@@ -239,7 +242,7 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, SRC_SelectOpt
result->index = n_sources;
result->type = type;
result->sel_option = sel_option;
result->sel_options = sel_options;
SRC_SetRefid(result, ref_id, addr);
SRC_ResetInstance(result);
@@ -412,7 +415,7 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
{
inst->reachability <<= 1;
inst->reachability |= !!reachable;
inst->reachability &= ~(-1 << SOURCE_REACH_BITS);
inst->reachability %= 1U << SOURCE_REACH_BITS;
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
@@ -427,9 +430,11 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
REF_SetUnsynchronised();
}
/* Try to replace NTP sources that are unreachable or falsetickers */
if (inst->type == SRC_NTP && (inst->status == SRC_FALSETICKER ||
(!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS))) {
/* Try to replace NTP sources that are unreachable, falsetickers, or
have root distance larger than the allowed maximum */
if (inst->type == SRC_NTP &&
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
inst->status == SRC_FALSETICKER || inst->status == SRC_BAD_DISTANCE)) {
NSR_HandleBadSource(inst->ip_addr);
}
}
@@ -513,7 +518,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
{
struct timeval src_ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion, elapsed;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
int i, index, combined;
@@ -524,6 +529,10 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
if (sources[selected_source_index]->type == SRC_NTP)
sel_src_distance += reselect_distance;
for (i = combined = 0; i < n_sel_sources; i++) {
index = sel_sources[i];
SST_GetTrackingData(sources[index]->stats, &src_ref_time,
@@ -536,8 +545,7 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
are not close, or it was recently marked as distant */
if (index != selected_source_index &&
(sources[index]->sel_info.root_distance > combine_limit *
(reselect_distance + sources[selected_source_index]->sel_info.root_distance) ||
(sources[index]->sel_info.root_distance > combine_limit * sel_src_distance ||
fabs(*frequency - src_frequency) >
combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) {
/* Use a smaller penalty in first few updates */
@@ -597,8 +605,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
struct SelectInfo *si;
struct timeval now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
int n_badstats_sources, max_sel_reach, max_badstat_reach;
int depth, best_depth, combined, stratum, min_stratum, max_score_index;
int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score;
@@ -625,14 +635,20 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints = 0;
n_sel_sources = 0;
n_badstats_sources = 0;
sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
assert(sources[i]->status != SRC_OK);
/* If some sources are specified with the require option, at least one
of them will have to be selectable in order to update the clock */
if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 1;
/* Ignore sources which were added with the noselect option */
if (sources[i]->sel_option == SRC_SelectNoselect) {
if (sources[i]->sel_options & SRC_SELECT_NOSELECT) {
sources[i]->status = SRC_UNSELECTABLE;
continue;
}
@@ -651,6 +667,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Require the root distance to be below the allowed maximum */
if (si->root_distance > max_distance) {
sources[i]->status = SRC_BAD_DISTANCE;
continue;
}
sources[i]->status = SRC_OK; /* For now */
if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago)
@@ -660,6 +682,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
max_sel_reach = sources[i]->reachability;
}
orphan_stratum = REF_GetOrphanStratum();
orphan_source = INVALID_SOURCE;
for (i = 0; i < n_sources; i++) {
if (sources[i]->status != SRC_OK)
continue;
@@ -674,7 +699,56 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* When the local reference is configured with the orphan option, NTP
sources that have stratum equal to the configured local stratum are
considered to be orphans (i.e. serving local time while not being
synchronised with real time) and are excluded from the normal source
selection. Sources with stratum larger than the local stratum are
considered to be directly on indirectly synchronised to an orphan and
are always ignored.
If no selectable source is available and all orphan sources have
reference IDs larger than the local ID, no source will be selected and
the local reference mode will be activated at some point, i.e. this host
will become an orphan. Otherwise, the orphan source with the smallest
reference ID will be selected. This ensures a group of servers polling
each other (with the same orphan configuration) which have no external
source can settle down to a state where only one server is serving its
local unsychronised time and others are synchronised to it. */
if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) {
sources[i]->status = SRC_ORPHAN;
if (si->stratum == orphan_stratum && sources[i]->reachability &&
(orphan_source == INVALID_SOURCE ||
sources[i]->ref_id < sources[orphan_source]->ref_id))
orphan_source = i;
continue;
}
++n_sel_sources;
}
/* If no selectable source is available, consider the orphan source */
if (!n_sel_sources && orphan_source != INVALID_SOURCE) {
uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr);
if (!local_ref_id) {
LOG(LOGS_ERR, LOGF_Sources, "Unknown local refid in orphan mode");
} else if (sources[orphan_source]->ref_id < local_ref_id) {
sources[orphan_source]->status = SRC_OK;
n_sel_sources = 1;
DEBUG_LOG(LOGF_Sources, "selecting orphan refid=%"PRIx32,
sources[orphan_source]->ref_id);
}
}
for (i = 0; i < n_sources; i++) {
if (sources[i]->status != SRC_OK)
continue;
si = &sources[i]->sel_info;
j1 = n_endpoints;
j2 = j1 + 1;
@@ -725,22 +799,27 @@ SRC_SelectSource(SRC_Instance updated_inst)
If we get a case like
<----------------------->
<-->
<-->
<===========>
<-->
<-->
<===========>
we will build the interval as shown with '=', whereas with an extra source we get
<----------------------->
<------->
<-->
<-->
<==>
<------->
<-->
<-->
<==>
The first case is just bad luck - we need extra sources to
detect the falseticker, so just make an arbitrary choice based
on stratum & stability etc.
Intervals from sources specified with the trust option have higher
priority in the search.
*/
trust_depth = best_trust_depth = 0;
depth = best_depth = 0;
best_lo = best_hi = 0.0;
@@ -748,14 +827,20 @@ SRC_SelectSource(SRC_Instance updated_inst)
switch (sort_list[i].tag) {
case LOW:
depth++;
if (depth > best_depth) {
if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST)
trust_depth++;
if (trust_depth > best_trust_depth ||
(trust_depth == best_trust_depth && depth > best_depth)) {
best_trust_depth = trust_depth;
best_depth = depth;
best_lo = sort_list[i].offset;
}
break;
case HIGH:
if (depth == best_depth)
if (trust_depth == best_trust_depth && depth == best_depth)
best_hi = sort_list[i].offset;
if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST)
trust_depth--;
depth--;
break;
default:
@@ -763,9 +848,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
}
if (best_depth <= n_sel_sources / 2) {
/* Could not even get half the reachable sources to agree -
clearly we can't synchronise. */
if (best_depth <= n_sel_sources / 2 && !best_trust_depth) {
/* Could not even get half the reachable sources to agree and there
are no trusted sources - clearly we can't synchronise */
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no majority", NULL);
@@ -786,65 +871,35 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_sel_sources = 0;
for (i = 0; i < n_sources; i++) {
/* This should be the same condition to get into the endpoint
list */
if (sources[i]->status != SRC_OK)
continue;
/* This should be the same condition to get into the endpoint
list */
/* Check if source's interval contains the best interval, or
is wholly contained within it */
if ((sources[i]->sel_info.lo_limit <= best_lo &&
/* Check if source's interval contains the best interval, or is wholly
contained within it. If there are any trusted sources the first
condition is applied only to them to not allow non-trusted sources to
move the final offset outside the interval. */
if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) &&
sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) ||
(sources[i]->sel_info.lo_limit >= best_lo &&
sources[i]->sel_info.hi_limit <= best_hi)) {
sel_sources[n_sel_sources++] = i;
if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 0;
} else {
sources[i]->status = SRC_FALSETICKER;
}
}
#if 0
/* We now have a list of indices for the sources which pass the
false-ticker test. Now go on to reject those whose variance is
greater than the minimum distance of any other */
/* Find minimum distance */
index = sel_sources[0];
min_distance = sources[index]->sel_info.root_distance;
for (i = 1; i < n_sel_sources; i++) {
index = sel_sources[i];
distance = sources[index]->sel_info.root_distance;
if (distance < min_distance) {
min_distance = distance;
}
}
/* Now go through and prune any NTP sources that have excessive
variance */
for (i = 0; i < n_sel_sources; i++) {
index = sel_sources[i];
if (sources[index]->type == SRC_NTP &&
sqrt(sources[index]->sel_info.variance) > min_distance) {
sel_sources[i] = INVALID_SOURCE;
sources[index]->status = SRC_JITTERY;
}
}
/* Now crunch the list and mark all sources as selectable */
for (i = j = 0; i < n_sel_sources; i++) {
index = sel_sources[i];
if (index == INVALID_SOURCE)
continue;
sel_sources[j++] = index;
}
n_sel_sources = j;
#endif
if (n_sel_sources == 0 || n_sel_sources < CNF_GetMinSources()) {
if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) {
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: %s selectable sources",
n_sel_sources ? "not enough" : "no");
!n_sel_sources ? "no" :
sel_req_source ? "no required source in" : "not enough");
selected_source_index = INVALID_SOURCE;
}
mark_ok_sources(SRC_WAITS_SOURCES);
@@ -869,16 +924,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* If there are any sources with prefer option, reduce the list again
only to the preferred sources */
for (i = j = 0; i < n_sel_sources; i++) {
if (sources[sel_sources[i]]->sel_option == SRC_SelectPrefer)
sel_sources[j++] = sel_sources[i];
for (i = 0; i < n_sel_sources; i++) {
if (sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER)
break;
}
if (j > 0) {
for (i = 0; i < n_sel_sources; i++) {
if (sources[sel_sources[i]]->sel_option != SRC_SelectPrefer)
if (i < n_sel_sources) {
for (i = j = 0; i < n_sel_sources; i++) {
if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER))
sources[sel_sources[i]]->status = SRC_NONPREFERRED;
else
sel_sources[j++] = sel_sources[i];
}
assert(j > 0);
n_sel_sources = j;
sel_prefer = 1;
} else {
@@ -909,7 +966,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
for (i = 0; i < n_sources; i++) {
/* Reset score for non-selectable sources */
if (sources[i]->status != SRC_OK ||
(sel_prefer && sources[i]->sel_option != SRC_SelectPrefer)) {
(sel_prefer && !(sources[i]->sel_options & SRC_SELECT_PREFER))) {
sources[i]->sel_score = 1.0;
sources[i]->distant = DISTANT_PENALTY;
continue;
@@ -1125,25 +1182,23 @@ SRC_DumpSources(void)
direc_len = strlen(direc);
file_len = direc_len + 24;
filename = MallocArray(char, file_len); /* a bit of slack */
if (mkdir_and_parents(direc)) {
for (i=0; i<n_sources; i++) {
a = (sources[i]->ref_id) >> 24;
b = ((sources[i]->ref_id) >> 16) & 0xff;
c = ((sources[i]->ref_id) >> 8) & 0xff;
d = ((sources[i]->ref_id)) & 0xff;
snprintf(filename, file_len-1, "%s/%d.%d.%d.%d.dat", direc, a, b, c, d);
out = fopen(filename, "w");
if (!out) {
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
} else {
SST_SaveToFile(sources[i]->stats, out);
fclose(out);
}
for (i = 0; i < n_sources; i++) {
a = (sources[i]->ref_id) >> 24;
b = ((sources[i]->ref_id) >> 16) & 0xff;
c = ((sources[i]->ref_id) >> 8) & 0xff;
d = ((sources[i]->ref_id)) & 0xff;
snprintf(filename, file_len - 1, "%s/%d.%d.%d.%d.dat", direc, a, b, c, d);
out = fopen(filename, "w");
if (!out) {
LOG(LOGS_WARN, LOGF_Sources, "Could not open dump file %s", filename);
} else {
SST_SaveToFile(sources[i]->stats, out);
fclose(out);
}
} else {
LOG(LOGS_ERR, LOGF_Sources, "Could not create directory %s", direc);
}
Free(filename);
}
@@ -1251,7 +1306,9 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
switch (src->status) {
case SRC_UNSELECTABLE:
case SRC_BAD_STATS:
case SRC_BAD_DISTANCE:
case SRC_STALE:
case SRC_ORPHAN:
case SRC_WAITS_STATS:
report->state = RPT_UNREACH;
break;
@@ -1280,20 +1337,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
break;
}
switch (src->sel_option) {
case SRC_SelectNormal:
report->sel_option = RPT_NORMAL;
break;
case SRC_SelectPrefer:
report->sel_option = RPT_PREFER;
break;
case SRC_SelectNoselect:
report->sel_option = RPT_NOSELECT;
break;
default:
assert(0);
}
report->sel_options = src->sel_options;
report->reachability = src->reachability;
/* Call stats module to fill out estimates */

View File

@@ -55,17 +55,10 @@ typedef enum {
SRC_REFCLOCK /* Rerefence clock */
} SRC_Type;
/* Options used when selecting sources */
typedef enum {
SRC_SelectNormal,
SRC_SelectNoselect,
SRC_SelectPrefer
} SRC_SelectOption;
/* Function to create a new instance. This would be called by one of
the individual source-type instance creation routines. */
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, SRC_SelectOption sel_option, IPAddr *addr, int min_samples, int max_samples);
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, IPAddr *addr, int min_samples, int max_samples);
/* Function to get rid of a source when it is being unconfigured.
This may cause the current reference source to be reselected, if this

View File

@@ -845,7 +845,7 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timeval *now
UTI_DiffTimevals(&ago, now, &inst->sample_times[i]);
report->latest_meas_ago = ago.tv_sec;
} else {
report->latest_meas_ago = 86400 * 365 * 10;
report->latest_meas_ago = (uint32_t)-1;
report->orig_latest_meas = 0;
report->latest_meas = 0;
report->latest_meas_err = 0;

View File

@@ -42,11 +42,11 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
int sel_options;
uint32_t authkey;
double max_delay;
double max_delay_ratio;
double max_delay_dev_ratio;
SRC_SelectOption sel_option;
} SourceParameters;
#define SRC_DEFAULT_PORT 123
@@ -63,4 +63,10 @@ typedef struct {
#define SRC_DEFAULT_MAXSAMPLES (-1)
#define INACTIVE_AUTHKEY 0
/* Flags for source selection */
#define SRC_SELECT_NOSELECT 0x1
#define SRC_SELECT_PREFER 0x2
#define SRC_SELECT_TRUST 0x4
#define SRC_SELECT_REQUIRE 0x8
#endif /* GOT_SRCPARAMS_H */

View File

@@ -1,38 +0,0 @@
/*
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.
*
**********************************************************************
=======================================================================
Replacement strerror function for systems that don't have it
*/
#include "config.h"
#ifdef SUNOS
#include <errno.h>
extern char *sys_errlist[];
char *strerror(int n) {
return sys_errlist[n];
}
#endif /* SUNOS */

64
stubs.c
View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
* Copyright (C) Miroslav Lichvar 2014-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
@@ -32,33 +32,57 @@
#include "keys.h"
#include "logging.h"
#include "manual.h"
#include "memory.h"
#include "nameserv.h"
#include "nameserv_async.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_sources.h"
#include "privops.h"
#include "refclock.h"
#include "sched.h"
#ifndef FEAT_ASYNCDNS
#define MAX_ADDRESSES 16
/* This is a blocking implementation used when asynchronous resolving is not available */
struct DNS_Async_Instance {
const char *name;
DNS_NameResolveHandler handler;
void *arg;
};
static void
resolve_name(void *anything)
{
struct DNS_Async_Instance *inst;
IPAddr addrs[DNS_MAX_ADDRESSES];
DNS_Status status;
int i;
inst = (struct DNS_Async_Instance *)anything;
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
addrs[i].family != IPADDR_UNSPEC; i++)
;
(inst->handler)(status, i, addrs, inst->arg);
Free(inst);
}
void
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
{
IPAddr addrs[MAX_ADDRESSES];
DNS_Status status;
int i;
struct DNS_Async_Instance *inst;
status = DNS_Name2IPAddress(name, addrs, MAX_ADDRESSES);
inst = MallocNew(struct DNS_Async_Instance);
inst->name = name;
inst->handler = handler;
inst->arg = anything;
for (i = 0; status == DNS_Success && i < MAX_ADDRESSES &&
addrs[i].family != IPADDR_UNSPEC; i++)
;
(handler)(status, i, addrs, anything);
SCH_AddTimeoutByDelay(0.0, resolve_name, inst);
}
#endif /* !FEAT_ASYNCDNS */
@@ -75,6 +99,11 @@ CAM_Finalise(void)
{
}
void
CAM_OpenUnixSocket(void)
{
}
int
CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
{
@@ -169,6 +198,11 @@ NSR_HandleBadSource(IPAddr *address)
{
}
void
NSR_RefreshAddresses(void)
{
}
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
@@ -196,6 +230,12 @@ NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
return 0;
}
uint32_t
NSR_GetLocalRefid(IPAddr *address)
{
return 0;
}
int
NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
{

76
sys.c
View File

@@ -27,23 +27,19 @@
#include "config.h"
#include "sysincl.h"
#include "sys.h"
#include "logging.h"
#if defined (LINUX)
#if defined(LINUX)
#include "sys_linux.h"
#endif
#if defined (SOLARIS)
#elif defined(SOLARIS)
#include "sys_solaris.h"
#endif
#if defined (SUNOS)
#include "sys_sunos.h"
#endif
#if defined (__NetBSD__)
#elif defined(NETBSD) || defined(FREEBSD)
#include "sys_netbsd.h"
#elif defined(MACOSX)
#include "sys_macosx.h"
#endif
/* ================================================== */
@@ -51,23 +47,17 @@
void
SYS_Initialise(void)
{
#if defined(LINUX)
SYS_Linux_Initialise();
#endif
#if defined(SOLARIS)
#elif defined(SOLARIS)
SYS_Solaris_Initialise();
#endif
#if defined(SUNOS)
SYS_SunOS_Initialise();
#endif
#if defined(__NetBSD__)
#elif defined(NETBSD) || defined(FREEBSD)
SYS_NetBSD_Initialise();
#elif defined(MACOSX)
SYS_MacOSX_Initialise();
#else
#error Unknown system
#endif
}
/* ================================================== */
@@ -75,30 +65,31 @@ SYS_Initialise(void)
void
SYS_Finalise(void)
{
#if defined(LINUX)
SYS_Linux_Finalise();
#endif
#if defined(SOLARIS)
#elif defined(SOLARIS)
SYS_Solaris_Finalise();
#endif
#if defined(SUNOS)
SYS_SunOS_Finalise();
#endif
#if defined(__NetBSD__)
#elif defined(NETBSD) || defined(FREEBSD)
SYS_NetBSD_Finalise();
#elif defined(MACOSX)
SYS_MacOSX_Finalise();
#else
#error Unknown system
#endif
}
/* ================================================== */
void SYS_DropRoot(char *user)
void SYS_DropRoot(uid_t uid, gid_t gid)
{
#if defined(LINUX) && defined (FEAT_PRIVDROP)
SYS_Linux_DropRoot(user);
SYS_Linux_DropRoot(uid, gid);
#elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
SYS_Solaris_DropRoot(uid, gid);
#elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)
SYS_NetBSD_DropRoot(uid, gid);
#elif defined(MACOSX) && defined(FEAT_PRIVDROP)
SYS_MacOSX_DropRoot(uid, gid);
#else
LOG_FATAL(LOGF_Sys, "dropping root privileges not supported");
#endif
@@ -106,10 +97,23 @@ void SYS_DropRoot(char *user)
/* ================================================== */
void SYS_EnableSystemCallFilter(int level)
{
#if defined(LINUX) && defined(FEAT_SCFILTER)
SYS_Linux_EnableSystemCallFilter(level);
#else
LOG_FATAL(LOGF_Sys, "system call filter not supported");
#endif
}
/* ================================================== */
void SYS_SetScheduler(int SchedPriority)
{
#if defined(LINUX) && defined(HAVE_SCHED_SETSCHEDULER)
SYS_Linux_SetScheduler(SchedPriority);
#elif defined(MACOSX)
SYS_MacOSX_SetScheduler(SchedPriority);
#else
LOG_FATAL(LOGF_Sys, "scheduler priority setting not supported");
#endif

8
sys.h
View File

@@ -35,8 +35,12 @@ extern void SYS_Initialise(void);
/* Called at the end of the run to do final clean-up */
extern void SYS_Finalise(void);
/* Drop root privileges to the specified user */
extern void SYS_DropRoot(char *user);
/* Drop root privileges to the specified user and group */
extern void SYS_DropRoot(uid_t uid, gid_t gid);
/* Enable a system call filter to allow only system calls
which chronyd normally needs after initialization */
extern void SYS_EnableSystemCallFilter(int level);
extern void SYS_SetScheduler(int SchedPriority);
extern void SYS_LockMemory(void);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
* Copyright (C) Miroslav Lichvar 2014-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
@@ -34,6 +34,7 @@
#include "local.h"
#include "localp.h"
#include "logging.h"
#include "privops.h"
#include "sched.h"
#include "util.h"
@@ -43,6 +44,8 @@
static lcl_ReadFrequencyDriver drv_read_freq;
static lcl_SetFrequencyDriver drv_set_freq;
static lcl_SetSyncStatusDriver drv_set_sync_status;
static lcl_AccrueOffsetDriver drv_accrue_offset;
static lcl_OffsetCorrectionDriver drv_get_offset_correction;
/* Current frequency as requested by the local module (in ppm) */
static double base_freq;
@@ -74,9 +77,8 @@ static struct timeval slew_start;
#define MIN_SLEW_TIMEOUT 1.0
#define MAX_SLEW_TIMEOUT 1.0e4
/* Scheduler timeout ID and flag if the timer is currently running */
/* Scheduler timeout ID for ending of the currently running slew */
static SCH_TimeoutID slew_timeout_id;
static int slew_timer_running;
/* Suggested offset correction rate (correction time * offset) */
static double correction_rate;
@@ -85,6 +87,16 @@ static double correction_rate;
real frequency of the clock */
static double slew_error;
/* Minimum offset that the system driver can slew faster than the maximum
frequency offset that it allows to be set directly */
static double fastslew_min_offset;
/* Maximum slew rate of the system driver */
static double fastslew_max_rate;
/* Flag indicating that the system driver is currently slewing */
static int fastslew_active;
/* ================================================== */
static void handle_end_of_slew(void *anything);
@@ -107,6 +119,50 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
}
}
/* ================================================== */
static void
start_fastslew(void)
{
if (!drv_accrue_offset)
return;
drv_accrue_offset(offset_register, 0.0);
DEBUG_LOG(LOGF_SysGeneric, "fastslew offset=%e", offset_register);
offset_register = 0.0;
fastslew_active = 1;
}
/* ================================================== */
static void
stop_fastslew(struct timeval *now)
{
double corr;
if (!drv_get_offset_correction || !fastslew_active)
return;
/* Cancel the remaining offset */
drv_get_offset_correction(now, &corr, NULL);
drv_accrue_offset(corr, 0.0);
offset_register -= corr;
}
/* ================================================== */
static double
clamp_freq(double freq)
{
if (freq > max_freq)
return max_freq;
if (freq < -max_freq)
return -max_freq;
return freq;
}
/* ================================================== */
/* End currently running slew and start a new one */
@@ -117,8 +173,7 @@ update_slew(void)
double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */
if (slew_timer_running)
SCH_RemoveTimeout(slew_timeout_id);
SCH_RemoveTimeout(slew_timeout_id);
LCL_ReadRawTime(&now);
@@ -126,6 +181,8 @@ update_slew(void)
UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
offset_register -= slew_freq * duration;
stop_fastslew(&now);
/* Estimate how long should the next slew take */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
duration = MAX_SLEW_TIMEOUT;
@@ -143,12 +200,16 @@ update_slew(void)
else if (corr_freq > max_corr_freq)
corr_freq = max_corr_freq;
/* Let the system driver perform the slew if the requested frequency
offset is too large for the frequency driver */
if (drv_accrue_offset && fabs(corr_freq) >= fastslew_max_rate &&
fabs(offset_register) > fastslew_min_offset) {
start_fastslew();
corr_freq = 0.0;
}
/* Get the new real frequency and clamp it */
total_freq = base_freq + corr_freq * (1.0e6 - base_freq);
if (total_freq > max_freq)
total_freq = max_freq;
else if (total_freq < -max_freq)
total_freq = -max_freq;
total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq));
/* Set the new frequency (the actual frequency returned by the call may be
slightly different from the requested frequency due to rounding) */
@@ -167,8 +228,8 @@ update_slew(void)
/* Compute the duration of the slew and clamp it. If the slewing frequency
is zero or has wrong sign (e.g. due to rounding in the frequency driver or
when base_freq is larger than max_freq), use maximum timeout and try again
on the next update. */
when base_freq is larger than max_freq, or fast slew is active), use the
maximum timeout and try again on the next update. */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
offset_register * slew_freq <= 0.0) {
duration = MAX_SLEW_TIMEOUT;
@@ -183,9 +244,7 @@ update_slew(void)
/* Restart timer for the next update */
UTI_AddDoubleToTimeval(&now, duration, &end_of_slew);
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now;
slew_timer_running = 1;
DEBUG_LOG(LOGF_SysGeneric, "slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
offset_register, correction_rate, base_freq, total_freq, slew_freq,
@@ -197,7 +256,7 @@ update_slew(void)
static void
handle_end_of_slew(void *anything)
{
slew_timer_running = 0;
slew_timeout_id = 0;
update_slew();
}
@@ -238,13 +297,25 @@ static void
offset_convert(struct timeval *raw,
double *corr, double *err)
{
double duration;
double duration, fastslew_corr, fastslew_err;
UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
*corr = slew_freq * duration - offset_register;
if (err)
*err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0;
if (drv_get_offset_correction && fastslew_active) {
drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
if (fastslew_corr == 0.0 && fastslew_err == 0.0)
fastslew_active = 0;
} else {
fastslew_corr = fastslew_err = 0.0;
}
*corr = slew_freq * duration + fastslew_corr - offset_register;
if (err) {
*err = fastslew_err;
if (fabs(duration) <= max_freq_change_delay)
*err += slew_error;
}
}
/* ================================================== */
@@ -259,7 +330,7 @@ apply_step_offset(double offset)
LCL_ReadRawTime(&old_time);
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
if (PRV_SetTime(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysGeneric, "settimeofday() failed");
return 0;
}
@@ -295,6 +366,9 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_freq,
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
double min_fastslew_offset, double max_fastslew_rate,
lcl_AccrueOffsetDriver sys_accrue_offset,
lcl_OffsetCorrectionDriver sys_get_offset_correction,
lcl_SetLeapDriver sys_set_leap,
lcl_SetSyncStatusDriver sys_set_sync_status)
{
@@ -302,6 +376,8 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
drv_read_freq = sys_read_freq;
drv_set_freq = sys_set_freq;
drv_accrue_offset = sys_accrue_offset;
drv_get_offset_correction = sys_get_offset_correction;
drv_set_sync_status = sys_set_sync_status;
base_freq = (*drv_read_freq)();
@@ -310,6 +386,10 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
fastslew_min_offset = min_fastslew_offset;
fastslew_max_rate = max_fastslew_rate / 1.0e6;
fastslew_active = 0;
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, sys_apply_step_offset ?
sys_apply_step_offset : apply_step_offset,
@@ -323,14 +403,18 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
void
SYS_Generic_Finalise(void)
{
struct timeval now;
/* Must *NOT* leave a slew running - clock could drift way off
if the daemon is not restarted */
if (slew_timer_running) {
SCH_RemoveTimeout(slew_timeout_id);
slew_timer_running = 0;
}
(*drv_set_freq)(base_freq);
SCH_RemoveTimeout(slew_timeout_id);
slew_timeout_id = 0;
(*drv_set_freq)(clamp_freq(base_freq));
LCL_ReadRawTime(&now);
stop_fastslew(&now);
}
/* ================================================== */

View File

@@ -35,6 +35,9 @@ extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_s
lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_freq,
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
double min_fastslew_offset, double max_fastslew_rate,
lcl_AccrueOffsetDriver sys_accrue_offset,
lcl_OffsetCorrectionDriver sys_get_offset_correction,
lcl_SetLeapDriver sys_set_leap,
lcl_SetSyncStatusDriver sys_set_sync_status);

View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2009-2012, 2014
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-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
@@ -35,31 +35,50 @@
#if defined(HAVE_SCHED_SETSCHEDULER)
# include <sched.h>
int SchedPriority = 0;
#endif
#if defined(HAVE_MLOCKALL)
# include <sys/mman.h>
#include <sys/resource.h>
int LockAll = 0;
#endif
#ifdef FEAT_PRIVDROP
#include <sys/types.h>
#include <pwd.h>
#include <sys/prctl.h>
#include <sys/capability.h>
#include <grp.h>
#endif
#include "sys_generic.h"
#ifdef FEAT_SCFILTER
#include <sys/prctl.h>
#include <seccomp.h>
#include <termios.h>
#ifdef FEAT_PHC
#include <linux/ptp_clock.h>
#endif
#ifdef FEAT_PPS
#include <linux/pps.h>
#endif
#ifdef FEAT_RTC
#include <linux/rtc.h>
#endif
#endif
#include "sys_linux.h"
#include "sys_timex.h"
#include "conf.h"
#include "logging.h"
#include "wrap_adjtimex.h"
#include "privops.h"
#include "util.h"
/* The threshold for adjtimex maxerror when the kernel sets the UNSYNC flag */
#define UNSYNC_MAXERROR 16.0
/* Frequency scale to convert from ppm to the timex freq */
#define FREQ_SCALE (double)(1 << 16)
/* Definitions used if missed in the system headers */
#ifndef ADJ_SETOFFSET
#define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */
#endif
#ifndef ADJ_NANO
#define ADJ_NANO 0x2000 /* select nanosecond resolution */
#endif
/* This is the uncompensated system tick value */
static int nominal_tick;
@@ -103,11 +122,19 @@ our_round(double x)
static int
apply_step_offset(double offset)
{
if (TMX_ApplyStepOffset(-offset) < 0) {
DEBUG_LOG(LOGF_SysLinux, "adjtimex() failed");
return 0;
struct timex txc;
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
txc.time.tv_sec = -offset;
txc.time.tv_usec = 1.0e9 * (-offset - txc.time.tv_sec);
if (txc.time.tv_usec < 0) {
txc.time.tv_sec--;
txc.time.tv_usec += 1000000000;
}
if (SYS_Timex_Adjust(&txc, 1) < 0)
return 0;
return 1;
}
@@ -121,6 +148,7 @@ apply_step_offset(double offset)
static double
set_frequency(double freq_ppm)
{
struct timex txc;
long required_tick;
double required_freq;
int required_delta_tick;
@@ -144,14 +172,15 @@ set_frequency(double freq_ppm)
required_freq = -(freq_ppm - dhz * required_delta_tick);
required_tick = nominal_tick - required_delta_tick;
if (TMX_SetFrequency(&required_freq, required_tick) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex failed for set_frequency, freq_ppm=%10.4e required_freq=%10.4e required_tick=%ld",
freq_ppm, required_freq, required_tick);
}
txc.modes = ADJ_TICK | ADJ_FREQUENCY;
txc.freq = required_freq * FREQ_SCALE;
txc.tick = required_tick;
SYS_Timex_Adjust(&txc, 0);
current_delta_tick = required_delta_tick;
return dhz * current_delta_tick - required_freq;
return dhz * current_delta_tick - txc.freq / FREQ_SCALE;
}
/* ================================================== */
@@ -160,61 +189,15 @@ set_frequency(double freq_ppm)
static double
read_frequency(void)
{
long tick;
double freq;
struct timex txc;
if (TMX_GetFrequency(&freq, &tick) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
}
txc.modes = 0;
current_delta_tick = nominal_tick - tick;
return dhz * current_delta_tick - freq;
}
SYS_Timex_Adjust(&txc, 0);
/* ================================================== */
current_delta_tick = nominal_tick - txc.tick;
static void
set_leap(int leap)
{
int current_leap;
if (TMX_GetLeap(&current_leap) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
}
if (current_leap == leap)
return;
if (TMX_SetLeap(leap) < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed in set_leap");
}
LOG(LOGS_INFO, LOGF_SysLinux, "System clock status set to %s leap second",
leap ? (leap > 0 ? "insert" : "delete") : "not insert/delete");
}
/* ================================================== */
static void
set_sync_status(int synchronised, double est_error, double max_error)
{
if (synchronised) {
if (est_error > UNSYNC_MAXERROR)
est_error = UNSYNC_MAXERROR;
if (max_error >= UNSYNC_MAXERROR) {
max_error = UNSYNC_MAXERROR;
synchronised = 0;
}
} else {
est_error = max_error = UNSYNC_MAXERROR;
}
/* Clear the UNSYNC flag only if rtcsync is enabled */
if (!CNF_GetRtcSync())
synchronised = 0;
TMX_SetSync(synchronised, est_error, max_error);
return dhz * current_delta_tick - txc.freq / FREQ_SCALE;
}
/* ================================================== */
@@ -225,10 +208,16 @@ set_sync_status(int synchronised, double est_error, double max_error)
* a +/- 10% movement of tick away from the nominal value 1e6/USER_HZ. */
static int
guess_hz(int tick)
guess_hz(void)
{
int i, tick_lo, tick_hi, ihz;
struct timex txc;
int i, tick, tick_lo, tick_hi, ihz;
double tick_nominal;
txc.modes = 0;
SYS_Timex_Adjust(&txc, 0);
tick = txc.tick;
/* Pick off the hz=100 case first */
if (tick >= 9000 && tick <= 11000) {
return 100;
@@ -246,6 +235,8 @@ guess_hz(int tick)
}
/* oh dear. doomed. */
LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %d", tick);
return 0;
}
@@ -287,21 +278,12 @@ static void
get_version_specific_details(void)
{
int major, minor, patch;
long tick;
double freq;
struct utsname uts;
hz = get_hz();
if (!hz) {
if (TMX_GetFrequency(&freq, &tick) < 0)
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
hz = guess_hz(tick);
if (!hz)
LOG_FATAL(LOGF_SysLinux, "Can't determine hz from tick %ld", tick);
}
if (!hz)
hz = guess_hz();
dhz = (double) hz;
nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
@@ -344,6 +326,48 @@ get_version_specific_details(void)
hz, nominal_tick, max_tick_bias);
}
/* ================================================== */
static void
reset_adjtime_offset(void)
{
struct timex txc;
/* Reset adjtime() offset */
txc.modes = ADJ_OFFSET_SINGLESHOT;
txc.offset = 0;
SYS_Timex_Adjust(&txc, 0);
}
/* ================================================== */
static int
test_step_offset(void)
{
struct timex txc;
/* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
This seems to be the only way how to verify that the kernel really
supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
mode. */
txc.modes = MOD_MAXERROR;
txc.maxerror = 0;
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror != 0)
return 0;
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
txc.time.tv_sec = 0;
txc.time.tv_usec = 0;
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror < 100000)
return 0;
return 1;
}
/* ================================================== */
/* Initialisation code for this module */
@@ -352,20 +376,18 @@ SYS_Linux_Initialise(void)
{
get_version_specific_details();
if (TMX_ResetOffset() < 0) {
LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
}
reset_adjtime_offset();
if (have_setoffset && TMX_TestStepOffset() < 0) {
if (have_setoffset && !test_step_offset()) {
LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
have_setoffset = 0;
}
SYS_Generic_CompleteFreqDriver(1.0e6 * max_tick_bias / nominal_tick,
1.0 / tick_update_hz,
read_frequency, set_frequency,
have_setoffset ? apply_step_offset : NULL,
set_leap, set_sync_status);
SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
1.0 / tick_update_hz,
read_frequency, set_frequency,
have_setoffset ? apply_step_offset : NULL,
0.0, 0.0, NULL, NULL);
}
/* ================================================== */
@@ -374,42 +396,29 @@ SYS_Linux_Initialise(void)
void
SYS_Linux_Finalise(void)
{
SYS_Generic_Finalise();
SYS_Timex_Finalise();
}
/* ================================================== */
#ifdef FEAT_PRIVDROP
void
SYS_Linux_DropRoot(char *user)
SYS_Linux_DropRoot(uid_t uid, gid_t gid)
{
struct passwd *pw;
const char *cap_text;
cap_t cap;
if (user == NULL)
return;
if ((pw = getpwnam(user)) == NULL) {
LOG_FATAL(LOGF_SysLinux, "getpwnam(%s) failed", user);
}
if (prctl(PR_SET_KEEPCAPS, 1)) {
LOG_FATAL(LOGF_SysLinux, "prctl() failed");
}
if (setgroups(0, NULL)) {
LOG_FATAL(LOGF_SysLinux, "setgroups() failed");
}
UTI_DropRoot(uid, gid);
if (setgid(pw->pw_gid)) {
LOG_FATAL(LOGF_SysLinux, "setgid(%d) failed", pw->pw_gid);
}
/* Keep CAP_NET_BIND_SERVICE only if NTP port can be opened */
cap_text = CNF_GetNTPPort() ?
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
if (setuid(pw->pw_uid)) {
LOG_FATAL(LOGF_SysLinux, "setuid(%d) failed", pw->pw_uid);
}
if ((cap = cap_from_text("cap_net_bind_service,cap_sys_time=ep")) == NULL) {
if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL(LOGF_SysLinux, "cap_from_text() failed");
}
@@ -418,8 +427,153 @@ SYS_Linux_DropRoot(char *user)
}
cap_free(cap);
}
#endif
DEBUG_LOG(LOGF_SysLinux, "Privileges dropped to user %s", user);
/* ================================================== */
#ifdef FEAT_SCFILTER
static
void check_seccomp_applicability(void)
{
int mail_enabled;
double mail_threshold;
char *mail_user;
CNF_GetMailOnChange(&mail_enabled, &mail_threshold, &mail_user);
if (mail_enabled)
LOG_FATAL(LOGF_SysLinux, "mailonchange directive cannot be used with -F enabled");
}
/* ================================================== */
void
SYS_Linux_EnableSystemCallFilter(int level)
{
const int syscalls[] = {
/* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(gettimeofday), SCMP_SYS(settimeofday),
SCMP_SYS(time),
/* Process */
SCMP_SYS(clone), SCMP_SYS(exit), SCMP_SYS(exit_group), SCMP_SYS(getrlimit),
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigprocmask),
SCMP_SYS(set_tid_address), SCMP_SYS(sigreturn), SCMP_SYS(wait4),
/* Memory */
SCMP_SYS(brk), SCMP_SYS(madvise), SCMP_SYS(mmap), SCMP_SYS(mmap2),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
/* Filesystem */
SCMP_SYS(access), SCMP_SYS(chmod), SCMP_SYS(chown), SCMP_SYS(chown32),
SCMP_SYS(fstat), SCMP_SYS(fstat64), SCMP_SYS(lseek), SCMP_SYS(rename),
SCMP_SYS(stat), SCMP_SYS(stat64), SCMP_SYS(statfs), SCMP_SYS(statfs64),
SCMP_SYS(unlink),
/* Socket */
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
SCMP_SYS(recvfrom), SCMP_SYS(recvmsg), SCMP_SYS(sendmmsg),
SCMP_SYS(sendmsg), SCMP_SYS(sendto),
/* TODO: check socketcall arguments */
SCMP_SYS(socketcall),
/* General I/O */
SCMP_SYS(_newselect), SCMP_SYS(close), SCMP_SYS(open), SCMP_SYS(pipe),
SCMP_SYS(poll), SCMP_SYS(read), SCMP_SYS(futex), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(write),
/* Miscellaneous */
SCMP_SYS(uname),
};
const int socket_domains[] = {
AF_NETLINK, AF_UNIX, AF_INET,
#ifdef FEAT_IPV6
AF_INET6,
#endif
};
const static int socket_options[][2] = {
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND },
#ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
{ SOL_SOCKET, SO_TIMESTAMP },
};
const static int fcntls[] = { F_GETFD, F_SETFD };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
#ifdef FEAT_PPS
PTP_SYS_OFFSET,
#endif
#ifdef FEAT_PPS
PPS_FETCH,
#endif
#ifdef FEAT_RTC
RTC_RD_TIME, RTC_SET_TIME, RTC_UIE_ON, RTC_UIE_OFF,
#endif
};
scmp_filter_ctx *ctx;
int i;
/* Check if the chronyd configuration is supported */
check_seccomp_applicability();
/* Start the helper process, which will run without any seccomp filter. It
will be used for getaddrinfo(), for which it's difficult to maintain a
list of required system calls (with glibc it depends on what NSS modules
are installed and enabled on the system). */
PRV_StartHelper();
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP);
if (ctx == NULL)
LOG_FATAL(LOGF_SysLinux, "Failed to initialize seccomp");
/* Add system calls that are always allowed */
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0)
goto add_failed;
}
/* Allow sockets to be created only in selected domains */
for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
SCMP_A0(SCMP_CMP_EQ, socket_domains[i])) < 0)
goto add_failed;
}
/* Allow setting only selected sockets options */
for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 3,
SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]),
SCMP_A2(SCMP_CMP_EQ, socket_options[i][1]),
SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0)
goto add_failed;
}
/* Allow only selected fcntl calls */
for (i = 0; i < sizeof (fcntls) / sizeof (*fcntls); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 1,
SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0 ||
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl64), 1,
SCMP_A1(SCMP_CMP_EQ, fcntls[i])) < 0)
goto add_failed;
}
/* Allow only selected ioctls */
for (i = 0; i < sizeof (ioctls) / sizeof (*ioctls); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0)
goto add_failed;
}
if (seccomp_load(ctx) < 0)
LOG_FATAL(LOGF_SysLinux, "Failed to load seccomp rules");
LOG(LOGS_INFO, LOGF_SysLinux, "Loaded seccomp filter");
seccomp_release(ctx);
return;
add_failed:
LOG_FATAL(LOGF_SysLinux, "Failed to add seccomp rules");
}
#endif

View File

@@ -31,7 +31,9 @@ extern void SYS_Linux_Initialise(void);
extern void SYS_Linux_Finalise(void);
extern void SYS_Linux_DropRoot(char *user);
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid);
extern void SYS_Linux_EnableSystemCallFilter(int level);
extern void SYS_Linux_MemLockAll(int LockAll);

455
sys_macosx.c Normal file
View File

@@ -0,0 +1,455 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2001
* Copyright (C) J. Hannken-Illjes 2001
* 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.
*
**********************************************************************
=======================================================================
Driver file for the MacOS X operating system.
*/
#include "config.h"
#ifdef MACOSX
#include "sysincl.h"
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <pthread.h>
#include "sys_macosx.h"
#include "conf.h"
#include "localp.h"
#include "logging.h"
#include "sched.h"
#include "privops.h"
#include "util.h"
/* ================================================== */
/* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when
gettimeofday() returned T0 */
static double offset_register;
/* This register contains the epoch to which the offset is referenced */
static struct timeval T0;
/* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */
static double current_freq;
/* This register contains the number of seconds of adjustment that
were passed to adjtime last time it was called. */
static double adjustment_requested;
/* Interval in seconds between adjustments to cancel systematic drift */
#define DRIFT_REMOVAL_INTERVAL (4.0)
#define DRIFT_REMOVAL_INTERVAL_MIN (0.5)
/* If current_drift_removal_interval / drift_removal_interval exceeds this
ratio, then restart the drift removal timer */
#define DRIFT_REMOVAL_RESTART_RATIO (8.0)
static double drift_removal_interval;
static double current_drift_removal_interval;
static struct timeval Tdrift;
/* weighting applied to error in calculating drift_removal_interval */
#define ERROR_WEIGHT (0.5)
/* minimum resolution of current_frequency */
#define FREQUENCY_RES (1.0e-9)
#define NANOS_PER_MSEC (1000000ULL)
/* RTC synchronisation - once an hour */
static struct timeval last_rtc_sync;
#define RTC_SYNC_INTERVAL (60 * 60.0)
/* ================================================== */
static void
clock_initialise(void)
{
struct timeval newadj, oldadj;
offset_register = 0.0;
adjustment_requested = 0.0;
current_freq = 0.0;
drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
current_drift_removal_interval = DRIFT_REMOVAL_INTERVAL;
if (gettimeofday(&T0, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
Tdrift = T0;
last_rtc_sync = T0;
newadj.tv_sec = 0;
newadj.tv_usec = 0;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
}
/* ================================================== */
static void
clock_finalise(void)
{
/* Nothing to do yet */
}
/* ================================================== */
static void
start_adjust(void)
{
struct timeval newadj, oldadj;
struct timeval T1;
double elapsed, accrued_error, predicted_error, drift_removal_elapsed;
double adjust_required;
double rounding_error;
double old_adjust_remaining;
/* Determine the amount of error built up since the last adjustment */
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
accrued_error = elapsed * current_freq;
UTI_DiffTimevalsToDouble(&drift_removal_elapsed, &T1, &Tdrift);
/* To allow for the clock being stepped either forward or backwards, clamp
the elapsed time to bounds [ 0.0, current_drift_removal_interval ] */
drift_removal_elapsed = MIN(MAX(0.0, drift_removal_elapsed), current_drift_removal_interval);
predicted_error = (current_drift_removal_interval - drift_removal_elapsed) / 2.0 * current_freq;
DEBUG_LOG(LOGF_SysMacOSX, "drift_removal_elapsed: %.3f current_drift_removal_interval: %.3f predicted_error: %.3f",
1.0e6 * drift_removal_elapsed,
1.0e6 * current_drift_removal_interval,
1.0e6 * predicted_error);
adjust_required = - (accrued_error + offset_register + predicted_error);
UTI_DoubleToTimeval(adjust_required, &newadj);
UTI_TimevalToDouble(&newadj, &adjustment_requested);
rounding_error = adjust_required - adjustment_requested;
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
offset_register = rounding_error - old_adjust_remaining - predicted_error;
T0 = T1;
}
/* ================================================== */
static void
stop_adjust(void)
{
struct timeval T1;
struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved;
double elapsed, elapsed_plus_adjust;
zeroadj.tv_sec = 0;
zeroadj.tv_usec = 0;
if (PRV_AdjustTime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved;
offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
adjustment_requested = 0.0;
T0 = T1;
}
/* ================================================== */
/* Positive offset means system clock is fast of true time, therefore
slew backwards */
static void
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;
start_adjust();
}
/* ================================================== */
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, T1;
stop_adjust();
if (gettimeofday(&old_time, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (PRV_SetTime(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, -offset, &T1);
T0 = T1;
start_adjust();
return 1;
}
/* ================================================== */
static double
set_frequency(double new_freq_ppm)
{
stop_adjust();
current_freq = new_freq_ppm * 1.0e-6;
start_adjust();
return current_freq * 1.0e6;
}
/* ================================================== */
static double
read_frequency(void)
{
return current_freq * 1.0e6;
}
/* ================================================== */
static void
get_offset_correction(struct timeval *raw,
double *corr, double *err)
{
stop_adjust();
*corr = -offset_register;
start_adjust();
if (err)
*err = 0.0;
}
/* ================================================== */
/* Cancel systematic drift */
static SCH_TimeoutID drift_removal_id;
/* ================================================== */
/* This is the timer callback routine which is called periodically to
invoke a time adjustment to take out the machine's drift.
Otherwise, times reported through this software (e.g. by running
ntpdate from another machine) show the machine being correct (since
they correct for drift build-up), but any program on this machine
that reads the system time will be given an erroneous value, the
degree of error depending on how long it is since
get_offset_correction was last called. */
static void
drift_removal_timeout(SCH_ArbitraryArgument not_used)
{
stop_adjust();
if (gettimeofday(&Tdrift, NULL) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
current_drift_removal_interval = drift_removal_interval;
start_adjust();
drift_removal_id = SCH_AddTimeoutByDelay(drift_removal_interval, drift_removal_timeout, NULL);
}
/* ================================================== */
/* use est_error to calculate the drift_removal_interval and
update the RTC */
static void
set_sync_status(int synchronised, double est_error, double max_error)
{
double interval;
if (!synchronised) {
drift_removal_interval = MAX(drift_removal_interval, DRIFT_REMOVAL_INTERVAL);
} else {
if (CNF_GetRtcSync()) {
struct timeval now;
double rtc_sync_elapsed;
SCH_GetLastEventTime(NULL, NULL, &now);
UTI_DiffTimevalsToDouble(&rtc_sync_elapsed, &now, &last_rtc_sync);
if (fabs(rtc_sync_elapsed) >= RTC_SYNC_INTERVAL) {
/* update the RTC by applying a step of 0.0 secs */
apply_step_offset(0.0);
last_rtc_sync = now;
DEBUG_LOG(LOGF_SysMacOSX, "rtc synchronised");
}
}
interval = ERROR_WEIGHT * est_error / (fabs(current_freq) + FREQUENCY_RES);
drift_removal_interval = MAX(interval, DRIFT_REMOVAL_INTERVAL_MIN);
DEBUG_LOG(LOGF_SysMacOSX, "est_error: %.3f current_freq: %.3f est drift_removal_interval: %.3f act drift_removal_interval: %.3f",
est_error * 1.0e6, current_freq * 1.0e6, interval, drift_removal_interval);
}
if (current_drift_removal_interval / drift_removal_interval > DRIFT_REMOVAL_RESTART_RATIO) {
/* recover from a large est_error by resetting the timer */
SCH_ArbitraryArgument unused;
SCH_RemoveTimeout(drift_removal_id);
unused = NULL;
drift_removal_timeout(unused);
}
}
/* ================================================== */
/*
Give chronyd real time priority so that time critical calculations
are not pre-empted by the kernel.
*/
static int
set_realtime(void)
{
/* https://developer.apple.com/library/ios/technotes/tn2169/_index.html */
mach_timebase_info_data_t timebase_info;
double clock2abs;
thread_time_constraint_policy_data_t policy;
int kr;
mach_timebase_info(&timebase_info);
clock2abs = ((double)timebase_info.denom / (double)timebase_info.numer) * NANOS_PER_MSEC;
policy.period = 0;
policy.computation = (uint32_t)(5 * clock2abs); /* 5 ms of work */
policy.constraint = (uint32_t)(10 * clock2abs);
policy.preemptible = 0;
kr = thread_policy_set(
pthread_mach_thread_np(pthread_self()),
THREAD_TIME_CONSTRAINT_POLICY,
(thread_policy_t)&policy,
THREAD_TIME_CONSTRAINT_POLICY_COUNT);
if (kr != KERN_SUCCESS) {
LOG(LOGS_WARN, LOGF_SysMacOSX, "Cannot set real-time priority: %d", kr);
return -1;
}
return 0;
}
/* ================================================== */
void
SYS_MacOSX_SetScheduler(int SchedPriority)
{
if (SchedPriority) {
set_realtime();
}
}
/* ================================================== */
#ifdef FEAT_PRIVDROP
void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid)
{
PRV_StartHelper();
UTI_DropRoot(uid, gid);
}
#endif
/* ================================================== */
void
SYS_MacOSX_Initialise(void)
{
clock_initialise();
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset,
get_offset_correction,
NULL /* set_leap */,
set_sync_status);
drift_removal_id = SCH_AddTimeoutByDelay(drift_removal_interval, drift_removal_timeout, NULL);
}
/* ================================================== */
void
SYS_MacOSX_Finalise(void)
{
SCH_RemoveTimeout(drift_removal_id);
clock_finalise();
}
/* ================================================== */
#endif

View File

@@ -2,33 +2,37 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
*
* Copyright (C) Richard P. Curnow 1997-2001
* Copyright (C) J. Hannken-Illjes 2001
* 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.
*
*
**********************************************************************
=======================================================================
Header file for Solaris driver
Header file for MacOS X driver
*/
#ifndef GOT_SYS_SUNOS_H
#define GOT_SYS_SUNOS_H
#ifndef GOT_SYS_MACOSX_H
#define GOT_SYS_MACOSX_H
void SYS_SunOS_Initialise(void);
void SYS_SunOS_Finalise(void);
void SYS_MacOSX_SetScheduler(int SchedPriority);
void SYS_MacOSX_DropRoot(uid_t uid, gid_t gid);
void SYS_MacOSX_Initialise(void);
void SYS_MacOSX_Finalise(void);
#endif

View File

@@ -4,6 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2001
* Copyright (C) J. Hannken-Illjes 2001
* Copyright (C) Miroslav Lichvar 2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -22,176 +23,33 @@
=======================================================================
Driver file for the NetBSD operating system.
Driver file for the NetBSD and FreeBSD operating system.
*/
#include "config.h"
#ifdef __NetBSD__
#include <kvm.h>
#include <nlist.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include "sysincl.h"
#include "sys_netbsd.h"
#include "localp.h"
#include "sys_timex.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
/* ================================================== */
/* Maximum frequency offset accepted by the kernel (in ppm) */
#define MAX_FREQ 500.0
/* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when
gettimeofday() returned T0 */
/* Minimum assumed rate at which the kernel updates the clock frequency */
#define MIN_TICK_RATE 100
static double offset_register;
/* Interval between kernel updates of the adjtime() offset */
#define ADJTIME_UPDATE_INTERVAL 1.0
/* This register contains the epoch to which the offset is referenced */
/* Maximum adjtime() slew rate (in ppm) */
#define MAX_ADJTIME_SLEWRATE 5000.0
static struct timeval T0;
/* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */
static double current_freq;
/* This register contains the number of seconds of adjustment that
were passed to adjtime last time it was called. */
static double adjustment_requested;
/* Kernel parameters to calculate adjtime error. */
static int kern_tickadj;
static long kern_bigadj;
/* ================================================== */
static void
clock_initialise(void)
{
struct timeval newadj, oldadj;
offset_register = 0.0;
adjustment_requested = 0.0;
current_freq = 0.0;
if (gettimeofday(&T0, NULL) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
}
newadj.tv_sec = 0;
newadj.tv_usec = 0;
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
}
}
/* ================================================== */
static void
clock_finalise(void)
{
/* Nothing to do yet */
}
/* ================================================== */
static void
start_adjust(void)
{
struct timeval newadj, oldadj;
struct timeval T1;
double elapsed, accrued_error;
double adjust_required;
struct timeval exact_newadj;
long delta, tickdelta;
double rounding_error;
double old_adjust_remaining;
/* Determine the amount of error built up since the last adjustment */
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
accrued_error = elapsed * current_freq;
adjust_required = - (accrued_error + offset_register);
UTI_DoubleToTimeval(adjust_required, &exact_newadj);
/* At this point, we need to round the required adjustment the
same way the kernel does. */
delta = exact_newadj.tv_sec * 1000000 + exact_newadj.tv_usec;
if (delta > kern_bigadj || delta < -kern_bigadj)
tickdelta = 10 * kern_tickadj;
else
tickdelta = kern_tickadj;
if (delta % tickdelta)
delta = delta / tickdelta * tickdelta;
newadj.tv_sec = 0;
newadj.tv_usec = delta;
UTI_NormaliseTimeval(&newadj);
/* Add rounding error back onto offset register. */
UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
}
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
offset_register = rounding_error - old_adjust_remaining;
T0 = T1;
UTI_TimevalToDouble(&newadj, &adjustment_requested);
}
/* ================================================== */
static void
stop_adjust(void)
{
struct timeval T1;
struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved;
double elapsed, elapsed_plus_adjust;
zeroadj.tv_sec = 0;
zeroadj.tv_usec = 0;
if (adjtime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
}
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved;
offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
adjustment_requested = 0.0;
T0 = T1;
}
/* Minimum offset adjtime() slews faster than MAX_FREQ */
#define MIN_FASTSLEW_OFFSET 1.0
/* ================================================== */
@@ -201,61 +59,20 @@ stop_adjust(void)
static void
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;
start_adjust();
struct timeval newadj, oldadj;
}
UTI_DoubleToTimeval(-offset, &newadj);
/* ================================================== */
if (PRV_AdjustTime(&newadj, &oldadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, T1;
stop_adjust();
if (gettimeofday(&old_time, NULL) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
/* Add the old remaining adjustment if not zero */
UTI_TimevalToDouble(&oldadj, &offset);
if (offset != 0.0) {
UTI_AddDoubleToTimeval(&newadj, offset, &newadj);
if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
}
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysNetBSD, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
T0 = T1;
start_adjust();
return 1;
}
/* ================================================== */
static double
set_frequency(double new_freq_ppm)
{
stop_adjust();
current_freq = new_freq_ppm * 1.0e-6;
start_adjust();
return current_freq * 1.0e6;
}
/* ================================================== */
static double
read_frequency(void)
{
return current_freq * 1.0e6;
}
/* ================================================== */
@@ -264,11 +81,21 @@ static void
get_offset_correction(struct timeval *raw,
double *corr, double *err)
{
stop_adjust();
*corr = -offset_register;
start_adjust();
if (err)
*err = 0.0;
struct timeval remadj;
double adjustment_remaining;
if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
*corr = adjustment_remaining;
if (err) {
if (*corr != 0.0)
*err = 1.0e-6 * MAX_ADJTIME_SLEWRATE / ADJTIME_UPDATE_INTERVAL;
else
*err = 0.0;
}
}
/* ================================================== */
@@ -276,42 +103,10 @@ get_offset_correction(struct timeval *raw,
void
SYS_NetBSD_Initialise(void)
{
static struct nlist nl[] = {
{"_tickadj"},
{"_bigadj"},
{NULL}
};
kvm_t *kt;
kt = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
if (!kt) {
LOG_FATAL(LOGF_SysNetBSD, "Cannot open kvm");
}
if (kvm_nlist(kt, nl) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "Cannot read kernel symbols");
}
if (kvm_read(kt, nl[0].n_value, (char *)(&kern_tickadj), sizeof(int)) < 0) {
LOG_FATAL(LOGF_SysNetBSD, "Cannot read from _tickadj");
}
if (kvm_read(kt, nl[1].n_value, (char *)(&kern_bigadj), sizeof(long)) < 0) {
/* kernel doesn't have the symbol, use one second instead */
kern_bigadj = 1000000;
}
kvm_close(kt);
clock_initialise();
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset,
get_offset_correction,
NULL /* set_leap */,
NULL /* set_sync_status */);
SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE,
NULL, NULL, NULL,
MIN_FASTSLEW_OFFSET, MAX_ADJTIME_SLEWRATE,
accrue_offset, get_offset_correction);
}
/* ================================================== */
@@ -319,10 +114,31 @@ SYS_NetBSD_Initialise(void)
void
SYS_NetBSD_Finalise(void)
{
clock_finalise();
SYS_Timex_Finalise();
}
/* ================================================== */
#ifdef FEAT_PRIVDROP
void
SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
{
#ifdef NETBSD
int fd;
#endif
#endif /* __NetBSD__ */
/* On NetBSD the helper is used only for socket binding, but on FreeBSD
it's used also for setting and adjusting the system clock */
PRV_StartHelper();
UTI_DropRoot(uid, gid);
#ifdef NETBSD
/* Check if we have write access to /dev/clockctl */
fd = open("/dev/clockctl", O_WRONLY);
if (fd < 0)
LOG_FATAL(LOGF_SysNetBSD, "Can't write to /dev/clockctl");
close(fd);
#endif
}
#endif

View File

@@ -32,4 +32,6 @@ void SYS_NetBSD_Initialise(void);
void SYS_NetBSD_Finalise(void);
void SYS_NetBSD_DropRoot(uid_t uid, gid_t gid);
#endif

View File

@@ -26,420 +26,21 @@
#include "config.h"
#ifdef SOLARIS
#include <kvm.h>
#include <fcntl.h>
#include <nlist.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <stdio.h>
#include "sysincl.h"
#include "privops.h"
#include "sys_solaris.h"
#include "localp.h"
#include "sched.h"
#include "logging.h"
#include "sys_timex.h"
#include "util.h"
/* ================================================== */
/* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when
gettimeofday() returned T0 */
static double offset_register;
/* This register contains the epoch to which the offset is referenced */
static struct timeval T0;
/* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */
static double current_freq;
/* This register contains the number of seconds of adjustment that
were passed to adjtime last time it was called. */
static double adjustment_requested;
/* ================================================== */
/* On Solaris 2.5 & 2.5.1, passing an argument of zero as the new
delta to adjtime does not zero out the adjustment - the remaining
adjustment is returned as the old delta arg, but the adjustment keeps
running. To get round this, we set adjustments of +/-1us when we
really want zero. Alternate adjustments are used to avoid a drift
from building up. */
static struct timeval zeroes[2] = {
{0, 1},
{-1, 999999}
};
static int index=0;
/* If 1, need to run dosynctodr(). If 0, don't */
static int need_dosynctodr = -1;
#define GET_ZERO (zeroes[index^=1])
/* ================================================== */
static void
clock_initialise(void)
{
struct timeval newadj, oldadj;
offset_register = 0.0;
adjustment_requested = 0.0;
current_freq = 0.0;
if (gettimeofday(&T0, NULL) < 0) {
LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed");
}
newadj = GET_ZERO;
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysSolaris, "adjtime() failed");
}
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysSolaris, "adjtime() failed");
}
}
/* ================================================== */
static void
clock_finalise(void)
{
/* Nothing to do yet */
}
/* ================================================== */
static void
start_adjust(void)
{
struct timeval newadj, oldadj;
struct timeval T1;
double elapsed, accrued_error;
double adjust_required;
struct timeval exact_newadj;
double rounding_error;
double old_adjust_remaining;
/* Determine the amount of error built up since the last adjustment */
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
accrued_error = elapsed * current_freq;
adjust_required = - (accrued_error + offset_register);
UTI_DoubleToTimeval(adjust_required, &exact_newadj);
/* At this point, we will need to call the adjustment rounding
algorithm in the system-specific layer. For now, just assume the
adjustment can be applied exactly. */
newadj = exact_newadj;
/* Want to *add* rounding error back onto offset register */
UTI_DiffTimevalsToDouble(&rounding_error, &exact_newadj, &newadj);
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysSolaris, "adjtime() failed");
}
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
offset_register = rounding_error - old_adjust_remaining;
T0 = T1;
UTI_TimevalToDouble(&newadj, &adjustment_requested);
}
/* ================================================== */
static void
stop_adjust(void)
{
struct timeval T1;
struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved;
double elapsed, elapsed_plus_adjust;
zeroadj = GET_ZERO;
if (adjtime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysSolaris, "adjtime() failed");
}
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved;
offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
adjustment_requested = 0.0;
T0 = T1;
}
/* ================================================== */
/* Positive offset means system clock is fast of true time, therefore
slew backwards */
static void
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;
start_adjust();
}
/* ================================================== */
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, rounded_new_time, T1;
double rounding_error;
stop_adjust();
if (gettimeofday(&old_time, NULL) < 0) {
LOG_FATAL(LOGF_SysSolaris, "gettimeofday() failed");
}
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
/* The settimeofday function (on Solaris 2.5/Sparc20 at least) does
not work quite as we would want. The time we want to set is
rounded to the nearest second and that time is used. Also, the
clock appears to start from that second boundary plus about 4ms.
For now we'll tolerate this small error. */
rounded_new_time.tv_usec = 0;
if (new_time.tv_usec >= 500000) {
rounded_new_time.tv_sec = new_time.tv_sec + 1;
} else {
rounded_new_time.tv_sec = new_time.tv_sec;
}
UTI_DiffTimevalsToDouble(&rounding_error, &rounded_new_time, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysSolaris, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
T0 = T1;
offset_register += rounding_error;
start_adjust();
return 1;
}
/* ================================================== */
static double
set_frequency(double new_freq_ppm)
{
stop_adjust();
current_freq = new_freq_ppm * 1.0e-6;
start_adjust();
return current_freq * 1.0e6;
}
/* ================================================== */
static double
read_frequency(void)
{
return current_freq * 1.0e6;
}
/* ================================================== */
static void
get_offset_correction(struct timeval *raw,
double *corr, double *err)
{
stop_adjust();
*corr = -offset_register;
start_adjust();
if (err)
*err = 0.0;
}
/* ================================================== */
static void
immediate_step(void)
{
}
/* ================================================== */
/* Interval in seconds between adjustments to cancel systematic drift */
#define DRIFT_REMOVAL_INTERVAL (4.0)
static int drift_removal_running = 0;
static SCH_TimeoutID drift_removal_id;
/* ================================================== */
/* This is the timer callback routine which is called periodically to
invoke a time adjustment to take out the machine's drift.
Otherwise, times reported through this software (e.g. by running
ntpdate from another machine) show the machine being correct (since
they correct for drift build-up), but any program on this machine
that reads the system time will be given an erroneous value, the
degree of error depending on how long it is since
get_offset_correction was last called. */
static void
drift_removal_timeout(SCH_ArbitraryArgument not_used)
{
stop_adjust();
start_adjust();
drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
}
/* ================================================== */
static void
check_need_dosynctodr(void)
{
struct utsname name;
int result;
int major, minor, veryminor, n_fields;
result = uname(&name);
if (result < 0) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot use uname to detect Solaris version");
need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */
return;
}
n_fields = sscanf(name.release, "%d.%d.%d\n", &major, &minor, &veryminor);
if (n_fields < 2) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Solaris version doesn't appear to be of the form X.Y[.Z]");
need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */
return;
}
if (major != 5) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Solaris major version doesn't appear to be 5");
need_dosynctodr = 0; /* Assume recent Solaris where it isn't needed */
return;
}
/* The 'rule of thumb' is that from Solaris 2.6 onwards, dosynctodr() doesn't
* need to be called, and in fact it is counter-productive to do so. For
* earlier versions, it is required. */
if (minor < 6) {
need_dosynctodr = 1;
} else {
need_dosynctodr = 0;
}
}
/* ================================================== */
static void
set_dosynctodr(unsigned long on_off)
{
static struct nlist nl[] = {
{"dosynctodr"},
{NULL}
};
kvm_t *kt;
unsigned long read_back;
assert(on_off == 1 || on_off == 0);
kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
if (!kt) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot open kvm to change dosynctodr");
return;
}
if (kvm_nlist(kt, nl) < 0) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot read dosynctodr in nlist");
kvm_close(kt);
return;
}
if (kvm_write(kt, nl[0].n_value, (char *)(&on_off), sizeof(unsigned long)) < 0) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot write to dosynctodr");
kvm_close(kt);
return;
}
if (kvm_read(kt, nl[0].n_value, (char *)(&read_back), sizeof(unsigned long)) < 0) {
LOG(LOGS_ERR, LOGF_SysSolaris, "Cannot read from dosynctodr");
kvm_close(kt);
return;
}
kvm_close(kt);
assert(read_back == on_off);
}
/* ================================================== */
void
SYS_Solaris_Initialise(void)
{
check_need_dosynctodr();
/* Need to do KVM stuff to turn off dosynctodr. */
clock_initialise();
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset,
get_offset_correction,
NULL /* set_leap */,
NULL /* set_sync_status */);
/* Turn off the kernel switch that keeps the system clock in step
with the non-volatile clock */
if (need_dosynctodr) {
set_dosynctodr(0);
}
drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
drift_removal_running = 1;
/* The kernel allows the frequency to be set in the full range off int32_t */
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
0.0, 0.0, NULL, NULL);
}
/* ================================================== */
@@ -447,21 +48,16 @@ SYS_Solaris_Initialise(void)
void
SYS_Solaris_Finalise(void)
{
if (drift_removal_running) {
SCH_RemoveTimeout(drift_removal_id);
}
clock_finalise();
/* When exiting, we want to return the machine to its 'autonomous'
tracking mode */
if (need_dosynctodr) {
set_dosynctodr(1);
}
SYS_Timex_Finalise();
}
/* ================================================== */
#endif /* SOLARIS */
#ifdef FEAT_PRIVDROP
void
SYS_Solaris_DropRoot(uid_t uid, gid_t gid)
{
PRV_StartHelper();
UTI_DropRoot(uid, gid);
}
#endif

View File

@@ -31,4 +31,6 @@ void SYS_Solaris_Initialise(void);
void SYS_Solaris_Finalise(void);
void SYS_Solaris_DropRoot(uid_t uid, gid_t gid);
#endif

View File

@@ -1,418 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
*
* 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.
*
**********************************************************************
=======================================================================
Driver file for the SunOS 4.1.x operating system.
*/
#include "config.h"
#ifdef SUNOS
#include <kvm.h>
#include <fcntl.h>
#include <nlist.h>
#include <assert.h>
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#include "sys_sunos.h"
#include "localp.h"
#include "logging.h"
#include "util.h"
#include "sched.h"
/* ================================================== */
/* This register contains the number of seconds by which the local
clock was estimated to be fast of reference time at the epoch when
gettimeofday() returned T0 */
static double offset_register;
/* This register contains the epoch to which the offset is referenced */
static struct timeval T0;
/* This register contains the current estimate of the system
frequency, in absolute (NOT ppm) */
static double current_freq;
/* This register contains the number of seconds of adjustment that
were passed to adjtime last time it was called. */
static double adjustment_requested;
/* Eventually, this needs to be a user-defined parameter - e.g. user
might want 5 to get much finer resolution like xntpd. We stick
with a reasonable number so that slewing can work.
This value has to be a factor of 1 million, otherwise the noddy
method we use for rounding an adjustment to the nearest multiple of
this value won't work!!
*/
static unsigned long our_tickadj = 100;
/* ================================================== */
static void
clock_initialise(void)
{
struct timeval newadj, oldadj;
offset_register = 0.0;
adjustment_requested = 0.0;
current_freq = 0.0;
if (gettimeofday(&T0, NULL) < 0) {
LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed");
}
newadj.tv_sec = 0;
newadj.tv_usec = 0;
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysSunOS, "adjtime() failed");
}
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysSunOS, "adjtime() failed");
}
}
/* ================================================== */
static void
clock_finalise(void)
{
/* Nothing to do yet */
}
/* ================================================== */
static void
start_adjust(void)
{
struct timeval newadj, oldadj;
struct timeval T1;
double elapsed, accrued_error;
double adjust_required;
struct timeval exact_newadj;
double rounding_error;
double old_adjust_remaining;
long remainder, multiplier;
/* Determine the amount of error built up since the last adjustment */
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
accrued_error = elapsed * current_freq;
adjust_required = - (accrued_error + offset_register);
UTI_DoubleToTimeval(adjust_required, &exact_newadj);
/* At this point, we need to round the required adjustment to the
closest multiple of _tickadj --- because SunOS can't process
other adjustments exactly and will silently discard the residual.
Obviously such behaviour can't be tolerated for us. */
newadj = exact_newadj;
remainder = newadj.tv_usec % our_tickadj;
multiplier = newadj.tv_usec / our_tickadj;
if (remainder >= (our_tickadj >> 1)) {
newadj.tv_usec = (multiplier + 1) * our_tickadj;
} else {
newadj.tv_usec = multiplier * our_tickadj;
}
UTI_NormaliseTimeval(&newadj);
/* Want to *add* rounding error back onto offset register. Note
that the exact adjustment was the offset register *negated* */
UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
if (adjtime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysSunOS, "adjtime() failed");
}
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
offset_register = rounding_error - old_adjust_remaining;
T0 = T1;
UTI_TimevalToDouble(&newadj, &adjustment_requested);
}
/* ================================================== */
static void
stop_adjust(void)
{
struct timeval T1;
struct timeval zeroadj, remadj;
double adjustment_remaining, adjustment_achieved;
double gap;
double elapsed, elapsed_plus_adjust;
zeroadj.tv_sec = 0;
zeroadj.tv_usec = 0;
if (adjtime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysSunOS, "adjtime() failed");
}
if (gettimeofday(&T1, NULL) < 0) {
LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed");
}
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
adjustment_achieved = adjustment_requested - adjustment_remaining;
elapsed_plus_adjust = elapsed - adjustment_achieved;
offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
adjustment_requested = 0.0;
T0 = T1;
}
/* ================================================== */
/* Positive offset means system clock is fast of true time, therefore
slew backwards */
static void
accrue_offset(double offset, double corr_rate)
{
stop_adjust();
offset_register += offset;
start_adjust();
}
/* ================================================== */
/* Positive offset means system clock is fast of true time, therefore
step backwards */
static int
apply_step_offset(double offset)
{
struct timeval old_time, new_time, T1;
stop_adjust();
if (gettimeofday(&old_time, NULL) < 0) {
LOG_FATAL(LOGF_SysSunOS, "gettimeofday() failed");
}
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysSunOS, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
T0 = T1;
start_adjust();
return 1;
}
/* ================================================== */
static double
set_frequency(double new_freq_ppm)
{
stop_adjust();
current_freq = new_freq_ppm * 1.0e-6;
start_adjust();
return current_freq * 1.0e6;
}
/* ================================================== */
static double
read_frequency(void)
{
return current_freq * 1.0e6;
}
/* ================================================== */
static void
get_offset_correction(struct timeval *raw,
double *corr, double *err)
{
stop_adjust();
*corr = -offset_register;
start_adjust();
if (err)
*err = 0.0;
}
/* ================================================== */
static void
immediate_step(void)
{
}
/* ================================================== */
/* Interval in seconds between adjustments to cancel systematic drift */
#define DRIFT_REMOVAL_INTERVAL (4.0)
static int drift_removal_running = 0;
static SCH_TimeoutID drift_removal_id;
/* ================================================== */
/* This is the timer callback routine which is called periodically to
invoke a time adjustment to take out the machine's drift.
Otherwise, times reported through this software (e.g. by running
ntpdate from another machine) show the machine being correct (since
they correct for drift build-up), but any program on this machine
that reads the system time will be given an erroneous value, the
degree of error depending on how long it is since
get_offset_correction was last called. */
static void
drift_removal_timeout(SCH_ArbitraryArgument not_used)
{
stop_adjust();
start_adjust();
drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
}
/* ================================================== */
static void
setup_kernel(unsigned long on_off)
{
static struct nlist nl[] = {
{"_dosynctodr"},
{"_tick"},
{"_tickadj"},
{NULL}
};
kvm_t *kt;
unsigned long read_back;
unsigned long our_tick = 10000;
unsigned long default_tickadj = 625;
assert(on_off == 1 || on_off == 0);
kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
if (!kt) {
LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot open kvm");
return;
}
if (kvm_nlist(kt, nl) < 0) {
LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot read kernel symbols");
kvm_close(kt);
return;
}
if (kvm_write(kt, nl[0].n_value, (char *)(&on_off), sizeof(unsigned long)) < 0) {
LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _dosynctodr");
kvm_close(kt);
return;
}
if (kvm_write(kt, nl[1].n_value, (char *)(&our_tick), sizeof(unsigned long)) < 0) {
LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _tick");
kvm_close(kt);
return;
}
if (kvm_write(kt, nl[2].n_value,
(char *)(on_off ? &default_tickadj : &our_tickadj),
sizeof(unsigned long)) < 0) {
LOG(LOGS_ERR, LOGF_SysSunOS, "Cannot write to _tickadj");
kvm_close(kt);
return;
}
kvm_close(kt);
}
/* ================================================== */
void
SYS_SunOS_Initialise(void)
{
/* Need to do KVM stuff to turn off dosynctodr. */
clock_initialise();
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, apply_step_offset,
get_offset_correction,
NULL /* set_leap */,
NULL /* set_sync_status */);
/* Turn off the kernel switch that keeps the system clock in step
with the non-volatile clock */
setup_kernel(0);
drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
drift_removal_running = 1;
}
/* ================================================== */
void
SYS_SunOS_Finalise(void)
{
if (drift_removal_running) {
SCH_RemoveTimeout(drift_removal_id);
}
/* Turn dosynctodr back on?? */
clock_finalise();
/* When exiting, we want to return the machine to its 'autonomous'
tracking mode */
setup_kernel(1);
}
/* ================================================== */
#endif /* SUNOS */

249
sys_timex.c Normal file
View File

@@ -0,0 +1,249 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-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.
*
**********************************************************************
=======================================================================
Driver for systems that implement the adjtimex()/ntp_adjtime() system call
*/
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "privops.h"
#include "sys_generic.h"
#include "sys_timex.h"
#include "logging.h"
#ifdef PRIVOPS_ADJUSTTIMEX
#define NTP_ADJTIME PRV_AdjustTimex
#define NTP_ADJTIME_NAME "ntp_adjtime"
#else
#ifdef LINUX
#define NTP_ADJTIME adjtimex
#define NTP_ADJTIME_NAME "adjtimex"
#else
#define NTP_ADJTIME ntp_adjtime
#define NTP_ADJTIME_NAME "ntp_adjtime"
#endif
#endif
/* Maximum frequency offset accepted by the kernel (in ppm) */
#define MAX_FREQ 500.0
/* Frequency scale to convert from ppm to the timex freq */
#define FREQ_SCALE (double)(1 << 16)
/* Threshold for the timex maxerror when the kernel sets the UNSYNC flag */
#define MAX_SYNC_ERROR 16.0
/* Minimum assumed rate at which the kernel updates the clock frequency */
#define MIN_TICK_RATE 100
/* Saved timex status */
static int status;
/* ================================================== */
static double
read_frequency(void)
{
struct timex txc;
txc.modes = 0;
SYS_Timex_Adjust(&txc, 0);
return txc.freq / -FREQ_SCALE;
}
/* ================================================== */
static double
set_frequency(double freq_ppm)
{
struct timex txc;
txc.modes = MOD_FREQUENCY;
txc.freq = freq_ppm * -FREQ_SCALE;
SYS_Timex_Adjust(&txc, 0);
return txc.freq / -FREQ_SCALE;
}
/* ================================================== */
static void
set_leap(int leap)
{
struct timex txc;
int applied;
applied = 0;
if (!leap) {
txc.modes = 0;
if (SYS_Timex_Adjust(&txc, 1) == TIME_WAIT)
applied = 1;
}
status &= ~(STA_INS | STA_DEL);
if (leap > 0)
status |= STA_INS;
else if (leap < 0)
status |= STA_DEL;
txc.modes = MOD_STATUS;
txc.status = status;
SYS_Timex_Adjust(&txc, 0);
LOG(LOGS_INFO, LOGF_SysTimex, "System clock status %s leap second",
leap ? (leap > 0 ? "set to insert" : "set to delete") :
(applied ? "reset after" : "set to not insert/delete"));
}
/* ================================================== */
static void
set_sync_status(int synchronised, double est_error, double max_error)
{
struct timex txc;
if (synchronised) {
if (est_error > MAX_SYNC_ERROR)
est_error = MAX_SYNC_ERROR;
if (max_error >= MAX_SYNC_ERROR) {
max_error = MAX_SYNC_ERROR;
synchronised = 0;
}
} else {
est_error = max_error = MAX_SYNC_ERROR;
}
#ifdef LINUX
/* On Linux clear the UNSYNC flag only if rtcsync is enabled */
if (!CNF_GetRtcSync())
synchronised = 0;
#endif
if (synchronised)
status &= ~STA_UNSYNC;
else
status |= STA_UNSYNC;
txc.modes = MOD_STATUS | MOD_ESTERROR | MOD_MAXERROR;
txc.status = status;
txc.esterror = est_error * 1.0e6;
txc.maxerror = max_error * 1.0e6;
SYS_Timex_Adjust(&txc, 1);
}
/* ================================================== */
static void
initialise_timex(void)
{
struct timex txc;
status = STA_UNSYNC;
/* Reset PLL offset */
txc.modes = MOD_OFFSET | MOD_STATUS;
txc.status = STA_PLL | status;
txc.offset = 0;
SYS_Timex_Adjust(&txc, 0);
/* Turn PLL off */
txc.modes = MOD_STATUS;
txc.status = status;
SYS_Timex_Adjust(&txc, 0);
}
/* ================================================== */
void
SYS_Timex_Initialise(void)
{
SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL, NULL, NULL,
0.0, 0.0, NULL, NULL);
}
/* ================================================== */
void
SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay,
lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_freq,
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
double min_fastslew_offset, double max_fastslew_rate,
lcl_AccrueOffsetDriver sys_accrue_offset,
lcl_OffsetCorrectionDriver sys_get_offset_correction)
{
initialise_timex();
SYS_Generic_CompleteFreqDriver(max_set_freq_ppm, max_set_freq_delay,
sys_read_freq ? sys_read_freq : read_frequency,
sys_set_freq ? sys_set_freq : set_frequency,
sys_apply_step_offset,
min_fastslew_offset, max_fastslew_rate,
sys_accrue_offset, sys_get_offset_correction,
set_leap, set_sync_status);
}
/* ================================================== */
void
SYS_Timex_Finalise(void)
{
SYS_Generic_Finalise();
}
/* ================================================== */
int
SYS_Timex_Adjust(struct timex *txc, int ignore_error)
{
int state;
#ifdef SOLARIS
/* The kernel seems to check the constant even when it's not being set */
if (!(txc->modes & MOD_TIMECONST))
txc->constant = 10;
#endif
state = NTP_ADJTIME(txc);
if (state < 0) {
if (!ignore_error)
LOG_FATAL(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
txc->modes, strerror(errno));
else
DEBUG_LOG(LOGF_SysTimex, NTP_ADJTIME_NAME"(0x%x) failed : %s",
txc->modes, strerror(errno));
}
return state;
}

48
sys_timex.h Normal file
View File

@@ -0,0 +1,48 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2015
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for a driver based on the adjtimex()/ntp_adjtime() function
*/
#ifndef GOT_SYS_TIMEX_H
#define GOT_SYS_TIMEX_H
#include "localp.h"
extern void SYS_Timex_Initialise(void);
/* Initialise with some driver functions replaced with special versions */
extern void SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay,
lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_freq,
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
double min_fastslew_offset, double max_fastslew_rate,
lcl_AccrueOffsetDriver sys_accrue_offset,
lcl_OffsetCorrectionDriver sys_get_offset_correction);
extern void SYS_Timex_Finalise(void);
/* Wrapper for adjtimex()/ntp_adjtime() */
extern int SYS_Timex_Adjust(struct timex *txc, int ignore_error);
#endif /* GOT_SYS_GENERIC_H */

View File

@@ -29,22 +29,17 @@
#ifndef GOT_SYSINCL_H
#define GOT_SYSINCL_H
#if defined (SOLARIS) || defined(SUNOS) || defined(LINUX) || defined(__NetBSD__)
#if !defined(__NetBSD__) && !defined(__FreeBSD__)
#include <alloca.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <float.h>
#if !defined(__FreeBSD__)
#include <malloc.h>
#endif
#include <glob.h>
#include <grp.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pwd.h>
#include <resolv.h>
#include <signal.h>
#include <stdarg.h>
@@ -59,8 +54,14 @@
#include <sys/types.h>
#include <sys/un.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#if defined(LINUX) || defined(FREEBSD) || defined(NETBSD) || defined(SOLARIS)
#include <sys/timex.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
@@ -70,62 +71,9 @@
/* Tough */
#endif
/* One or other of these to make getsid() visible */
#define __EXTENSIONS__ 1
#define __USE_XOPEN_EXTENDED 1
#include <unistd.h>
#endif
#ifdef FEAT_IPV6
/* For inet_ntop() */
#include <arpa/inet.h>
#endif
#if defined (SOLARIS) || defined(SUNOS)
/* Only needed on these platforms, and doesn't exist on some Linux
versions. */
#include <nlist.h>
#endif
#if defined (WINNT)
/* Designed to work with the GCC from the GNAT-3.10 for Win32
distribution */
#define Win32_Winsock
#include <assert.h>
#include <ctype.h>
#if 1
/* Cheat and inline the necessary bits from <errno.h>. We don't
include it directly because it redefines some EXXX constants that
conflict with <windows32/sockets.h> (included by <windows.h>) */
int* _errno();
int* __doserrno();
#define errno (*_errno())
#define _doserrno (*__doserrno())
#define ENOENT 2
#else
#include <errno.h>
#endif
#include <float.h>
#include <math.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <windows.h>
#endif
#endif /* GOT_SYSINCL_H */

View File

@@ -5,8 +5,14 @@
cd ../..
for opts in \
"--enable-debug" \
"--enable-scfilter" \
"--disable-asyncdns" \
"--disable-ipv6" \
"--disable-privdrop" \
"--disable-readline" \
"--disable-rtc" \
"--disable-sechash" \
"--disable-cmdmon" \
"--disable-ntp" \
"--disable-refclock" \
@@ -15,5 +21,5 @@ for opts in \
"--disable-cmdmon --disable-ntp --disable-refclock"
do
./configure $opts
make || exit 1
make "$@" || exit 1
done

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