Compare commits

...

224 Commits

Author SHA1 Message Date
Miroslav Lichvar
db286ca6ea doc: update NEWS 2016-11-21 12:03:45 +01:00
Miroslav Lichvar
85fbfd9b15 sources: add new status for sources that overlap trusted sources
Sources that overlap trusted sources should be displayed in the chronyc
sources report with the '-' symbol and they shouldn't trigger a
replacement.
2016-11-21 12:03:45 +01:00
Miroslav Lichvar
b819c7fe55 refclock: don't compare sample time with samples from previous poll
This is an improvement of commit 0a848e2528.
2016-11-21 12:03:27 +01:00
Miroslav Lichvar
2b5c86b9a3 refclock: fix check for old samples
The fix in commit 0a848e2528 was
incorrect.
2016-11-21 12:03:15 +01:00
Miroslav Lichvar
0a848e2528 refclock: require new samples to have newer timestamp
If all or most SHM/SOCK samples collected in a polling interval had the
same local timestamp, the dispersion could end up as nan, which could
trigger an assert failure later in the code.

Before accumulating a refclock sample, check if the timestamp is newer
than the previous one.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
b443ec5ea5 test: add smooth unit test 2016-11-21 12:02:51 +01:00
Miroslav Lichvar
37d1467368 smooth: fix selection of 1st stage direction
When the smoothing process is updated with extremely small (e.g.
sub-nanosecond) values, both directions may give a negative length of
the 1st or 3rd stage due to numerical errors and the selection will fail
an in assertion. Rework the code to select the direction which gives a
smaller error.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
1d9d19d76b client: flush stdout after printing prompt
Apparently fgets() doesn't flush stdout in some libc implementations.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
9603f0552a client: fix printing of negative poll in sources report again
This was broken in commit 3f51805e62.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
12befc2afd ntp: fix processing of kernel timestamps on non-Linux systems
When the SO_TIMESTAMP socket option was enabled, the expected type of
control messages containing timestamps was SO_TIMESTAMP instead of
SCM_TIMESTAMP. This worked on Linux, where the two values are equal, but
not on the other supported systems. The timestamps were ignored and this
probably worsened the accuracy and stability of the synchronisation.
2016-11-21 12:02:51 +01:00
Miroslav Lichvar
78f20f7b3e conf: fix parsing of refclock directive
Don't accept refclock directive which has as the last argument an option
that requires a value.
2016-11-21 12:02:51 +01:00
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
125 changed files with 8550 additions and 7738 deletions

15
.gitignore vendored
View File

@@ -7,18 +7,19 @@
tags
/RELEASES
/Makefile
/chrony.conf.5
/chrony.info
/chrony.html
/chrony.texi
/chrony.txt
/chronyc
/chronyc.1
/chronyd
/chronyd.8
/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@
@@ -74,11 +71,12 @@ $(HASH_OBJ) : $(patsubst %.o,%.c,$(HASH_OBJ))
distclean : clean
-rm -f .DS_Store
-rm -f Makefile
-rm -f chrony.conf.5 chrony.texi chronyc.1 chronyd.8
-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
@@ -96,9 +94,6 @@ 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)$(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)$(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,12 +101,13 @@ install: chronyd chronyc
chmod 755 $(DESTDIR)$(SBINDIR)/chronyd
cp chronyc $(DESTDIR)$(BINDIR)/chronyc
chmod 755 $(DESTDIR)$(BINDIR)/chronyc
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 $<
@@ -120,30 +116,9 @@ install: chronyd chronyc
$(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

80
NEWS
View File

@@ -1,3 +1,75 @@
New in version 2.4.1
====================
Bug fixes
---------
* Fix processing of kernel timestamps on non-Linux systems
* Fix crash with smoothtime directive
* Fix validation of refclock sample times
* Fix parsing of refclock directive
New in version 2.4
==================
Enhancements
------------
* Add orphan option to local directive for orphan mode compatible with ntpd
* Add distance option to local directive to set activation threshold
(1 second by default)
* Add maxdrift directive to set maximum allowed drift of system clock
* Try to replace NTP sources exceeding maximum distance
* Randomise source replacement to avoid getting stuck with bad sources
* Randomise selection of sources from pools on start
* Ignore reference timestamp as ntpd doesn't always set it correctly
* Modify tracking report to use same values as seen by NTP clients
* Add -c option to chronyc to write reports in CSV format
* Provide detailed manual pages
Bug fixes
---------
* Fix SOCK refclock to work correctly when not specified as last refclock
* Fix initstepslew and -q/-Q options to accept time from own NTP clients
* Fix authentication with keys using 512-bit hash functions
* Fix crash on exit when multiple signals are received
* Fix conversion of very small floating-point numbers in command packets
Removed features
----------------
* Drop documentation in Texinfo format
New in version 2.3
==================
Enhancements
------------
* Add support for NTP and command response rate limiting
* Add support for dropping root privileges on Mac OS X, FreeBSD, Solaris
* Add require and trust options for source selection
* Enable logchange by default (1 second threshold)
* Set RTC on Mac OS X with rtcsync directive
* Allow binding to NTP port after dropping root privileges on NetBSD
* Drop CAP_NET_BIND_SERVICE capability on Linux when NTP port is disabled
* Resolve names in separate process when seccomp filter is enabled
* Replace old records in client log when memory limit is reached
* Don't reveal local time and synchronisation state in client packets
* Don't keep client sockets open for longer than necessary
* Ignore poll in KoD RATE packets as ntpd doesn't always set it correctly
* Warn when using keys shorter than 80 bits
* Add keygen command to generate random keys easily
* Add serverstats command to report NTP and command packet statistics
Bug fixes
---------
* Fix clock correction after making step on Mac OS X
* Fix building on Solaris
New in version 2.2.1
====================
Security fixes
--------------
* Restrict authentication of NTP server/peer to specified key (CVE-2016-1567)
New in version 2.2
==================
@@ -5,22 +77,26 @@ 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
* 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 Solaris
* Fix building on NetBSD, Solaris
* Restore time from driftfile with -s option if reading RTC failed
Removed features

17
README
View File

@@ -53,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?
=================================
@@ -84,6 +84,12 @@ 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
=======
@@ -105,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 :
@@ -130,6 +140,8 @@ Erik Bryer <ebryer@spots.ab.ca>
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.
@@ -216,6 +228,9 @@ Andreas Piesk <apiesk@virbus.de>
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

@@ -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 */

View File

@@ -103,6 +103,12 @@ ARR_GetElement(ARR_Instance array, unsigned int index)
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;
}

56
candm.h
View File

@@ -91,11 +91,10 @@
#define REQ_SMOOTHING 51
#define REQ_SMOOTHTIME 52
#define REQ_REFRESH 53
#define N_REQUEST_TYPES 54
/* 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_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 {
@@ -213,6 +212,8 @@ typedef struct {
typedef struct {
int32_t on_off;
int32_t stratum;
Float distance;
int32_t orphan;
int32_t EOR;
} REQ_Local;
@@ -243,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;
@@ -284,7 +287,7 @@ typedef struct {
typedef struct {
uint32_t first_index;
uint32_t n_indices;
uint32_t n_clients;
int32_t EOR;
} REQ_ClientAccessesByIndex;
@@ -335,9 +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, smoothing report, smoothtime command
makestep, smoothing, smoothtime
Authentication was removed later in version 6.
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
@@ -351,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
/* ================================================== */
@@ -431,7 +440,9 @@ typedef struct {
#define RPY_MANUAL_LIST 11
#define RPY_ACTIVITY 12
#define RPY_SMOOTHING 13
#define N_REPLY_TYPES 14
#define RPY_SERVER_STATS 14
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define N_REPLY_TYPES 16
/* Status codes */
#define STT_SUCCESS 0
@@ -478,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;
@@ -545,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;
@@ -562,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 {
@@ -623,6 +648,7 @@ 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;

View File

@@ -1,67 +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. 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 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,74 +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 or comma-separated list of addresses
(default @CHRONYSOCKDIR@/chronyd.sock,127.0.0.1,::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\-d\fR
print debugging messages (if compiled with debugging support)
.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 is ignored and is provided only for compatibility.
.TP
\fB\-a\fR
this option is ignored and is provided only for compatibility.
.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)
.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,161 +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
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.
.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 should be used only on
systems where the kernel can maintain clock compensation whilst not under
\fBchronyd\fR's control (i.e. Linux, FreeBSD, NetBSD and Solaris).
.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 system user to which \fBchronyd\fR will switch
after start in order to drop root privileges. It overrides the \fBuser\fR
directive (default \fB@DEFAULT_USER@\fR). It may be set to a non-root user
only when \fBchronyd\fR is compiled with support for Linux capabilities
(libcap) or on NetBSD with the \fB/dev/clockctl\fR device.
.TP
\fB\-F\fR \fIlevel\fR
This option configures a system call filter when \fBchronyd\fR 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).
.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 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.

1115
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,208 +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);
}
}
/* ================================================== */
static Node *
get_node(IPAddr *ip)
{
uint32_t ip6[4];
switch (ip->family) {
case IPADDR_INET4:
return (Node *)find_subnet(&top_subnet4, &ip->addr.in4, 1, 0);
case IPADDR_INET6:
split_ip6(ip, ip6);
return (Node *)find_subnet(&top_subnet6, ip6, 4, 0);
default:
return NULL;
}
}
/* ================================================== */
void
CLG_LogNTPClientAccess (IPAddr *client, time_t now)
{
Node *node;
if (active) {
node = get_node(client);
if (node == NULL)
return;
node->ip_addr = *client;
++node->client_hits;
node->last_ntp_hit = now;
}
}
/* ================================================== */
void
CLG_LogNTPPeerAccess(IPAddr *client, time_t now)
{
Node *node;
if (active) {
node = get_node(client);
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)
{
Node *node;
if (active) {
node = get_node(client);
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 */

253
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2015
* 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
@@ -130,6 +130,9 @@ static const char permissions[] = {
PERMIT_OPEN, /* SMOOTHING */
PERMIT_AUTH, /* SMOOTHTIME */
PERMIT_AUTH, /* REFRESH */
PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
PERMIT_AUTH, /* LOCAL2 */
};
/* ================================================== */
@@ -246,30 +249,51 @@ prepare_socket(int family, int port_number)
/* ================================================== */
static void
do_size_checks(void)
{
int i, request_length, padding_length, reply_length;
CMD_Request request;
CMD_Reply reply;
assert(offsetof(CMD_Request, data) == 20);
assert(offsetof(CMD_Reply, data) == 28);
for (i = 0; i < N_REQUEST_TYPES; i++) {
request.version = PROTO_VERSION_NUMBER;
request.command = htons(i);
request_length = PKL_CommandLength(&request);
padding_length = PKL_CommandPaddingLength(&request);
if (padding_length > MAX_PADDING_LENGTH || padding_length > request_length ||
request_length > sizeof (CMD_Request) ||
(request_length && request_length < offsetof(CMD_Request, data)))
assert(0);
}
for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i);
reply.status = STT_SUCCESS;
reply.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply))
assert(0);
}
}
/* ================================================== */
void
CAM_Initialise(int family)
{
int i, port_number;
int port_number;
assert(!initialised);
initialised = 1;
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
for (i = 0; i < N_REQUEST_TYPES; i++) {
CMD_Request r;
int command_length, padding_length;
r.version = PROTO_VERSION_NUMBER;
r.command = htons(i);
command_length = PKL_CommandLength(&r);
padding_length = PKL_CommandPaddingLength(&r);
assert(padding_length <= MAX_PADDING_LENGTH && padding_length <= command_length);
assert(command_length == 0 || command_length >= offsetof(CMD_Reply, data));
}
initialised = 1;
sock_fdu = -1;
port_number = CNF_GetCommandPort();
if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4))
@@ -561,11 +585,10 @@ handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int on_off, stratum;
on_off = ntohl(rx_message->data.local.on_off);
if (on_off) {
stratum = ntohl(rx_message->data.local.stratum);
REF_EnableLocal(stratum);
if (ntohl(rx_message->data.local.on_off)) {
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan));
} else {
REF_DisableLocal();
}
@@ -661,17 +684,11 @@ handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
break;
}
switch (report.sel_option) {
case RPT_NORMAL:
tx_message->data.source_data.flags = htons(0);
break;
case RPT_PREFER:
tx_message->data.source_data.flags = htons(RPY_SD_FLAG_PREFER);
break;
case RPT_NOSELECT:
tx_message->data.source_data.flags = htons(RPY_SD_FLAG_NOSELECT);
break;
}
tx_message->data.source_data.flags =
htons((report.sel_options & SRC_SELECT_PREFER ? RPY_SD_FLAG_PREFER : 0) |
(report.sel_options & SRC_SELECT_NOSELECT ? RPY_SD_FLAG_NOSELECT : 0) |
(report.sel_options & SRC_SELECT_TRUST ? RPY_SD_FLAG_TRUST : 0) |
(report.sel_options & SRC_SELECT_REQUIRE ? RPY_SD_FLAG_REQUIRE : 0));
tx_message->data.source_data.reachability = htons(report.reachability);
tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
@@ -764,8 +781,11 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.sel_option = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SelectPrefer :
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SelectNoselect : SRC_SelectNormal;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
@@ -1017,50 +1037,50 @@ handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CLG_Status result;
RPT_ClientAccessByIndex_Report report;
unsigned long first_index, n_indices, n_indices_in_table;
int i, j;
RPY_ClientAccesses_Client *client;
int n_indices;
uint32_t i, j, req_first_index, req_n_clients;
struct timeval now;
SCH_GetLastEventTime(&now, NULL, NULL);
first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
n_indices = ntohl(rx_message->data.client_accesses_by_index.n_indices);
if (n_indices > MAX_CLIENT_ACCESSES)
n_indices = MAX_CLIENT_ACCESSES;
req_first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
if (req_n_clients > MAX_CLIENT_ACCESSES)
req_n_clients = MAX_CLIENT_ACCESSES;
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX);
for (i = 0, j = 0; i < n_indices; i++) {
result = CLG_GetClientAccessReportByIndex(first_index + i, &report,
now.tv_sec, &n_indices_in_table);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices_in_table);
switch (result) {
case CLG_SUCCESS:
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.client_accesses_by_index.clients[j].ip);
tx_message->data.client_accesses_by_index.clients[j].client_hits = htonl(report.client_hits);
tx_message->data.client_accesses_by_index.clients[j].peer_hits = htonl(report.peer_hits);
tx_message->data.client_accesses_by_index.clients[j].cmd_hits_auth = htonl(report.cmd_hits_auth);
tx_message->data.client_accesses_by_index.clients[j].cmd_hits_normal = htonl(report.cmd_hits_normal);
tx_message->data.client_accesses_by_index.clients[j].cmd_hits_bad = htonl(report.cmd_hits_bad);
tx_message->data.client_accesses_by_index.clients[j].last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
tx_message->data.client_accesses_by_index.clients[j].last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
j++;
break;
case CLG_INDEXTOOLARGE:
break; /* ignore this index */
case CLG_INACTIVE:
tx_message->status = htons(STT_INACTIVE);
return;
default:
assert(0);
break;
}
n_indices = CLG_GetNumberOfIndices();
if (n_indices < 0) {
tx_message->status = htons(STT_INACTIVE);
return;
}
tx_message->data.client_accesses_by_index.next_index = htonl(first_index + i);
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
memset(tx_message->data.client_accesses_by_index.clients, 0,
sizeof (tx_message->data.client_accesses_by_index.clients));
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
continue;
client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits);
client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htonl(report.ntp_drops);
client->cmd_drops = htonl(report.cmd_drops);
client->ntp_interval = report.ntp_interval;
client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval;
client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
}
tx_message->data.client_accesses_by_index.next_index = htonl(i);
tx_message->data.client_accesses_by_index.n_clients = htonl(j);
}
@@ -1149,36 +1169,43 @@ handle_refresh(CMD_Request *rx_message, CMD_Reply *tx_message)
NSR_RefreshAddresses();
}
/* ================================================== */
static void
handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
}
/* ================================================== */
/* Read a packet and process it */
static void
read_from_cmd_socket(void *anything)
{
int status;
int read_length; /* Length of packet read */
int expected_length; /* Expected length of packet without auth data */
unsigned long flags;
CMD_Request rx_message;
CMD_Reply tx_message;
int rx_message_length;
int sock_fd;
int status, read_length, expected_length, rx_message_length;
int localhost, allowed, sock_fd, log_index;
union sockaddr_all where_from;
socklen_t from_length;
IPAddr remote_ip;
unsigned short remote_port;
int localhost;
int allowed;
unsigned short rx_command;
struct timeval now;
struct timeval cooked_now;
unsigned short remote_port, rx_command;
struct timeval now, cooked_now;
flags = 0;
rx_message_length = sizeof(rx_message);
from_length = sizeof(where_from);
sock_fd = (long)anything;
status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, flags,
status = recvfrom(sock_fd, (char *)&rx_message, rx_message_length, 0,
&where_from.sa, &from_length);
if (status < 0) {
@@ -1235,25 +1262,19 @@ read_from_cmd_socket(void *anything)
return;
}
/* Message size sanity check */
if (read_length >= offsetof(CMD_Request, data)) {
expected_length = PKL_CommandLength(&rx_message);
} else {
expected_length = 0;
}
if (expected_length < offsetof(CMD_Request, data) ||
if (read_length < offsetof(CMD_Request, data) ||
read_length < offsetof(CMD_Reply, data) ||
rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
rx_message.res1 != 0 ||
rx_message.res2 != 0) {
/* We don't know how to process anything like this */
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
/* We don't know how to process anything like this or an error reply
would be larger than the request */
DEBUG_LOG(LOGF_CmdMon, "Command packet dropped");
return;
}
expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command);
tx_message.version = PROTO_VERSION_NUMBER;
@@ -1271,10 +1292,8 @@ read_from_cmd_socket(void *anything)
tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG(LOGF_CmdMon, "Read command packet with protocol version %d (expected %d) from %s",
rx_message.version, PROTO_VERSION_NUMBER, UTI_SockaddrToString(&where_from.sa));
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid version (%d != %d)",
rx_message.version, PROTO_VERSION_NUMBER);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION);
@@ -1283,11 +1302,9 @@ read_from_cmd_socket(void *anything)
return;
}
if (rx_command >= N_REQUEST_TYPES) {
DEBUG_LOG(LOGF_CmdMon, "Read command packet with invalid command %d from %s",
rx_command, UTI_SockaddrToString(&where_from.sa));
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
if (rx_command >= N_REQUEST_TYPES ||
expected_length < (int)offsetof(CMD_Request, data)) {
DEBUG_LOG(LOGF_CmdMon, "Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(&tx_message, &where_from);
@@ -1295,10 +1312,8 @@ read_from_cmd_socket(void *anything)
}
if (read_length < expected_length) {
DEBUG_LOG(LOGF_CmdMon, "Read incorrectly sized command packet from %s",
UTI_SockaddrToString(&where_from.sa));
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
DEBUG_LOG(LOGF_CmdMon, "Command packet is too short (%d < %d)", read_length,
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(&tx_message, &where_from);
@@ -1307,7 +1322,14 @@ read_from_cmd_socket(void *anything)
/* OK, we have a valid message. Now dispatch on message type and process it. */
CLG_LogCommandAccess(&remote_ip, CLG_CMD_NORMAL, cooked_now.tv_sec);
log_index = CLG_LogCommandAccess(&remote_ip, &cooked_now);
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitCommandResponseRate(log_index)) {
DEBUG_LOG(LOGF_CmdMon, "Command packet discarded to limit response rate");
return;
}
if (rx_command >= N_REQUEST_TYPES) {
/* This should be already handled */
@@ -1395,7 +1417,7 @@ read_from_cmd_socket(void *anything)
handle_settime(&rx_message, &tx_message);
break;
case REQ_LOCAL:
case REQ_LOCAL2:
handle_local(&rx_message, &tx_message);
break;
@@ -1507,7 +1529,7 @@ read_from_cmd_socket(void *anything)
handle_cyclelogs(&rx_message, &tx_message);
break;
case REQ_CLIENT_ACCESSES_BY_INDEX:
case REQ_CLIENT_ACCESSES_BY_INDEX2:
handle_client_accesses_by_index(&rx_message, &tx_message);
break;
@@ -1547,8 +1569,13 @@ read_from_cmd_socket(void *anything)
handle_refresh(&rx_message, &tx_message);
break;
case REQ_SERVER_STATS:
handle_server_stats(&rx_message, &tx_message);
break;
default:
assert(0);
DEBUG_LOG(LOGF_CmdMon, "Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}
} else {

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014
* 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);

216
conf.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2015
* 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
@@ -50,15 +50,12 @@ static int parse_int(char *line, int *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 *);
@@ -68,11 +65,11 @@ 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 *);
/* ================================================== */
@@ -88,6 +85,7 @@ static char *rtc_file = NULL;
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;
@@ -110,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 */
@@ -124,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;
@@ -149,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;
@@ -187,6 +185,16 @@ static char *bind_cmd_path;
* 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 */
@@ -324,6 +332,7 @@ CNF_Initialise(int r)
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);
}
@@ -413,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")) {
@@ -425,17 +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, "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")) {
@@ -465,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")) {
@@ -480,6 +492,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
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")) {
@@ -493,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")) {
@@ -517,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")) {
@@ -608,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();
}
}
/* ================================================== */
@@ -634,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;
@@ -648,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();
@@ -675,17 +696,17 @@ parse_refclock(char *line)
line = CPS_SplitWord(line);
param = Strdup(p);
while (*line) {
cmd = line;
for (cmd = line; *cmd; line += n, cmd = line) {
line = CPS_SplitWord(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;
@@ -721,18 +742,24 @@ 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");
if (*cmd) {
command_parse_error();
return;
}
@@ -745,11 +772,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;
}
@@ -791,13 +818,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;
}
/* ================================================== */
@@ -860,11 +883,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;
}
}
/* ================================================== */
@@ -1026,41 +1044,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
@@ -1503,6 +1486,14 @@ CNF_GetMaxUpdateSkew(void)
/* ================================================== */
double
CNF_GetMaxDrift(void)
{
return max_drift;
}
/* ================================================== */
double
CNF_GetMaxClockError(void)
{
@@ -1575,10 +1566,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;
@@ -1622,11 +1615,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;
}
/* ================================================== */
@@ -1788,6 +1780,26 @@ 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, int *leap_only)
{

7
conf.h
View File

@@ -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);
@@ -83,6 +83,7 @@ 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);
@@ -91,13 +92,15 @@ 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 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);

83
configure vendored
View File

@@ -95,13 +95,14 @@ For better control, use the options below.
--disable-rtc Don't include RTC even on Linux
--disable-privdrop Disable support for dropping root privileges
--without-libcap Don't use libcap even if it is available
--disable-scfilter Disable support for system call filtering
--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
@@ -110,7 +111,6 @@ 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]
@@ -199,8 +199,9 @@ try_rtc=0
feat_droproot=1
try_libcap=-1
try_clockctl=0
feat_scfilter=1
feat_scfilter=0
try_seccomp=-1
priv_ops=""
readline_lib=""
readline_inc=""
ncurses_lib=""
@@ -214,6 +215,7 @@ feat_asyncdns=1
feat_forcednsretry=1
ntp_era_split=""
default_user="root"
default_hwclockfile=""
mail_program="/usr/lib/sendmail"
for option
@@ -258,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/^.*=//;'`
;;
@@ -303,6 +302,9 @@ do
--without-libcap|--disable-linuxcaps)
try_libcap=0
;;
--enable-scfilter)
feat_scfilter=1
;;
--disable-scfilter)
feat_scfilter=0
;;
@@ -321,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/^.*=//;'`
;;
@@ -367,10 +372,13 @@ case $OPERATINGSYSTEM in
add_def LINUX
echo "Configuring for " $SYSTEM
;;
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
echo "Configuring for $SYSTEM"
;;
NetBSD)
@@ -384,6 +392,10 @@ case $OPERATINGSYSTEM in
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)
@@ -395,6 +407,10 @@ case $OPERATINGSYSTEM in
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 ")"
;;
* )
@@ -471,7 +487,7 @@ then
split_days=0
else
split_seconds=`date '+%s'`
if [ "x$split_seconds" = "" ]; then
if [ "x$split_seconds" = "x" ]; then
echo "error: could not get current time, --with-ntp-era option is needed"
exit 1
fi
@@ -514,6 +530,13 @@ 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' '' "$EXTRA_LIBS" '
struct sockaddr_in6 n;
@@ -522,13 +545,13 @@ if [ $feat_ipv6 = "1" ] && \
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
@@ -552,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
@@ -590,6 +617,7 @@ 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" ] && \
@@ -597,9 +625,21 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
'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);'
@@ -753,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
@@ -784,6 +819,7 @@ if [ "x$SETCHRONYVARDIR" != "x" ]; then
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\""
@@ -796,12 +832,14 @@ 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}%;\
@@ -820,11 +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

@@ -1,20 +1,27 @@
#!/bin/sh
# chronylogrotate.sh
# ChronyControl
#
# Created by Bryan Christianson on 12/07/15.
# 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
if [ ! -e "$LOGDIR" ]; then
echo "missing directory: $LOGDIR"
exit 1
fi
cd $LOGDIR
rotate () {
prefix=$1
@@ -33,13 +40,19 @@ rotate () {
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 -a -f /etc/chrony.d/chrony.conf cyclelogs > /dev/null
/usr/local/bin/chronyc cyclelogs > /dev/null
exit $?

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.

View File

@@ -1,59 +1,91 @@
// 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:
Frequently Asked Questions
==========================
== `chrony` compared to other programs
== +chrony+ compared to other programs
=== How does `chrony` compare to `ntpd`?
=== 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
`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.
For a more detailed comparison of features and performance, see the
http://chrony.tuxfamily.org/comparison.html[comparison page] on the +chrony+
website and the
http://chrony.tuxfamily.org/manual.html#Comparison-with-ntpd[Comparison with
ntpd] section in the manual.
If your computer is connected to the internet only for few minutes at a time,
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+
on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work much better for you.
The original reason +chrony+ was written was that +ntpd+ (called +xntpd+ at the
time) could not to do anything sensible on a PC which was connected to the 'net
only for about 5 minutes once or twice a day, mainly to upload/download email
and news. The requirements were
* slew the time to correct it when going online and NTP servers
become visible
* determine the rate at which the computer gains or loses time and
use this information to keep it reasonably correct between connects
to the 'net. This has to be done using a method that does not care
about the intermittent availability of the references or the fact
the computer is turned off between groups of measurements.
* maintain the time across reboots, by working out the error and
drift rate of the computer's real-time clock and using this
information to set the system clock correctly at boot up.
Also, when working with isolated networks with no true time references at all
+ntpd+ was found to give no help with managing the local clock's gain/loss rate
on the NTP master node (which was set from watch). Some automated support was
added to +chrony+ to deal with this.
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 master, with
the others as clients of it. Add a +local+ directive to the master's
'chrony.conf' file. This configuration will be better because
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
@@ -62,76 +94,79 @@ the others as clients of it. Add a +local+ directive to the master's
=== 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 hostnames specified in the +server+ and +peer+ directives in
increasing intervals until it succeeds. The +online+ command can be
issued from +chronyc+ to try to resolve them immediately.
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?
=== 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
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
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.
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.
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.
On Linux, if +chronyd+ is compiled with support for Linux capabilities
(available in the libcap library), or on NetBSD with the +/dev/clockctl+
device, you can specify an unprivileged user with the +-u+ option or +user+
directive in the 'chrony.conf' file to drop root privileges after start. The
configure option +--with-user+ can be used to drop the privileges by default.
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.
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 compromised. The filter
shouldn't be enabled without testing that it allows all system calls needed
with the specific configuration and libraries that +chronyd+ is using (e.g.
libc and its NSS configuration). If +chronyd+ is getting killed, some system
call is missing and the filter has to be disabled until it's patched to allow
that call.
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 falsetickers and combine
measurements from multiple sources.
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+.
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
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 on many factors, including the ratio
between the wander of the clock and the network jitter (sometimes expressed in
NTP documents as the Allan intercept), the temperature sensitivity of the
crystal oscillator and the maximum rate of change of the temperature.
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
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
----
@@ -147,96 +182,126 @@ 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
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?
=== 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 same host as +chronyd+ is running.
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
This is the most common problem. There are a number of reasons, see the
following questions.
=== Behind a firewall?
If there is a firewall between you and the NTP server you're trying to use, the
packets may be blocked. Try using a tool like +wireshark+ or +tcpdump+ to see if
you're getting responses from the server. If you have an external modem, see
if the receive light blinks straight after the transmit light (when the link is
quiet apart from the NTP traffic.) Try adding +log measurements+ to the
'chrony.conf' file and look in the 'measurements.log' file after +chronyd+ has
been running for a short period. See if any measurements appear.
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.
=== Are NTP servers specified with the +offline+ option?
When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like
this:
Check that you're using +chronyc+\'s +online+ and +offline+ commands
appropriately. Again, check in 'measurements.log' to see if you're getting any
----
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?
=== 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 correct 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
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
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 RTC or virtual
machine which can be suspended and resumed with incorrect time) it may be
necessary to allow the step at any clock update. The example above would
change to
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+
== Issues with `chronyc`
=== I keep getting the error +506 Cannot talk to daemon+
=== 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.
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 `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.
323/udp. You need to amend the firewall configuration in this case.
=== I keep getting the error +501 Not authorised+
=== I keep getting the error `501 Not authorised`
Since version 2.2, the +password+ command doesn't do anything and +chronyc+
needs to run under the root or chrony user, which are allowed to access the
Unix domain command socket.
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.
=== Is the +chronyc+ / +chronyd+ protocol documented anywhere?
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).
Only by the source code :-) See 'cmdmon.c' (+chronyd+ side) and 'client.c'
(+chronyc+ side).
=== 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
@@ -246,96 +311,94 @@ 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
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 will tell +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
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.
`chronyd` is started on boot. See the documentation for details.
=== I want to use +chronyd+'s RTC support. Must I disable +hwclock+?
=== 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
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+
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
=== I just keep getting the `513 RTC driver not running` message
For the real time clock support to work, you need the following three
For the real-time clock support to work, you need the following three
things
* a kernel that is supported (e.g. 2.2 onwards)
* enhanced RTC support compiled into the kernel
* an +rtcfile+ directive in your 'chrony.conf' file
* an RTC in your computer
* a Linux kernel with enabled RTC support
* an `rtcfile` directive in your _chrony.conf_ file
== 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 current 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.
== Linux-specific issues
=== I get +Could not open /dev/rtc, Device or resource busy+ in my syslog 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.
== Microsoft Windows
== NTP-specific issues
=== Does +chrony+ support Windows?
=== Can `chronyd` be driven from broadcast NTP servers?
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'
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
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
@@ -21,12 +21,6 @@ rtcsync
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Disable logging of client accesses.
noclientlog
# Send message to syslog when clock adjustment is larger than 0.5 seconds.
logchange 0.5
# 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
@@ -128,15 +112,15 @@ driftfile /var/lib/chrony/drift
#######################################################################
### 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
@@ -207,6 +191,13 @@ driftfile /var/lib/chrony/drift
! 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
@@ -233,6 +224,7 @@ driftfile /var/lib/chrony/drift
# 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 ::
@@ -248,6 +240,11 @@ driftfile /var/lib/chrony/drift
# syntax and meaning is the same as for 'allow' and 'deny', except that
# 'cmdallow' and 'cmddeny' control access to the chronyd's command port.
# Rate limiting can be enabled also for command packets. (Note,
# commands from localhost are never limited.)
! cmdratelimit interval 1 burst 16
#######################################################################
### REAL TIME CLOCK
# chronyd can characterise the system's real-time clock. This is the

View File

@@ -1,15 +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.
# chronyd is configured to switch to after start.
#
# Don't use the example keys! The keys need to be random for maximum security.
# These shell commands can be used to generate random MD5 and SHA1 keys on
# systems which have the /dev/urandom device:
# echo "1 MD5 HEX:$(tr -d -c '[:xdigit:]' < /dev/urandom | head -c 32)"
# echo "1 SHA1 HEX:$(tr -d -c '[:xdigit:]' < /dev/urandom | head -c 40)"
# 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
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995

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,29 +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/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

19
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;
@@ -290,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)

1
keys.h
View File

@@ -37,6 +37,7 @@ extern void KEY_Reload(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);

16
local.c
View File

@@ -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);
}
/* ================================================== */

View File

@@ -47,7 +47,7 @@ extern int log_debug_enabled;
#if DEBUG > 0
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__);
LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__);
#else
#define LOG_MESSAGE(severity, facility, ...) \
LOG_Message(severity, __VA_ARGS__);
@@ -100,6 +100,7 @@ typedef enum {
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_PrivOps,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
@@ -108,7 +109,6 @@ typedef enum {
LOGF_SysMacOSX,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysSunOS,
LOGF_SysTimex,
LOGF_SysWinnt,
LOGF_TempComp,

17
main.c
View File

@@ -49,6 +49,7 @@
#include "refclock.h"
#include "clientlog.h"
#include "nameserv.h"
#include "privops.h"
#include "smooth.h"
#include "tempcomp.h"
#include "util.h"
@@ -68,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)
{
@@ -107,6 +120,7 @@ MAI_CleanupAndExit(void)
SYS_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
delete_pidfile();
@@ -351,6 +365,8 @@ int main
int system_log = 1;
int config_args = 0;
do_platform_checks();
LOG_Initialise();
/* Parse command line options */
@@ -463,6 +479,7 @@ int main
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();

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,29 +38,13 @@ echo $version > version.txt
sed -i -e "s%@@VERSION@@%${version}%" examples/chrony.spec
for m in 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
./configure && make chrony.txt getdate.c || exit 1
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
a2x --lynx -f text doc/faq.adoc || exit 1
mv doc/faq.text FAQ
rm -rf doc
iconv -f utf-8 -t ascii//TRANSLIT < doc/installation.txt > INSTALL
iconv -f utf-8 -t ascii//TRANSLIT < doc/faq.txt > FAQ
make distclean
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

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++)
;

6
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)

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2015
* 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);
@@ -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,28 +817,38 @@ 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_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;
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 (smooth_time) {
@@ -809,14 +859,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
local_receive = *local_rx;
}
if (are_we_synchronised) {
leap = (int) leap_status;
} else {
leap = LEAP_Unsynchronised;
}
/* 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;
@@ -825,7 +869,7 @@ 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 */
@@ -836,19 +880,22 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* Now fill in timestamps */
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
@@ -866,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),
@@ -886,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);
@@ -909,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:
@@ -944,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
@@ -957,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;
@@ -975,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;
@@ -1008,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);
}
/* ================================================== */
@@ -1099,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;
@@ -1126,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;
@@ -1165,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. */
@@ -1183,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 &&
@@ -1202,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;
@@ -1271,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
@@ -1292,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;
@@ -1326,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;
@@ -1368,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);
@@ -1408,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;
}
@@ -1421,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);
}
@@ -1481,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;
@@ -1489,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) {
@@ -1497,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:
@@ -1520,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:
@@ -1574,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) {
@@ -1620,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 */
@@ -1646,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 */
@@ -1659,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);
@@ -1966,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

@@ -37,6 +37,7 @@
#include "local.h"
#include "logging.h"
#include "conf.h"
#include "privops.h"
#include "util.h"
#define INVALID_SOCK_FD -1
@@ -161,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
@@ -184,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
@@ -202,24 +203,26 @@ prepare_socket(int family, int port_number, int client_only)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not 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",
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
@@ -531,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;
@@ -541,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;
@@ -553,7 +556,7 @@ read_from_socket(void *anything)
#endif
#ifdef SO_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
@@ -629,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;
@@ -647,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
@@ -53,11 +53,11 @@ typedef struct {
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 */
@@ -70,6 +70,7 @@ static int auto_start_sources = 0;
struct UnresolvedSource {
char *name;
int port;
int random_order;
int replacement;
union {
struct {
@@ -202,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++) {
@@ -243,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;
}
/* ================================================== */
@@ -266,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);
@@ -320,7 +311,7 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type, So
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);
@@ -337,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)
@@ -350,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();
@@ -367,13 +368,17 @@ 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, "(%d) %s", i + 1, 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;
@@ -516,6 +521,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, Source
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;
@@ -657,6 +663,11 @@ resolve_source_replacement(SourceRecord *record)
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
us->port = record->remote_addr->port;
/* If there never was a valid reply from this source (e.g. it was a bad
replacement), ignore the order of addresses from the resolver to not get
stuck to a pair of addresses if the order doesn't change, or a group of
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
us->random_order = record->tentative;
us->replacement = 1;
us->replace_source = *record->remote_addr;
@@ -742,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
@@ -761,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",
record->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

@@ -83,6 +83,9 @@ 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,145 +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.null.EOR);
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.null.EOR);
case REQ_SOURCE_DATA :
return offsetof(CMD_Request, data.source_data.EOR);
case REQ_REKEY :
return offsetof(CMD_Request, data.null.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.null.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.null.EOR);
case REQ_SOURCESTATS :
return offsetof(CMD_Request, data.sourcestats.EOR);
case REQ_RTCREPORT :
return offsetof(CMD_Request, data.null.EOR);
case REQ_TRIMRTC :
return offsetof(CMD_Request, data.null.EOR);
case REQ_CYCLELOGS :
return offsetof(CMD_Request, data.null.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.null.EOR);
case REQ_MANUAL_DELETE:
return offsetof(CMD_Request, data.manual_delete.EOR);
case REQ_MAKESTEP:
return offsetof(CMD_Request, data.null.EOR);
case REQ_ACTIVITY:
return offsetof(CMD_Request, data.null.EOR);
case REQ_RESELECT:
return offsetof(CMD_Request, data.null.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);
case REQ_SMOOTHING:
return offsetof(CMD_Request, data.null.EOR);
case REQ_SMOOTHTIME:
return offsetof(CMD_Request, data.smoothtime.EOR);
case REQ_REFRESH:
return offsetof(CMD_Request, data.null.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;
@@ -180,139 +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.null.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.null.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.null.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.null.EOR, data.tracking.EOR);
case REQ_SOURCESTATS:
return PADDING_LENGTH(data.sourcestats.EOR, data.sourcestats.EOR);
case REQ_RTCREPORT:
return PADDING_LENGTH(data.null.EOR, data.rtc.EOR);
case REQ_TRIMRTC:
return PADDING_LENGTH(data.null.EOR, data.null.EOR);
case REQ_CYCLELOGS:
return PADDING_LENGTH(data.null.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.null.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.null.EOR, data.null.EOR);
case REQ_ACTIVITY:
return PADDING_LENGTH(data.null.EOR, data.activity.EOR);
case REQ_RESELECT:
return PADDING_LENGTH(data.null.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);
case REQ_SMOOTHING:
return PADDING_LENGTH(data.null.EOR, data.smoothing.EOR);
case REQ_SMOOTHTIME:
return PADDING_LENGTH(data.smoothtime.EOR, data.null.EOR);
case REQ_REFRESH:
return PADDING_LENGTH(data.null.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;
}
/* ================================================== */
@@ -320,65 +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);
case RPY_SMOOTHING:
return offsetof(CMD_Reply, data.smoothing.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,12 +87,12 @@ 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;
static int valid_sample_time(RCL_Instance instance, struct timeval *tv);
static int valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked);
static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
@@ -106,6 +106,7 @@ static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion);
static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static int filter_get_samples(struct MedianFilter *filter);
static int filter_select_samples(struct MedianFilter *filter);
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
@@ -114,13 +115,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 +149,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 +164,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 +237,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 +264,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",
@@ -369,11 +373,9 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, sample_time))
!valid_sample_time(instance, sample_time, &cooked_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 +383,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);
@@ -410,7 +413,7 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
dispersion += instance->precision;
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0) ||
!valid_sample_time(instance, pulse_time))
!valid_sample_time(instance, pulse_time, &cooked_time))
return 0;
rate = instance->pps_rate;
@@ -471,15 +474,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;
@@ -500,18 +504,25 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
}
static int
valid_sample_time(RCL_Instance instance, struct timeval *tv)
valid_sample_time(RCL_Instance instance, struct timeval *raw, struct timeval *cooked)
{
struct timeval raw_time;
double diff;
struct timeval raw_time, last_sample_time;
double diff, last_offset, last_dispersion;
LCL_ReadRawTime(&raw_time);
UTI_DiffTimevalsToDouble(&diff, &raw_time, tv);
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));
UTI_DiffTimevalsToDouble(&diff, &raw_time, raw);
if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
(filter_get_samples(&instance->filter) > 0 &&
filter_get_last_sample(&instance->filter, &last_sample_time,
&last_offset, &last_dispersion) &&
UTI_CompareTimevals(&last_sample_time, cooked) >= 0)) {
DEBUG_LOG(LOGF_Refclock, "%s refclock sample not valid age=%.6f raw=%s cooked=%s",
UTI_RefidToString(instance->ref_id), diff,
UTI_TimevalToString(raw), UTI_TimevalToString(cooked));
return 0;
}
return 1;
}
@@ -529,9 +540,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 */
@@ -715,6 +727,12 @@ filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time,
return 1;
}
static int
filter_get_samples(struct MedianFilter *filter)
{
return filter->used;
}
static const struct FilterSample *tmp_sorted_array;
static int

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-2015
* 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 */
@@ -232,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 */
@@ -255,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);
@@ -264,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;
@@ -273,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();
}
@@ -428,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;
@@ -464,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);
@@ -481,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);
@@ -539,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);
@@ -686,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;
@@ -738,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;
@@ -760,7 +750,6 @@ set_leap_timeout(time_t now)
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
leap_timer_running = 1;
}
/* ================================================== */
@@ -1170,29 +1159,37 @@ 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 = NTP_REFID_LOCAL;
@@ -1233,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;
}
/* ================================================== */
@@ -1270,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;
}
/* ================================================== */
@@ -1286,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)
@@ -1322,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 = NTP_REFID_UNSYNC;
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 = NTP_REFID_LOCAL;
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;

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,16 +1036,19 @@ 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 {
@@ -1092,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
@@ -1117,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);

48
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;
@@ -356,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;
}
@@ -397,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);
@@ -421,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) {
@@ -436,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);
}
/* ================================================== */
@@ -690,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,

View File

@@ -137,7 +137,7 @@ get_smoothing(struct timeval *now, double *poffset, double *pfreq,
static void
update_stages(void)
{
double s1, s2, s, l1, l2, l3, lc, f, f2;
double s1, s2, s, l1, l2, l3, lc, f, f2, l1t[2], l3t[2], err[2];
int i, dir;
/* Prepare the three stages so that the integral of the frequency offset
@@ -146,22 +146,41 @@ update_stages(void)
s1 = smooth_offset / max_wander;
s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
l1 = l2 = l3 = 0.0;
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. If length of the 1st stage comes out negative, switch
its direction. */
for (dir = -1; dir <= 1; dir += 2) {
frequency limit. The direction of the 1st stage is selected so that
the lengths will not be negative. With extremely small offsets both
directions may give a negative length due to numerical errors, so select
the one which gives a smaller error. */
for (i = 0, dir = -1; i <= 1; i++, dir += 2) {
err[i] = 0.0;
s = dir * s1 + s2;
if (s >= 0.0) {
l3 = sqrt(s);
l1 = l3 - dir * smooth_freq / max_wander;
if (l1 >= 0.0)
break;
if (s < 0.0) {
err[i] += -s;
s = 0.0;
}
l3t[i] = sqrt(s);
l1t[i] = l3t[i] - dir * smooth_freq / max_wander;
if (l1t[i] < 0.0) {
err[i] += l1t[i] * l1t[i];
l1t[i] = 0.0;
}
}
assert(dir <= 1 && l1 >= 0.0 && l3 >= 0.0);
if (err[0] < err[1]) {
l1 = l1t[0];
l3 = l3t[0];
dir = -1;
} else {
l1 = l1t[1];
l3 = l3t[1];
dir = 1;
}
l2 = 0.0;
/* If the limit was reached, shorten 1st+3rd stages and set a 2nd stage */
f = dir * smooth_freq + l1 * max_wander - max_freq;

168
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
@@ -73,6 +73,8 @@ typedef enum {
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_UNTRUSTED, /* Overlaps trusted sources */
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 +120,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;
@@ -209,7 +211,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;
@@ -241,7 +243,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);
@@ -414,7 +416,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++;
@@ -429,9 +431,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);
}
}
@@ -602,8 +606,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;
@@ -630,14 +636,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;
}
@@ -671,6 +683,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;
@@ -685,7 +700,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;
@@ -736,22 +800,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;
@@ -759,14 +828,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:
@@ -774,9 +849,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);
@@ -797,28 +872,38 @@ 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 if (sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) {
sources[i]->status = SRC_UNTRUSTED;
} else {
sources[i]->status = SRC_FALSETICKER;
}
}
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);
@@ -844,12 +929,12 @@ 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 = 0; i < n_sel_sources; i++) {
if (sources[sel_sources[i]]->sel_option == SRC_SelectPrefer)
if (sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER)
break;
}
if (i < n_sel_sources) {
for (i = j = 0; i < n_sel_sources; i++) {
if (sources[sel_sources[i]]->sel_option != SRC_SelectPrefer)
if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER))
sources[sel_sources[i]]->status = SRC_NONPREFERRED;
else
sel_sources[j++] = sel_sources[i];
@@ -885,7 +970,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;
@@ -1227,6 +1312,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
case SRC_BAD_STATS:
case SRC_BAD_DISTANCE:
case SRC_STALE:
case SRC_ORPHAN:
case SRC_WAITS_STATS:
report->state = RPT_UNREACH;
break;
@@ -1236,6 +1322,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
case SRC_JITTERY:
report->state = RPT_JITTERY;
break;
case SRC_UNTRUSTED:
case SRC_WAITS_SOURCES:
case SRC_NONPREFERRED:
case SRC_WAITS_UPDATE:
@@ -1255,20 +1342,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 */

15
stubs.c
View File

@@ -38,13 +38,12 @@
#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 {
@@ -57,14 +56,14 @@ static void
resolve_name(void *anything)
{
struct DNS_Async_Instance *inst;
IPAddr addrs[MAX_ADDRESSES];
IPAddr addrs[DNS_MAX_ADDRESSES];
DNS_Status status;
int i;
inst = (struct DNS_Async_Instance *)anything;
status = DNS_Name2IPAddress(inst->name, addrs, MAX_ADDRESSES);
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
for (i = 0; status == DNS_Success && i < MAX_ADDRESSES &&
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
addrs[i].family != IPADDR_UNSPEC; i++)
;
@@ -231,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)
{

12
sys.c
View File

@@ -36,8 +36,6 @@
#include "sys_linux.h"
#elif defined(SOLARIS)
#include "sys_solaris.h"
#elif defined(SUNOS)
#include "sys_sunos.h"
#elif defined(NETBSD) || defined(FREEBSD)
#include "sys_netbsd.h"
#elif defined(MACOSX)
@@ -53,8 +51,6 @@ SYS_Initialise(void)
SYS_Linux_Initialise();
#elif defined(SOLARIS)
SYS_Solaris_Initialise();
#elif defined(SUNOS)
SYS_SunOS_Initialise();
#elif defined(NETBSD) || defined(FREEBSD)
SYS_NetBSD_Initialise();
#elif defined(MACOSX)
@@ -73,8 +69,6 @@ SYS_Finalise(void)
SYS_Linux_Finalise();
#elif defined(SOLARIS)
SYS_Solaris_Finalise();
#elif defined(SUNOS)
SYS_SunOS_Finalise();
#elif defined(NETBSD) || defined(FREEBSD)
SYS_NetBSD_Finalise();
#elif defined(MACOSX)
@@ -90,8 +84,12 @@ void SYS_DropRoot(uid_t uid, gid_t gid)
{
#if defined(LINUX) && defined (FEAT_PRIVDROP)
SYS_Linux_DropRoot(uid, gid);
#elif defined(NETBSD) && defined(FEAT_PRIVDROP)
#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

View File

@@ -34,6 +34,7 @@
#include "local.h"
#include "localp.h"
#include "logging.h"
#include "privops.h"
#include "sched.h"
#include "util.h"
@@ -76,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;
@@ -173,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);
@@ -245,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,
@@ -259,7 +256,7 @@ update_slew(void)
static void
handle_end_of_slew(void *anything)
{
slew_timer_running = 0;
slew_timeout_id = 0;
update_slew();
}
@@ -333,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;
}
@@ -410,10 +407,9 @@ SYS_Generic_Finalise(void)
/* 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;
}
SCH_RemoveTimeout(slew_timeout_id);
slew_timeout_id = 0;
(*drv_set_freq)(clamp_freq(base_freq));

View File

@@ -45,12 +45,12 @@
#ifdef FEAT_PRIVDROP
#include <sys/prctl.h>
#include <sys/capability.h>
#include <grp.h>
#endif
#ifdef FEAT_SCFILTER
#include <sys/prctl.h>
#include <seccomp.h>
#include <termios.h>
#ifdef FEAT_PHC
#include <linux/ptp_clock.h>
#endif
@@ -66,6 +66,8 @@
#include "sys_timex.h"
#include "conf.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
/* Frequency scale to convert from ppm to the timex freq */
#define FREQ_SCALE (double)(1 << 16)
@@ -403,25 +405,20 @@ SYS_Linux_Finalise(void)
void
SYS_Linux_DropRoot(uid_t uid, gid_t gid)
{
const char *cap_text;
cap_t cap;
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(gid)) {
LOG_FATAL(LOGF_SysLinux, "setgid(%d) failed", 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(uid)) {
LOG_FATAL(LOGF_SysLinux, "setuid(%d) failed", 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");
}
@@ -430,8 +427,6 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
}
cap_free(cap);
DEBUG_LOG(LOGF_SysLinux, "Root dropped to uid %d gid %d", uid, gid);
}
#endif
@@ -460,15 +455,17 @@ SYS_Linux_EnableSystemCallFilter(int level)
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(rt_sigreturn), SCMP_SYS(sigreturn),
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(munmap), SCMP_SYS(shmdt),
SCMP_SYS(mprotect), SCMP_SYS(mremap), SCMP_SYS(munmap), SCMP_SYS(shmdt),
/* Filesystem */
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(unlink),
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),
@@ -502,7 +499,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
const static int fcntls[] = { F_GETFD, F_SETFD };
const static unsigned long ioctls[] = {
FIONREAD,
FIONREAD, TCGETS,
#ifdef FEAT_PPS
PTP_SYS_OFFSET,
#endif
@@ -520,6 +517,12 @@ SYS_Linux_EnableSystemCallFilter(int level)
/* 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");
@@ -563,7 +566,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
}
if (seccomp_load(ctx) < 0)
LOG(LOGS_INFO, LOGF_SysLinux, "Failed to load seccomp rules");
LOG_FATAL(LOGF_SysLinux, "Failed to load seccomp rules");
LOG(LOGS_INFO, LOGF_SysLinux, "Loaded seccomp filter");
seccomp_release(ctx);

View File

@@ -31,25 +31,18 @@
#ifdef MACOSX
#include <sys/sysctl.h>
#include <sys/time.h>
#include <nlist.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/time.h>
#include <stdio.h>
#include <signal.h>
#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 "sched.h"
#include "logging.h"
#include "sched.h"
#include "privops.h"
#include "util.h"
/* ================================================== */
@@ -96,6 +89,11 @@ static struct timeval Tdrift;
#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
@@ -113,11 +111,12 @@ clock_initialise(void)
LOG_FATAL(LOGF_SysMacOSX, "gettimeofday() failed");
}
Tdrift = T0;
last_rtc_sync = T0;
newadj.tv_sec = 0;
newadj.tv_usec = 0;
if (adjtime(&newadj, &oldadj) < 0) {
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
}
@@ -169,7 +168,7 @@ start_adjust(void)
UTI_TimevalToDouble(&newadj, &adjustment_requested);
rounding_error = adjust_required - adjustment_requested;
if (adjtime(&newadj, &oldadj) < 0) {
if (PRV_AdjustTime(&newadj, &oldadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
@@ -193,7 +192,7 @@ stop_adjust(void)
zeroadj.tv_sec = 0;
zeroadj.tv_usec = 0;
if (adjtime(&zeroadj, &remadj) < 0) {
if (PRV_AdjustTime(&zeroadj, &remadj) < 0) {
LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
}
@@ -244,12 +243,12 @@ apply_step_offset(double offset)
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
if (PRV_SetTime(&new_time, NULL) < 0) {
DEBUG_LOG(LOGF_SysMacOSX, "settimeofday() failed");
return 0;
}
UTI_AddDoubleToTimeval(&T0, offset, &T1);
UTI_AddDoubleToTimeval(&T0, -offset, &T1);
T0 = T1;
start_adjust();
@@ -294,7 +293,6 @@ get_offset_correction(struct timeval *raw,
/* Cancel systematic drift */
static int drift_removal_running = 0;
static SCH_TimeoutID drift_removal_id;
/* ================================================== */
@@ -326,7 +324,8 @@ drift_removal_timeout(SCH_ArbitraryArgument not_used)
/* ================================================== */
/* use est_error to calculate the drift_removal_interval */
/* 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)
@@ -336,6 +335,20 @@ set_sync_status(int synchronised, double est_error, double max_error)
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);
@@ -401,6 +414,17 @@ SYS_MacOSX_SetScheduler(int SchedPriority)
/* ================================================== */
#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)
{
@@ -414,7 +438,6 @@ SYS_MacOSX_Initialise(void)
drift_removal_id = SCH_AddTimeoutByDelay(drift_removal_interval, drift_removal_timeout, NULL);
drift_removal_running = 1;
}
/* ================================================== */
@@ -422,9 +445,7 @@ SYS_MacOSX_Initialise(void)
void
SYS_MacOSX_Finalise(void)
{
if (drift_removal_running) {
SCH_RemoveTimeout(drift_removal_id);
}
SCH_RemoveTimeout(drift_removal_id);
clock_finalise();
}

View File

@@ -31,6 +31,7 @@
#define GOT_SYS_MACOSX_H
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);

View File

@@ -23,7 +23,7 @@
=======================================================================
Driver file for the NetBSD operating system.
Driver file for the NetBSD and FreeBSD operating system.
*/
#include "config.h"
@@ -33,6 +33,7 @@
#include "sys_netbsd.h"
#include "sys_timex.h"
#include "logging.h"
#include "privops.h"
#include "util.h"
/* Maximum frequency offset accepted by the kernel (in ppm) */
@@ -62,14 +63,14 @@ accrue_offset(double offset, double corr_rate)
UTI_DoubleToTimeval(-offset, &newadj);
if (adjtime(&newadj, &oldadj) < 0)
if (PRV_AdjustTime(&newadj, &oldadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
/* Add the old remaining adjustment if not zero */
UTI_TimevalToDouble(&oldadj, &offset);
if (offset != 0.0) {
UTI_AddDoubleToTimeval(&newadj, offset, &newadj);
if (adjtime(&newadj, NULL) < 0)
if (PRV_AdjustTime(&newadj, NULL) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
}
}
@@ -83,7 +84,7 @@ get_offset_correction(struct timeval *raw,
struct timeval remadj;
double adjustment_remaining;
if (adjtime(NULL, &remadj) < 0)
if (PRV_AdjustTime(NULL, &remadj) < 0)
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
@@ -122,23 +123,22 @@ SYS_NetBSD_Finalise(void)
void
SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
{
#ifdef NETBSD
int fd;
#endif
if (setgroups(0, NULL))
LOG_FATAL(LOGF_SysNetBSD, "setgroups() failed : %s", strerror(errno));
/* 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();
if (setgid(gid))
LOG_FATAL(LOGF_SysNetBSD, "setgid(%d) failed : %s", gid, strerror(errno));
if (setuid(uid))
LOG_FATAL(LOGF_SysNetBSD, "setuid(%d) failed : %s", uid, strerror(errno));
DEBUG_LOG(LOGF_SysNetBSD, "Root dropped to uid %d gid %d", uid, gid);
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

@@ -28,8 +28,10 @@
#include "sysincl.h"
#include "privops.h"
#include "sys_solaris.h"
#include "sys_timex.h"
#include "util.h"
/* ================================================== */
@@ -37,7 +39,8 @@ void
SYS_Solaris_Initialise(void)
{
/* 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);
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
0.0, 0.0, NULL, NULL);
}
/* ================================================== */
@@ -47,3 +50,14 @@ SYS_Solaris_Finalise(void)
{
SYS_Timex_Finalise();
}
/* ================================================== */
#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

@@ -30,10 +30,15 @@
#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"
@@ -41,6 +46,7 @@
#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

View File

@@ -27,8 +27,6 @@
#ifndef GOT_SYS_TIMEX_H
#define GOT_SYS_TIMEX_H
#include <sys/timex.h>
#include "localp.h"
extern void SYS_Timex_Initialise(void);

View File

@@ -35,6 +35,7 @@
#include <fcntl.h>
#include <float.h>
#include <glob.h>
#include <grp.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
@@ -53,10 +54,15 @@
#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>
#elif HAVE_STDINT_H

View File

@@ -6,12 +6,12 @@ cd ../..
for opts in \
"--enable-debug" \
"--enable-scfilter" \
"--disable-asyncdns" \
"--disable-ipv6" \
"--disable-privdrop" \
"--disable-readline" \
"--disable-rtc" \
"--disable-scfilter" \
"--disable-sechash" \
"--disable-cmdmon" \
"--disable-ntp" \

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "default test settings"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "large network"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "large frequency offset"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "large time offset"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "external time step"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "large jitter"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "large wander"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "NTP eras"
# Assume NTP_ERA_SPLIT is between years 1960 and 1990

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "source selection"
@@ -35,7 +35,6 @@ base_delay="(+ 1e-3 (equal 0.1 to 2) (equal 0.1 to 3))"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "minpoll/maxpoll options"
wander=0.0

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "iburst option"
freq_offset=1e-4

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "initstepslew directive"
freq_offset=0.0

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "driftfile directive"
servers=0

View File

@@ -1,20 +1,43 @@
#!/bin/bash
. test.common
. ./test.common
test_start "NTP authentication"
server_conf="keyfile tmp/keys"
client_conf="keyfile tmp/keys"
server_conf="keyfile tmp/server.keys"
client_conf="keyfile tmp/client.keys"
cat > tmp/keys <<-EOF
1 $(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24)
2 ASCII:$(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24)
3 MD5 ASCII:$(tr -c -d 'a-zA-Z' < /dev/urandom 2> /dev/null | head -c 24)
4 MD5 HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | head -c 32)
cat > tmp/server.keys <<-EOF
1 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
2 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
3 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
4 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
EOF
for key in 1 2 3 4; do
cat > tmp/client.keys <<-EOF
1 k]<j.Jtw^Oo;z5E>n\_0-x=)yP\f<)Z^
2 ASCII:k]<j.Jtw^Oo;z5E>n\_0-x=)yP\f<)Z^
3 MD5 ASCII:k]<j.Jtw^Oo;z5E>n\_0-x=)yP\f<)Z^
4 MD5 HEX:6B5D3C6A2E4A74775E4F6F3B7A35453E6E5C5F302D783D2979505C663C295A5E
EOF
keys=4
if grep -q 'FEAT_SECHASH 1' ../../config.h; then
hashes="MD5 SHA1 SHA256 SHA384 SHA512"
else
hashes="MD5"
fi
for hash in $hashes; do
keys=$[$keys + 1]
key=$(echo $keys $hash HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | \
head -c $[$RANDOM % 64 * 2 + 2]))
echo "$key" >> tmp/server.keys
echo "$key" >> tmp/client.keys
done
for key in $(seq $keys); do
client_server_options="key $key"
run_test || test_fail
check_chronyd_exit || test_fail
@@ -31,7 +54,7 @@ check_chronyd_exit || test_fail
check_sync && test_fail
check_packet_interval || test_fail
server_conf="keyfile tmp/keys"
server_conf="keyfile tmp/server.keys"
client_conf=""
run_test || test_fail
@@ -39,4 +62,24 @@ check_chronyd_exit || test_fail
# This check must fail as the client doesn't know the key
check_sync && test_fail
check_packet_interval || test_fail
client_conf="keyfile tmp/client.keys"
clients=2
peers=2
max_sync_time=500
base_delay="$default_base_delay (* -1 (equal 0.1 from 3) (equal 0.1 to 1))"
client_lpeer_options="key 1"
client_rpeer_options="key 1"
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
client_rpeer_options="key 2"
run_test || test_fail
check_chronyd_exit || test_fail
# This check must fail as the peers are using different keys"
check_sync && test_fail
test_pass

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "SHM refclock"
servers=0

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "allow/deny directives"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "NTP peers"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "makestep directive"
client_conf="makestep 0 -1

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "chronyc"
@@ -13,7 +13,7 @@ check_chronyd_exit || test_fail
check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\)
Stratum : 2
Ref time \(UTC\) : Fri Jan 1 00:1.:.. 2010
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time
Last offset : [+-]0\.000...... seconds
RMS offset : 0\.000...... seconds
@@ -25,7 +25,7 @@ Root dispersion : 0\.000... seconds
Update interval : [0-9]+\.. seconds
Leap status : Normal
210 Number of sources = 1
MS Name/IP address Stratum Poll Reach LastRx Last sample
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
210 Number of sources = 1

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "reply to client configured as server"

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