Compare commits

...

142 Commits

Author SHA1 Message Date
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
104 changed files with 3717 additions and 1622 deletions

3
.gitignore vendored
View File

@@ -22,3 +22,6 @@ tags
/version.h
/test/simulation/clknetsim
/test/simulation/tmp
/test/unit/Makefile
/test/unit/*.test
/test/unit/*.o

View File

@@ -120,6 +120,7 @@ install: chronyd chronyc
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc
$(MAKE) -C test/unit check
cd test/simulation && ./run
install-docs : docs

36
NEWS
View File

@@ -1,3 +1,36 @@
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
==================
@@ -12,6 +45,7 @@ Enhancements
* 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
@@ -23,7 +57,7 @@ Enhancements
Bug fixes
---------
* Fix building on Solaris
* Fix building on NetBSD, Solaris
* Restore time from driftfile with -s option if reading RTC failed
Removed features

1
README
View File

@@ -130,6 +130,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Bryan Christianson <bryan@whatroute.net>
Support for Mac OS X
Support for privilege separation
Juliusz Chroboczek <jch@pps.jussieu.fr>
Fix install rule in Makefile if chronyd file is in use.

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;
}

49
candm.h
View File

@@ -91,7 +91,9 @@
#define REQ_SMOOTHING 51
#define REQ_SMOOTHTIME 52
#define REQ_REFRESH 53
#define N_REQUEST_TYPES 54
#define REQ_SERVER_STATS 54
#define REQ_CLIENT_ACCESSES_BY_INDEX2 55
#define N_REQUEST_TYPES 56
/* Special utoken value used to log on with first exchange being the
password. (This time value has long since gone by) */
@@ -243,6 +245,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 +288,7 @@ typedef struct {
typedef struct {
uint32_t first_index;
uint32_t n_indices;
uint32_t n_clients;
int32_t EOR;
} REQ_ClientAccessesByIndex;
@@ -335,9 +339,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 +361,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 +441,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 +490,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 +559,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 +579,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 +649,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

@@ -3,7 +3,7 @@
@afourwide
@paragraphindent 0
@setfilename chrony.info
@settitle User guide for the chrony suite
@settitle User guide for the chrony suite version @CHRONY_VERSION@
@c @setchapternewpage off
@ifinfo
@@ -49,7 +49,6 @@ Copyright @copyright{} 2009-2015 Miroslav Lichvar
* Other time synchronisation packages:: Comparision with other software
* Distribution and warranty:: There is no warranty
* Bug reporting:: How to report bugs and make suggestions
* Contributing:: Areas where contributions are particularly welcome
@end menu
@c }}}
@c {{{ S:Overview
@@ -138,9 +137,9 @@ The `reference' implementation of the Network Time Protocol is the
program @code{ntpd}, available via
@uref{http://www.ntp.org/, The NTP home page}.
One of the main differences between @code{ntpd} and @code{chronyd} is in
the algorithms used to control the computer's clock. Things
@code{chronyd} can do better than @code{ntpd}:
One of the main differences between @code{ntpd} and @code{chronyd} is in how
they control the computer's clock. Things @code{chronyd} can do better than
@code{ntpd}:
@itemize @bullet
@item
@@ -160,13 +159,16 @@ longer periods of time.
@item
@code{chronyd} in the default configuration never steps the time to not
upset other running programs. @code{ntpd} can be configured to never
step the time too, but it has to use a different means of adjusting the
clock, which has some
disadvantages.
step the time too, but in that case it has to use a different means of
adjusting the clock (daemon loop instead of kernel discipline), which may
have a negative effect on accuracy of the clock.
@item
@code{chronyd} can adjust the rate of the clock in a larger range, which
allows it to operate even on machines with broken or unstable clock
(e.g. in some virtual machines).
@item
@code{chronyd} is smaller, it uses less memory and it wakes up the CPU only
when necessary, which is better for power saving.
@end itemize
Things @code{chronyd} can do that @code{ntpd} can't:
@@ -192,21 +194,36 @@ Things @code{ntpd} can do that @code{chronyd} can't:
@itemize @bullet
@item
@code{ntpd} supports all operating modes from RFC 5905, including
broadcast, multicast and manycast client / server. It supports the
orphan mode and it also supports authentication based on public-key
cryptography described in RFC 5906.
@code{ntpd} supports all operating modes from RFC 5905, including broadcast,
multicast, and manycast server/client. However, the broadcast and multicast
modes are inherently less accurate and less secure (even with authentication)
than the ordinary server/client mode and should generally be avoided.
@item
@code{ntpd} has been ported to more types of computer / operating
system.
@code{ntpd} supports the Autokey protocol (RFC 5906) to authenticate servers
with public-key cryptography. Note that the protocol has been shown to be
insecure and it will be probably replaced with an implementation of the Network
Time Security (NTS) specification.
@item
@code{ntpd} includes drivers for many reference clocks. @code{chronyd}
relies on other programs (e.g. gpsd) to access the data from the
reference clocks.
@code{ntpd} supports the orphan mode, which allows synchronisation to a common
timescale in isolated networks with multiple servers. With @code{chronyd}
there can be only one master and all other computers have to be directly or
indirectly synchronised to it.
@item
@code{ntpd} has been ported to more operating systems.
@item
@code{ntpd} includes a large number of reference clock drivers. @code{chronyd}
relies on other programs (e.g. @code{gpsd}) to access the timing data via the
@code{SHM} or @code{SOCK} driver.
@end itemize
A comparison of NTP implementations that includes more features and also
their performance is on the @uref{http://chrony.tuxfamily.org/comparison.html,
chrony comparison} page.
@node Comparison with timed
@subsection timed
@code{timed} is a program that is part of the BSD networking suite. It
@@ -272,39 +289,6 @@ pin-point the problem in some cases. Please be patient and plan for this!
Of course, if you can debug the problem yourself and send us a source code
patch to fix it, we will be very grateful!
@c }}}
@c {{{ S:Contributions
@node Contributing
@section Contributions
Although chrony is now a fairly mature and established project, there are still
areas that could be improved. If you can program in C and have some expertise
in these areas, you might be able to fill the gaps.
Particular areas that need addressing are :
@enumerate
@item Porting to other Unices
This involves creating equivalents of sys_solaris.c, sys_linux.c etc for the
new system.
@item Porting to Windows NT
A small amount of work on this was done under Cygwin. Only the sorting
out of the include files has really been achieved so far. The two main
areas still to address are
@enumerate
@item The system clock driver.
@item How to make chronyd into an NT service (i.e. what to replace fork(),
setsid() etc with so that chronyd can be automatically started in the system
bootstrap.
@end enumerate
@item More drivers for reference clock support
@end enumerate
@c }}}
@c }}}
@c {{{ Ch:Installation
@@ -355,9 +339,16 @@ for C-family shells.
If the software cannot (yet) be built on your system, an error message
will be shown. Otherwise, @file{Makefile} will be generated.
If editline or readline library is available, chronyc will be built with line
editing support. If you don't want this, specify the --disable-readline flag
to configure. Please refer to @pxref{line editing support} for more information.
On Linux, if development files for the libcap library are available,
@code{chronyd} will be built with support for dropping root privileges.
On other systems no extra library is needed. The default user which
@code{chronyd} should run as can be specified with the @code{--with-user}
option of the configure script.
If development files for the editline or readline library are available,
@code{chronyc} will be built with line editing support. If you don't want
this, specify the --disable-readline flag to configure. Please refer to
@pxref{line editing support} for more information.
If a @file{timepps.h} header is available (e.g. from the
@uref{http://linuxpps.org/, LinuxPPS project}), @code{chronyd} will be built with PPS API
@@ -423,7 +414,11 @@ makestep 1.0 3
rtcsync
@end example
Then, @code{chronyd} can be run.
Then, @code{chronyd} can be run. For security reasons, it's recommended to
create an unprivileged user for @code{chronyd} and specify it with the
@code{-u} command-line option or the @code{user} directive in the configuration
file, or set the default user with the @code{--with-user} configure option
before building.
@c }}}
@menu
* line editing support:: If libraries are in a non-standard place
@@ -992,14 +987,25 @@ no RTC or the RTC is broken (e.g. it has no battery).
@item -u <user>
This option sets the name of the system user to which @code{chronyd} will
switch after start in order to drop root privileges. It overrides the
@code{user} directive (default @code{@DEFAULT_USER@}). It may be set to a
non-root user only when @code{chronyd} is compiled with support for Linux
capabilities (libcap) or on NetBSD with the @code{/dev/clockctl} device.
@code{user} directive (default @code{@DEFAULT_USER@}).
On Linux, @code{chronyd} needs to be compiled with support for the
@code{libcap} library. On Mac OS X, FreeBSD, NetBSD and Solaris @code{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.
@item -F <level>
This option configures a system call filter when @code{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 @code{chrony} is installed as the filter needs to
allow also system calls made from libraries that @code{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,
@code{chronyd} could be killed even in normal operation.
@item -q
When run in this mode, @code{chronyd} will set the system clock once
and exit. It will not detach from the terminal.
@@ -1099,6 +1105,7 @@ the configuration file is ignored.
* cmdallow directive:: Give monitoring access to chronyc on other computers
* cmddeny directive:: Deny monitoring access to chronyc on other computers
* cmdport directive:: Set port to use for runtime monitoring
* cmdratelimit directive:: Limit command response rate
* combinelimit directive:: Limit sources included in combining algorithm
* corrtimeratio directive:: Set correction time ratio
* deny directive:: Deny access to NTP clients
@@ -1134,6 +1141,7 @@ the configuration file is ignored.
* pidfile directive:: Specify the file where chronyd's pid is written
* pool directive:: Specify an NTP pool
* port directive:: Set NTP server port
* ratelimit directive:: Limit NTP response rate
* refclock directive:: Specify a reference clock
* reselectdist directive:: Set improvement in distance needed to reselect a source
* rtcautotrim directive:: Specify threshold at which RTC is trimmed automatically
@@ -1363,11 +1371,13 @@ directive}).
@c {{{ clientloglimit
@node clientloglimit directive
@subsection clientloglimit
This directive specifies the maximum size of the memory allocated to
log client accesses. When the limit is reached, only information for
clients that have already been logged will be updated. If 0 is
specified, the memory size will be unlimited. The default is 524288
bytes.
This directive specifies the maximum amount of memory that @code{chronyd} is
allowed to allocate for logging of client accesses. The default limit is
524288 bytes, which allows monitoring of several thousands of addresses at the
same time.
In older @code{chrony} versions if the limit was set to 0, the memory
allocation was unlimited.
An example of the use of this directive is
@@ -1428,6 +1438,20 @@ This would make @code{chronyd} use 257/udp as its command port.
(@code{chronyc} would need to be run with the @code{-p 257} switch to
inter-operate correctly).
@c }}}
@c {{{ cmdratelimit
@node cmdratelimit directive
@subsection cmdratelimit
This directive enables response rate limiting for command packets. It's
similar to the @code{ratelimit} directive (@pxref{ratelimit directive}), except
responses to the localhost are never limited and the default interval is 1 (2
seconds), default burst is 16, and default leak rate is 2.
An example of use of the command is
@example
cmdratelimit interval 2
@end example
@c }}}
@c {{{ combinelimit
@node combinelimit directive
@subsection combinelimit
@@ -1710,28 +1734,25 @@ pairs. The format of the file is shown below
...
@end example
Each line consists of an ID, a name of authentication hash function (optional)
Each line consists of an ID, name of an authentication hash function (optional)
and a password. The ID can be any unsigned integer in the range 1 through
2**32-1. The hash function is MD5 by default, depending on how was
@code{chronyd} compiled, other allowed hash functions may be SHA1, SHA256,
SHA384, SHA512, RMD128, RMD160, RMD256, RMD320, TIGER and WHIRLPOOL. The
password can be encoded as a string of characters not containing a space with
optional @code{ASCII:} prefix or as a hexadecimal number with @code{HEX:}
prefix.
2**32-1. The default hash function is MD5. Depending on how @code{chronyd}
was compiled, other supported functions may be SHA1, SHA256, SHA384, SHA512,
RMD128, RMD160, RMD256, RMD320, TIGER and WHIRLPOOL. The password can be
specified as a string of characters not containing white space with an optional
@code{ASCII:} prefix, or as a hexadecimal number with the @code{HEX:} prefix.
The maximum length of the line is 2047 characters.
The password is used with the hash function to generate and verify a message
authentication code (MAC) in NTP packets.
For maximum security, it's recommended to use SHA1 or stronger hash function.
The passwords should be random and they should be as long as the output size of
the configured hash function, e.g. 160 bits with SHA1.
authentication code (MAC) in NTP packets. It's recommended to use SHA1 or a
stronger hash function with random passwords specified in the hexadecimal
format that have at least 128 bits. @code{chronyd} will log a warning to
syslog on start if a source is specified in the configuration file with a key
that has password shorter than 80 bits.
These shell commands can be used to generate random MD5 and SHA1 keys on
systems which have the @code{/dev/urandom} device:
@example
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)"
@end example
The @code{keygen} command of @code{chronyc} (@pxref{keygen command}) can be
used to generate random keys for the key file. By default, it generates
160-bit MD5 or SHA1 keys.
@c }}}
@c {{{ leapsecmode
@node leapsecmode directive
@@ -2254,24 +2275,22 @@ used to disable it entirely.
@c {{{ logchange
@node logchange directive
@subsection logchange
This directive forces @code{chronyd} to send a message to syslog if it
makes a system clock adjustment larger than a threshold value. An
example of use is
This directive sets the threshold for the adjustment of the system clock
that will generate a syslog message.
By default, the threshold is 1 second.
An example of use is
@example
logchange 0.5
logchange 0.1
@end example
which would cause a syslog message to be generated a system clock error
of over 0.5 seconds starts to be compensated.
of over 0.1 seconds starts to be compensated.
Clock errors detected either via NTP packets or via timestamps entered
Clock errors detected via NTP packets, reference clocks, or timestamps entered
via the @code{settime} command of @code{chronyc} are logged.
This directive assumes that syslog messages are appearing where somebody
can see them. This allows that person to see if a large error has
arisen, e.g. because of a fault, or because of faulty timezone handling,
for example when summer time (daylight saving) starts or ends.
@c }}}
@c {{{ logdir
@node logdir directive
@@ -2394,7 +2413,11 @@ includes the accumulated dispersion, which may be large when the source is no
longer synchronised, and half of the total round-trip delay to the primary
source.
By default, the maximum distance is 3 seconds.
By default, the maximum root distance is 3 seconds.
Setting @code{maxdistance} to a larger value can be useful to allow
synchronisation with a server that only has a very infrequent connection to its
sources and can accumulate a large dispersion between updates of its clock.
The syntax is
@@ -2520,6 +2543,9 @@ The syntax of this directive is identical to that for the @code{server}
directive (@pxref{server directive}), except that it is used to specify
an NTP peer rather than an NTP server.
When a key is specified by the @code{key} option to enable authentication, both
peers must be configured to use the same key and the same key number.
Please note that NTP peers that are not configured with a key to enable
authentication are vulnerable to a denial-of-service attack. An attacker
knowing that NTP hosts A and B are peering with each other can send a packet
@@ -2592,6 +2618,54 @@ port 11123
This would change the NTP port served by @code{chronyd} on the computer to
udp/11123.
@c }}}
@c {{{ ratelimit
@node ratelimit directive
@subsection ratelimit
This directive enables response rate limiting for NTP packets. Its purpose is
to reduce network traffic with misconfigured or broken NTP clients that are
polling the server too frequently. The limits are applied to individual IP
addresses. If multiple clients share one IP address (e.g. multiple hosts
behind NAT), the sum of their traffic will be limited. If a client that
increases its polling rate when it doesn't receive a reply is detected, its
rate limiting will be temporarily suspended to avoid increasing the overall
amount of traffic. The maximum number of IP addresses which can be monitored
at the same time depends on the memory limit set by the @code{clientloglimit}
directive.
The @code{ratelimit} directive supports a number of subfields (which
may be defined in any order):
@table @code
@item interval
This option sets the minimum interval between responses. It is defined as a
power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
is -4 and the maximum value is 12.
@item burst
This option sets the maximum number of responses that can be send in a burst,
temporarily exceeding the limit specified by the @code{interval} option. This
is useful for clients that make rapid measurements on start (e.g.
@code{chronyd} with the @code{iburst} option). The default value is 8. The
minimum value is 1 and the maximum value is 255.
@item leak
This option sets the rate at which responses are randomly allowed even if the
limits specified by the @code{interval} and @code{burst} options are exceeded.
This is necessary to prevent an attacker who is sending requests with a spoofed
source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 3 by default, i.e. on average at
least every eighth request has a response. The minimum value is 1 and the
maximum value is 4.
@end table
An example use of the command is
@example
ratelimit interval 4 burst 4
@end example
This would reduce the response rate for IP addresses that send packets on
average more frequently than once per 16 seconds and/or send packets in bursts
with more than 4 packets.
@c }}}
@c {{{ refclock
@node refclock directive
@subsection refclock
@@ -2726,6 +2800,18 @@ Prefer this source over sources without prefer option.
@item noselect
Never select this source. This is useful for monitoring or with sources
which are not very accurate, but are locked with a PPS refclock.
@item trust
Assume time from this source is always true. It can be rejected as a
falseticker in the source selection only if another source with this option
doesn't agree with it.
@item require
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 @code{trust} option this may be useful to allow a
trusted, but not very precise, reference clock to be safely combined with
unauthenticated NTP sources in order to improve the accuracy of the clock.
They can be selected and used for synchronisation only if they agree with
the trusted and required source.
@item minsamples
Set the minimum number of samples kept for this source. This overrides the
@code{minsamples} directive (@pxref{minsamples directive}).
@@ -2848,12 +2934,17 @@ Note that this setting is overriden when the @code{hwclockfile} directive
@node rtcsync directive
@subsection rtcsync
The @code{rtcsync} directive will enable a kernel mode where the
system time is copied to the real time clock (RTC) every 11 minutes.
The @code{rtcsync} directive enables a mode where the system time is
periodically copied to the real time clock (RTC).
This directive is supported only on Linux and cannot be used when the
normal RTC tracking is enabled, i.e. when the @code{rtcfile} directive
is used. On other systems this directive does nothing.
On Linux the RTC copy is performed by the kernel every 11 minutes. This
directive cannot be used when the normal RTC tracking is enabled,
i.e. when the @code{rtcfile} directive is used.
On Mac OS X, chronyd will perform the RTC copy every 60 minutes when the
system clock is in a synchronised state.
On other systems this directive does nothing.
@c }}}
@c {{{ sched_priority
@node sched_priority directive
@@ -3025,6 +3116,19 @@ Prefer this source over sources without prefer option.
@item noselect
Never select this source. This is particularly useful for monitoring.
@item trust
Assume time from this source is always true. It can be rejected as a
falseticker in the source selection only if another source with this option
doesn't agree with it.
@item require
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 @code{trust} option this may be useful to allow a
trusted authenticated source to be safely combined with unauthenticated sources
in order to improve the accuracy of the clock. They can be selected and used
for synchronisation only if they agree with the trusted and required source.
@item minsamples
Set the minimum number of samples kept for this source. This overrides the
@code{minsamples} directive (@pxref{minsamples directive}).
@@ -3180,11 +3284,15 @@ Valid measurements with corresponding compensations are logged to the
@subsection user
The @code{user} directive sets the name of the system user to which
@code{chronyd} will switch after start in order to drop root privileges.
It may be set to a non-root user only when @code{chronyd} is compiled with
support for Linux capabilities (libcap) or on NetBSD with the
@code{/dev/clockctl} device.
The default value is @code{@DEFAULT_USER@}.
On Linux, @code{chronyd} needs to be compiled with support for the
@code{libcap} library. On Mac OS X, FreeBSD, NetBSD and Solaris @code{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.
The default value is @code{@DEFAULT_USER@}. The configure script has a
@code{--with-user} option, which sets the default value.
@c }}}
@c }}}
@c {{{ S:Running chronyc
@@ -3338,6 +3446,7 @@ interface.
* dump command:: Dump measurement histories to files
* exit command:: Exit from chronyc
* help command:: Generate help summary
* keygen command:: Generate key for key file
* local command:: Let computer be a server when it is unsynchronised
* makestep command:: Correct the system clock by stepping instead of slewing
* manual command:: Enable/disable/configure options for settime
@@ -3357,6 +3466,7 @@ interface.
* reselectdist command:: Set improvement in distance needed to reselect a source
* retries command:: Set maximum number of retries
* rtcdata command:: Display RTC parameters
* serverstats command:: Display statistics of the server
* settime command:: Provide a manual input of the current time
* smoothing command:: Display current time smoothing state
* smoothtime command:: Reset/activate server time smoothing
@@ -3571,24 +3681,24 @@ burst 2/10 foo.example.net
@node clients command
@comment node-name, next, previous, up
@subsubsection clients
This command shows a list of all clients that have accessed the server,
This command shows a list of clients that have accessed the server,
through either the NTP or command/monitoring ports. It doesn't include
access to the Unix domain comamnd socket. There are no arguments.
accesses over the Unix domain comamnd socket. There are no arguments.
An example of the output is
@example
Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC
========================= ====== ====== ====== ====== ====== ==== ====
localhost 0 0 0 1 0 29y 0
aardvark.xxx 4 0 0 0 0 49 29y
badger.xxx 4 0 0 0 0 6 29y
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
localhost 2 0 2 - 133 15 0 -1 7
foo.example.net 12 0 6 - 23 0 0 - -
@end example
Each row shows the data for a single host. Only hosts that have passed
the host access checks (set with the @code{allow}, @code{deny},
@code{cmdallow} and @code{cmddeny} commands or configuration file
directives) are logged.
directives) are logged. The intervals are displayed as a power of 2 in
seconds.
The columns are as follows:
@@ -3596,29 +3706,24 @@ The columns are as follows:
@item
The hostname of the client
@item
The number of times the client has accessed the server using an NTP
client mode packet.
The number of NTP packets received from the client.
@item
The number of times the client has accessed the server using an NTP
symmetric active mode packet.
The number of NTP packets dropped to limit the response rate.
@item
The number of authenticated command packets that have been processed from the
client. Authentication is no longer supported in command packets, so the
number should be always zero.
The average interval between NTP packets.
@item
The number of unauthenticated command packets that have been processed
from the client.
@item
The number of bad command packets received from the client (not all
forms of bad packet are logged).
The average interval between NTP packets after limiting the response rate.
@item
Time since the last NTP packet was received
@item
Time since the last command packet was received
The number of command packets received from the client.
@item
The number of command packets dropped to limit the response rate.
@item
The average interval between command packets.
@item
Time since the last command packet was received.
@end enumerate
The last two entries will be shown as the time since 1970 if no packet
of that type has ever been received.
@c }}}
@c {{{ cmdaccheck
@node cmdaccheck command
@@ -3772,6 +3877,31 @@ The exit command exits from chronyc and returns the user to the shell
@subsubsection help
The help command displays a summary of the commands and their arguments.
@c }}}
@c {{{ keygen
@node keygen command
@subsubsection keygen
The @code{keygen} command generates a key that can be added to the
key file (@pxref{keyfile directive}) to allow NTP authentication between
server and client, or peers. The key is generated from the @code{/dev/urandom}
device and it's printed to standard output.
The command has three optional arguments. The first argument is the key number
(by default 1), which will be specified with the @code{key} option of the
@code{server} or @code{peer} directives in the configuration file. The second
argument is the hash function (by default SHA1 or MD5 if SHA1 is not available)
and the third argument is the number of bits the key should have, between 80
and 4096 bits (by default 160 bits).
An example is
@example
keygen 73 SHA1 256
@end example
which generates a 256-bit SHA-1 key with number 73. The printed line would
then be securely transferred and added to key files on both server and client,
or peers.
@c }}}
@c {{{ local
@node local command
@subsubsection local
@@ -4056,8 +4186,8 @@ next measurement has been made.
@node offline command
@subsubsection offline
The @code{offline} command is used to warn @code{chronyd} that the network
connection to a particular host or hosts is about to be lost. It should
be used on computers with a dial-up or similar connection to their time
connection to a particular host or hosts is about to be lost. It can
be used on computers with intermittent connection to their time
sources, to warn @code{chronyd} that the connection is about to be broken.
An example of how to use @code{offline} in this case is shown in
@@ -4094,14 +4224,6 @@ achieve this. The situation is shown in the figure below.
@end example
If the source to which @code{chronyd} is currently synchronised is indicated
offline in this way, @code{chronyd} will continue to treat it as the
synchronisation source. If the network connection were broken without
the @code{offline} command being used, @code{chronyd} would assume that the
source had failed and would attempt to pick another synchronisation
source.
There are four forms of the @code{offline} command. The first form is a
wildcard, meaning all sources. The second form allows an IP address mask
and a masked address to be specified. The third form uses the CIDR
@@ -4258,6 +4380,24 @@ right when it crosses a particular second boundary. Then it would be 1
microsecond fast when it crosses its next second boundary.
@end table
@c }}}
@c {{{ serverstats command
@node serverstats command
@subsubsection serverstats command
The @code{serverstats} command displays how many valid NTP and command requests
@code{chronyd} as a server received from clients, how many of them were dropped
to limit the response rate as configured by the @code{ratelimit} and
@code{cmdratelimit} directives, and how many client log records were dropped
due to the memory limit configured by the @code{clientloglimit} directive. An
example of the output is shown below.
@example
NTP packets received : 1598
NTP packets dropped : 8
Command packets received : 19
Command packets dropped : 0
Client log records dropped : 0
@end example
@c }}}
@c {{{ settime
@node settime command
@subsubsection settime

View File

@@ -101,15 +101,25 @@ RTC or the RTC is broken (e.g. it has no battery).
\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.
directive from the configuration file (default \fB@DEFAULT_USER@\fR).
On Linux, \fBchronyd\fR needs to be compiled with support for the \fBlibcap\fR
library. On Mac OS X, FreeBSD, NetBSD and Solaris \fBchronyd\fR 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.
.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).
It's recommended to enable the filter only when it's known to work on the
version of the system where \fBchrony\fR is installed as the filter needs to
allow also system calls made from libraries that \fBchronyd\fR 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,
\fBchronyd\fR could be killed even in normal operation.
.TP
.B \-q
When run in this mode, chronyd will set the system clock once

224
client.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
@@ -135,14 +135,12 @@ read_line(void)
/* ================================================== */
#define MAX_ADDRESSES 16
static ARR_Instance
get_sockaddrs(const char *hostnames, int port)
{
ARR_Instance addrs;
char *hostname, *s1, *s2;
IPAddr ip_addrs[MAX_ADDRESSES];
IPAddr ip_addrs[DNS_MAX_ADDRESSES];
union sockaddr_all *addr;
int i;
@@ -163,12 +161,12 @@ get_sockaddrs(const char *hostnames, int port)
LOG_FATAL(LOGF_Client, "Unix socket path too long");
addr->un.sun_family = AF_UNIX;
} else {
if (DNS_Name2IPAddress(hostname, ip_addrs, MAX_ADDRESSES) != DNS_Success) {
if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) {
DEBUG_LOG(LOGF_Client, "Could not get IP address for %s", hostname);
break;
}
for (i = 0; i < MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) {
addr = (union sockaddr_all *)ARR_GetNewElement(addrs);
UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr);
DEBUG_LOG(LOGF_Client, "Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i]));
@@ -1117,8 +1115,10 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
(data.params.online ? REQ_ADDSRC_ONLINE : 0) |
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.sel_option == SRC_SelectPrefer ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_option == SRC_SelectNoselect ? REQ_ADDSRC_NOSELECT : 0));
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
result = 1;
break;
@@ -1191,7 +1191,7 @@ give_help(void)
"makestep\0Correct clock by stepping immediately\0"
"makestep <threshold> <updates>\0Configure automatic clock stepping\0"
"maxupdateskew <skew>\0Modify maximum valid skew to update frequency\0"
"waitsync [max-tries [max-correction [max-skew [interval]]]]\0"
"waitsync [<max-tries> [<max-correction> [<max-skew> [<interval>]]]]\0"
"Wait until synchronised in specified limits\0"
"\0\0"
"Time sources:\0\0"
@@ -1225,6 +1225,7 @@ give_help(void)
"\0\0NTP access:\0\0"
"accheck <address>\0Check whether address is allowed\0"
"clients\0Report on clients that have accessed the server\0"
"serverstats\0Display statistics of the server\0"
"allow [<subnet>]\0Allow access to subnet as a default\0"
"allow all [<subnet>]\0Allow access to subnet and all children\0"
"deny [<subnet>]\0Deny access to subnet as a default\0"
@@ -1255,6 +1256,7 @@ give_help(void)
"dns -4|-6|-46\0Resolve hostnames only to IPv4/IPv6/both addresses\0"
"timeout <milliseconds>\0Set initial response timeout\0"
"retries <retries>\0Set maximum number of retries\0"
"keygen [<id> [<type> [<bits>]]]\0Generate key for key file\0"
"exit|quit\0Leave the program\0"
"help\0Generate this help\0"
"\0";
@@ -1366,6 +1368,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
if (recv_status < 0) {
/* If we get connrefused here, it suggests the sendto is
going to a dead port */
DEBUG_LOG(LOGF_Client, "Could not receive : %s", strerror(errno));
n_attempts++;
if (n_attempts > max_retries) {
@@ -1543,7 +1546,10 @@ static void
print_seconds(unsigned long s)
{
unsigned long d;
if (s <= 1024) {
if (s == (uint32_t)-1) {
printf(" -");
} else if (s <= 1024) {
printf("%4ld", s);
} else if (s < 36000) {
printf("%3ldm", s / 60);
@@ -1643,6 +1649,18 @@ print_signed_freq_ppm(double f)
/* ================================================== */
static void
print_clientlog_interval(int rate)
{
if (rate >= 127) {
printf(" -");
} else {
printf("%2d", rate);
}
}
/* ================================================== */
static int
check_for_verbose_flag(char *line)
{
@@ -1945,6 +1963,29 @@ process_cmd_tracking(char *line)
}
return 0;
}
/* ================================================== */
static int
process_cmd_serverstats(char *line)
{
CMD_Request request;
CMD_Reply reply;
request.command = htons(REQ_SERVER_STATS);
if (!request_reply(&request, &reply, RPY_SERVER_STATS, 0))
return 0;
printf("NTP packets received : %"PRIu32"\n", ntohl(reply.data.server_stats.ntp_hits));
printf("NTP packets dropped : %"PRIu32"\n", ntohl(reply.data.server_stats.ntp_drops));
printf("Command packets received : %"PRIu32"\n", ntohl(reply.data.server_stats.cmd_hits));
printf("Command packets dropped : %"PRIu32"\n", ntohl(reply.data.server_stats.cmd_drops));
printf("Client log records dropped : %"PRIu32"\n", ntohl(reply.data.server_stats.log_drops));
return 1;
}
/* ================================================== */
static int
@@ -2046,87 +2087,65 @@ process_cmd_clients(char *line)
{
CMD_Request request;
CMD_Reply reply;
unsigned long next_index;
int j;
IPAddr ip;
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;
char hostname_buf[50];
int n_replies;
int n_indices_in_table;
uint32_t i, n_clients, next_index, n_indices;
RPY_ClientAccesses_Client *client;
char hostname[26];
next_index = 0;
printf("Hostname Client Peer CmdAuth CmdNorm CmdBad LstN LstC\n"
"========================= ====== ====== ====== ====== ====== ==== ====\n");
printf("Hostname NTP Drop Int IntL Last Cmd Drop Int Last\n"
"===============================================================================\n");
do {
request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX);
while (1) {
request.command = htons(REQ_CLIENT_ACCESSES_BY_INDEX2);
request.data.client_accesses_by_index.first_index = htonl(next_index);
request.data.client_accesses_by_index.n_indices = htonl(MAX_CLIENT_ACCESSES);
request.data.client_accesses_by_index.n_clients = htonl(MAX_CLIENT_ACCESSES);
if (request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX, 0)) {
n_replies = ntohl(reply.data.client_accesses_by_index.n_clients);
n_indices_in_table = ntohl(reply.data.client_accesses_by_index.n_indices);
if (n_replies == 0) {
goto finished;
}
for (j=0; j<n_replies; j++) {
UTI_IPNetworkToHost(&reply.data.client_accesses_by_index.clients[j].ip, &ip);
if (ip.family != IPADDR_UNSPEC) {
/* UNSPEC implies that the node could not be found in
the daemon's tables; we shouldn't ever generate this
case, but ignore it if we do. (In future there might
be a protocol to reset the client logging; if another
administrator runs that while we're doing the clients
command, there will be a race condition that could
cause this). */
client_hits = ntohl(reply.data.client_accesses_by_index.clients[j].client_hits);
peer_hits = ntohl(reply.data.client_accesses_by_index.clients[j].peer_hits);
cmd_hits_auth = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_auth);
cmd_hits_normal = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_normal);
cmd_hits_bad = ntohl(reply.data.client_accesses_by_index.clients[j].cmd_hits_bad);
last_ntp_hit_ago = ntohl(reply.data.client_accesses_by_index.clients[j].last_ntp_hit_ago);
last_cmd_hit_ago = ntohl(reply.data.client_accesses_by_index.clients[j].last_cmd_hit_ago);
if (no_dns) {
snprintf(hostname_buf, sizeof(hostname_buf),
"%s", UTI_IPToString(&ip));
} else {
DNS_IPAddress2Name(&ip, hostname_buf, sizeof(hostname_buf));
hostname_buf[25] = 0;
}
printf("%-25s %6ld %6ld %6ld %6ld %6ld ",
hostname_buf,
client_hits, peer_hits,
cmd_hits_auth, cmd_hits_normal, cmd_hits_bad);
print_seconds(last_ntp_hit_ago);
printf(" ");
print_seconds(last_cmd_hit_ago);
printf("\n");
}
}
/* Set the next index to probe based on what the server tells us */
next_index = ntohl(reply.data.client_accesses_by_index.next_index);
if (next_index >= n_indices_in_table) {
goto finished;
}
} else {
if (!request_reply(&request, &reply, RPY_CLIENT_ACCESSES_BY_INDEX2, 0))
return 0;
}
} while (1); /* keep going until all subnets have been expanded,
down to single nodes */
finished:
n_clients = ntohl(reply.data.client_accesses_by_index.n_clients);
n_indices = ntohl(reply.data.client_accesses_by_index.n_indices);
for (i = 0; i < n_clients && i < MAX_CLIENT_ACCESSES; i++) {
client = &reply.data.client_accesses_by_index.clients[i];
UTI_IPNetworkToHost(&client->ip, &ip);
/* UNSPEC means the record could not be found in the daemon's tables.
We shouldn't ever generate this case, but ignore it if we do. */
if (ip.family == IPADDR_UNSPEC)
continue;
if (no_dns)
snprintf(hostname, sizeof (hostname), "%s", UTI_IPToString(&ip));
else
DNS_IPAddress2Name(&ip, hostname, sizeof (hostname));
printf("%-25s", hostname);
printf(" %6"PRIu32" %5"PRIu32" ",
ntohl(client->ntp_hits), ntohl(client->ntp_drops));
print_clientlog_interval(client->ntp_interval);
printf(" ");
print_clientlog_interval(client->ntp_timeout_interval);
printf(" ");
print_seconds(ntohl(client->last_ntp_hit_ago));
printf(" %6"PRIu32" %5"PRIu32" ",
ntohl(client->cmd_hits), ntohl(client->cmd_drops));
print_clientlog_interval(client->cmd_interval);
printf(" ");
print_seconds(ntohl(client->last_cmd_hit_ago));
printf("\n");
}
/* Set the next index to probe based on what the server tells us */
next_index = ntohl(reply.data.client_accesses_by_index.next_index);
if (next_index >= n_indices || n_clients < MAX_CLIENT_ACCESSES)
break;
}
return 1;
}
@@ -2419,6 +2438,39 @@ process_cmd_retries(const char *line)
/* ================================================== */
static int
process_cmd_keygen(char *line)
{
char hash_name[17];
unsigned char key[512];
unsigned int i, length, id = 1, bits = 160;
#ifdef FEAT_SECHASH
snprintf(hash_name, sizeof (hash_name), "SHA1");
#else
snprintf(hash_name, sizeof (hash_name), "MD5");
#endif
sscanf(line, "%u %16s %d", &id, hash_name, &bits);
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
if (HSH_GetHashId(hash_name) < 0) {
LOG(LOGS_ERR, LOGF_Client, "Unknown hash function %s", hash_name);
return 0;
}
UTI_GetRandomBytesUrandom(key, length);
printf("%u %s HEX:", id, hash_name);
for (i = 0; i < length; i++)
printf("%02hhX", key[i]);
printf("\n");
return 1;
}
/* ================================================== */
static int
process_line(char *line)
{
@@ -2505,6 +2557,9 @@ process_line(char *line)
do_normal_submit = 0;
give_help();
ret = 1;
} else if (!strcmp(command, "keygen")) {
ret = process_cmd_keygen(line);
do_normal_submit = 0;
} else if (!strcmp(command, "local")) {
do_normal_submit = process_cmd_local(&tx_message, line);
} else if (!strcmp(command, "makestep")) {
@@ -2556,6 +2611,9 @@ process_line(char *line)
} else if (!strcmp(command, "rtcdata")) {
do_normal_submit = 0;
ret = process_cmd_rtcreport(line);
} else if (!strcmp(command, "serverstats")) {
do_normal_submit = 0;
ret = process_cmd_serverstats(line);
} else if (!strcmp(command, "settime")) {
do_normal_submit = 0;
ret = process_cmd_settime(line);
@@ -2653,7 +2711,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2015 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2016 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n",

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

203
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,8 @@ static const char permissions[] = {
PERMIT_OPEN, /* SMOOTHING */
PERMIT_AUTH, /* SMOOTHTIME */
PERMIT_AUTH, /* REFRESH */
PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
};
/* ================================================== */
@@ -265,7 +267,20 @@ CAM_Initialise(int family)
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));
assert((command_length >= offsetof(CMD_Request, data) &&
command_length <= sizeof (CMD_Request)) || command_length == 0);
}
for (i = 1; i < N_REPLY_TYPES; i++) {
CMD_Reply r;
int reply_length;
r.reply = htons(i);
r.status = STT_SUCCESS;
r.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
reply_length = PKL_ReplyLength(&r);
assert((reply_length >= offsetof(CMD_Reply, data) &&
reply_length <= sizeof (CMD_Reply)) || reply_length == 0);
}
sock_fdu = -1;
@@ -661,17 +676,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 +773,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 +1029,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 +1161,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 +1254,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 +1284,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 +1294,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 +1304,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 +1314,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 */
@@ -1507,7 +1521,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 +1561,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

@@ -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;

181
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 *);
/* ================================================== */
@@ -149,10 +146,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 +182,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 */
@@ -414,7 +419,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")) {
@@ -426,17 +431,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")) {
@@ -466,7 +474,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")) {
@@ -494,13 +502,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")) {
@@ -518,7 +529,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")) {
@@ -609,25 +620,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();
}
}
/* ================================================== */
@@ -635,12 +651,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;
@@ -649,13 +664,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();
@@ -682,11 +697,11 @@ parse_refclock(char *line)
if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "lock")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
break;
lock_ref_id = ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
break;
@@ -722,18 +737,25 @@ parse_refclock(char *line)
break;
} else if (!strcasecmp(cmd, "noselect")) {
n = 0;
sel_option = SRC_SelectNoselect;
sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
n = 0;
sel_option = SRC_SelectPrefer;
sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "trust")) {
n = 0;
sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
n = 0;
sel_options |= SRC_SELECT_REQUIRE;
} else {
break;
other_parse_error("Invalid refclock option");
return;
}
line += n;
}
if (*line) {
other_parse_error("Invalid/unreadable refclock parameter");
command_parse_error();
return;
}
@@ -746,11 +768,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;
}
@@ -861,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;
}
}
/* ================================================== */
@@ -1027,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
@@ -1623,11 +1605,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;
}
/* ================================================== */
@@ -1789,6 +1770,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)
{

4
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);
@@ -98,6 +98,8 @@ 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);

66
configure vendored
View File

@@ -95,7 +95,7 @@ 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
@@ -200,8 +200,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=""
@@ -305,6 +306,9 @@ do
--without-libcap|--disable-linuxcaps)
try_libcap=0
;;
--enable-scfilter)
feat_scfilter=1
;;
--disable-scfilter)
feat_scfilter=0
;;
@@ -372,10 +376,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)
@@ -389,6 +396,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)
@@ -400,6 +411,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 ")"
;;
* )
@@ -476,7 +491,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
@@ -519,6 +534,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;
@@ -527,13 +549,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
@@ -557,6 +579,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
@@ -595,6 +621,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" ] && \
@@ -602,9 +629,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);'
@@ -802,12 +841,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 test/unit/Makefile chrony.conf.5 chrony.texi chronyc.1 chronyd.8
do
echo Creating $f
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
@@ -831,7 +872,8 @@ do
s%@CHRONYSOCKDIR@%${CHRONYSOCKDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_HWCLOCK_FILE@%${default_hwclockfile}%;\
s%@DEFAULT_USER@%${default_user}%;"\
s%@DEFAULT_USER@%${default_user}%;\
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
< ${f}.in > $f
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 $?

View File

@@ -49,6 +49,44 @@ added to +chrony+ to deal with this.
== 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 by adding 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 on Linux, 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 kernel mode in
which the system time is copied to the RTC every 11 minutes.
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
@@ -90,21 +128,24 @@ 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.
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?

View File

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

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

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

@@ -51,7 +51,7 @@ 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
if [ $(wc -l < INSTALL) -gt 120 -o $(wc -l < INSTALL) -lt 85 ]; then
echo "INSTALL generated incorrectly?"
exit 3
fi

View File

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

View File

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

View File

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

View File

@@ -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));
}
}
@@ -738,6 +760,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 +800,14 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
)
{
NTP_Packet message;
int leap, auth_len, length, ret;
int leap, 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 +816,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;
are_we_synchronised = 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) {
@@ -825,7 +874,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 +885,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 +918,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 +938,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);
@@ -911,7 +963,7 @@ transmit_timeout(void *arg)
NCR_Instance inst = (NCR_Instance) arg;
int sent;
inst->timer_running = 0;
inst->tx_timeout_id = 0;
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
@@ -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;
@@ -1190,11 +1248,13 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
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 &&
@@ -1271,9 +1331,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
@@ -1326,24 +1387,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 +1411,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);
@@ -1421,7 +1463,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 +1523,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 +1531,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 +1538,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 +1560,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 +1613,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 +1656,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 +1682,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 +1693,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);

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
@@ -189,7 +190,7 @@ prepare_socket(int family, int port_number, int client_only)
#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");
@@ -206,20 +207,22 @@ prepare_socket(int family, int port_number, int client_only)
}
#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");
}
#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");
}
#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;
@@ -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

@@ -57,7 +57,8 @@ typedef struct {
sources from the pool respond first */
} 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 */
@@ -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);

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,120 @@
#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 */
REQ_LENGTH_ENTRY(local, null), /* 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 */
};
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 +156,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 +177,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

@@ -233,7 +233,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 +260,7 @@ RCL_AddRefclock(RefclockParameters *params)
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_option, NULL,
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples);
DEBUG_LOG(LOGF_Refclock, "refclock %s refid=%s poll=%d dpoll=%d filter=%d",
@@ -372,8 +372,6 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
!valid_sample_time(instance, sample_time))
return 0;
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
switch (leap) {
case LEAP_Normal:
case LEAP_InsertSecond:
@@ -381,10 +379,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);

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

@@ -80,8 +80,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 +105,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 */
@@ -234,7 +232,7 @@ REF_Initialise(void)
enable_local_stratum = CNF_AllowLocalReference(&local_stratum);
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 +253,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 +262,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 +271,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 +421,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 +455,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 +472,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 +530,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 +676,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 +728,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 +748,6 @@ set_leap_timeout(time_t now)
}
leap_timeout_id = SCH_AddTimeout(&when, leap_start_timeout, NULL);
leap_timer_running = 1;
}
/* ================================================== */

View File

@@ -38,7 +38,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 */
@@ -88,15 +88,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;

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;
@@ -581,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) {
@@ -630,7 +627,7 @@ switch_interrupts(int onoff)
static void
measurement_timeout(void *any)
{
timeout_running = 0;
timeout_id = 0;
switch_interrupts(1);
}
@@ -894,7 +891,6 @@ turn_off_interrupt:
switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
}
@@ -907,7 +903,6 @@ turn_off_interrupt:
switch_interrupts(0);
timeout_running = 1;
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
}
@@ -916,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;
@@ -936,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);
}
/* ================================================== */
@@ -946,7 +939,6 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
void
RTC_Linux_StartMeasurements(void)
{
timeout_running = 0;
measurement_timeout(NULL);
}
@@ -1126,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);
}

41
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;
}
@@ -278,6 +276,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 +306,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 +374,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 +418,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 +442,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 +460,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);
}
/* ================================================== */

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,

100
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
@@ -118,7 +118,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 +209,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 +241,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 +414,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++;
@@ -602,8 +602,9 @@ 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;
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 +631,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;
}
@@ -736,22 +743,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 +771,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 +792,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 +815,35 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_sel_sources = 0;
for (i = 0; i < n_sources; i++) {
/* This should be the same condition to get into the endpoint
list */
if (sources[i]->status != SRC_OK)
continue;
/* This should be the same condition to get into the endpoint
list */
/* Check if source's interval contains the best interval, or
is wholly contained within it */
if ((sources[i]->sel_info.lo_limit <= best_lo &&
/* Check if source's interval contains the best interval, or is wholly
contained within it. If there are any trusted sources the first
condition is applied only to them to not allow non-trusted sources to
move the final offset outside the interval. */
if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) &&
sources[i]->sel_info.lo_limit <= best_lo &&
sources[i]->sel_info.hi_limit >= best_hi) ||
(sources[i]->sel_info.lo_limit >= best_lo &&
sources[i]->sel_info.hi_limit <= best_hi)) {
sel_sources[n_sel_sources++] = i;
if (sources[i]->sel_options & SRC_SELECT_REQUIRE)
sel_req_source = 0;
} else {
sources[i]->status = SRC_FALSETICKER;
}
}
if (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 +869,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 +910,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;
@@ -1255,20 +1280,7 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
break;
}
switch (src->sel_option) {
case SRC_SelectNormal:
report->sel_option = RPT_NORMAL;
break;
case SRC_SelectPrefer:
report->sel_option = RPT_PREFER;
break;
case SRC_SelectNoselect:
report->sel_option = RPT_NOSELECT;
break;
default:
assert(0);
}
report->sel_options = src->sel_options;
report->reachability = src->reachability;
/* Call stats module to fill out estimates */

View File

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

View File

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

View File

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

View File

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

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,7 +45,6 @@
#ifdef FEAT_PRIVDROP
#include <sys/prctl.h>
#include <sys/capability.h>
#include <grp.h>
#endif
#ifdef FEAT_SCFILTER
@@ -66,6 +65,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 +404,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 +426,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 +454,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 +498,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 +516,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");

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,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "NTP authentication"
@@ -39,4 +39,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/keys"
clients=2
peers=2
max_sync_time=300
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"

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "port and acquisitionport directives"

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "leap second"
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "presend option"
min_sync_time=140

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "fallback drift"
limit=100000

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "maxdelay options"
max_sync_time=2000

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
test_start "smoothtime option"
server_strata=2

View File

@@ -0,0 +1,68 @@
#!/bin/bash
. ./test.common
test_start "source selection options"
servers=3
falsetickers=2
base_delay=0.6
client_server_conf="
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3 trust"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_conf="
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3 prefer"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
# This check is expected to fail
check_sync && test_fail
base_delay=1.1
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
base_delay=$default_base_delay
falsetickers=1
client_server_conf="
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3 require"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_conf="
server 192.168.123.1 require
server 192.168.123.2
server 192.168.123.3"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
# These checks are expected to fail
check_source_selection && test_fail
check_sync && test_fail
test_pass

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
# Test fix in commit 60d0fa299307076143da94d36deb7b908fa9bdb7

View File

@@ -1,6 +1,6 @@
#!/bin/bash
. test.common
. ./test.common
# Test fix in commit 4253075a97141edfa62043ab71bd0673587e6629

View File

@@ -1,12 +1,11 @@
This is a collection of simulation tests. They use clknetsim to simulate
multiple systems connected in a network. It's available at
This is a collection of simulation tests using the clknetsim simulator
(supported on Linux only).
https://github.com/mlichvar/clknetsim
If this directory doesn't have a clknetsim subdirectory, a known working
revision will be downloaded and compiled automatically.
Currently it runs only on Linux.
The CLKNETSIM_PATH environment variable should point to the directory where
clknetsim was downloaded and compiled. If the variable is not set, the tests
will look for clknetsim in ./clknetsim in the current directory.
The tests are written in bash and they can be run directly. The ./run script
runs all tests.

View File

@@ -1,7 +1,5 @@
#!/bin/bash
. test.common
passed=() failed=() skipped=()
[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_])

View File

@@ -15,27 +15,11 @@
export LC_ALL=C
export PATH=../../:$PATH
export CLKNETSIM_PATH=clknetsim
export CLKNETSIM_PATH=${CLKNETSIM_PATH:-clknetsim}
# Known working clknetsim revision
clknetsim_revision=1e56224dee1db69c0027e9bd63c2a202d4765959
clknetsim_url=https://github.com/mlichvar/clknetsim/archive/$clknetsim_revision.tar.gz
# Only Linux is supported
if [ "$(uname -s)" != Linux ]; then
echo "Simulation tests supported only on Linux"
exit 3
fi
# Try to download clknetsim if not found
if [ ! -e $CLKNETSIM_PATH ]; then
curl -L "$clknetsim_url" | tar xz || exit 3
ln -s clknetsim-$clknetsim_revision clknetsim || exit 3
fi
# Try to build clknetsim if not built
if [ ! -x $CLKNETSIM_PATH/clknetsim -o ! -e $CLKNETSIM_PATH/clknetsim.so ]; then
make -C clknetsim || exit 3
if [ ! -x $CLKNETSIM_PATH/clknetsim ]; then
echo "SKIP (clknetsim not found)"
exit 9
fi
. $CLKNETSIM_PATH/clknetsim.bash
@@ -69,7 +53,11 @@ default_client_server_conf=""
default_server_server_options=""
default_client_server_options=""
default_server_peer_options=""
default_server_lpeer_options=""
default_server_rpeer_options=""
default_client_peer_options=""
default_client_lpeer_options=""
default_client_rpeer_options=""
default_server_conf=""
default_client_conf=""
default_chronyc_conf=""
@@ -189,7 +177,8 @@ get_chronyd_conf() {
done
for i in $(seq 1 $peers); do
[ $i -eq $peer -o $i -gt $servers ] && continue
echo "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $server_peer_options"
echo -n "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $server_peer_options "
[ $i -lt $peer ] && echo "$server_lpeer_options" || echo "$server_rpeer_options"
done
echo "$server_conf"
else
@@ -202,7 +191,8 @@ get_chronyd_conf() {
fi
for i in $(seq 1 $peers); do
[ $i -eq $peer -o $i -gt $clients ] && continue
echo "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $client_peer_options"
echo -n "peer 192.168.123.$[$servers * ($stratum - 1) + $i] $client_peer_options "
[ $i -lt $peer ] && echo "$client_lpeer_options" || echo "$client_rpeer_options"
done
echo "$client_conf"
fi

42
test/unit/Makefile.in Normal file
View File

@@ -0,0 +1,42 @@
TEST_WRAPPER =
CHRONY_SRCDIR = ../..
CC = @CC@
CFLAGS = @CFLAGS@
CPPFLAGS = -I$(CHRONY_SRCDIR) @CPPFLAGS@
LDFLAGS = @LDFLAGS@ @LIBS@ @EXTRA_LIBS@
SHARED_OBJS = test.o
TEST_OBJS := $(sort $(patsubst %.c,%.o,$(wildcard *.c)))
TESTS := $(patsubst %.o,%.test,$(filter-out $(SHARED_OBJS),$(TEST_OBJS)))
FILTER_OBJS = %/main.o %/client.o %/getdate.o
CHRONY_OBJS := $(filter-out $(FILTER_OBJS),$(wildcard $(CHRONY_SRCDIR)/*.o))
all: $(TESTS)
%.test: %.o $(SHARED_OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(CHRONY_OBJS:%/$*.o=) $(LDFLAGS)
%.o: %.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
check: $(TESTS)
@ret=0; \
for t in $^; do \
$(TEST_WRAPPER) ./$$t || ret=1; \
done; \
exit $$ret
clean:
rm -f *.o $(TESTS)
rm -rf .deps
.deps:
@mkdir .deps
.deps/%.d: %.c | .deps
@$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@
-include $(TEST_OBJS:%.o=.deps/%.d)

83
test/unit/addrfilt.c Normal file
View File

@@ -0,0 +1,83 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <addrfilt.c>
#include <logging.h>
#include <util.h>
#include "test.h"
void
test_unit(void)
{
int i, j, sub, maxsub;
IPAddr ip;
ADF_AuthTable table;
table = ADF_CreateTable();
for (i = 0; i < 100; i++) {
for (j = 0; j < 1000; j++) {
if (j % 2) {
maxsub = 32;
TST_GetRandomAddress(&ip, IPADDR_INET4, -1);
} else {
maxsub = 128;
TST_GetRandomAddress(&ip, IPADDR_INET6, -1);
}
DEBUG_LOG(0, "address %s", UTI_IPToString(&ip));
sub = random() % (maxsub + 1);
TEST_CHECK(!ADF_IsAllowed(table, &ip));
ADF_Allow(table, &ip, sub);
TEST_CHECK(ADF_IsAllowed(table, &ip));
if (sub < maxsub) {
TST_SwapAddressBit(&ip, sub);
TEST_CHECK(ADF_IsAllowed(table, &ip));
}
if (sub > 0) {
TST_SwapAddressBit(&ip, sub - 1);
TEST_CHECK(!ADF_IsAllowed(table, &ip));
if (sub % 4 != 1) {
ADF_Deny(table, &ip, sub - 1);
TST_SwapAddressBit(&ip, sub - 1);
TEST_CHECK(!ADF_IsAllowed(table, &ip));
}
}
if (sub > 4) {
ADF_AllowAll(table, &ip, sub - 4);
TEST_CHECK(ADF_IsAllowed(table, &ip));
}
ADF_DenyAll(table, &ip, 0);
}
ip.family = IPADDR_INET4;
ADF_DenyAll(table, &ip, 0);
ip.family = IPADDR_INET6;
ADF_DenyAll(table, &ip, 0);
}
ADF_DestroyTable(table);
}

84
test/unit/clientlog.c Normal file
View File

@@ -0,0 +1,84 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <clientlog.c>
#include "test.h"
void
test_unit(void)
{
int i, j, index;
struct timeval tv;
IPAddr ip;
char conf[][100] = {
"clientloglimit 10000",
"ratelimit interval 3 burst 4 leak 3",
"cmdratelimit interval 3 burst 4 leak 3",
};
CNF_Initialise(0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
CLG_Initialise();
TEST_CHECK(ARR_GetSize(records) == 16);
for (i = 0; i < 500; i++) {
DEBUG_LOG(0, "iteration %d", i);
tv.tv_sec = (time_t)random() & 0x0fffffff;
tv.tv_usec = 0;
for (j = 0; j < 1000; j++) {
TST_GetRandomAddress(&ip, IPADDR_UNSPEC, i % 8 ? -1 : i / 8 % 9);
DEBUG_LOG(0, "address %s", UTI_IPToString(&ip));
if (random() % 2) {
index = CLG_LogNTPAccess(&ip, &tv);
TEST_CHECK(index >= 0);
CLG_LimitNTPResponseRate(index);
} else {
index = CLG_LogCommandAccess(&ip, &tv);
TEST_CHECK(index >= 0);
CLG_LimitCommandResponseRate(index);
}
UTI_AddDoubleToTimeval(&tv, (1 << random() % 14) / 100.0, &tv);
}
}
DEBUG_LOG(0, "records %d", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 128);
for (i = j = 0; i < 10000; i++) {
tv.tv_sec += 1;
index = CLG_LogNTPAccess(&ip, &tv);
TEST_CHECK(index >= 0);
if (!CLG_LimitNTPResponseRate(index))
j++;
}
DEBUG_LOG(0, "requests %u responses %u", i, j);
TEST_CHECK(j * 4 < i && j * 6 > i);
CLG_Finalise();
CNF_Finalise();
}

99
test/unit/ntp_sources.c Normal file
View File

@@ -0,0 +1,99 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <ntp_sources.c>
#include <conf.h>
#include <ntp_io.h>
#include "test.h"
void
test_unit(void)
{
int i, j, k, slot, found;
uint32_t hash = 0;
NTP_Remote_Address addrs[256], addr;
SourceParameters params;
char conf[] = "port 0";
memset(&params, 0, sizeof (params));
CNF_Initialise(0);
CNF_ParseLine(NULL, 1, conf);
LCL_Initialise();
SCH_Initialise();
SRC_Initialise();
NIO_Initialise(IPADDR_UNSPEC);
NCR_Initialise();
NSR_Initialise();
for (i = 0; i < 6; i++) {
TEST_CHECK(ARR_GetSize(records) == 1);
DEBUG_LOG(0, "collision mod %u", 1U << i);
for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
do {
TST_GetRandomAddress(&addrs[j].ip_addr, IPADDR_UNSPEC, -1);
} while (UTI_IPToHash(&addrs[j].ip_addr) % (1U << i) != hash % (1U << i));
addrs[j].port = random() % 1024;
if (!j)
hash = UTI_IPToHash(&addrs[j].ip_addr);
DEBUG_LOG(0, "adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr),
UTI_IPToHash(&addrs[j].ip_addr) % (1U << i));
NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, &params);
for (k = 0; k < j; k++) {
addr = addrs[k];
find_slot(&addr, &slot, &found);
TEST_CHECK(found == 2);
TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
&addr.ip_addr, NULL));
addr.port++;
find_slot(&addr, &slot, &found);
TEST_CHECK(found == 1);
TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr,
&addr.ip_addr, NULL));
}
}
for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) {
DEBUG_LOG(0, "removing source %s", UTI_IPToString(&addrs[j].ip_addr));
NSR_RemoveSource(&addrs[j]);
for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) {
find_slot(&addrs[k], &slot, &found);
TEST_CHECK(found == (k <= j ? 0 : 2));
}
}
}
NSR_Finalise();
NCR_Finalise();
NIO_Finalise();
SRC_Finalise();
SCH_Finalise();
LCL_Finalise();
CNF_Finalise();
}

137
test/unit/sources.c Normal file
View File

@@ -0,0 +1,137 @@
/*
**********************************************************************
* 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.
*
**********************************************************************
*/
#include <sources.c>
#include "test.h"
void
test_unit(void)
{
SRC_Instance srcs[16];
RPT_SourceReport report;
IPAddr addr;
int i, j, k, l, samples, sel_options;
double offset, delay, disp;
struct timeval tv;
CNF_Initialise(0);
LCL_Initialise();
TST_RegisterDummyDrivers();
SCH_Initialise();
SRC_Initialise();
REF_Initialise();
REF_SetMode(REF_ModeIgnore);
for (i = 0; i < 1000; i++) {
DEBUG_LOG(0, "iteration %d", i);
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
TEST_CHECK(n_sources == j);
TST_GetRandomAddress(&addr, IPADDR_UNSPEC, -1);
sel_options = i & random() & (SRC_SELECT_NOSELECT | SRC_SELECT_PREFER |
SRC_SELECT_TRUST | SRC_SELECT_REQUIRE);
DEBUG_LOG(0, "added source %d options %d", j, sel_options);
srcs[j] = SRC_CreateNewInstance(UTI_IPToRefid(&addr), SRC_NTP, sel_options, &addr,
SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES);
SRC_UpdateReachability(srcs[j], 1);
samples = (i + j) % 5 + 3;
offset = TST_GetRandomDouble(-1.0, 1.0);
for (k = 0; k < samples; k++) {
SCH_GetLastEventTime(&tv, NULL, NULL);
UTI_AddDoubleToTimeval(&tv, TST_GetRandomDouble(k - samples, k - samples + 1), &tv);
offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
disp = TST_GetRandomDouble(1.0e-6, 1.0e-1);
DEBUG_LOG(0, "source %d sample %d offset %f delay %f disp %f", j, k,
offset, delay, disp);
SRC_AccumulateSample(srcs[j], &tv, offset, delay, disp, delay, disp,
1, LEAP_Normal);
}
for (k = 0; k <= j; k++) {
int passed = 0, trusted = 0, trusted_passed = 0, required = 0, required_passed = 0;
double trusted_lo = DBL_MAX, trusted_hi = DBL_MIN;
double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
SRC_SelectSource(srcs[k]);
DEBUG_LOG(0, "source %d status %d", k, sources[k]->status);
for (l = 0; l <= j; l++) {
TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
if (sources[l]->sel_options & SRC_SELECT_NOSELECT) {
TEST_CHECK(sources[l]->status == SRC_UNSELECTABLE);
} else if (sources[l]->status != SRC_BAD_DISTANCE) {
if (sources[l]->status >= SRC_NONPREFERRED) {
passed++;
if (passed_lo > sources[l]->sel_info.lo_limit)
passed_lo = sources[l]->sel_info.lo_limit;
if (passed_hi < sources[l]->sel_info.hi_limit)
passed_hi = sources[l]->sel_info.hi_limit;
}
if (sources[l]->sel_options & SRC_SELECT_TRUST) {
trusted++;
if (trusted_lo > sources[l]->sel_info.lo_limit)
trusted_lo = sources[l]->sel_info.lo_limit;
if (trusted_hi < sources[l]->sel_info.hi_limit)
trusted_hi = sources[l]->sel_info.hi_limit;
if (sources[l]->status >= SRC_NONPREFERRED)
trusted_passed++;
}
if (sources[l]->sel_options & SRC_SELECT_REQUIRE) {
required++;
if (sources[l]->status >= SRC_NONPREFERRED)
required_passed++;
}
if (sources[l]->sel_options & SRC_SELECT_PREFER)
TEST_CHECK(sources[l]->status != SRC_NONPREFERRED);
}
}
DEBUG_LOG(0, "sources %d passed %d trusted %d/%d required %d/%d", j, passed,
trusted_passed, trusted, required_passed, required);
TEST_CHECK(!trusted || !passed || (passed_lo >= trusted_lo && passed_hi <= trusted_hi));
TEST_CHECK(!passed || trusted != 1 || (trusted == 1 && trusted_passed == 1));
TEST_CHECK(!passed || !required || required_passed > 0);
}
}
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
SRC_ReportSource(j, &report, &tv);
SRC_DestroyInstance(srcs[j]);
}
}
REF_Finalise();
SRC_Finalise();
SCH_Finalise();
LCL_Finalise();
CNF_Finalise();
}

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