Compare commits

...

197 Commits

Author SHA1 Message Date
Miroslav Lichvar
a5c865937f doc: update NEWS 2014-06-06 16:42:20 +02:00
Miroslav Lichvar
f48fd84d76 make_release: fix man page preparation 2014-06-06 16:42:20 +02:00
Miroslav Lichvar
a8693a21f8 Check return value of chmod() and fcntl() 2014-06-06 12:07:31 +02:00
Miroslav Lichvar
9b630a0664 reference: fix adjusting of last reference update time
The timestamp is in cooked time, it needs to be adjusted on all changes,
not only on step.
2014-06-06 11:31:43 +02:00
Miroslav Lichvar
79ac20c161 client: fix printing of negative poll in sources report 2014-06-06 10:13:25 +02:00
Miroslav Lichvar
cb74f3e7ad Update copyright years 2014-06-05 18:06:56 +02:00
Miroslav Lichvar
c4865e2cb6 doc: update acknowledgements 2014-06-05 18:06:56 +02:00
Miroslav Lichvar
20d2363fb7 reference: rework makestep
Rework makestep to cancel accumulated offset and step with the new
offset instead of accumulating new offset first, canceling all
accumulated offset and making the step.

This avoids two large frequency changes to initiate and cancel a slew
before making the step.
2014-06-05 14:46:22 +02:00
Miroslav Lichvar
64ba5a5b65 test: extend 109-makestep 2014-06-05 14:26:07 +02:00
Miroslav Lichvar
9913851413 ntp: cook SO_TIMESTAMP timestamp
This is a partial revert of 8aa9eb19c8.
With the new generic driver cooking is cheap and it should be slighly
more accurate than reusing offset correction from the scheduler
timestamps.
2014-06-04 16:58:41 +02:00
Miroslav Lichvar
e9a8503c6b reference: cook timestamp when setting reference
This is a partial revert of 8aa9eb19c8.
With the new generic driver cooking is cheap and it should be slighly
more accurate than reusing offset correction from the scheduler
timestamps.
2014-06-04 16:58:41 +02:00
Miroslav Lichvar
a02d7555c2 test: add 113-leapsecond 2014-06-04 16:58:41 +02:00
Miroslav Lichvar
779b341b61 reference: announce leap second only on last day of June and December 2014-06-04 16:58:41 +02:00
Miroslav Lichvar
a646cf7923 logging: convert rate limited messages to debug messages 2014-06-04 12:26:27 +02:00
Miroslav Lichvar
0dea8d97f4 logging: move check of enabled debugging to DEBUG_LOG macro
This avoids unnecessary calls to the logging function when debugging
messages are not logged. The cost is a slight increase in the size of
the binary (when compiled with debug messages).
2014-06-04 12:12:04 +02:00
Miroslav Lichvar
97ba9e4d47 local: round log value of clock precision
This is similar to what the reference NTP implementation does. With 1us
clock resolution that gives -20 instead of -19.
2014-06-04 12:12:04 +02:00
Miroslav Lichvar
3f3ebd3b3b ntp: update comment in get_transmit_delay() 2014-06-03 18:10:27 +02:00
Miroslav Lichvar
571669ad6c test: update packet interval check for new clknetsim 2014-06-03 18:10:27 +02:00
Miroslav Lichvar
855eb09d58 test: check also minimum outgoing packet interval 2014-06-03 18:10:27 +02:00
Miroslav Lichvar
ead3ca14a0 main: delay switching to normal mode after initstepslew
This prevents polling interval shorter than the burst interval
if some configured servers were used also for initstepslew.
2014-06-03 18:10:27 +02:00
Miroslav Lichvar
34f12c0864 test: fix packet interval check 2014-06-03 14:59:21 +02:00
Miroslav Lichvar
28876d6afa test: always write packet log 2014-06-03 11:34:53 +02:00
Miroslav Lichvar
cea68ebc6f test: extend 005-externalstep 2014-06-02 18:26:01 +02:00
Miroslav Lichvar
a33a955163 local: reset daemon after unexpected time jump
Add a new change type and use it when an unexpected time jump is
detected in the scheduler to reset reference times, offset and slewing,
NCR instances (with their polling interval), synchronization status, and
drop all sourcestats, manual, refclock and RTC samples.

This should make the recovery more graceful if the estimated jump has a
large error (e.g. select didn't timeout, or after system suspend).
2014-06-02 17:38:32 +02:00
Miroslav Lichvar
a3e60c93da sched: try to detect also forward time jumps 2014-06-02 16:48:57 +02:00
Miroslav Lichvar
44c9744d69 local: replace is_step_change parameter of change handler with enum
Prepare for a new change type that will be added later.
2014-06-02 16:46:53 +02:00
Miroslav Lichvar
b69b648d18 local: refactor invocation of parameter change handlers 2014-06-02 16:46:53 +02:00
Miroslav Lichvar
3ebebac695 ntp: reset NCR instance thoroughly when switching to offline 2014-06-02 16:46:53 +02:00
Miroslav Lichvar
13d734c8d2 sourcestats: reset SST instance thoroughly when dropping samples 2014-06-02 16:46:53 +02:00
Miroslav Lichvar
c903c5f72b sourcestats: remove forgotten declaration 2014-06-02 16:46:53 +02:00
Miroslav Lichvar
b03c7581f2 configure: fix test code to be compilable with -Werror 2014-06-02 16:46:53 +02:00
Hattink, Tjalling [FINT]
2ed9853bcc rtc: more reliable method of reading rtc for initial trim
When chrony reads in the linux rtc for the first time to trim the system
clock, it only reads it once. As it is possible that the rtc updates
itself during the read operation, the reported rtc time could be false.
To prevent this I've added a loop that reads the rtc clock twice, if the
seconds do not match retry the two read operations. If they match you
can assume the read operation was successful.

This is based on the hwclock implementation of reading the rtc clock
from the util-linux package.
2014-06-02 16:15:17 +02:00
Miroslav Lichvar
26e00ffbeb refclock: don't include average dispersion in unfiltered samples
The dispersion of refclock samples before filtering now includes only
offset correction error and precision.

This should fix a problem where locked PPS got stuck with large average
dispersion and didn't accept new samples due failing check of offset
and dispersion.
2014-05-23 16:15:28 +02:00
Miroslav Lichvar
b745b6d546 refclock: add maxdispersion option
This can be used to prevent accumulation of samples with estimated
dispersion above given limit. By default, this limit is disabled.
2014-05-23 16:15:28 +02:00
Miroslav Lichvar
e147f2f11e sys: drop frequency scaling in Linux driver
Since the kernel USER_HZ constant was introduced and the internal HZ
can't be reliably detected in user-space, the frequency scaling constant
used with older kernels is just a random guess.

Remove the scaling completely and let the closed loop compensate for the
error. To prevent thrashing between two states when the system's
frequency error is close to a multiple of USER_HZ, stick to the current
tick value if it's next to the new required tick. This is used only on
archs where USER_HZ is 100 as the frequency adjustment is limited to 500
ppm.

The linux_hz and linux_freq_scale directives are no longer supported,
but allowed by the config parser.
2014-05-23 16:15:28 +02:00
Miroslav Lichvar
14687d003d sys: set tick_update_hz to 100 by default in Linux driver
We can't reliably detect the internal kernel HZ, it may not even be
fixed (CONFIG_NO_HZ). Use a fixed value of 100.
2014-05-23 16:15:28 +02:00
Miroslav Lichvar
e8bb95ba55 sys: avoid notification of neglible dispersion on slew update 2014-05-23 16:15:28 +02:00
Miroslav Lichvar
a43810533f doc: update description of refclock options 2014-05-23 16:15:24 +02:00
Miroslav Lichvar
98bbfdf73c refclock: fix sample validation with sub-second poll 2014-05-23 12:00:16 +02:00
Miroslav Lichvar
ee53d816ce reference: fix logging of initial unsynchronized tracking entry 2014-05-23 12:00:16 +02:00
Miroslav Lichvar
e176587e96 main: initialize reference mode properly 2014-05-23 12:00:16 +02:00
Tjalling Hattink
8210be0f17 refclock: honour leap second flag in the PPS refclock
This patch fixes leap second handling for the PPS refclock. Without the
patch the PPS refclock will always report LEAP_normal. But if a locked
refclock (the SHM clock in my case) does report a leap state it should
also be taken over by the PPS refclock, otherwise chrony will still use
LEAP_normal when the PPS clock is used as reference source.

The patch will copy the leap state from the refclock. In case the PPS
clock is not specifically locked to another refclock it will take over
the leap state from the local clock.

I've tested this patch by simulating a leap second through the samples
for the SHM clock, and with the patch you will see chrony properly jump
forward or backward on the leap second. Without the patch it will not do
this and the clock becomes desynchronized and no leap state is reported
upstream to other NTP clients.

Signed-off-by: Tjalling Hattink <t.hattink@fugro.nl>
2014-05-22 13:28:46 +02:00
Miroslav Lichvar
f88a712d01 sys: use maximum timeout for offsets below minimum correction
There is no need to try to correct offsets below the specified minimum
(1 nanosecond), let the clock drift away after crossing zero offset and
avoid unnecessary updates.
2014-05-22 13:28:46 +02:00
Miroslav Lichvar
9cf78b974a conf: add option to set maximum slew rate
With the generic driver, the maxslewrate directive sets the maximum
frequency offset that the driver is allowed to use to slew the time. By
default, it's set to 83333.333 (1/12). This is identical to what Linux
fast slewing used to use.
2014-05-22 13:28:46 +02:00
Miroslav Lichvar
3e1dc801b0 conf: change default corrtimeratio to 3.0
This improves the overall frequency accuracy of the clock at a slight
cost in the time accuracy.
2014-05-21 12:08:10 +02:00
Miroslav Lichvar
cf3c7b3bd6 sys: add apply_step_offset function to generic driver
Move the generic code away from the Linux driver and keep there only
stepping by adjtimex(ADJ_SETOFFSET).
2014-05-20 17:14:33 +02:00
Miroslav Lichvar
0b7f64cb33 test: update for recent changes in Linux driver 2014-05-20 17:14:33 +02:00
Miroslav Lichvar
ec4542bbe4 sys: convert Linux driver to use generic offset functions
Strip all slewing code (adjtime(), freq locked nano PLL, fast tick
slewing) from the Linux driver and use the new generic frequency only
slewing instead. The advantages include stable clock control with very
short update intervals, good control of the slewing frequency, cheap
cooking of raw time stamps and unlimited frequency offset.
2014-05-20 17:14:33 +02:00
Miroslav Lichvar
fc235a3f16 sys: introduce generic driver
This driver is intended to complete system-specific drivers that don't
have implemented all required driver functionality. Currently, it
implements offset functions working on top of system-specific frequency
functions. Offsets are corrected by changing frequency, similarly to
fast slewing implemented in the Linux driver.
2014-05-20 16:05:42 +02:00
Miroslav Lichvar
4cf8395470 test: make 110-chronyc even more tolerant 2014-05-16 18:51:06 +02:00
Miroslav Lichvar
fd03d823f2 doc: update faq.txt 2014-05-16 18:51:06 +02:00
Miroslav Lichvar
e65fa1aa7b client: don't override hostname with -4 or -6 after -h 2014-05-16 18:51:06 +02:00
Miroslav Lichvar
3de72917c3 client: set default hostname to 127.0.0.1 instead of localhost
This is to make sure chronyd will see the remote address as 127.0.0.1
and allow access even when localhost resolves to an address of a
non-loopback interface.
2014-05-16 18:51:06 +02:00
Miroslav Lichvar
b3b2f67d2f client: enable IP_RECVERR socket option
This is useful to get ECONNREFUSED when the host replies with ICMP port
unreachable message and avoid having to wait for timeout.
2014-05-16 18:51:06 +02:00
Miroslav Lichvar
c2dc25e062 sys: remove unused static variables in Linux driver 2014-05-16 18:50:58 +02:00
Miroslav Lichvar
ad9c360845 doc: update for separate client sockets 2014-04-30 18:48:43 +02:00
Miroslav Lichvar
a65686e83f doc: update chronyd -r and chronyc -h descriptions 2014-04-30 18:48:43 +02:00
Miroslav Lichvar
fe35de6931 main: switch errors in initialization to fatal errors 2014-04-30 18:47:53 +02:00
Miroslav Lichvar
3f1aea2f53 test: require latest clknetsim
This is needed for async name resolving and dropping root privileges.
2014-04-29 15:23:11 +02:00
Miroslav Lichvar
0c542dcd3d client: shorten default timeout with localhost and async resolving
When chronyd is compiled with asynchronous name resolving, it should
always respond quickly. Shorten the default chronyc timeout for
localhost.
2014-04-29 15:23:11 +02:00
Miroslav Lichvar
5483567190 nameserv: add asynchronous resolving with POSIX threads
Run getaddrinfo()/gethostbyname() in separate thread to avoid blocking.
Only one resolving thread is running at one time, so this should work
also on systems where the functions are not thread-safe.
2014-04-29 15:19:06 +02:00
Miroslav Lichvar
d243f1f8fe configure: check if getaddrinfo() is available
This allows disabling IPv6 support and keeping getaddrinfo().
2014-04-29 12:43:03 +02:00
Miroslav Lichvar
9b137b2e37 ntp: start resolving only from NSR_ResolveSources
Also, use macros to define the minimum and maximum resolving interval.
2014-04-29 12:43:03 +02:00
Miroslav Lichvar
6ee357d230 ntp: use async name resolving for NTP sources
Use the new asynchronous call to resolve addresses of NTP servers
configured by the server/peer directives. Introduce a callback to be
notified when the first resolving attempt ends to correctly finish
chronyd initialization (dumpfile reload and reference mode end).
2014-04-29 12:43:03 +02:00
Miroslav Lichvar
779e40ed66 ntp: delay initial transmission until first resolving ends
This will be needed to prevent loading of dump files after sources have
already accumulated samples and possibly reference was already updated
when async resolving of sources is implemented.
2014-04-29 12:42:45 +02:00
Miroslav Lichvar
be3439fef1 sourcestats: assert dump file is loaded with no accumulated samples 2014-04-29 12:07:38 +02:00
Miroslav Lichvar
ed96b4d49d nameserv: prepare for asynchronous resolving
Introduce a new function with callback to resolve names to IP addresses
asynchronously. For now, use a blocking wrapper around DNS_Name2IPAddress.
2014-04-29 12:07:38 +02:00
Miroslav Lichvar
5ca8aa7840 configure: sed Makefile with MYCPPFLAGS 2014-04-29 12:07:38 +02:00
Miroslav Lichvar
1eede1bc08 configure: replace unnecessary variables in Makefile 2014-04-29 12:07:38 +02:00
Miroslav Lichvar
cc86461d9b refclock: remove duplicated declaration 2014-04-29 12:07:38 +02:00
Miroslav Lichvar
29c5ca9091 cmdmon: fix doffset command with negative values on 64-bit systems 2014-04-28 14:12:05 +02:00
Miroslav Lichvar
86fbcdc62b reference: negate offset printed in maxchange log message
This makes it consistent with other log messages.
2014-04-11 16:09:43 +02:00
Miroslav Lichvar
e82220974e makefile: add dependency to check target 2014-04-10 18:22:34 +02:00
Miroslav Lichvar
46951b8598 main: setup access restrictions before initstepslew 2014-04-10 18:22:09 +02:00
Miroslav Lichvar
08faca03b7 ntp: close client socket when offline 2014-04-10 18:02:29 +02:00
Miroslav Lichvar
3217421797 ntp: close only client socket when destroying NCR instance 2014-04-10 17:50:24 +02:00
Miroslav Lichvar
7fa22d4c25 sources: ignore inactive sources in special mode ending 2014-04-10 17:50:24 +02:00
Miroslav Lichvar
8671002bd7 sources: add flag that source is active
When source is set as active, it's receiving reachability updates (e.g.
offline NTP sources are not active).

Also add function to count active sources.
2014-04-10 17:48:58 +02:00
Miroslav Lichvar
bc6b40568d ntp: reduce burst timeout to 2.0
With the new special mode ending it can be now equal to the burst
polling interval.
2014-04-10 17:16:30 +02:00
Miroslav Lichvar
3888b9dcec sources: rework special mode ending with unreachable sources
Instead of giving up when a source has 7 reach updates, continue as long
as at least one source has fewer than 7 updates and can still have 3
samples to be selectable in that number of updates.

When no sources are responding, it will give up sooner.
2014-04-10 17:16:13 +02:00
Miroslav Lichvar
ae104b5c28 regress: make minimum number of samples for regression public 2014-04-10 17:15:13 +02:00
Miroslav Lichvar
5cb7e6c9c3 sched: fix main loop to allow timeout handlers modify fd set or quit
With special reference update modes, the timeout handlers may add or
remove file descriptors from the read fd set, so it needs to be copied
for select() call after they are dispatched. Also, they can now request
quit, so the exit flag needs to be checked before select() to avoid
hanging.
2014-04-10 11:47:43 +02:00
Miroslav Lichvar
ff31702f74 configure: add option to set default user
The default user is root by default, which disables root dropping by
default. The user directive or the -u option can still be used to set
the user.
2014-04-09 16:30:06 +02:00
Miroslav Lichvar
3edd3fe5a4 main: support configuration commands on command line
If there are extra arguments on the chronyd command line, they will be
parsed as lines in a configuration file and the normal configuration file
will be ignored.
2014-04-09 15:16:35 +02:00
Miroslav Lichvar
4f3fb95981 conf: allow NULL as filename 2014-04-09 12:56:11 +02:00
Miroslav Lichvar
7c7ab95e2e conf: split line parsing from CNF_ReadFile 2014-04-09 12:36:13 +02:00
Miroslav Lichvar
70feea48f8 main: add -q/-Q options to set clock/print offset once and exit 2014-04-09 12:15:07 +02:00
Miroslav Lichvar
c546c48d0d reference: add UpdateOnce and PrintOnce modes 2014-04-09 12:14:47 +02:00
Miroslav Lichvar
0baf00e1c0 logging: print warning message when not compiled with debug support 2014-04-09 12:09:25 +02:00
Miroslav Lichvar
788e7fcd89 logging: set debug level instead of on/off
Prefix messages written to terminal with filename, line and function
name only with debug level 2 and higher.
2014-04-09 12:09:23 +02:00
Miroslav Lichvar
7c45b1d2a3 logging: update format of messages written to terminal
Move the time stamp to start of the line and print full date in ISO 8601
format.
2014-04-09 12:08:59 +02:00
Miroslav Lichvar
93b66ac141 reference: exit with non-zero code when maxchange limit is reached
Use ending of normal mode to signal a failure.
2014-04-09 09:59:58 +02:00
Miroslav Lichvar
610284dcc3 sources: log selection messages only in normal reference update mode
We don't want to see source selection messages when initstepslew is
running.
2014-04-09 09:59:58 +02:00
Miroslav Lichvar
60d8586b6d ntp: reduce burst timeout to 2.5 seconds
This reduces the maximum time initstepslew can take.
2014-04-09 09:59:58 +02:00
Miroslav Lichvar
70928dba52 ntp: set maximum number of iburst samples to size of reach register
Explicitly set the number of iburst samples to the size of the register
to make sure there are at least 7 reachability updates and the
initstepslew mode can be ended.
2014-04-09 09:59:57 +02:00
Miroslav Lichvar
7fda9c6723 ntp: drop initstepslew NTP implementation
The initstepslew code has its own minimal NTP implementation. Drop the
code, add a new initstepslew mode to the reference updating code and
use regular NTP sources with iburst flag for initstepslew addresses
instead. When an update is made or a source is found unreachable, log a
message, remove the initstepslew sources and switch to normal mode.

This reduces code duplication and makes initstepslew use features
implemented only in the main code like source combining or SO_TIMESTAMP
support.
2014-04-09 09:54:40 +02:00
Miroslav Lichvar
4932f9d077 sources: replace beginning flag with size of reachability register
This will allow to detect sources that are not reachable on start.
2014-04-08 17:00:47 +02:00
Miroslav Lichvar
0094128ca6 sources: split source selection from sample accumulation
This will allow postponing source selection and reference update, which
could be useful in burst modes.
2014-04-08 17:00:47 +02:00
Miroslav Lichvar
de5178575f git: update .gitignore 2014-04-08 17:00:47 +02:00
Miroslav Lichvar
9eac078c18 test: add missing run script 2014-04-08 17:00:47 +02:00
Miroslav Lichvar
05c5445fe2 conf: add bindacqaddress directive for client sockets 2014-04-03 15:47:32 +02:00
Miroslav Lichvar
f9d8b6f99e ntp: set only necessary socket options on client sockets 2014-04-03 13:36:25 +02:00
Miroslav Lichvar
597a37d66e test: add 112-port 2014-03-26 12:24:36 +01:00
Miroslav Lichvar
73e4986866 ntp: fix comment on NCR_ProcessUnknown 2014-03-26 11:24:25 +01:00
Miroslav Lichvar
91e74c704b ntp: accept packets from unknown sources only from server sockets 2014-03-26 11:18:18 +01:00
Miroslav Lichvar
727bf195d1 test: update for latest clknetsim
Latest clknetsim now allows source and destination port numbers to
differ. This fixes the tests to work with the recent changes that added
client NTP sockets.
2014-03-25 17:33:55 +01:00
Miroslav Lichvar
b13836e9cc ntp: don't create server sockets if port is configured to 0 2014-03-25 15:27:18 +01:00
Miroslav Lichvar
cf12d72f21 ntp: use separate connected sockets for each server
If acquisitionport is set to 0 (default), create and connect a new
socket for each server instead of using one socket per address family
for all servers.
2014-03-25 15:27:18 +01:00
Miroslav Lichvar
5c2bbaca3b ntp: use separate client sockets
Use separate sockets for NTP server or peer and client packets. The port
number is configured by the acquisitionport directive. With the default
value of 0 the port is assigned randomly by the kernel. It can be equal
to the value configured by the port directive to use the server sockets
for all packets as before.
2014-03-25 15:25:23 +01:00
Miroslav Lichvar
b717904f9e ntp: don't try to bind acquire socket if port is equal to ntp port 2014-03-25 15:24:38 +01:00
Miroslav Lichvar
f2c4ab09a8 ntp: check if packet was received by right socket 2014-03-25 15:24:38 +01:00
Miroslav Lichvar
9a657cd4a3 ntp: store socket in NTP instance
This is preparation for separate client sockets.
2014-03-25 15:22:59 +01:00
Miroslav Lichvar
308de81221 ntp: split local_ip_addr from NTP_Remote_Address struct 2014-03-25 11:34:35 +01:00
Miroslav Lichvar
6823109cfb ntp: set invalid socket fd by macro 2014-03-25 11:34:31 +01:00
Miroslav Lichvar
a02149cf65 doc: improve commandkey and keyfile descriptions 2014-03-21 15:49:00 +01:00
Miroslav Lichvar
7aa4bbf621 ntp: set minpoll from received KoD RATE at most to 10
Limit changing minpoll to a reasonable maximum in case the server is
broken or temporarily misconfigured.
2014-03-21 15:32:14 +01:00
Miroslav Lichvar
5afddad0d2 ntp: print warning when source is added with unknown key 2014-03-21 14:36:51 +01:00
Miroslav Lichvar
0380cf0c76 ntp: reset negative minpoll or maxpoll to default values 2014-03-21 13:25:14 +01:00
Miroslav Lichvar
6c2a1e62e0 cmdparse: don't allow NTP key ID of 0
Key number 0 is used as inactive key, prevent the user from
inadvertently not using authentication.
2014-03-21 13:20:37 +01:00
Miroslav Lichvar
6560628209 test: add 111-knownclient 2014-03-21 13:20:36 +01:00
Miroslav Lichvar
3cc81376a6 test: add port number check 2014-03-21 13:20:36 +01:00
Miroslav Lichvar
8d02e5f680 ntp: make use of NCR_ProcessUnknown in NCR_ProcessKnown
After recent changes the code in NCR_ProcessKnown is now identical and
can be replaced with NCR_ProcessUnknown call.
2014-03-21 13:20:34 +01:00
Miroslav Lichvar
f9e2213afd ntp: don't store tx time stamp when replying to known source 2014-03-21 13:20:31 +01:00
Miroslav Lichvar
8b362ba3e7 ntp: don't reply to known source if missing key or invalid auth
This is now similar to replying to unknown sources.
2014-03-21 13:20:29 +01:00
Miroslav Lichvar
eecec8fffa test: extend 105-ntpauth 2014-03-21 13:20:29 +01:00
Miroslav Lichvar
a26058d425 ntp: don't send requests with unknown key
There is no point in sending a request if the configured key is missing.
A reply would be ignored anyway.
2014-03-21 13:20:27 +01:00
Miroslav Lichvar
c14b81f3a9 ntp: remove unnecessary KEY_KeyKnown calls 2014-03-21 13:20:25 +01:00
Miroslav Lichvar
0059a43254 keys: don't cache position for unknown keys 2014-03-21 13:20:24 +01:00
Miroslav Lichvar
7dd3cc354d client: print positive signed freq and offset values with sign 2014-03-21 13:20:24 +01:00
Miroslav Lichvar
ce34aa0763 test: make 110-chronyc more tolerant 2014-03-21 13:20:24 +01:00
Miroslav Lichvar
7a512ad9c3 tempcomp: print warning message on error 2014-03-21 13:20:24 +01:00
Miroslav Lichvar
0a56c0e8c1 tempcomp: use macro to set maximum allowed compensation 2014-03-21 13:20:24 +01:00
Miroslav Lichvar
0b71504ee9 sourcestats: fix signedness in scanf format 2014-03-21 13:20:24 +01:00
Miroslav Lichvar
9479c6451e makefile: improve check rule 2014-03-21 13:20:20 +01:00
Miroslav Lichvar
115e83f3aa Add simulation tests
Use clknetsim to run multiple chronyd instances with simulated clocks
and network. It allows fast and reproducible testing, without real
network.

Included are several tests of performance in different clock/network
conditions, chronyd options, NTP authentication, chronyc, and past bug
fixes.
2014-02-27 18:34:52 +01:00
Miroslav Lichvar
ea526b96dd configure: suppress pkg-config errors 2014-02-05 08:53:15 +01:00
Miroslav Lichvar
726cf84e19 Check array index before reading 2014-02-04 16:02:21 +01:00
Miroslav Lichvar
dc8a46363f Merge branch '1.29-security' 2014-01-31 17:06:08 +01:00
Miroslav Lichvar
916ca7ab86 make_release: set owner and group in released tarball to root 2014-01-31 13:37:55 +01:00
Miroslav Lichvar
be036ed58a make_release: remove config.log and config.h 2014-01-31 13:37:52 +01:00
Miroslav Lichvar
2afdd4544d Update NEWS 2014-01-31 13:12:59 +01:00
Miroslav Lichvar
c4e61835d3 Update faq.txt 2014-01-30 15:59:45 +01:00
Miroslav Lichvar
e15ce69d08 Send cmdmon error replies only to allowed hosts
The status codes STT_BADPKTVERSION, STT_BADPKTLENGTH, STT_NOHOSTACCESS
were sent even to hosts that were not allowed by cmdallow. Deprecate
STT_NOHOSTACCESS and ignore packets from hosts not allowed by cmdallow
completely.
2014-01-30 15:59:45 +01:00
Miroslav Lichvar
d537ed11fd Support previous protocol version in chronyc
This adds compatibility with chronyd using the previous protocol version
(chrony versions 1.27, 1.28, 1.29).
2014-01-30 15:59:45 +01:00
Miroslav Lichvar
dba458d50c Add padding to cmdmon requests to prevent amplification attack
To prevent an attacker using chronyd in an amplification attack, change
the protocol to include padding in request packets so that the largest
possible reply is not larger than the request. Request packets that
don't include this padding are ignored as invalid.

This is an incompatible change in the protocol. Clients from chrony
1.27, 1.28 and 1.29 will receive NULL reply with STT_BADPKTVERSION and
print "Protocol version mismatch". Clients from 1.26 and older will not
receive a reply as it would be larger than the request if it was padded
to be compatible with their protocol.
2014-01-30 15:59:45 +01:00
Miroslav Lichvar
3e23430926 Set maximum number of samples in manual list reply to 16
In chronyd the maximum number of manual samples is 16, so there is no
need to keep room for 32 samples in the command reply. This limits the
maximum assumed size of the reply packet.
2014-01-30 15:59:45 +01:00
Miroslav Lichvar
3f507b782c Replace number and total fields in cmdmon reply packet with padding
They were not used for anything and there is no plan to change that.
2014-01-24 16:53:32 +01:00
Miroslav Lichvar
2fc3525fdf Don't read uninitialized memory in client packet length check
Before calling PKL_ReplyLength() check that the packet has full header.
This didn't change the outcome of the test if the packet was shorter as
the invalid result from PKL_ReplyLength() was either larger than length
of the packet or smaller than header length, failing the length check in
both cases.
2014-01-24 16:53:32 +01:00
Miroslav Lichvar
0f3e464202 Remove superfluous code in read_from_cmd_socket() 2014-01-24 16:53:32 +01:00
Miroslav Lichvar
925d7119ec Fix writing of drift and RTC files
Without sequence points the driftfile and RTC file could be closed
before new values were written.
2014-01-21 18:23:12 +01:00
Miroslav Lichvar
f456cd57b9 Fix selecting of sources with prefer option
List of selectable sources that is used in combining was trimmed to
sources with prefer option, but scoring algorithm considered all
selectable sources. When a source without prefer was selected and
no source was combined, it caused assertion failure.
2014-01-21 17:18:48 +01:00
Miroslav Lichvar
ea58500cef Remove superfluous code in SRC_SelectSource 2014-01-21 16:41:00 +01:00
Miroslav Lichvar
4048b200ed Fix error message when chronyc can't open keyfile 2014-01-21 14:51:32 +01:00
Miroslav Lichvar
54211f0f6e Update comment on setting poll in reply packet 2014-01-17 18:10:32 +01:00
Miroslav Lichvar
4b5f465026 Don't allow maxpoll to be set shorter than minpoll 2014-01-17 17:37:07 +01:00
Miroslav Lichvar
7efd1151cb Convert linux kernel info messages to debug 2014-01-17 17:30:06 +01:00
Miroslav Lichvar
19dbe52930 Update linux_freq_scale and linux_hz documentation 2014-01-10 15:56:53 +01:00
Miroslav Lichvar
2a981b7d39 Print error message on invalid syntax with all chronyc commands 2014-01-10 11:14:39 +01:00
Miroslav Lichvar
d34ebdb431 Simplify expression used in frequency accumulation 2014-01-09 18:31:35 +01:00
Miroslav Lichvar
60d0fa2993 Fix frequency accumulation again
This is a revert of commit 99d18abf updated for later changes. It seems
in that commit the calculation was changed to match the reversed dfreq
added in 1a7415a6, which itself was calculated incorrectly. Fix the
calculation of updated frequency and matching dfreq.
2014-01-09 18:30:14 +01:00
Miroslav Lichvar
8545ba733a Convert disabled log message in rtc_linux.c to DEBUG_LOG 2014-01-09 18:23:37 +01:00
Miroslav Lichvar
04c8a8c75d Update documentation on trimrtc command 2013-12-12 17:11:11 +01:00
Miroslav Lichvar
b7ed44f113 Improve description of refclock delay option 2013-12-11 17:59:14 +01:00
Miroslav Lichvar
46a39716b6 Fix default device in rtcdevice description 2013-12-11 11:25:00 +01:00
Miroslav Lichvar
e77b0070af Add option to read RTC LOCAL/UTC setting from hwclock's adjtime file 2013-12-11 11:22:04 +01:00
Miroslav Lichvar
3c5cf81e32 Replace /sbin/clock with /sbin/hwclock in documentation and comments 2013-12-10 17:54:05 +01:00
Miroslav Lichvar
be14dbffef Make naming of RTC config functions consistent 2013-12-10 17:54:05 +01:00
Miroslav Lichvar
b4f6a0f94a Fix ordering of sections in documentation 2013-11-29 17:34:38 +01:00
Miroslav Lichvar
c15acff39a Make section descriptions consistent in documentation 2013-11-29 17:34:32 +01:00
Miroslav Lichvar
ed0ac6e3f6 Write fatal messages also to stderr when started with -n 2013-11-28 18:17:50 +01:00
Miroslav Lichvar
6cc9c4940c Don't try to write to parent logging fd when closed 2013-11-28 18:11:37 +01:00
Miroslav Lichvar
0e8b27556d Merge config parsing functions for common data types 2013-11-28 17:33:20 +01:00
Miroslav Lichvar
162c6a49b5 Add option to trim RTC automatically 2013-11-27 17:35:00 +01:00
Miroslav Lichvar
61dbdd80b9 Fix REF_GetOurStratum description 2013-11-27 16:28:09 +01:00
Miroslav Lichvar
8df1bedb1b Remove forgotten macros 2013-11-27 16:11:19 +01:00
Miroslav Lichvar
b55e914273 Use N_SAMPLES_PER_REGRESSION macro in rtc_linux module 2013-11-27 15:43:17 +01:00
Miroslav Lichvar
1c3aff37de Convert TRACEON LOG messages to DEBUG_LOG 2013-11-27 14:35:41 +01:00
Miroslav Lichvar
4bbc5520b8 Add support for debug messages
Add new DEBUG_LOG macro for debug messages. The messages are enabled
when compiled with --enable-debug and they are printed when the -d
option is used twice.
2013-11-27 14:35:38 +01:00
Miroslav Lichvar
0731cd6950 Fix log messages 2013-11-26 18:41:51 +01:00
Miroslav Lichvar
f88e96599c Set printf format attribute for logging functions with gcc 2013-11-26 18:41:49 +01:00
Miroslav Lichvar
cd7bfa2510 Refactor logging
- merge LOG_Line_Function, LOG_Fatal_Function and LOG_Position
- use C99 variadic macros for LOG and LOG_FATAL
2013-11-26 18:40:43 +01:00
Miroslav Lichvar
ea418b2e18 Update see also in man pages 2013-11-26 13:34:12 +01:00
Miroslav Lichvar
c00d93a897 Add refclock trace messages 2013-11-26 11:56:53 +01:00
Miroslav Lichvar
bc361c48a0 Enable refclock error messages 2013-11-26 11:44:06 +01:00
Miroslav Lichvar
1f39169be3 Fix stratum with non-PPS SOCK refclock and local stratum 2013-11-25 17:58:35 +01:00
Miroslav Lichvar
030833087d Append -lcap to EXTRA_LIBS in configure 2013-11-15 13:22:45 +01:00
Miroslav Lichvar
c38dbcc6b5 Link with -lrt for clock_gettime() if needed 2013-11-15 13:22:43 +01:00
Miroslav Lichvar
ba1d548cc8 Fix compilation of PHC driver on systems without PTP_SYS_OFFSET 2013-11-15 10:08:47 +01:00
Miroslav Lichvar
7261d11bb0 Add assert for parameter m in RGR_FindBestRegression() 2013-10-10 16:37:40 +02:00
Miroslav Lichvar
f38872eab3 Fix regression validity check in handle_relock_after_trim() 2013-10-07 17:50:27 +02:00
Miroslav Lichvar
1939aae70e Fix id printed in duplicate key warning 2013-10-07 17:50:27 +02:00
Miroslav Lichvar
3f85d1dcc1 Remove unused code in manual.c 2013-10-07 17:50:27 +02:00
Miroslav Lichvar
922e2fe23b Fix Clang static analyzer warnings about never read values 2013-10-07 17:50:27 +02:00
Paul Menzel
d5a9c1535e rtc_linux.c: Remove useless assignment error = -1;
The Clang static analyzer scan-build from Debian clang version 3.4-1
found the following unneeded assignment.

        rtc_linux.c:756:5: warning: Value stored to 'error' is never read
            error = 1;
            ^       ~

Indeed, if in that if branch, the function returns without ever looking
at the variable `error`. So remove the line.
2013-10-07 16:42:03 +02:00
Miroslav Lichvar
169eee6792 Add dependency on chrony.txt to install target 2013-09-12 14:22:32 +02:00
Miroslav Lichvar
9c398051bb Add URLs to documentation for gpsd, radioclk and linuxpps 2013-08-14 19:02:49 +02:00
Miroslav Lichvar
1d289787b6 Add PHC refclock driver
Implement a driver which allows using PTP hardware clock (PHC) as a
reference clock. It uses the PTP_SYS_OFFSET ioctl or clock_gettime()
to measure the offset between the PTP clock and the system clock. Ten
readings are made for every driver poll and the fastest one is returned.

As PHCs are typically kept in TAI instead of UTC, it's necessary to set
the TAI/UTC offset manually by the offset option. This could be improved
by obtaining the offset automatically from the right/UTC timezone.
2013-08-14 18:52:23 +02:00
88 changed files with 4641 additions and 3545 deletions

2
.gitignore vendored
View File

@@ -17,3 +17,5 @@ config.h
config.log
tags
version.h
/test/simulation/clknetsim
/test/simulation/tmp

View File

@@ -39,10 +39,11 @@ with line editing support. If you don't want this, specify the
--disable-readline flag to configure. Please refer to the chrony.txt
file for more information.
If a `timepps.h' header is available, chronyd will be built with PPS
API reference clock driver. If the header is installed in a location
that isn't normally searched by the compiler, you can add it to the
searched locations by setting CPPFLAGS variable to -I/path/to/timepps.
If a 'timepps.h' header is available (e.g. from the LinuxPPS project
(http://linuxpps.org/)), chronyd will be built with PPS API reference
clock driver. If the header is installed in a location that isn't
normally searched by the compiler, you can add it to the searched
locations by setting 'CPPFLAGS' variable to '-I/path/to/timepps'.
Now type

View File

@@ -42,10 +42,10 @@ OBJS = util.o sched.o regress.o local.o \
sys.o main.o ntp_io.o ntp_core.o ntp_sources.o \
sources.o sourcestats.o reference.o \
logging.o conf.o cmdmon.o keys.o \
nameserv.o acquire.o manual.o addrfilt.o \
nameserv.o nameserv_async.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
broadcast.o refclock.o refclock_shm.o refclock_sock.o \
refclock_pps.o tempcomp.o $(HASH_OBJ)
broadcast.o refclock.o refclock_phc.o refclock_pps.o \
refclock_shm.o refclock_sock.o tempcomp.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@
@@ -66,10 +66,10 @@ EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@
all : chronyd chronyc
chronyd : $(OBJS) $(EXTRA_OBJS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) @HASH_LINK@ $(LIBS) $(EXTRA_LIBS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) @READLINE_LINK@ @HASH_LINK@ $(LIBS) $(EXTRA_CLI_LIBS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
client.o : client.c
$(CC) $(CFLAGS) $(CPPFLAGS) @READLINE_COMPILE@ -c $<
@@ -92,7 +92,7 @@ getdate :
# For install, don't use the install command, because its switches
# seem to vary between systems.
install: chronyd chronyc
install: chronyd chronyc chrony.txt
[ -d $(DESTDIR)$(SYSCONFDIR) ] || mkdir -p $(DESTDIR)$(SYSCONFDIR)
[ -d $(DESTDIR)$(SBINDIR) ] || mkdir -p $(DESTDIR)$(SBINDIR)
[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
@@ -129,6 +129,9 @@ install: chronyd chronyc
%.s : %.c
$(CC) $(CFLAGS) $(CPPFLAGS) -S $<
check : chronyd chronyc
cd test/simulation && ./run
install-docs : docs
[ -d $(DESTDIR)$(DOCDIR) ] || mkdir -p $(DESTDIR)$(DOCDIR)
cp chrony.txt $(DESTDIR)$(DOCDIR)/chrony.txt

58
NEWS
View File

@@ -1,3 +1,61 @@
New in version 1.30
===================
Enhancements
------------
* Add asynchronous name resolving with POSIX threads
* Add PTP hardware clock (PHC) refclock driver
* Add new generic clock driver to slew by adjusting frequency only
(without kernel PLL or adjtime) and use it on Linux
* Add rtcautotrim directive to trim RTC automatically
* Add hwclockfile directive to share RTC LOCAL/UTC setting with hwclock
* Add maxslewrate directive to set maximum allowed slew rate
* Add maxdispersion option for refclocks
* Add -q/-Q options to set clock/print offset once and exit
* Allow directives to be specified on chronyd command line
* Replace frequency scaling in Linux driver with retaining of tick
* Try to detect unexpected forward time jumps and reset state
* Exit with non-zero code when maxchange limit is reached
* Improve makestep to not start and stop slew unnecessarily
* Change default corrtimeratio to 3.0 to improve frequency error
* Announce leap second only on last day of June and December
* Use separate connected client sockets for each NTP server
* Remove separate NTP implementation used for initstepslew
* Limit maximum minpoll set by KoD RATE to default maxpoll
* Don't send NTP requests with unknown key
* Print warning when source is added with unknown key
* Take leap second in PPS refclock from locked source
* Make reading of RTC for initial trim more reliable
* Add configure option to set default user to drop root privileges
* Add configure option to compile with debug messages
* Print debug messages when -d is used more than once
* Change format of messages written to terminal with -d
* Write fatal messages also to stderr with -n
* Use IP_RECVERR socket option in chronyc to not wait unnecessarily
* Shorten default chronyc timeout for localhost
* Change default hostname in chronyc from localhost to 127.0.0.1
* Print error message on invalid syntax with all chronyc commands
* Include simulation test suite using clknetsim
Bug fixes
---------
* Fix crash when selecting with multiple preferred sources
* Fix frequency calculation with large frequency offsets
* Fix code writing drift and RTC files to compile correctly
* Fix -4/-6 options in chronyc to not reset hostname set by -h
* Fix refclock sample validation with sub-second polling interval
* Set stratum correctly with non-PPS SOCK refclock and local stratum
* Modify dispersion accounting in refclocks to prevent PPS getting
stuck with large dispersion and not accepting new samples
New in version 1.29.1
=====================
Security fixes
--------------
* Modify chronyc protocol to prevent amplification attacks (CVE-2014-0021)
(incompatible with previous protocol version, chronyc supports both)
New in version 1.29
===================

7
README
View File

@@ -125,7 +125,6 @@ Richard P. Curnow <rc@rc0.org.uk>
Maintainers
===========
John Hasler <john@dhh.gt.org>
Miroslav Lichvar <mlichvar@redhat.com>
@@ -179,6 +178,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
Port to NetBSD
John Hasler <john@dhh.gt.org>
Project and website at tuxfamily.org
Changes to support 64 bit machines (i.e. those where
sizeof(unsigned long) > 4)
Bug fix to initstepslew directive
@@ -186,6 +186,11 @@ John Hasler <john@dhh.gt.org>
Memory locking and real-time scheduler support
Fix fault where chronyd enters an endless loop
Tjalling Hattink <t.hattink@fugro.nl>
Fix scheduler to allow stepping clock from timeout handler
Patch to take leap second in PPS refclock from locked source
Patch to make reading of RTC for initial trim more reliable
Liam Hatton <me@liamhatton.com>
Advice on configuring for Linux on PPC

796
acquire.c
View File

@@ -1,796 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Processing to perform the equivalent of what ntpdate does. That is,
make a rapid-fire set of measurements to a designated set of
sources, and step or slew the local clock to bring it into line with
the result.
This is kept completely separate of the main chronyd processing, by
using a separate socket for sending/receiving the measurement
packets. That way, ntp_core.c can be kept completely independent of
this functionality.
A few of the finer points of how to construct valid RFC1305 packets
and validate responses for this case have been cribbed from the
ntpdate source.
*/
#include "config.h"
#include "sysincl.h"
#include "acquire.h"
#include "memory.h"
#include "sched.h"
#include "local.h"
#include "logging.h"
#include "ntp.h"
#include "util.h"
#include "main.h"
#include "conf.h"
/* ================================================== */
/* Interval between firing off the first sample to successive sources */
#define INTER_SOURCE_START (0.2)
#define MAX_SAMPLES 8
#define MAX_DEAD_PROBES 4
#define N_GOOD_SAMPLES 4
#define RETRANSMISSION_TIMEOUT (1.0)
#define NTP_VERSION 3
#define NTP_MAX_COMPAT_VERSION 4
#define NTP_MIN_COMPAT_VERSION 2
typedef struct {
IPAddr ip_addr; /* Address of the server */
int sanity; /* Flag indicating whether source
looks sane or not */
int n_dead_probes; /* Number of probes sent to the server
since a good one */
int n_samples; /* Number of samples accumulated */
int n_total_samples; /* Total number of samples received
including useless ones */
double offsets[MAX_SAMPLES]; /* In seconds, positive means local
clock is fast of reference */
double root_distances[MAX_SAMPLES]; /* in seconds */
double inter_lo; /* Low end of estimated range of offset */
double inter_hi; /* High end of estimated range of offset */
NTP_int64 last_tx; /* Transmit timestamp in last packet
transmitted to source. */
int timer_running;
SCH_TimeoutID timeout_id;
} SourceRecord;
static SourceRecord *sources;
static int n_sources;
static int n_started_sources;
static int n_completed_sources;
static double init_slew_threshold;
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef HAVE_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
static int sock_fd4 = -1;
#ifdef HAVE_IPV6
static int sock_fd6 = -1;
#endif
/* ================================================== */
static void (*saved_after_hook)(void *) = NULL;
static void *saved_after_hook_anything = NULL;
/* ================================================== */
typedef struct {
double offset;
enum {LO, HIGH} type;
int index;
} Endpoint;
typedef struct {
double lo;
double hi;
} Interval;
/* ================================================== */
static void read_from_socket(void *anything);
static void transmit_timeout(void *x);
static void wind_up_acquisition(void);
static void start_source_timeout_handler(void *not_used);
/* ================================================== */
static SCH_TimeoutID source_start_timeout_id;
/* ================================================== */
void
ACQ_Initialise(void)
{
}
/* ================================================== */
void
ACQ_Finalise(void)
{
}
/* ================================================== */
static int
prepare_socket(int family)
{
unsigned short port_number = CNF_GetAcquisitionPort();
int sock_fd;
socklen_t addrlen;
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG_FATAL(LOGF_Acquire, "Could not open socket : %s", strerror(errno));
}
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
if (port_number == 0) {
/* Don't bother binding this socket - we're not fussed what port
number it gets */
} else {
union sockaddr_in46 my_addr;
memset(&my_addr, 0, sizeof (my_addr));
switch (family) {
case AF_INET:
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
addrlen = sizeof (my_addr.in4);
break;
#ifdef HAVE_IPV6
case AF_INET6:
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
my_addr.in6.sin6_addr = in6addr_any;
addrlen = sizeof (my_addr.in6);
break;
#endif
default:
assert(0);
}
if (bind(sock_fd, &my_addr.u, addrlen) < 0) {
LOG(LOGS_ERR, LOGF_Acquire, "Could not bind socket : %s", strerror(errno));
/* but keep running */
}
}
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
return sock_fd;
}
/* ================================================== */
static void
initialise_io(int family)
{
if (family == IPADDR_INET4 || family == IPADDR_UNSPEC)
sock_fd4 = prepare_socket(AF_INET);
#ifdef HAVE_IPV6
if (family == IPADDR_INET6 || family == IPADDR_UNSPEC)
sock_fd6 = prepare_socket(AF_INET6);
#endif
}
/* ================================================== */
static void
finalise_io(void)
{
if (sock_fd4 >= 0) {
SCH_RemoveInputFileHandler(sock_fd4);
close(sock_fd4);
}
sock_fd4 = -1;
#ifdef HAVE_IPV6
if (sock_fd6 >= 0) {
SCH_RemoveInputFileHandler(sock_fd6);
close(sock_fd6);
}
sock_fd6 = -1;
#endif
}
/* ================================================== */
static void
probe_source(SourceRecord *src)
{
NTP_Packet pkt;
int version = NTP_VERSION;
NTP_Mode my_mode = MODE_CLIENT;
struct timeval cooked;
union sockaddr_in46 his_addr;
int sock_fd;
socklen_t addrlen;
uint32_t ts_fuzz;
#if 0
printf("Sending probe to %s sent=%d samples=%d\n", UTI_IPToString(&src->ip_addr), src->n_probes_sent, src->n_samples);
#endif
pkt.lvm = (((LEAP_Unsynchronised << 6) & 0xc0) |
((version << 3) & 0x38) |
((my_mode) & 0x7));
pkt.stratum = 0;
pkt.poll = 4;
pkt.precision = -6; /* as ntpdate */
pkt.root_delay = UTI_DoubleToInt32(1.0); /* 1 second */
pkt.root_dispersion = UTI_DoubleToInt32(1.0); /* likewise */
pkt.reference_id = 0;
pkt.reference_ts.hi = 0; /* Set to 0 */
pkt.reference_ts.lo = 0; /* Set to 0 */
pkt.originate_ts.hi = 0; /* Set to 0 */
pkt.originate_ts.lo = 0; /* Set to 0 */
pkt.receive_ts.hi = 0; /* Set to 0 */
pkt.receive_ts.lo = 0; /* Set to 0 */
/* And do transmission */
memset(&his_addr, 0, sizeof (his_addr));
switch (src->ip_addr.family) {
case IPADDR_INET4:
his_addr.in4.sin_addr.s_addr = htonl(src->ip_addr.addr.in4);
his_addr.in4.sin_port = htons(123); /* Fixed for now */
his_addr.in4.sin_family = AF_INET;
addrlen = sizeof (his_addr.in4);
sock_fd = sock_fd4;
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
memcpy(&his_addr.in6.sin6_addr.s6_addr, &src->ip_addr.addr.in6,
sizeof (his_addr.in6.sin6_addr.s6_addr));
his_addr.in6.sin6_port = htons(123); /* Fixed for now */
his_addr.in6.sin6_family = AF_INET6;
addrlen = sizeof (his_addr.in6);
sock_fd = sock_fd6;
break;
#endif
default:
assert(0);
}
ts_fuzz = UTI_GetNTPTsFuzz(LCL_GetSysPrecisionAsLog());
LCL_ReadCookedTime(&cooked, NULL);
UTI_TimevalToInt64(&cooked, &pkt.transmit_ts, ts_fuzz);
if (sendto(sock_fd, (void *) &pkt, NTP_NORMAL_PACKET_SIZE,
0,
&his_addr.u, addrlen) < 0) {
LOG(LOGS_WARN, LOGF_Acquire, "Could not send to %s : %s",
UTI_IPToString(&src->ip_addr),
strerror(errno));
}
src->last_tx = pkt.transmit_ts;
++(src->n_dead_probes);
src->timer_running = 1;
src->timeout_id = SCH_AddTimeoutByDelay(RETRANSMISSION_TIMEOUT, transmit_timeout, (void *) src);
}
/* ================================================== */
static void
transmit_timeout(void *x)
{
SourceRecord *src = (SourceRecord *) x;
src->timer_running = 0;
#if 0
printf("Timeout expired for server %s\n", UTI_IPToString(&src->ip_addr));
#endif
if (src->n_dead_probes < MAX_DEAD_PROBES) {
probe_source(src);
} else {
/* Source has croaked or is taking too long to respond */
++n_completed_sources;
if (n_completed_sources == n_sources) {
wind_up_acquisition();
}
}
}
/* ================================================== */
#define MAX_STRATUM 15
static void
process_receive(NTP_Packet *msg, SourceRecord *src, struct timeval *now)
{
unsigned long lvm;
int leap, version, mode;
double root_delay, root_dispersion;
double total_root_delay, total_root_dispersion, total_root_distance;
struct timeval local_orig, local_average, remote_rx, remote_tx, remote_average;
double remote_interval, local_interval;
double delta, theta, epsilon;
int n;
/* Most of the checks are from ntpdate */
/* Need to do something about authentication */
lvm = msg->lvm;
leap = (lvm >> 6) & 0x3;
version = (lvm >> 3) & 0x7;
mode = lvm & 0x7;
if ((leap == LEAP_Unsynchronised) ||
(version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) ||
(mode != MODE_SERVER && mode != MODE_PASSIVE)) {
return;
}
if (msg->stratum > MAX_STRATUM) {
return;
}
/* Check whether server is responding to our last request */
if ((msg->originate_ts.hi != src->last_tx.hi) ||
(msg->originate_ts.lo != src->last_tx.lo)) {
return;
}
/* Check that the server is sane */
if (((msg->originate_ts.hi == 0) && (msg->originate_ts.lo == 0)) ||
((msg->receive_ts.hi == 0) && (msg->receive_ts.lo) == 0)) {
return;
}
root_delay = UTI_Int32ToDouble(msg->root_delay);
root_dispersion = UTI_Int32ToDouble(msg->root_dispersion);
UTI_Int64ToTimeval(&src->last_tx, &local_orig);
UTI_Int64ToTimeval(&msg->receive_ts, &remote_rx);
UTI_Int64ToTimeval(&msg->transmit_ts, &remote_tx);
UTI_AverageDiffTimevals(&remote_rx, &remote_tx, &remote_average, &remote_interval);
UTI_AverageDiffTimevals(&local_orig, now, &local_average, &local_interval);
delta = local_interval - remote_interval;
/* Defined as positive if we are fast. Note this sign convention is
opposite to that used in ntp_core.c */
UTI_DiffTimevalsToDouble(&theta, &local_average, &remote_average);
/* Could work out epsilon - leave till later */
epsilon = 0.0;
total_root_delay = fabs(delta) + root_delay;
total_root_dispersion = epsilon + root_dispersion;
total_root_distance = 0.5 * fabs(total_root_delay) + total_root_dispersion;
n = src->n_samples;
#if 0
printf("Sample %d theta=%.6f delta=%.6f root_del=%.6f root_disp=%.6f root_dist=%.6f\n",
n, theta, delta, total_root_delay, total_root_dispersion, total_root_distance);
#endif
src->offsets[n] = theta;
src->root_distances[n] = total_root_distance;
++(src->n_samples);
}
/* ================================================== */
static void
read_from_socket(void *anything)
{
int status;
ReceiveBuffer msg;
union sockaddr_in46 his_addr;
int sock_fd;
socklen_t his_addr_len;
int flags;
int message_length;
IPAddr remote_ip;
int i, ok;
struct timeval now;
SourceRecord *src;
flags = 0;
message_length = sizeof(msg);
his_addr_len = sizeof(his_addr);
/* Get timestamp */
SCH_GetLastEventTime(&now, NULL, NULL);
sock_fd = (long)anything;
status = recvfrom (sock_fd, (char *)&msg, message_length, flags,
&his_addr.u, &his_addr_len);
if (status < 0) {
LOG(LOGS_WARN, LOGF_Acquire, "Error reading from socket, %s", strerror(errno));
return;
}
switch (his_addr.u.sa_family) {
case AF_INET:
remote_ip.family = IPADDR_INET4;
remote_ip.addr.in4 = ntohl(his_addr.in4.sin_addr.s_addr);
break;
#ifdef HAVE_IPV6
case AF_INET6:
remote_ip.family = IPADDR_INET6;
memcpy(&remote_ip.addr.in6, his_addr.in6.sin6_addr.s6_addr,
sizeof (remote_ip.addr.in6));
break;
#endif
default:
assert(0);
}
#if 0
printf("Got message from %s\n", UTI_IPToString(&remote_ip));
#endif
/* Find matching host */
ok = 0;
for (i=0; i<n_sources; i++) {
if (UTI_CompareIPs(&remote_ip, &sources[i].ip_addr, NULL) == 0) {
ok = 1;
break;
}
}
if (ok) {
src = sources + i;
++src->n_total_samples;
src->n_dead_probes = 0; /* reset this when we actually receive something */
/* If we got into this function, we know the retransmission timeout has not
expired for the source */
if (src->timer_running) {
SCH_RemoveTimeout(src->timeout_id);
src->timer_running = 0;
}
process_receive(&msg.ntp_pkt, src, &now);
/* Check if server done and requeue timeout */
if ((src->n_samples >= N_GOOD_SAMPLES) ||
(src->n_total_samples >= MAX_SAMPLES)) {
++n_completed_sources;
#if 0
printf("Source %s completed\n", UTI_IPToString(&src->ip_addr));
#endif
if (n_completed_sources == n_sources) {
wind_up_acquisition();
}
} else {
/* Send the next probe */
probe_source(src);
}
}
}
/* ================================================== */
static void
start_next_source(void)
{
probe_source(sources + n_started_sources);
#if 0
printf("Trying to start source %s\n", UTI_IPToString(&sources[n_started_sources].ip_addr));
#endif
n_started_sources++;
if (n_started_sources < n_sources) {
source_start_timeout_id = SCH_AddTimeoutByDelay(INTER_SOURCE_START, start_source_timeout_handler, NULL);
}
}
/* ================================================== */
static int
endpoint_compare(const void *a, const void *b)
{
const Endpoint *aa = (const Endpoint *) a;
const Endpoint *bb = (const Endpoint *) b;
if (aa->offset < bb->offset) {
return -1;
} else if (aa->offset > bb->offset) {
return +1;
} else {
return 0;
}
}
/* ================================================== */
static void
process_measurements(void)
{
SourceRecord *s;
Endpoint *eps;
int i, j;
int n_sane_sources;
double lo, hi;
double inter_lo, inter_hi;
int depth;
int best_depth;
int n_at_best_depth;
Interval *intervals;
double estimated_offset;
int index1, index2;
n_sane_sources = 0;
/* First, get a consistent interval for each source. Those for
which this is not possible are considered to be insane. */
for (i=0; i<n_sources; i++) {
s = sources + i;
/* If we got no measurements, the source is insane */
if (s->n_samples == 0) {
s->sanity = 0;
} else {
s->sanity = 1; /* so far ... */
lo = s->offsets[0] - s->root_distances[0];
hi = s->offsets[0] + s->root_distances[0];
inter_lo = lo;
inter_hi = hi;
for (j=1; j<s->n_samples; j++) {
lo = s->offsets[j] - s->root_distances[j];
hi = s->offsets[j] + s->root_distances[j];
if ((inter_hi <= lo) || (inter_lo >= hi)) {
/* Oh dear, we won't get an interval for this source */
s->sanity = 0;
break;
} else {
inter_lo = (lo < inter_lo) ? inter_lo : lo;
inter_hi = (hi > inter_hi) ? inter_hi : hi;
}
}
if (s->sanity) {
s->inter_lo = inter_lo;
s->inter_hi = inter_hi;
}
}
if (s->sanity) {
++n_sane_sources;
}
}
/* Now build the endpoint list, similar to the RFC1305 clock
selection algorithm. */
eps = MallocArray(Endpoint, 2*n_sane_sources);
intervals = MallocArray(Interval, n_sane_sources);
j = 0;
for (i=0; i<n_sources; i++) {
s = sources + i;
if (s->sanity) {
eps[j].offset = s->inter_lo;
eps[j].type = LO;
eps[j].index = i;
eps[j+1].offset = s->inter_hi;
eps[j+1].type = HIGH;
eps[j+1].index = i;
j += 2;
}
}
qsort(eps, 2*n_sane_sources, sizeof(Endpoint), endpoint_compare);
/* Now do depth searching algorithm */
n_at_best_depth = best_depth = depth = 0;
for (i=0; i<2*n_sane_sources; i++) {
#if 0
fprintf(stderr, "Endpoint type %s source index %d [ip=%s] offset=%.6f\n",
(eps[i].type == LO) ? "LO" : "HIGH",
eps[i].index,
UTI_IPToString(&sources[eps[i].index].ip_addr),
eps[i].offset);
#endif
switch (eps[i].type) {
case LO:
depth++;
if (depth > best_depth) {
best_depth = depth;
n_at_best_depth = 0;
intervals[0].lo = eps[i].offset;
} else if (depth == best_depth) {
intervals[n_at_best_depth].lo = eps[i].offset;
} else {
/* Nothing to do */
}
break;
case HIGH:
if (depth == best_depth) {
intervals[n_at_best_depth].hi = eps[i].offset;
n_at_best_depth++;
}
depth--;
break;
}
}
if (best_depth > 0) {
if ((n_at_best_depth % 2) == 1) {
index1 = (n_at_best_depth - 1) / 2;
estimated_offset = 0.5 * (intervals[index1].lo + intervals[index1].hi);
} else {
index2 = (n_at_best_depth / 2);
index1 = index2 - 1;
estimated_offset = 0.5 * (intervals[index1].lo + intervals[index2].hi);
}
/* Apply a step change to the system clock. As per sign
convention in local.c and its children, a positive offset means
the system clock is fast of the reference, i.e. it needs to be
stepped backwards. */
if (fabs(estimated_offset) > init_slew_threshold) {
LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (step)",
fabs(estimated_offset),
(estimated_offset >= 0) ? "fast" : "slow");
LCL_ApplyStepOffset(estimated_offset);
} else {
LOG(LOGS_INFO, LOGF_Acquire, "System's initial offset : %.6f seconds %s of true (slew)",
fabs(estimated_offset),
(estimated_offset >= 0) ? "fast" : "slow");
LCL_AccumulateOffset(estimated_offset, 0.0);
}
} else {
LOG(LOGS_WARN, LOGF_Acquire, "No intersecting endpoints found");
}
Free(intervals);
Free(eps);
}
/* ================================================== */
static void
wind_up_acquisition(void)
{
/* Now process measurements */
process_measurements();
Free(sources);
finalise_io();
if (saved_after_hook) {
(saved_after_hook)(saved_after_hook_anything);
}
}
/* ================================================== */
static void
start_source_timeout_handler(void *not_used)
{
start_next_source();
}
/* ================================================== */
void
ACQ_StartAcquisition(int n, IPAddr *ip_addrs, double threshold, void (*after_hook)(void *), void *anything)
{
int i, ip4, ip6;
int k, duplicate_ip;
saved_after_hook = after_hook;
saved_after_hook_anything = anything;
init_slew_threshold = threshold;
n_started_sources = 0;
n_completed_sources = 0;
n_sources = 0;
sources = MallocArray(SourceRecord, n);
for (i = ip4 = ip6 = 0; i < n; i++) {
/* check for duplicate IP addresses and ignore them */
duplicate_ip = 0;
for (k = 0; k < i; k++) {
duplicate_ip |= UTI_CompareIPs(&(sources[k].ip_addr),
&ip_addrs[i],
NULL) == 0;
}
if (!duplicate_ip) {
sources[n_sources].ip_addr = ip_addrs[i];
sources[n_sources].n_samples = 0;
sources[n_sources].n_total_samples = 0;
sources[n_sources].n_dead_probes = 0;
if (ip_addrs[i].family == IPADDR_INET4)
ip4++;
else if (ip_addrs[i].family == IPADDR_INET6)
ip6++;
n_sources++;
} else {
LOG(LOGS_WARN, LOGF_Acquire, "Ignoring duplicate source: %s",
UTI_IPToString(&ip_addrs[i]));
}
}
initialise_io((ip4 && ip6) ? IPADDR_UNSPEC : (ip4 ? IPADDR_INET4 : IPADDR_INET6));
/* Start sampling first source */
start_next_source();
}
/* ================================================== */

View File

@@ -46,9 +46,13 @@ typedef struct {
typedef struct {
IPAddr ip_addr;
IPAddr local_ip_addr;
unsigned short port;
} NTP_Remote_Address;
typedef struct {
IPAddr ip_addr;
int sock_fd;
} NTP_Local_Address;
#endif /* GOT_ADDRESSING_H */

View File

@@ -40,6 +40,7 @@
typedef struct {
NTP_Remote_Address addr;
NTP_Local_Address local_addr;
int interval;
} Destination;
static Destination *destinations = 0;
@@ -114,7 +115,7 @@ timeout_handler(void *arbitrary)
ts_fuzz = UTI_GetNTPTsFuzz(message.precision);
LCL_ReadCookedTime(&local_transmit, NULL);
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
NIO_SendNormalPacket(&message, &d->addr);
NIO_SendNormalPacket(&message, &d->addr, &d->local_addr);
/* Requeue timeout. Don't care if interval drifts gradually, so just do it
* at the end. */
@@ -141,8 +142,10 @@ BRD_AddDestination(IPAddr *addr, unsigned short port, int interval)
}
destinations[n_destinations].addr.ip_addr = *addr;
destinations[n_destinations].addr.local_ip_addr.family = IPADDR_UNSPEC;
destinations[n_destinations].addr.port = port;
destinations[n_destinations].local_addr.ip_addr.family = IPADDR_UNSPEC;
destinations[n_destinations].local_addr.sock_fd =
NIO_GetServerSocket(&destinations[n_destinations].addr);
destinations[n_destinations].interval = interval;
SCH_AddTimeoutInClass((double) interval, 1.0, 0.0,

41
candm.h
View File

@@ -360,13 +360,24 @@ typedef struct {
Version 5 : auth data moved to the end of the packet to allow hashes with
different sizes, extended sources, tracking and activity reports, dropped
subnets accessed and client accesses
Version 6 : added padding to requests to prevent amplification attack,
changed maximum number of samples in manual list to 16
*/
#define PROTO_VERSION_NUMBER 5
#define PROTO_VERSION_NUMBER 6
/* The oldest protocol version that is compatible enough with
the current version to report a version mismatch */
#define PROTO_VERSION_MISMATCH_COMPAT 4
/* The oldest protocol versions that are compatible enough with the current
version to report a version mismatch for the server and the client */
#define PROTO_VERSION_MISMATCH_COMPAT_SERVER 5
#define PROTO_VERSION_MISMATCH_COMPAT_CLIENT 4
/* The first protocol version using padding in requests */
#define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently
defined by CLIENT_ACCESSES_BY_INDEX and MANUAL_LIST */
#define MAX_PADDING_LENGTH 396
/* ================================================== */
@@ -424,8 +435,13 @@ typedef struct {
REQ_ReselectDistance reselect_distance;
} data; /* Command specific parameters */
/* authentication of the packet, there is no hole after the actual data
from the data union, this field only sets the maximum auth size */
/* The following fields only set the maximum size of the packet.
There are no holes between them and the actual data. */
/* Padding used to prevent traffic amplification */
uint8_t padding[MAX_PADDING_LENGTH];
/* Authentication data */
uint8_t auth[MAX_HASH_LENGTH];
} CMD_Request;
@@ -465,6 +481,7 @@ typedef struct {
#define STT_BADSUBNET 7
#define STT_ACCESSALLOWED 8
#define STT_ACCESSDENIED 9
/* Deprecated */
#define STT_NOHOSTACCESS 10
#define STT_SOURCEALREADYKNOWN 11
#define STT_TOOMANYSOURCES 12
@@ -501,7 +518,7 @@ typedef struct {
typedef struct {
IPAddr ip_addr;
uint16_t poll;
int16_t poll;
uint16_t stratum;
uint16_t state;
uint16_t mode;
@@ -585,9 +602,10 @@ typedef struct {
uint32_t next_index; /* the index 1 beyond those processed on this call */
uint32_t n_clients; /* the number of valid entries in the following array */
RPY_ClientAccesses_Client clients[MAX_CLIENT_ACCESSES];
int32_t EOR;
} RPY_ClientAccessesByIndex;
#define MAX_MANUAL_LIST_SAMPLES 32
#define MAX_MANUAL_LIST_SAMPLES 16
typedef struct {
Timeval when;
@@ -599,6 +617,7 @@ typedef struct {
typedef struct {
uint32_t n_samples;
RPY_ManualListSample samples[MAX_MANUAL_LIST_SAMPLES];
int32_t EOR;
} RPY_ManualList;
typedef struct {
@@ -618,9 +637,9 @@ typedef struct {
uint16_t command; /* Which command is being replied to */
uint16_t reply; /* Which format of reply this is */
uint16_t status; /* Status of command processing */
uint16_t number; /* Which packet this is in reply sequence */
uint16_t total; /* Number of replies to expect in this sequence */
uint16_t pad1; /* Get up to 4 byte alignment */
uint16_t pad1; /* Padding for compatibility and 4 byte alignment */
uint16_t pad2;
uint16_t pad3;
uint32_t sequence; /* Echo of client's sequence number */
uint32_t utoken; /* Unique token per incarnation of daemon */
uint32_t token; /* New command token (only if command was successfully

View File

@@ -55,7 +55,8 @@ With a good reference clock the accuracy can reach one microsecond.
.SH "SEE ALSO"
.BR chronyc(1),
.BR chrony(1)
.BR chrony.conf(5),
.BR chronyd(8)
.I http://chrony.tuxfamily.org/

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@ A summary of the options supported by \fBchronyc\fR is included below.
.TP
\fB\-h\fR \fIhostname\fR
specify hostname
specify hostname (default 127.0.0.1)
.TP
\fB\-p\fR \fIport-number\fR
specify port-number

View File

@@ -4,7 +4,7 @@ chronyd \- chrony background daemon
.SH SYNOPSIS
.B chronyd
[\fIOPTIONS\fR]
[\fIOPTIONS\fR] [\fIconfiguration commands\fR]
.SH DESCRIPTION
\fIchrony\fR is a pair of programs for maintaining the accuracy of computer
@@ -31,6 +31,9 @@ command:
Information messages and warnings will be logged to syslog.
If no configuration commands are specified on the command line,
\fBchronyd\fR will read the commands from the configuration file
(default \fI@SYSCONFDIR@/chrony.conf\fR).
.SH OPTIONS
A summary of the options supported by \fBchronyd\fR is included below.
@@ -52,7 +55,8 @@ terminal.
.B \-d
When run in this mode, the program will not detach itself from the
terminal, and all messages will be sent to the terminal instead of
to syslog.
to syslog. When \fBchronyd\fR was compiled with debugging support,
this option can be used twice to print also debugging messages.
.TP
\fB\-f\fR \fIconf-file\fR
This option can be used to specify an alternate location for the
@@ -78,7 +82,7 @@ with the \fB-r\fR option.
.B \-s
This option will set the system clock from the computer's real-time
clock. This is analogous to supplying the \fI-s\fR flag to the
\fI/sbin/clock\fR program during the Linux boot sequence.
\fI/sbin/hwclock\fR program during the Linux boot sequence.
Support for real-time clocks is limited at present - the criteria
are described in the section on the \fIrtcfile\fR directive in the
@@ -99,8 +103,17 @@ between the real time clock and system clock last time the
computer was on.
.TP
\fB\-u\fR \fIuser\fR
When this option is used, chronyd will drop root privileges to the specified
user. So far, it works only on Linux when compiled with capabilities support.
This option sets the name of the user to which will \fBchronyd\fR switch to
drop root privileges if compiled with Linux capabilities support (default
\fB@DEFAULT_USER@\fR).
.TP
.B \-q
When run in this mode, chronyd will set the system clock once
and exit. It will not detach from the terminal.
.TP
.B \-Q
This option is similar to \fB\-q\fR, but it will only print the offset and
not correct the clock.
.TP
.B \-v
This option displays \fBchronyd\fR's version number to the terminal and exits
@@ -119,8 +132,7 @@ To report bugs, please visit \fIhttp://chrony.tuxfamily.org/\fR
.SH "SEE ALSO"
\fBchronyd\fR is documented in detail in the documentation supplied with the
distribution (\fIchrony.txt\fR and \fIchrony.texi\fR) and is also available
from \fIhttp://go.to/chrony\fR
distribution (\fIchrony.txt\fR and \fIchrony.texi\fR).
.BR chrony(1),
.BR chronyc(1),
@@ -128,6 +140,8 @@ from \fIhttp://go.to/chrony\fR
.BR hwclock(8),
.BR ntpd(8)
.I http://chrony.tuxfamily.org/
.SH AUTHOR
Richard Curnow <rc@rc0.org.uk>

126
client.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2013
* Copyright (C) Miroslav Lichvar 2009-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -66,6 +66,8 @@ static int on_terminal = 0;
static int no_dns = 0;
static int recv_errqueue = 0;
/* ================================================== */
/* Ought to extract some code from util.c to make
a new set of utilities that can be linked into either
@@ -139,6 +141,7 @@ static void
open_io(const char *hostname, int port)
{
IPAddr ip;
int on_off = 1;
/* Note, this call could block for a while */
if (DNS_Name2IPAddress(hostname, &ip) != DNS_Success) {
@@ -176,6 +179,22 @@ open_io(const char *hostname, int port)
perror("Can't create socket");
exit(1);
}
/* Enable extended error reporting (e.g. ECONNREFUSED on ICMP unreachable) */
#ifdef IP_RECVERR
if (ip.family == IPADDR_INET4 &&
!setsockopt(sock_fd, IPPROTO_IP, IP_RECVERR, &on_off, sizeof(on_off))) {
recv_errqueue = 1;
}
#endif
#ifdef HAVE_IPV6
#ifdef IPV6_RECVERR
if (ip.family == IPADDR_INET6 &&
!setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVERR, &on_off, sizeof(on_off))) {
recv_errqueue = 1;
}
#endif
#endif
}
/* ================================================== */
@@ -613,9 +632,7 @@ process_cmd_local(CMD_Request *msg, const char *line)
p = line;
if (!*p) {
return 0;
} else if (!strcmp(p, "off")) {
if (!strcmp(p, "off")) {
msg->data.local.on_off = htonl(0);
msg->data.local.stratum = htonl(0);
} else if (sscanf(p, "stratum%d", &stratum) == 1) {
@@ -639,15 +656,14 @@ process_cmd_manual(CMD_Request *msg, const char *line)
p = line;
if (!*p) {
return 0;
} else if (!strcmp(p, "off")) {
if (!strcmp(p, "off")) {
msg->data.manual.option = htonl(0);
} else if (!strcmp(p, "on")) {
msg->data.manual.option = htonl(1);
} else if (!strcmp(p, "reset")) {
msg->data.manual.option = htonl(2);
} else {
fprintf(stderr, "Invalid syntax for manual command\n");
return 0;
}
msg->command = htons(REQ_MANUAL);
@@ -1242,6 +1258,7 @@ static unsigned long token = 0;
static int max_retries = 2;
static int initial_timeout = 1000;
static int proto_version = PROTO_VERSION_NUMBER;
/* This is the core protocol module. Complete particular fields in
the outgoing packet, send it, wait for a response, handle retries,
@@ -1260,13 +1277,13 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
int read_length;
int expected_length;
int command_length;
int padding_length;
int auth_length;
struct timeval tv;
int timeout;
int n_attempts;
fd_set rdfd, wrfd, exfd;
request->version = PROTO_VERSION_NUMBER;
request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0;
request->res2 = 0;
@@ -1281,6 +1298,14 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
n_attempts = 0;
do {
request->version = proto_version;
command_length = PKL_CommandLength(request);
padding_length = PKL_CommandPaddingLength(request);
assert(command_length > 0 && command_length > padding_length);
/* Zero the padding to avoid sending uninitialized data. This needs to be
done before generating auth data as it includes the padding. */
memset(((char *)request) + command_length - padding_length, 0, padding_length);
/* Decide whether to authenticate */
if (password) {
@@ -1294,9 +1319,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
auth_length = 0;
}
command_length = PKL_CommandLength(request);
assert(command_length > 0);
/* add empty MD5 auth so older servers will not drop the request
due to bad length */
if (!auth_length) {
@@ -1362,6 +1384,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
/* If we get connrefused here, it suggests the sendto is
going to a dead port - but only if the daemon machine is
running Linux (Solaris doesn't return anything) */
#ifdef IP_RECVERR
/* Fetch the message from the error queue */
if (recv_errqueue &&
recvfrom(sock_fd, (void *)reply, sizeof(CMD_Reply), MSG_ERRQUEUE,
&where_from.u, &where_from_len) < 0)
;
#endif
n_attempts++;
if (n_attempts > max_retries) {
return 0;
@@ -1369,7 +1400,11 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
} else {
read_length = recvfrom_status;
expected_length = PKL_ReplyLength(reply);
if (read_length >= offsetof(CMD_Reply, data)) {
expected_length = PKL_ReplyLength(reply);
} else {
expected_length = 0;
}
bad_length = (read_length < expected_length ||
expected_length < offsetof(CMD_Reply, data));
@@ -1399,8 +1434,8 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
continue;
}
bad_header = ((reply->version != PROTO_VERSION_NUMBER &&
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT &&
bad_header = ((reply->version != proto_version &&
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
ntohs(reply->status) == STT_BADPKTVERSION)) ||
(reply->pkt_type != PKT_TYPE_CMD_REPLY) ||
(reply->res1 != 0) ||
@@ -1415,12 +1450,24 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
continue;
}
#if PROTO_VERSION_NUMBER == 6
/* Protocol version 5 is similar to 6 except there is no padding.
If a version 5 reply with STT_BADPKTVERSION is received,
switch our version and try again. */
if (proto_version == PROTO_VERSION_NUMBER &&
reply->version == PROTO_VERSION_NUMBER - 1) {
proto_version = PROTO_VERSION_NUMBER - 1;
continue;
}
#else
#error unknown compatibility with PROTO_VERSION - 1
#endif
/* Good packet received, print out results */
#if 0
printf("Reply cmd=%d reply=%d stat=%d num=%d tot=%d seq=%d utok=%08lx tok=%d\n",
printf("Reply cmd=%d reply=%d stat=%d seq=%d utok=%08lx tok=%d\n",
ntohs(reply->command), ntohs(reply->reply),
ntohs(reply->status), ntohs(reply->number),
ntohs(reply->total),
ntohs(reply->status),
ntohl(reply->sequence),
ntohl(reply->utoken),
ntohl(reply->token));
@@ -1650,6 +1697,18 @@ print_freq_ppm(double f)
/* ================================================== */
static void
print_signed_freq_ppm(double f)
{
if (fabs(f) < 99999.5) {
printf("%+10.3f", f);
} else {
printf("%+10.0f", f);
}
}
/* ================================================== */
static int
check_for_verbose_flag(char *line)
{
@@ -1674,8 +1733,8 @@ process_cmd_sources(char *line)
double orig_latest_meas, latest_meas, latest_meas_err;
IPAddr ip_addr;
uint32_t latest_meas_ago;
uint16_t poll, stratum;
uint16_t state, mode, flags, reachability;
int16_t poll;
uint16_t stratum, state, mode, flags, reachability;
char hostname_buf[50];
/* Check whether to output verbose headers */
@@ -1844,7 +1903,7 @@ process_cmd_sourcestats(char *line)
printf("%-25s %3lu %3lu ", hostname_buf, n_samples, n_runs);
print_seconds(span_seconds);
printf(" ");
print_freq_ppm(resid_freq_ppm);
print_signed_freq_ppm(resid_freq_ppm);
printf(" ");
print_freq_ppm(skew_ppm);
printf(" ");
@@ -1933,7 +1992,7 @@ process_cmd_tracking(char *line)
rms_offset = UTI_FloatNetworkToHost(reply.data.tracking.rms_offset);
printf("System time : %.9f seconds %s of NTP time\n", fabs(correction),
(correction > 0.0) ? "slow" : "fast");
printf("Last offset : %.9f seconds\n", last_offset);
printf("Last offset : %+.9f seconds\n", last_offset);
printf("RMS offset : %.9f seconds\n", rms_offset);
freq_ppm = UTI_FloatNetworkToHost(reply.data.tracking.freq_ppm);
resid_freq_ppm = UTI_FloatNetworkToHost(reply.data.tracking.resid_freq_ppm);
@@ -1942,7 +2001,7 @@ process_cmd_tracking(char *line)
root_dispersion = UTI_FloatNetworkToHost(reply.data.tracking.root_dispersion);
last_update_interval = UTI_FloatNetworkToHost(reply.data.tracking.last_update_interval);
printf("Frequency : %.3f ppm %s\n", fabs(freq_ppm), (freq_ppm < 0.0) ? "slow" : "fast");
printf("Residual freq : %.3f ppm\n", resid_freq_ppm);
printf("Residual freq : %+.3f ppm\n", resid_freq_ppm);
printf("Skew : %.3f ppm\n", skew_ppm);
printf("Root delay : %.6f seconds\n", root_delay);
printf("Root dispersion : %.6f seconds\n", root_dispersion);
@@ -2582,7 +2641,7 @@ authenticate_from_config(const char *filename)
in = fopen(keyfile, "r");
if (!in) {
fprintf(stderr, "Could not open keyfile %s\n", filename);
fprintf(stderr, "Could not open keyfile %s\n", keyfile);
return 0;
}
@@ -2656,7 +2715,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2013 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2014 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",
@@ -2670,9 +2729,9 @@ main(int argc, char **argv)
{
char *line;
const char *progname = argv[0];
const char *hostname = "localhost";
const char *hostname = NULL;
const char *conf_file = DEFAULT_CONF_FILE;
int quit = 0, ret = 1, multi = 0, auto_auth = 0;
int quit = 0, ret = 1, multi = 0, auto_auth = 0, family = IPADDR_UNSPEC;
int port = DEFAULT_CANDM_PORT;
/* Parse command line options */
@@ -2699,11 +2758,9 @@ main(int argc, char **argv)
} else if (!strcmp(*argv, "-n")) {
no_dns = 1;
} else if (!strcmp(*argv, "-4")) {
DNS_SetAddressFamily(IPADDR_INET4);
hostname = "127.0.0.1";
family = IPADDR_INET4;
} else if (!strcmp(*argv, "-6")) {
DNS_SetAddressFamily(IPADDR_INET6);
hostname = "::1";
family = IPADDR_INET6;
} else if (!strcmp("-v", *argv) || !strcmp("--version",*argv)) {
printf("chronyc (chrony) version %s\n", CHRONY_VERSION);
exit(0);
@@ -2730,6 +2787,15 @@ main(int argc, char **argv)
return 1;
}
DNS_SetAddressFamily(family);
if (!hostname) {
hostname = family == IPADDR_INET6 ? "::1" : "127.0.0.1";
#ifdef FEAT_ASYNCDNS
initial_timeout /= 10;
#endif
}
open_io(hostname, port);
if (auto_auth) {

105
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2012
* Copyright (C) Miroslav Lichvar 2009-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -265,11 +265,25 @@ prepare_socket(int family)
void
CAM_Initialise(int family)
{
int i;
assert(!initialised);
initialised = 1;
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
for (i = 0; i < N_REQUEST_TYPES; i++) {
CMD_Request r;
int command_length, padding_length;
r.version = PROTO_VERSION_NUMBER;
r.command = htons(i);
command_length = PKL_CommandLength(&r);
padding_length = PKL_CommandPaddingLength(&r);
assert(padding_length <= MAX_PADDING_LENGTH && padding_length <= command_length);
assert(command_length == 0 || command_length >= offsetof(CMD_Reply, data));
}
utoken = (unsigned long) time(NULL);
issued_tokens = returned_tokens = issue_pointer = 0;
@@ -720,7 +734,7 @@ transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
status = sendto(sock_fd, (void *) msg, tx_message_length, 0,
&where_to->u, addrlen);
if (status < 0 && !LOG_RateLimited()) {
if (status < 0) {
unsigned short port;
IPAddr ip;
@@ -741,7 +755,7 @@ transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
assert(0);
}
LOG(LOGS_WARN, LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&ip), port);
DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&ip), port);
}
}
@@ -1262,7 +1276,6 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
NSR_Status status;
UTI_IPNetworkToHost(&rx_message->data.ntp_source.ip_addr, &rem_addr.ip_addr);
rem_addr.local_ip_addr.family = IPADDR_UNSPEC;
rem_addr.port = (unsigned short)(ntohl(rx_message->data.ntp_source.port));
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
@@ -1310,7 +1323,6 @@ handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
NSR_Status status;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &rem_addr.ip_addr);
rem_addr.local_ip_addr.family = IPADDR_UNSPEC;
rem_addr.port = 0;
status = NSR_RemoveSource(&rem_addr);
@@ -1366,8 +1378,8 @@ handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
{
long sec, usec;
double doffset;
sec = (long)(ntohl(rx_message->data.doffset.sec));
usec = (long)(ntohl(rx_message->data.doffset.usec));
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec);
doffset = (double) sec + 1.0e-6 * (double) usec;
LOG(LOGS_INFO, LOGF_CmdMon, "Accumulated delta offset of %.6f seconds", doffset);
LCL_AccumulateOffset(doffset, 0.0);
@@ -1577,7 +1589,7 @@ handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message)
static void
handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message)
{
LCL_MakeStep(0.0);
LCL_MakeStep();
tx_message->status = htons(STT_SUCCESS);
}
@@ -1708,7 +1720,13 @@ read_from_cmd_socket(void *anything)
assert(0);
}
allowed = ADF_IsAllowed(access_auth_table, &remote_ip) || localhost;
if (!(localhost || ADF_IsAllowed(access_auth_table, &remote_ip))) {
/* The client is not allowed access, so don't waste any more time
on him. Note that localhost is always allowed access
regardless of the defined access rules - otherwise, we could
shut ourselves out completely! */
return;
}
/* Message size sanity check */
if (read_length >= offsetof(CMD_Request, data)) {
@@ -1718,13 +1736,13 @@ read_from_cmd_socket(void *anything)
}
if (expected_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 */
if (allowed)
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
return;
}
@@ -1738,9 +1756,9 @@ read_from_cmd_socket(void *anything)
tx_message.command = rx_message.command;
tx_message.sequence = rx_message.sequence;
tx_message.reply = htons(RPY_NULL);
tx_message.number = htons(1);
tx_message.total = htons(1);
tx_message.pad1 = 0;
tx_message.pad2 = 0;
tx_message.pad3 = 0;
tx_message.utoken = htonl(utoken);
/* Set this to a default (invalid) value. This protects against the
token field being set to an arbitrary value if we reject the
@@ -1749,29 +1767,21 @@ read_from_cmd_socket(void *anything)
memset(&tx_message.auth, 0, sizeof(tx_message.auth));
if (rx_message.version != PROTO_VERSION_NUMBER) {
tx_message.status = htons(STT_NOHOSTACCESS);
if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon, "Read command packet with protocol version %d (expected %d) from %s:%hu", rx_message.version, PROTO_VERSION_NUMBER, UTI_IPToString(&remote_ip), remote_port);
}
if (allowed)
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
DEBUG_LOG(LOGF_CmdMon, "Read command packet with protocol version %d (expected %d) from %s:%hu", rx_message.version, PROTO_VERSION_NUMBER, UTI_IPToString(&remote_ip), remote_port);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT) {
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION);
/* add empty MD5 auth so older clients will not drop
the reply due to bad length */
memset(((char *)&tx_message) + PKL_ReplyLength(&tx_message), 0, 16);
transmit_reply(&tx_message, &where_from, 16);
transmit_reply(&tx_message, &where_from, 0);
}
return;
}
if (rx_command >= N_REQUEST_TYPES) {
if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon, "Read command packet with invalid command %d from %s:%hu", rx_command, UTI_IPToString(&remote_ip), remote_port);
}
if (allowed)
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
DEBUG_LOG(LOGF_CmdMon, "Read command packet with invalid command %d from %s:%hu", rx_command, UTI_IPToString(&remote_ip), remote_port);
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
tx_message.status = htons(STT_INVALID);
transmit_reply(&tx_message, &where_from, 0);
@@ -1779,35 +1789,15 @@ read_from_cmd_socket(void *anything)
}
if (read_length < expected_length) {
if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon, "Read incorrectly sized command packet from %s:%hu", UTI_IPToString(&remote_ip), remote_port);
}
if (allowed)
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
DEBUG_LOG(LOGF_CmdMon, "Read incorrectly sized command packet from %s:%hu", UTI_IPToString(&remote_ip), remote_port);
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(&tx_message, &where_from, 0);
return;
}
if (!allowed) {
/* The client is not allowed access, so don't waste any more time
on him. Note that localhost is always allowed access
regardless of the defined access rules - otherwise, we could
shut ourselves out completely! */
if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon, "Command packet received from unauthorised host %s port %d",
UTI_IPToString(&remote_ip),
remote_port);
}
tx_message.status = htons(STT_NOHOSTACCESS);
transmit_reply(&tx_message, &where_from, 0);
return;
}
/* OK, we have a valid message. Now dispatch on message type and process it. */
/* Do authentication stuff and command tokens here. Well-behaved
@@ -1882,8 +1872,8 @@ read_from_cmd_socket(void *anything)
tx_message_length = PKL_ReplyLength(prev_tx_message);
status = sendto(sock_fd, (void *) prev_tx_message, tx_message_length, 0,
&where_from.u, from_length);
if (status < 0 && !LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&remote_ip), remote_port);
if (status < 0) {
DEBUG_LOG(LOGF_CmdMon, "Could not send response to %s:%hu", UTI_IPToString(&remote_ip), remote_port);
}
return;
}
@@ -1937,8 +1927,6 @@ read_from_cmd_socket(void *anything)
/* This should be already handled */
assert(0);
} else {
allowed = 0;
/* Check level of authority required to issue the command */
switch(permissions[rx_command]) {
case PERMIT_AUTH:
@@ -1960,6 +1948,7 @@ read_from_cmd_socket(void *anything)
break;
default:
assert(0);
allowed = 0;
}
if (allowed) {
@@ -2011,8 +2000,8 @@ read_from_cmd_socket(void *anything)
case REQ_LOGON:
/* If the log-on fails, record the reason why */
if (!issue_token && !LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_CmdMon,
if (!issue_token) {
DEBUG_LOG(LOGF_CmdMon,
"Bad command logon from %s port %d (auth_ok=%d valid_ts=%d)",
UTI_IPToString(&remote_ip),
remote_port,

View File

@@ -134,7 +134,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
line += n;
}
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1) {
if (sscanf(line, "%lu%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) {
result = CPS_BadKey;
ok = 0;
done = 1;

911
conf.c

File diff suppressed because it is too large Load Diff

19
conf.h
View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -34,13 +35,13 @@ extern void CNF_SetRestarted(int);
extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void);
extern void CNF_ProcessInitStepSlew(void (*after_hook)(void *), void *anything);
extern unsigned short CNF_GetAcquisitionPort(void);
extern unsigned short CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
@@ -60,8 +61,8 @@ extern int CNF_GetGenerateCommandKey(void);
extern int CNF_GetDumpOnExit(void);
extern int CNF_GetManualEnabled(void);
extern int CNF_GetCommandPort(void);
extern int CNF_GetRTCOnUTC(void);
extern int CNF_GetRTCSync(void);
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);
@@ -70,16 +71,16 @@ extern int CNF_GetNoClientLog(void);
extern unsigned long CNF_GetClientLogLimit(void);
extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetPidFile(void);
extern char *CNF_GetLeapSecTimezone(void);
extern void CNF_GetLinuxHz(int *set, int *hz);
extern void CNF_GetLinuxFreqScale(int *set, double *freq_scale);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
extern double CNF_GetMaxClockError(void);
extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
@@ -99,4 +100,10 @@ extern char *CNF_GetUser(void);
extern int CNF_GetMaxSamples(void);
extern int CNF_GetMinSamples(void);
extern double CNF_GetRtcAutotrim(void);
extern char *CNF_GetHwclockFile(void);
extern int CNF_GetInitSources(void);
extern double CNF_GetInitStepThreshold(void);
#endif /* GOT_CONF_H */

81
configure vendored
View File

@@ -4,7 +4,7 @@
# chronyd/chronyc - Programs for keeping computer clocks accurate.
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Miroslav Lichvar 2009, 2012
# Copyright (C) Miroslav Lichvar 2009, 2012-2014
#
# =======================================================================
@@ -108,12 +108,15 @@ For better control, use the options below.
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-ipv6 Disable IPv6 support
--disable-phc Disable PHC support
--disable-pps Disable PPS API support
--disable-rtc Don't include RTC even on Linux
--disable-linuxcaps Disable Linux capabilities support
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--with-user=USER Specify default chronyd user [root]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-trace Enable tracing
--enable-debug Enable debugging support
Fine tuning of the installation directories:
--sysconfdir=DIR chrony.conf location [/etc]
@@ -170,7 +173,7 @@ EXTRA_OBJECTS=""
EXTRA_DEFS=""
SYSDEFS=""
# Support for readline (on by default)
debug=0
feat_readline=1
try_readline=1
try_editline=1
@@ -184,17 +187,21 @@ readline_lib=""
readline_inc=""
ncurses_lib=""
feat_ipv6=1
feat_phc=1
try_phc=0
feat_pps=1
try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
default_user="root"
mail_program="/usr/lib/sendmail"
for option
do
case "$option" in
--enable-trace )
add_def TRACEON
--enable-debug )
debug=1
;;
--disable-readline )
feat_readline=0
@@ -253,15 +260,24 @@ do
--disable-ipv6)
feat_ipv6=0
;;
--disable-phc)
feat_phc=0
;;
--disable-pps)
feat_pps=0
;;
--disable-linuxcaps)
feat_linuxcaps=0
;;
--disable-asyncdns)
feat_asyncdns=0
;;
--disable-forcednsretry)
feat_forcednsretry=0
;;
--with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -310,11 +326,12 @@ case $SYSTEM in
esac
;;
Linux* )
EXTRA_OBJECTS="sys_linux.o wrap_adjtimex.o"
EXTRA_OBJECTS="sys_generic.o sys_linux.o wrap_adjtimex.o"
try_linuxcaps=1
try_rtc=1
try_setsched=1
try_lockmem=1
try_phc=1
add_def LINUX
echo "Configuring for " $SYSTEM
if [ "${MACHINE}" = "alpha" ]; then
@@ -401,6 +418,21 @@ then
fi
fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' '' \
'return getaddrinfo(0, 0, 0, 0);'
then
add_def HAVE_GETADDRINFO
fi
if [ $feat_asyncdns = "1" ] && \
test_code 'pthread' 'pthread.h' '-pthread' '' \
'return pthread_create((void *)1, NULL, (void *)1, NULL);'
then
add_def FEAT_ASYNCDNS
add_def USE_PTHREAD_ASYNCDNS
MYCFLAGS="$MYCFLAGS -pthread"
fi
timepps_h=""
if [ $feat_pps = "1" ]; then
if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
@@ -416,7 +448,7 @@ fi
if [ "x$timepps_h" != "x" ] && \
test_code 'PPSAPI' "string.h $timepps_h" '' '' '
pps_handle_t h;
pps_handle_t h = 0;
pps_info_t i;
struct timespec ts;
return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);'
@@ -432,7 +464,7 @@ if [ $feat_linuxcaps = "1" ] && [ $try_linuxcaps = "1" ] && \
'prctl(PR_SET_KEEPCAPS, 1);cap_set_proc(cap_from_text("cap_sys_time=ep"));'
then
add_def FEAT_LINUXCAPS
EXTRA_LIBS="-lcap"
EXTRA_LIBS="$EXTRA_LIBS -lcap"
fi
if [ $feat_rtc = "1" ] && [ $try_rtc = "1" ] && \
@@ -443,6 +475,22 @@ then
add_def FEAT_RTC
fi
if [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
test_code '<linux/ptp_clock.h>' 'sys/ioctl.h linux/ptp_clock.h' '' '' \
'ioctl(1, PTP_CLOCK_GETCAPS, 0);'
then
if test_code 'clock_gettime()' 'time.h' '' '' 'clock_gettime(0, NULL);'; then
add_def FEAT_PHC
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(0, NULL);'
then
EXTRA_LIBS="$EXTRA_LIBS -lrt"
add_def FEAT_PHC
fi
fi
fi
if [ $try_setsched = "1" ] && \
test_code \
'sched_setscheduler()' \
@@ -507,6 +555,8 @@ if [ $feat_readline = "1" ]; then
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi
fi
EXTRA_CLI_LIBS="$EXTRA_CLI_LIBS $READLINE_LINK"
fi
HASH_OBJ="hash_intmd5.o"
@@ -514,8 +564,8 @@ HASH_COMPILE=""
HASH_LINK=""
if [ $try_nss = "1" ]; then
test_cflags="`pkg-config --cflags nss`"
test_link="`pkg-config --libs-only-L nss` -lfreebl3"
test_cflags="`pkg-config --cflags nss 2> /dev/null`"
test_link="`pkg-config --libs-only-L nss 2> /dev/null` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
"$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
@@ -523,6 +573,7 @@ if [ $try_nss = "1" ]; then
HASH_OBJ="hash_nss.o"
HASH_COMPILE="$test_cflags"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
add_def GENERATE_SHA1_KEY
fi
fi
@@ -534,6 +585,7 @@ if [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
HASH_OBJ="hash_tomcrypt.o"
HASH_COMPILE="-I/usr/include/tomcrypt"
HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK"
add_def GENERATE_SHA1_KEY
fi
fi
@@ -593,7 +645,9 @@ if [ "x$SETCHRONYVARDIR" != "x" ]; then
CHRONYVARDIR=$SETCHRONYVARDIR
fi
add_def DEBUG $debug
add_def DEFAULT_CONF_FILE "\"$SYSCONFDIR/chrony.conf\""
add_def DEFAULT_USER "\"$default_user\""
add_def MAIL_PROGRAM "\"$mail_program\""
if [ -f version.txt ]; then
@@ -608,15 +662,13 @@ do
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
s%@CC@%${MYCC}%;\
s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${CPPFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LIBS@%${LIBS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
s%@READLINE_COMPILE@%${READLINE_COMPILE}%;\
s%@READLINE_LINK@%${READLINE_LINK}%;\
s%@HASH_OBJ@%${HASH_OBJ}%;\
s%@HASH_LINK@%${HASH_LINK}%;\
s%@HASH_COMPILE@%${HASH_COMPILE}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\
@@ -625,7 +677,8 @@ do
s%@MANDIR@%${MANDIR}%;\
s%@INFODIR@%${INFODIR}%;\
s%@LOCALSTATEDIR@%${LOCALSTATEDIR}%;\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;"\
s%@CHRONYVARDIR@%${CHRONYVARDIR}%;\
s%@DEFAULT_USER@%${default_user}%;"\
< ${f}.in > $f
done

35
faq.txt
View File

@@ -107,24 +107,6 @@ file. This configuration will be better because
* if your external connection goes down, the computers on the LAN will maintain
a common time with each other.
S: Addressing issues
Q: I get the following error message : "Could not get IP adress for localhost"
Add a line like the following to your /etc/hosts file
127.0.0.1 localhost
Q: I have problems if I put the names of my NTP servers in the chrony.conf file.
If you have no connection to the Internet at boot time, chrony won't be able to
turn the names into IP addresses when it starts. There seem to be 2 solutions:
1. Put the numeric IP addresses in the chrony.conf file
or
2. Put the server->IP address mappings in your /etc/hosts file and ensure that
/etc/host.conf reads 'order hosts,bind'.
The problem is that chronyd (currently) isn't designed in a way that allows
hostname->IP address lookups during normal operation. I hope to work on this
problem very soon.
S: My computer is not synchronising.
This is the most common problem. There are a number of reasons, see the
following questions.
@@ -153,23 +135,10 @@ arise. You should always make X quite high (e.g. 10) in this directive.
S: Issues with chronyc
Q: I keep getting the error '510 No command access from this host --- Reply not authenticated'.
Q: I keep getting the error '506 Cannot talk to daemon'.
Make sure that the chrony.conf file (on the computer where chronyd is running)
has a 'cmdallow' entry for the computer you are running chronyc on. This
shouldn't be necessary for localhost, but some people still seem to need an
entry like 'cmdallow 127.0.0.1'. (It would be good to understand why problem
only affects some people).
Q: I cannot log in from chronyc to carry out privileged tasks.
This is the second most common problem.
Perhaps your /etc/chrony.keys file is badly formatted. Make sure that the
final line has a line feed at the end, otherwise the key on that line will work
as though the last character is missing. (Note, this bug was fixed in version
1.16.)
Q: When I enter a command and hit &lt;Return&gt;, chronyc hangs
This probably means that chronyc cannot communicate with chronyd.
isn't necessary for localhost.
Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, 'ps
-auxw') to see if it's running. Or try 'netstat -a' and see if the ports

37
keys.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2013
* Copyright (C) Miroslav Lichvar 2012-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -260,7 +260,7 @@ KEY_Reload(void)
/* Check for duplicates */
for (i = 1; i < n_keys; i++) {
if (keys[i - 1].id == keys[i].id) {
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %lu", key_id);
LOG(LOGS_WARN, LOGF_Keys, "Detected duplicate key %lu", keys[i].id);
}
}
@@ -295,13 +295,20 @@ lookup_key(unsigned long id)
static int
get_key_pos(unsigned long key_id)
{
if (!cache_valid || key_id != cache_key_id) {
int position;
if (cache_valid && key_id == cache_key_id)
return cache_key_pos;
position = lookup_key(key_id);
if (position >= 0) {
cache_valid = 1;
cache_key_pos = lookup_key(key_id);
cache_key_pos = position;
cache_key_id = key_id;
}
return cache_key_pos;
return position;
}
/* ================================================== */
@@ -321,25 +328,7 @@ KEY_GetCommandKey(void)
int
KEY_KeyKnown(unsigned long key_id)
{
int position;
if (cache_valid && (key_id == cache_key_id)) {
return 1;
} else {
position = lookup_key(key_id);
if (position >= 0) {
/* Store key in cache, we will probably be using it in a
minute... */
cache_valid = 1;
cache_key_pos = position;
cache_key_id = key_id;
return 1;
} else {
return 0;
}
}
return get_key_pos(key_id) >= 0;
}
/* ================================================== */

78
local.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011
* Copyright (C) Miroslav Lichvar 2011, 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -126,11 +126,15 @@ calculate_sys_precision(void)
assert(best_dusec > 0);
precision_quantum = best_dusec * 1.0e-6;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
while (best_dusec < 500000) {
while (best_dusec < 707107) {
precision_log--;
best_dusec *= 2;
}
DEBUG_LOG(LOGF_Local, "Clock precision %.9f (%d)", precision_quantum, precision_log);
}
/* ================================================== */
@@ -248,6 +252,20 @@ void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *
/* ================================================== */
static void
invoke_parameter_change_handlers(struct timeval *raw, struct timeval *cooked,
double dfreq, double doffset,
LCL_ChangeType change_type)
{
ChangeListEntry *ptr;
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything);
}
}
/* ================================================== */
void
LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
{
@@ -369,7 +387,6 @@ LCL_ReadAbsoluteFrequency(void)
void
LCL_SetAbsoluteFrequency(double afreq_ppm)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
double dfreq;
@@ -382,15 +399,13 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
afreq_ppm = (*drv_set_freq)(afreq_ppm);
dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 + current_freq_ppm);
dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 - current_freq_ppm);
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(&raw, &cooked, dfreq, 0.0, 0, ptr->anything);
}
invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
current_freq_ppm = afreq_ppm;
@@ -401,7 +416,6 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void
LCL_AccumulateDeltaFrequency(double dfreq)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
double old_freq_ppm;
@@ -411,20 +425,17 @@ LCL_AccumulateDeltaFrequency(double dfreq)
are handled in units of ppm, whereas the 'dfreq' argument is in
terms of the gradient of the (offset) v (local time) function. */
current_freq_ppm = (1.0 + dfreq) * current_freq_ppm + 1.0e6 * dfreq;
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
/* Call the system-specific driver for setting the frequency */
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 + old_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
LCL_ReadRawTime(&raw);
LCL_CookTime(&raw, &cooked, NULL);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(&raw, &cooked, dfreq, 0.0, 0, ptr->anything);
}
invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
}
/* ================================================== */
@@ -432,7 +443,6 @@ LCL_AccumulateDeltaFrequency(double dfreq)
void
LCL_AccumulateOffset(double offset, double corr_rate)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
@@ -444,10 +454,7 @@ LCL_AccumulateOffset(double offset, double corr_rate)
(*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(&raw, &cooked, 0.0, offset, 0, ptr->anything);
}
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
}
/* ================================================== */
@@ -455,7 +462,6 @@ LCL_AccumulateOffset(double offset, double corr_rate)
void
LCL_ApplyStepOffset(double offset)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
/* In this case, the cooked time to be passed to the notify clients
@@ -467,10 +473,7 @@ LCL_ApplyStepOffset(double offset)
(*drv_apply_step_offset)(offset);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(&raw, &cooked, 0.0, offset, 1, ptr->anything);
}
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
}
/* ================================================== */
@@ -479,12 +482,8 @@ void
LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
double offset, double dispersion)
{
ChangeListEntry *ptr;
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(raw, cooked, 0.0, offset, 1, ptr->anything);
}
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
lcl_InvokeDispersionNotifyHandlers(dispersion);
}
@@ -508,16 +507,14 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* Work out new absolute frequency. Note that absolute frequencies
are handled in units of ppm, whereas the 'dfreq' argument is in
terms of the gradient of the (offset) v (local time) function. */
current_freq_ppm = (1.0 + dfreq) * old_freq_ppm + 1.0e6 * dfreq;
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
DEBUG_LOG(LOGF_Local, "old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
old_freq_ppm, current_freq_ppm, doffset);
#endif
/* Call the system-specific driver for setting the frequency */
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 + old_freq_ppm);
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
(*drv_accrue_offset)(doffset, corr_rate);
@@ -561,17 +558,15 @@ lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
current_freq_ppm = (*drv_read_freq)();
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
#endif
DEBUG_LOG(LOGF_Local, "Local freq=%.3fppm", current_freq_ppm);
}
/* ================================================== */
/* Look at the current difference between the system time and the NTP
time, and make a step to cancel it if it's larger than the threshold. */
time, and make a step to cancel it. */
int
LCL_MakeStep(double threshold)
LCL_MakeStep(void)
{
struct timeval raw;
double correction;
@@ -579,14 +574,11 @@ LCL_MakeStep(double threshold)
LCL_ReadRawTime(&raw);
LCL_GetOffsetCorrection(&raw, &correction, NULL);
if (fabs(correction) <= threshold)
return 0;
/* Cancel remaining slew and make the step */
LCL_AccumulateOffset(correction, 0.0);
LCL_ApplyStepOffset(-correction);
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.3f seconds", correction);
LOG(LOGS_WARN, LOGF_Local, "System clock was stepped by %.6f seconds", correction);
return 1;
}

14
local.h
View File

@@ -67,16 +67,22 @@ extern void LCL_GetOffsetCorrection(struct timeval *raw, double *correction, dou
doffset : delta offset applied (positive => make local system fast
by that amount, negative => make it slow by that amount)
is_step_change : true if change is being applied as a jump (using
settimeofday rather than adjtime)
change_type : what type of change is being applied
anything : Passthrough argument from call to registration routine */
typedef enum {
LCL_ChangeAdjust,
LCL_ChangeStep,
LCL_ChangeUnknownStep
} LCL_ChangeType;
typedef void (*LCL_ParameterChangeHandler)
(struct timeval *raw, struct timeval *cooked,
double dfreq,
double doffset, int is_step_change,
double doffset,
LCL_ChangeType change_type,
void *anything
);
@@ -183,7 +189,7 @@ extern void LCL_Finalise(void);
/* Routine to convert the outstanding system clock error to a step and
apply it, e.g. if the system clock has ended up an hour wrong due
to a timezone problem. */
extern int LCL_MakeStep(double threshold);
extern int LCL_MakeStep(void);
/* Routine to schedule a leap second. Leap second will be inserted
at the end of the day if argument is positive, deleted if negative,

145
logging.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012
* Copyright (C) Miroslav Lichvar 2011-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -34,6 +34,9 @@
#include "mkdirpp.h"
#include "util.h"
/* This is used by DEBUG_LOG macro */
int log_debug_enabled = 0;
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
@@ -42,7 +45,9 @@ static int system_log = 0;
static int parent_fd = 0;
static time_t last_limited = 0;
#define DEBUG_LEVEL_PRINT_FUNCTION 2
#define DEBUG_LEVEL_PRINT_DEBUG 2
static int debug_level = 0;
#ifdef WINNT
static FILE *logfile;
@@ -98,86 +103,97 @@ LOG_Finalise(void)
/* ================================================== */
void
LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...)
static void log_message(int fatal, LOG_Severity severity, const char *message)
{
char buf[2048];
va_list other_args;
va_start(other_args, format);
vsnprintf(buf, sizeof(buf), format, other_args);
va_end(other_args);
#ifdef WINNT
if (logfile) {
fprintf(logfile, "%s\n", buf);
fprintf(logfile, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
#else
if (system_log) {
int priority;
switch (severity) {
case LOGS_DEBUG:
priority = LOG_DEBUG;
break;
case LOGS_INFO:
syslog(LOG_INFO, "%s", buf);
priority = LOG_INFO;
break;
case LOGS_WARN:
syslog(LOG_WARNING, "%s", buf);
priority = LOG_WARNING;
break;
case LOGS_ERR:
default:
syslog(LOG_ERR, "%s", buf);
priority = LOG_ERR;
break;
case LOGS_FATAL:
priority = LOG_CRIT;
break;
default:
assert(0);
}
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else {
fprintf(stderr, "%s\n", buf);
fprintf(stderr, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
#endif
}
/* ================================================== */
void
LOG_Fatal_Function(LOG_Facility facility, const char *format, ...)
void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
const char *function_name, const char *format, ...)
{
char buf[2048];
va_list other_args;
time_t t;
struct tm stm;
#ifdef WINNT
#else
if (!system_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
fprintf(stderr, "%s ", buf);
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(stderr, "%s:%d:(%s) ", filename, line_number, function_name);
}
#endif
va_start(other_args, format);
vsnprintf(buf, sizeof(buf), format, other_args);
va_end(other_args);
#ifdef WINNT
if (logfile) {
fprintf(logfile, "Fatal error : %s\n", buf);
}
#else
if (system_log) {
syslog(LOG_CRIT, "Fatal error : %s", buf);
} else {
fprintf(stderr, "Fatal error : %s\n", buf);
}
if (parent_fd) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */
}
#endif
switch (severity) {
case LOGS_DEBUG:
case LOGS_INFO:
case LOGS_WARN:
case LOGS_ERR:
log_message(0, severity, buf);
break;
case LOGS_FATAL:
log_message(1, severity, buf);
exit(1);
}
/* With syslog, send the message also to the grandparent
process or write it to stderr if not detached */
if (system_log) {
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */
} else if (parent_fd == 0) {
system_log = 0;
log_message(1, severity, buf);
}
}
/* ================================================== */
exit(1);
void
LOG_Position(const char *filename, int line_number, const char *function_name)
{
#ifdef WINNT
#else
time_t t;
struct tm stm;
char buf[64];
if (!system_log) {
/* Don't clutter up syslog with internal debugging info */
time(&t);
stm = *gmtime(&t);
strftime(buf, sizeof(buf), "%d-%H:%M:%S", &stm);
fprintf(stderr, "%s:%d:(%s)[%s] ", filename, line_number, function_name, buf);
break;
default:
assert(0);
}
#endif
}
/* ================================================== */
@@ -194,6 +210,18 @@ LOG_OpenSystemLog(void)
/* ================================================== */
void LOG_SetDebugLevel(int level)
{
debug_level = level;
if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
if (!DEBUG)
LOG(LOGS_WARN, LOGF_Logging, "Not compiled with full debugging support");
log_debug_enabled = 1;
}
}
/* ================================================== */
void
LOG_SetParentFd(int fd)
{
@@ -207,22 +235,7 @@ LOG_CloseParentFd()
{
if (parent_fd > 0)
close(parent_fd);
}
/* ================================================== */
int
LOG_RateLimited(void)
{
time_t now;
now = time(NULL);
if (last_limited + 10 > now && last_limited <= now)
return 1;
last_limited = now;
return 0;
parent_fd = -1;
}
/* ================================================== */

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2013-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -28,11 +29,35 @@
#ifndef GOT_LOGGING_H
#define GOT_LOGGING_H
/* Flag indicating whether debug messages are logged */
extern int log_debug_enabled;
/* Line logging macros. If the compiler is GNU C, we take advantage of
being able to get the function name also. */
#ifdef __GNUC__
#define FUNCTION_NAME __FUNCTION__
#define FORMAT_ATTRIBUTE_PRINTF(str, first) __attribute__ ((format (printf, str, first)))
#else
#define FUNCTION_NAME ""
#define FORMAT_ATTRIBUTE_PRINTF(str, first)
#endif
#define DEBUG_LOG(facility, ...) \
do { \
if (DEBUG && log_debug_enabled) \
LOG_Message(LOGS_DEBUG, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
} while (0)
#define LOG(severity, facility, ...) LOG_Message(severity, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
#define LOG_FATAL(facility, ...) LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__)
/* Definition of severity */
typedef enum {
LOGS_INFO,
LOGS_WARN,
LOGS_ERR
LOGS_ERR,
LOGS_FATAL,
LOGS_DEBUG
} LOG_Severity;
/* Definition of facility. Each message is tagged with who generated
@@ -56,14 +81,17 @@ typedef enum {
LOGF_Manual,
LOGF_Keys,
LOGF_Logging,
LOGF_Nameserv,
LOGF_Rtc,
LOGF_Regress,
LOGF_Sys,
LOGF_SysGeneric,
LOGF_SysLinux,
LOGF_SysNetBSD,
LOGF_SysSolaris,
LOGF_SysSunOS,
LOGF_SysWinnt,
LOGF_TempComp,
LOGF_RtcLinux,
LOGF_Refclock
} LOG_Facility;
@@ -75,13 +103,17 @@ extern void LOG_Initialise(void);
extern void LOG_Finalise(void);
/* Line logging function */
extern void LOG_Line_Function(LOG_Severity severity, LOG_Facility facility, const char *format, ...);
FORMAT_ATTRIBUTE_PRINTF(6, 7)
extern void LOG_Message(LOG_Severity severity, LOG_Facility facility,
int line_number, const char *filename,
const char *function_name, const char *format, ...);
/* Logging function for fatal errors */
extern void LOG_Fatal_Function(LOG_Facility facility, const char *format, ...);
/* Position in code reporting function */
extern void LOG_Position(const char *filename, int line_number, const char *function_name);
/* Set debug level:
0, 1 - only non-debug messages are logged
2 - debug messages are logged too, all messages are prefixed with
filename, line, and function name
*/
extern void LOG_SetDebugLevel(int level);
/* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void);
@@ -92,24 +124,13 @@ extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */
extern void LOG_CloseParentFd(void);
/* Return zero once per 10 seconds */
extern int LOG_RateLimited(void);
/* Line logging macro. If the compiler is GNU C, we take advantage of
being able to get the function name also. */
#if defined(__GNUC__)
#define LOG LOG_Position(__FILE__, __LINE__, __FUNCTION__); LOG_Line_Function
#define LOG_FATAL LOG_Position(__FILE__, __LINE__, __FUNCTION__); LOG_Fatal_Function
#else
#define LOG LOG_Position(__FILE__, __LINE__, ""); LOG_Line_Function
#define LOG_FATAL LOG_Position(__FILE__, __LINE__, ""); LOG_Fatal_Function
#endif /* defined (__GNUC__) */
/* File logging functions */
typedef int LOG_FileID;
extern LOG_FileID LOG_FileOpen(const char *name, const char *banner);
FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_FileWrite(LOG_FileID id, const char *format, ...);
extern void LOG_CreateLogFileDir(void);

137
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012
* Copyright (C) Miroslav Lichvar 2012-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -44,7 +44,6 @@
#include "conf.h"
#include "cmdmon.h"
#include "keys.h"
#include "acquire.h"
#include "manual.h"
#include "rtc.h"
#include "refclock.h"
@@ -60,10 +59,12 @@
static int initialised = 0;
/* ================================================== */
static int exit_status = 0;
static int reload = 0;
static REF_Mode ref_mode = REF_ModeNormal;
/* ================================================== */
static void
@@ -79,7 +80,7 @@ delete_pidfile(void)
void
MAI_CleanupAndExit(void)
{
if (!initialised) exit(0);
if (!initialised) exit(exit_status);
if (CNF_GetDumpOnExit()) {
SRC_DumpSources();
@@ -87,7 +88,6 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
ACQ_Finalise();
CLG_Finalise();
NSR_Finalise();
NCR_Finalise();
@@ -108,7 +108,7 @@ MAI_CleanupAndExit(void)
LOG_Finalise();
exit(0);
exit(exit_status);
}
/* ================================================== */
@@ -123,13 +123,10 @@ signal_cleanup(int x)
/* ================================================== */
static void
post_acquire_hook(void *anything)
ntp_source_resolving_end(void)
{
/* Close the pipe to the foreground process so it can exit */
LOG_CloseParentFd();
NSR_SetSourceResolvingEndHandler(NULL);
CNF_AddSources();
CNF_AddBroadcasts();
if (reload) {
/* Note, we want reload to come well after the initialisation from
the real time clock - this gives us a fighting chance that the
@@ -137,10 +134,62 @@ post_acquire_hook(void *anything)
semblence of validity about it. */
SRC_ReloadSources();
}
CNF_SetupAccessRestrictions();
RTC_StartMeasurements();
RCL_StartRefclocks();
NSR_StartSources();
NSR_AutoStartSources();
/* Special modes can end only when sources update their reachability.
Give up immediatelly if there are no active sources. */
if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
REF_SetUnsynchronised();
}
}
/* ================================================== */
static void
post_init_ntp_hook(void *anything)
{
if (ref_mode == REF_ModeInitStepSlew) {
/* Remove the initstepslew sources and set normal mode */
NSR_RemoveAllSources();
ref_mode = REF_ModeNormal;
REF_SetMode(ref_mode);
}
/* Close the pipe to the foreground process so it can exit */
LOG_CloseParentFd();
CNF_AddSources();
CNF_AddBroadcasts();
NSR_SetSourceResolvingEndHandler(ntp_source_resolving_end);
NSR_ResolveSources();
}
/* ================================================== */
static void
reference_mode_end(int result)
{
switch (ref_mode) {
case REF_ModeNormal:
case REF_ModeUpdateOnce:
case REF_ModePrintOnce:
exit_status = !result;
SCH_QuitProgram();
break;
case REF_ModeInitStepSlew:
/* Switch to the normal mode, the delay is used to prevent polling
interval shorter than the burst interval if some configured servers
were used also for initstepslew */
SCH_AddTimeoutByDelay(2.0, post_init_ntp_hook, NULL);
break;
default:
assert(0);
}
}
/* ================================================== */
@@ -148,7 +197,14 @@ post_acquire_hook(void *anything)
static void
post_init_rtc_hook(void *anything)
{
CNF_ProcessInitStepSlew(post_acquire_hook, NULL);
if (CNF_GetInitSources() > 0) {
CNF_AddInitSources();
NSR_StartSources();
assert(REF_GetMode() != REF_ModeNormal);
/* Wait for mode end notification */
} else {
(post_init_ntp_hook)(NULL);
}
}
/* ================================================== */
@@ -200,7 +256,7 @@ write_lockfile(void)
out = fopen(pidfile, "w");
if (!out) {
LOG(LOGS_ERR, LOGF_Main, "could not open lockfile %s for writing", pidfile);
LOG_FATAL(LOGF_Main, "could not open lockfile %s for writing", pidfile);
} else {
fprintf(out, "%d\n", getpid());
fclose(out);
@@ -222,14 +278,14 @@ go_daemon(void)
/* Create pipe which will the daemon use to notify the grandparent
when it's initialised or send an error message */
if (pipe(pipefd)) {
LOG(LOGS_ERR, LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno));
LOG_FATAL(LOGF_Logging, "Could not detach, pipe failed : %s", strerror(errno));
}
/* Does this preserve existing signal handlers? */
pid = fork();
if (pid < 0) {
LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'grandparent' */
char message[1024];
@@ -254,7 +310,7 @@ go_daemon(void)
pid = fork();
if (pid < 0) {
LOG(LOGS_ERR, LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
LOG_FATAL(LOGF_Logging, "Could not detach, fork failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
} else {
@@ -262,7 +318,7 @@ go_daemon(void)
/* Change current directory to / */
if (chdir("/") < 0) {
LOG(LOGS_ERR, LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
LOG_FATAL(LOGF_Logging, "Could not chdir to / : %s", strerror(errno));
}
/* Don't keep stdin/out/err from before. But don't close
@@ -290,6 +346,8 @@ int main
int do_init_rtc = 0, restarted = 0;
int other_pid;
int lock_memory = 0, sched_priority = 0;
int system_log = 1;
int config_args = 0;
LOG_Initialise();
@@ -326,14 +384,27 @@ int main
} else if (!strcmp("-n", *argv)) {
nofork = 1;
} else if (!strcmp("-d", *argv)) {
debug = 1;
debug++;
nofork = 1;
system_log = 0;
} else if (!strcmp("-q", *argv)) {
ref_mode = REF_ModeUpdateOnce;
nofork = 1;
system_log = 0;
} else if (!strcmp("-Q", *argv)) {
ref_mode = REF_ModePrintOnce;
nofork = 1;
system_log = 0;
} else if (!strcmp("-4", *argv)) {
address_family = IPADDR_INET4;
} else if (!strcmp("-6", *argv)) {
address_family = IPADDR_INET6;
} else {
} else if (*argv[0] == '-') {
LOG_FATAL(LOGF_Main, "Unrecognized command line option [%s]", *argv);
} else {
/* Process remaining arguments and configuration lines */
config_args = argc;
break;
}
}
@@ -348,16 +419,26 @@ int main
go_daemon();
}
if (!debug) {
if (system_log) {
LOG_OpenSystemLog();
}
LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, LOGF_Main, "chronyd version %s starting", CHRONY_VERSION);
DNS_SetAddressFamily(address_family);
CNF_SetRestarted(restarted);
CNF_ReadFile(conf_file);
/* Parse the config file or the remaining command line arguments */
if (!config_args) {
CNF_ReadFile(conf_file);
} else {
do {
CNF_ParseLine(NULL, config_args - argc + 1, *argv);
} while (++argv, --argc);
}
/* Check whether another chronyd may already be running. Do this after
* forking, so that message logging goes to the right place (i.e. syslog), in
@@ -400,7 +481,7 @@ int main
if (!user) {
user = CNF_GetUser();
}
if (user) {
if (user && strcmp(user, "root")) {
SYS_DropRoot(user);
}
@@ -412,13 +493,21 @@ int main
NCR_Initialise();
NSR_Initialise();
CLG_Initialise();
ACQ_Initialise();
MNL_Initialise();
TMC_Initialise();
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
CNF_SetupAccessRestrictions();
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew;
}
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
if (do_init_rtc) {
RTC_TimeInit(post_init_rtc_hook, NULL);
} else {

View File

@@ -34,7 +34,7 @@ echo $version > version.txt
sed -e "s%@@VERSION@@%${version}%" < chrony.spec.sample > chrony.spec
for m in chrony.1.in chronyc.1.in chrony.conf.5.in chronyd.8.in; do
for m in chrony.1 chronyc.1.in chrony.conf.5.in chronyd.8.in; do
sed -e "s%@VERSION@%${version}%;s%@MAN_DATE@%${mandate}%" \
< $m > ${m}_
mv -f ${m}_ $m
@@ -45,10 +45,10 @@ mv chrony.txt chrony.txt_
make distclean
mv chrony.txt_ chrony.txt
rm -f faqgen.pl make_release chrony.spec.sample .gitignore
rm -f config.h config.log faqgen.pl make_release chrony.spec.sample .gitignore
cd ..
tar cvf - $subdir | gzip -9 > ${subdir}.tar.gz
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz
gpg -b -a -o ${subdir}-tar-gz-asc.txt ${subdir}.tar.gz

View File

@@ -59,13 +59,6 @@ typedef struct {
static Sample samples[16];
static int n_samples;
static int replace_margin;
static int error;
/* Eventually these constants need to be user-defined in conf file */
#define REPLACE_MARGIN 300
#define ERROR_MARGIN 0.2
/* ================================================== */
static void
@@ -73,7 +66,7 @@ slew_samples(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *not_used);
/* ================================================== */
@@ -89,9 +82,6 @@ MNL_Initialise(void)
n_samples = 0;
replace_margin = REPLACE_MARGIN;
error = ERROR_MARGIN;
LCL_AddParameterChangeHandler(slew_samples, NULL);
}
@@ -226,11 +216,16 @@ slew_samples(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *not_used)
{
double delta_time;
int i;
if (change_type == LCL_ChangeUnknownStep) {
MNL_Reset();
}
for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(&samples[i].when, cooked, &samples[i].when, &delta_time,
dfreq, doffset);

View File

@@ -46,7 +46,7 @@ DNS_SetAddressFamily(int family)
DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *addr)
{
#ifdef HAVE_IPV6
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai;
int result;

144
nameserv_async.c Normal file
View File

@@ -0,0 +1,144 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Functions to asynchronously convert name to IP address
*/
#include "config.h"
#include "sysincl.h"
#include "nameserv_async.h"
#include "logging.h"
#include "memory.h"
#include "sched.h"
#include "util.h"
#ifdef FEAT_ASYNCDNS
#ifdef USE_PTHREAD_ASYNCDNS
#include <pthread.h>
/* ================================================== */
struct DNS_Async_Instance {
const char *name;
DNS_Status status;
IPAddr addr;
DNS_NameResolveHandler handler;
void *arg;
pthread_t thread;
int pipe[2];
};
static int resolving_threads = 0;
/* ================================================== */
static void *
start_resolving(void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
inst->status = DNS_Name2IPAddress(inst->name, &inst->addr);
/* Notify the main thread that the result is ready */
if (write(inst->pipe[1], "", 1) < 0)
;
return NULL;
}
/* ================================================== */
static void
end_resolving(void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
if (pthread_join(inst->thread, NULL)) {
LOG_FATAL(LOGF_Nameserv, "pthread_join() failed");
}
resolving_threads--;
SCH_RemoveInputFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
(inst->handler)(inst->status, &inst->addr, inst->arg);
Free(inst);
}
/* ================================================== */
void
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
{
struct DNS_Async_Instance *inst;
inst = MallocNew(struct DNS_Async_Instance);
inst->name = name;
inst->handler = handler;
inst->arg = anything;
inst->status = DNS_Failure;
if (pipe(inst->pipe)) {
LOG_FATAL(LOGF_Nameserv, "pipe() failed");
}
resolving_threads++;
assert(resolving_threads <= 1);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL(LOGF_Nameserv, "pthread_create() failed");
}
SCH_AddInputFileHandler(inst->pipe[0], end_resolving, inst);
}
/* ================================================== */
#else
#error
#endif
#else
/* This is a blocking implementation used when nothing else is available */
void
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
{
IPAddr addr;
DNS_Status status;
status = DNS_Name2IPAddress(name, &addr);
(handler)(status, &addr, anything);
}
/* ================================================== */
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -21,25 +21,21 @@
=======================================================================
Header file for acquisition module
Header for asynchronous nameserver functions
*/
#ifndef GOT_ACQUIRE_H
#define GOT_ACQUIRE_H
#include "addressing.h"
#ifndef GOT_NAMESERV_ASYNC_H
#define GOT_NAMESERV_ASYNC_H
typedef struct ACQ_SourceRecord *ACQ_Source;
#include "nameserv.h"
extern void ACQ_Initialise(void);
/* Function type for callback to process the result */
typedef void (*DNS_NameResolveHandler)(DNS_Status status, IPAddr *ip_addr, void *anything);
extern void ACQ_Finalise(void);
/* Request resolving of a name to IP address. The handler will be
called when the result is available, but it may be also called
directly from this function call. */
extern void DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything);
extern void ACQ_StartAcquisition(int n, IPAddr *ip_addrs, double init_slew_threshold,
void (*after_hook)(void *), void *anything);
extern void ACQ_AccumulateSample(ACQ_Source acq_source, double offset, double root_distance);
extern void ACQ_MissedSample(ACQ_Source acq_source);
#endif /* GOT_ACQUIRE_H */
#endif

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2013
* Copyright (C) Miroslav Lichvar 2009-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -47,11 +47,6 @@
static LOG_FileID logfileid;
/* ================================================== */
/* Day number of 1 Jan 1970 */
#define MJD_1970 40587
/* ================================================== */
/* Enumeration used for remembering the operating mode of one of the
sources */
@@ -69,6 +64,7 @@ typedef enum {
struct NCR_Instance_Record {
NTP_Remote_Address remote_addr; /* Needed for routing transmit packets */
NTP_Local_Address local_addr; /* Local address/socket used to send packets */
NTP_Mode mode; /* The source's NTP mode
(client/server or symmetric active peer) */
OperatingMode opmode; /* Whether we are sampling this source
@@ -77,6 +73,7 @@ struct NCR_Instance_Record {
pending to transmit to the source */
SCH_TimeoutID timeout_id; /* Scheduler's timeout ID, if we are
running on a timer. */
int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_offline; /* If 1, automatically go offline if server/peer
isn't responding */
@@ -188,7 +185,11 @@ struct NCR_Instance_Record {
/* Time to wait before retransmitting in burst mode, if we did not get
a reply to the previous probe */
#define BURST_TIMEOUT 8.0
#define BURST_TIMEOUT 2.0
/* Number of samples in initial burst */
#define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
/* Time to wait after sending echo to 'warm up' link */
#define WARM_UP_DELAY 4.0
@@ -212,6 +213,14 @@ struct NCR_Instance_Record {
/* INVALID or Unkown stratum from external server as per the NTP 4 docs */
#define NTP_INVALID_STRATUM 0
/* Minimum allowed poll interval */
#define MIN_POLL 0
/* Maximum poll interval set by KoD RATE */
#define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
#define INVALID_SOCK_FD -1
/* ================================================== */
static ADF_AuthTable access_auth_table;
@@ -247,13 +256,36 @@ NCR_Finalise(void)
static void
start_initial_timeout(NCR_Instance inst)
{
/* Check if we can transmit */
if (inst->tx_suspended) {
assert(!inst->timer_running);
return;
}
/* Start timer for first transmission */
/* Stop old timer if running */
if (inst->timer_running)
SCH_RemoveTimeout(inst->timeout_id);
/* Start new timer for transmission */
inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
inst->timer_running = 1;
if (!inst->timer_running) {
/* This will be the first transmission after mode change */
inst->timer_running = 1;
/* Mark source active */
SRC_SetActive(inst->source);
/* Open client socket */
if (inst->mode == MODE_CLIENT) {
assert(inst->local_addr.sock_fd == INVALID_SOCK_FD);
inst->local_addr.sock_fd = NIO_GetClientSocket(&inst->remote_addr);
}
}
}
/* ================================================== */
@@ -269,6 +301,17 @@ take_offline(NCR_Instance inst)
/* Mark source unreachable */
SRC_ResetReachability(inst->source);
/* And inactive */
SRC_UnsetActive(inst->source);
/* Close client socket */
if (inst->mode == MODE_CLIENT && inst->local_addr.sock_fd != INVALID_SOCK_FD) {
NIO_CloseClientSocket(inst->local_addr.sock_fd);
inst->local_addr.sock_fd = INVALID_SOCK_FD;
}
NCR_ResetInstance(inst);
}
/* ================================================== */
@@ -281,11 +324,16 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result = MallocNew(struct NCR_Instance_Record);
result->remote_addr = *remote_addr;
result->local_addr.ip_addr.family = IPADDR_UNSPEC;
switch (type) {
case NTP_SERVER:
/* Client socket will be obtained when timer is started */
result->local_addr.sock_fd = INVALID_SOCK_FD;
result->mode = MODE_CLIENT;
break;
case NTP_PEER:
result->local_addr.sock_fd = NIO_GetServerSocket(remote_addr);
result->mode = MODE_ACTIVE;
break;
default:
@@ -293,11 +341,22 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
}
result->minpoll = params->minpoll;
if (result->minpoll < MIN_POLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
result->maxpoll = params->maxpoll;
result->min_stratum = params->min_stratum;
if (result->maxpoll < MIN_POLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL;
if (result->maxpoll < result->minpoll)
result->maxpoll = result->minpoll;
result->min_stratum = params->min_stratum;
result->presend_minpoll = params->presend_minpoll;
result->presend_done = 0;
result->max_delay = params->max_delay;
result->max_delay_ratio = params->max_delay_ratio;
result->max_delay_dev_ratio = params->max_delay_dev_ratio;
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
if (params->authkey == INACTIVE_AUTHKEY) {
result->do_auth = 0;
@@ -305,51 +364,27 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
} else {
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 %lu",
UTI_IPToString(&result->remote_addr.ip_addr), result->auth_key_id);
}
}
result->max_delay = params->max_delay;
result->max_delay_ratio = params->max_delay_ratio;
result->max_delay_dev_ratio = params->max_delay_dev_ratio;
result->tx_count = 0;
result->remote_orig.hi = 0;
result->remote_orig.lo = 0;
result->poll_target = params->poll_target;
result->poll_score = 0.0;
if (params->online) {
start_initial_timeout(result);
result->opmode = MD_ONLINE;
} else {
result->timer_running = 0;
result->timeout_id = 0;
result->opmode = MD_OFFLINE;
}
if (params->iburst) {
NCR_InitiateSampleBurst(result, 4, 8);
}
result->auto_offline = params->auto_offline;
result->local_poll = params->minpoll;
result->remote_poll = 0;
result->remote_stratum = 0;
/* Create a source instance for this NTP source */
result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr), SRC_NTP, params->sel_option, &result->remote_addr.ip_addr);
result->local_rx.tv_sec = 0;
result->local_rx.tv_usec = 0;
result->local_tx.tv_sec = 0;
result->local_tx.tv_usec = 0;
result->local_ntp_tx.hi = 0;
result->local_ntp_tx.lo = 0;
result->timer_running = 0;
result->timeout_id = 0;
result->tx_suspended = 1;
result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
NCR_ResetInstance(result);
if (params->iburst) {
NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
}
return result;
}
/* ================================================== */
@@ -358,23 +393,53 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
void
NCR_DestroyInstance(NCR_Instance instance)
{
if (instance->opmode != MD_OFFLINE)
take_offline(instance);
/* This will destroy the source instance inside the
structure, which will cause reselection if this was the
synchronising source etc. */
SRC_DestroyInstance(instance->source);
/* Cancel any pending timeouts */
if (instance->timer_running) {
SCH_RemoveTimeout(instance->timeout_id);
instance->timer_running = 0;
}
/* Free the data structure */
Free(instance);
}
/* ================================================== */
void
NCR_StartInstance(NCR_Instance instance)
{
instance->tx_suspended = 0;
if (instance->opmode != MD_OFFLINE)
start_initial_timeout(instance);
}
/* ================================================== */
void
NCR_ResetInstance(NCR_Instance instance)
{
instance->tx_count = 0;
instance->presend_done = 0;
instance->local_poll = instance->minpoll;
instance->poll_score = 0.0;
instance->remote_poll = 0;
instance->remote_stratum = 0;
instance->remote_orig.hi = 0;
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;
}
/* ================================================== */
static int
check_packet_auth(NTP_Packet *pkt, unsigned long keyid, int auth_len)
{
@@ -520,8 +585,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
case MD_BURST_WAS_ONLINE:
case MD_BURST_WAS_OFFLINE:
/* With burst, the timeout for new transmit after valid reply is shorter
than the timeout without reply */
/* Burst modes */
delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL;
break;
default:
@@ -551,7 +615,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
(including adjustment to
reference), ignored if
NULL */
NTP_Remote_Address *where_to /* Where to address the reponse to */
NTP_Remote_Address *where_to, /* Where to address the reponse to */
NTP_Local_Address *from /* From what address to send it */
)
{
NTP_Packet message;
@@ -639,12 +704,17 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
(unsigned char *)&message.auth_data, sizeof (message.auth_data));
if (auth_len > 0) {
message.auth_keyid = htonl(key_id);
NIO_SendAuthenticatedPacket(&message, where_to,
NIO_SendAuthenticatedPacket(&message, where_to, from,
sizeof (message.auth_keyid) + auth_len);
} else {
DEBUG_LOG(LOGF_NtpCore,
"Could not generate auth data with key %lu to send packet",
key_id);
return;
}
} else {
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
NIO_SendNormalPacket(&message, where_to);
NIO_SendNormalPacket(&message, where_to, from);
}
if (local_tx) {
@@ -665,7 +735,6 @@ transmit_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance) arg;
double timeout_delay;
int do_auth;
inst->timer_running = 0;
@@ -686,10 +755,8 @@ transmit_timeout(void *arg)
return;
}
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_NtpCore, "Transmit timeout for [%s:%d]",
DEBUG_LOG(LOGF_NtpCore, "Transmit timeout for [%s:%d]",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->remote_addr.port);
#endif
/* Check whether we need to 'warm up' the link to the other end by
sending an echo exchange to ensure both ends' ARP caches are
@@ -701,7 +768,7 @@ transmit_timeout(void *arg)
!inst->presend_done) {
/* Send */
NIO_SendEcho(&inst->remote_addr);
NIO_SendEcho(&inst->remote_addr, &inst->local_addr);
inst->presend_done = 1;
@@ -735,18 +802,13 @@ transmit_timeout(void *arg)
}
}
if (inst->do_auth && KEY_KeyKnown(inst->auth_key_id)) {
do_auth = 1;
} else {
do_auth = 0;
}
transmit_packet(inst->mode, inst->local_poll,
NTP_VERSION,
do_auth, inst->auth_key_id,
inst->do_auth, inst->auth_key_id,
&inst->remote_orig,
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
&inst->remote_addr);
&inst->remote_addr,
&inst->local_addr);
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
@@ -998,11 +1060,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
if (inst->do_auth) {
if (auth_len > 0) {
auth_key_id = ntohl(message->auth_keyid);
if (!KEY_KeyKnown(auth_key_id)) {
test5 = 0;
} else {
test5 = check_packet_auth(message, auth_key_id, auth_len);
}
test5 = check_packet_auth(message, auth_key_id, auth_len);
} else {
/* If we expect authenticated info from this peer/server and the packet
doesn't have it, it's got to fail */
@@ -1080,55 +1138,61 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
root_delay = pkt_root_delay + fabs(delta);
root_dispersion = pkt_root_dispersion + epsilon;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_NtpCore, "lvm=%o stratum=%d poll=%d prec=%d",
DEBUG_LOG(LOGF_NtpCore, "lvm=%o stratum=%d poll=%d prec=%d",
message->lvm, message->stratum, message->poll, message->precision);
LOG(LOGS_INFO, LOGF_NtpCore, "Root delay=%08lx (%f), dispersion=%08lx (%f)",
DEBUG_LOG(LOGF_NtpCore, "Root delay=%08x (%f), dispersion=%08x (%f)",
message->root_delay, pkt_root_delay, message->root_dispersion, pkt_root_dispersion);
LOG(LOGS_INFO, LOGF_NtpCore, "Ref id=[%lx], ref_time=%08lx.%08lx [%s]",
DEBUG_LOG(LOGF_NtpCore, "Ref id=[%x], ref_time=%08x.%08x [%s]",
ntohl(message->reference_id),
message->reference_ts.hi, message->reference_ts.lo,
UTI_TimestampToString(&message->reference_ts));
LOG(LOGS_INFO, LOGF_NtpCore, "Originate=%08lx.%08lx [%s]",
DEBUG_LOG(LOGF_NtpCore, "Originate=%08x.%08x [%s]",
message->originate_ts.hi, message->originate_ts.lo,
UTI_TimestampToString(&message->originate_ts));
LOG(LOGS_INFO, LOGF_NtpCore, "Message receive=%08lx.%08lx [%s]",
DEBUG_LOG(LOGF_NtpCore, "Message receive=%08x.%08x [%s]",
message->receive_ts.hi, message->receive_ts.lo,
UTI_TimestampToString(&message->receive_ts));
LOG(LOGS_INFO, LOGF_NtpCore, "Transmit=%08lx.%08lx [%s]",
DEBUG_LOG(LOGF_NtpCore, "Transmit=%08x.%08x [%s]",
message->transmit_ts.hi, message->transmit_ts.lo,
UTI_TimestampToString(&message->transmit_ts));
LOG(LOGS_INFO, LOGF_NtpCore, "theta=%f delta=%f epsilon=%f root_delay=%f root_dispersion=%f",
DEBUG_LOG(LOGF_NtpCore, "theta=%f delta=%f epsilon=%f root_delay=%f root_dispersion=%f",
theta, delta, epsilon, root_delay, root_dispersion);
LOG(LOGS_INFO, LOGF_NtpCore, "test1=%d test2=%d test3=%d test4=%d valid_data=%d good_data=%d",
DEBUG_LOG(LOGF_NtpCore, "test1=%d test2=%d test3=%d test4=%d valid_data=%d good_data=%d",
test1, test2, test3, test4, valid_data, good_data);
LOG(LOGS_INFO, LOGF_NtpCore, "test5=%d test6=%d test7=%d test8=%d valid_header=%d good_header=%d",
DEBUG_LOG(LOGF_NtpCore, "test5=%d test6=%d test7=%d test8=%d valid_header=%d good_header=%d",
test5, test6, test7, test8, valid_header, good_header);
LOG(LOGS_INFO, LOGF_NtpCore, "kod_rate=%d valid_kod=%d", kod_rate, valid_kod);
#endif
DEBUG_LOG(LOGF_NtpCore, "kod_rate=%d valid_kod=%d", kod_rate, valid_kod);
/* Reduce polling rate if KoD RATE was received */
if (kod_rate && valid_kod) {
if (message->poll > inst->minpoll) {
inst->minpoll = message->poll;
/* 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 from %s, minpoll set to %d",
UTI_IPToString(&inst->remote_addr.ip_addr), 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;
LOG(LOGS_WARN, LOGF_NtpCore, "Received KoD RATE from %s, burst sampling stopped",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->minpoll);
UTI_IPToString(&inst->remote_addr.ip_addr));
}
requeue_transmit = 1;
@@ -1161,6 +1225,8 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
root_delay, root_dispersion,
message->stratum, (NTP_Leap) pkt_leap);
SRC_SelectSource(inst->source);
/* Now examine the registers. First though, if the prediction is
not even within +/- the peer distance of the peer, we are clearly
not tracking the peer at all well, so we back off the sampling
@@ -1259,18 +1325,24 @@ NCR_ProcessKnown
struct timeval *now, /* timestamp at time of receipt */
double now_err,
NCR_Instance inst, /* the instance record for this peer/server */
int sock_fd, /* the receiving socket */
int length /* the length of the received packet */
)
{
int pkt_mode;
int version;
int valid_auth, valid_key;
int authenticate_reply, auth_len;
unsigned long auth_key_id;
unsigned long reply_auth_key_id;
int auth_len;
/* Make sure the packet was received by the sending socket */
if (sock_fd != inst->local_addr.sock_fd) {
DEBUG_LOG(LOGF_NtpCore,
"Packet received by wrong socket %d (expected %d)",
sock_fd, inst->local_addr.sock_fd);
return;
}
/* Ignore packets from offline sources */
if (inst->opmode == MD_OFFLINE) {
if (inst->opmode == MD_OFFLINE || inst->tx_suspended) {
return;
}
@@ -1309,45 +1381,8 @@ NCR_ProcessKnown
one of the secondaries to flywheel it. The behaviour coded here
is required in the secondaries to make this possible. */
if (ADF_IsAllowed(access_auth_table, &inst->remote_addr.ip_addr)) {
CLG_LogNTPClientAccess(&inst->remote_addr.ip_addr, (time_t) now->tv_sec);
if (auth_len > 0) {
auth_key_id = ntohl(message->auth_keyid);
valid_key = KEY_KeyKnown(auth_key_id);
if (valid_key) {
valid_auth = check_packet_auth(message, auth_key_id, auth_len);
} else {
valid_auth = 0;
}
if (valid_key && valid_auth) {
authenticate_reply = 1;
reply_auth_key_id = auth_key_id;
} else {
authenticate_reply = 0;
reply_auth_key_id = 0UL;
}
} else {
authenticate_reply = 0;
reply_auth_key_id = 0UL;
}
transmit_packet(MODE_SERVER, inst->local_poll,
version,
authenticate_reply, reply_auth_key_id,
&message->transmit_ts,
now,
&inst->local_tx,
&inst->local_ntp_tx,
&inst->remote_addr);
} else if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
UTI_IPToString(&inst->remote_addr.ip_addr),
inst->remote_addr.port);
}
NCR_ProcessUnknown(message, now, now_err,
&inst->remote_addr, &inst->local_addr, length);
break;
@@ -1452,7 +1487,7 @@ NCR_ProcessKnown
/* ================================================== */
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
and it relates to a source we don't know (not our server or peer) */
void
NCR_ProcessUnknown
@@ -1460,15 +1495,23 @@ NCR_ProcessUnknown
struct timeval *now, /* timestamp at time of receipt */
double now_err, /* assumed error in the timestamp */
NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr,
int length /* the length of the received packet */
)
{
NTP_Mode his_mode;
NTP_Mode my_mode;
int my_poll, version;
int valid_key, valid_auth, auth_len;
int valid_auth, auth_len;
unsigned long key_id;
/* Ignore the packet if it wasn't received by server socket */
if (!NIO_IsServerSocket(local_addr->sock_fd)) {
DEBUG_LOG(LOGF_NtpCore, "NTP request packet received by client socket %d",
local_addr->sock_fd);
return;
}
/* Check version */
version = (message->lvm >> 3) & 0x7;
if (version < NTP_MIN_COMPAT_VERSION || version > NTP_MAX_COMPAT_VERSION) {
@@ -1505,18 +1548,13 @@ NCR_ProcessUnknown
/* Only reply if we know the key and the packet authenticates
properly. */
key_id = ntohl(message->auth_keyid);
valid_key = KEY_KeyKnown(key_id);
do_auth = 1;
if (valid_key) {
valid_auth = check_packet_auth(message, key_id, auth_len);
} else {
valid_auth = 0;
}
valid_auth = check_packet_auth(message, key_id, auth_len);
}
if (!do_auth || (valid_key && valid_auth)) {
my_poll = message->poll; /* What should this be set to? Does the client actually care? */
if (!do_auth || valid_auth) {
/* Reply with the same poll, the client may use it to control its poll */
my_poll = message->poll;
transmit_packet(my_mode, my_poll,
version,
@@ -1525,11 +1563,12 @@ NCR_ProcessUnknown
now, /* Time we received the packet */
NULL, /* Don't care when we send reply, we aren't maintaining state about this client */
NULL, /* Ditto */
remote_addr);
remote_addr,
local_addr);
}
}
} else if (!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
} else {
DEBUG_LOG(LOGF_NtpCore, "NTP packet received from unauthorised host %s port %d",
UTI_IPToString(&remote_addr->ip_addr),
remote_addr->port);
}
@@ -1545,19 +1584,13 @@ NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doff
prev = inst->local_rx;
if (inst->local_rx.tv_sec || inst->local_rx.tv_usec)
UTI_AdjustTimeval(&inst->local_rx, when, &inst->local_rx, &delta, dfreq, doffset);
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_NtpCore, "rx prev=[%s] new=[%s]",
DEBUG_LOG(LOGF_NtpCore, "rx prev=[%s] new=[%s]",
UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_rx));
#endif
prev = inst->local_tx;
if (inst->local_tx.tv_sec || inst->local_tx.tv_usec)
UTI_AdjustTimeval(&inst->local_tx, when, &inst->local_tx, &delta, dfreq, doffset);
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_NtpCore, "tx prev=[%s] new=[%s]",
DEBUG_LOG(LOGF_NtpCore, "tx prev=[%s] new=[%s]",
UTI_TimevalToString(&prev), UTI_TimevalToString(&inst->local_tx));
#else
(void)prev;
#endif
}
/* ================================================== */
@@ -1570,15 +1603,10 @@ NCR_TakeSourceOnline(NCR_Instance inst)
/* Nothing to do */
break;
case MD_OFFLINE:
if (!inst->timer_running) {
/* We are not already actively polling it */
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
inst->tx_count = 0;
inst->local_poll = inst->minpoll;
inst->poll_score = 0.5;
inst->opmode = MD_ONLINE;
start_initial_timeout(inst);
}
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
inst->opmode = MD_ONLINE;
NCR_ResetInstance(inst);
start_initial_timeout(inst);
break;
case MD_BURST_WAS_ONLINE:
/* Will revert */
@@ -1597,10 +1625,8 @@ NCR_TakeSourceOffline(NCR_Instance inst)
{
switch (inst->opmode) {
case MD_ONLINE:
if (inst->timer_running) {
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
take_offline(inst);
}
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
take_offline(inst);
break;
case MD_OFFLINE:
break;
@@ -1619,8 +1645,12 @@ NCR_TakeSourceOffline(NCR_Instance inst)
void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{
if (new_minpoll < MIN_POLL)
return;
inst->minpoll = new_minpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
if (inst->maxpoll < inst->minpoll)
NCR_ModifyMaxpoll(inst, inst->minpoll);
}
/* ================================================== */
@@ -1628,8 +1658,12 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
void
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
{
if (new_maxpoll < MIN_POLL)
return;
inst->maxpoll = new_maxpoll;
LOG(LOGS_INFO, LOGF_NtpCore, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
if (inst->minpoll > inst->maxpoll)
NCR_ModifyMinpoll(inst, inst->maxpoll);
}
/* ================================================== */
@@ -1704,20 +1738,11 @@ NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_sampl
case MD_ONLINE:
case MD_OFFLINE:
if (inst->opmode == MD_ONLINE)
inst->opmode = MD_BURST_WAS_ONLINE;
else
inst->opmode = MD_BURST_WAS_OFFLINE;
inst->opmode = inst->opmode == MD_ONLINE ?
MD_BURST_WAS_ONLINE : MD_BURST_WAS_OFFLINE;
inst->burst_good_samples_to_go = n_good_samples;
inst->burst_total_samples_to_go = n_total_samples;
if (inst->timer_running) {
SCH_RemoveTimeout(inst->timeout_id);
}
inst->timer_running = 1;
inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *) inst);
start_initial_timeout(inst);
break;
default:
assert(0);

View File

@@ -52,13 +52,19 @@ extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_
/* Destroy an instance */
extern void NCR_DestroyInstance(NCR_Instance instance);
/* Start an instance */
extern void NCR_StartInstance(NCR_Instance instance);
/* Reset an instance */
extern void NCR_ResetInstance(NCR_Instance inst);
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int length);
extern void NCR_ProcessKnown(NTP_Packet *message, struct timeval *now, double now_err, NCR_Instance data, int sock_fd, int length);
/* This routine is called when a new packet arrives off the network,
and we do not recognize its source */
extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int length);
extern void NCR_ProcessUnknown(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
/* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timeval *when, double dfreq, double doffset);

336
ntp_io.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -39,6 +39,8 @@
#include "conf.h"
#include "util.h"
#define INVALID_SOCK_FD -1
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef HAVE_IPV6
@@ -47,19 +49,24 @@ union sockaddr_in46 {
struct sockaddr u;
};
/* The file descriptors for the IPv4 and IPv6 sockets */
static int sock_fd4;
/* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4;
static int client_sock_fd4;
#ifdef HAVE_IPV6
static int sock_fd6;
static int server_sock_fd6;
static int client_sock_fd6;
#endif
/* Flag indicating we create a new connected client socket for each
server instead of sharing client_sock_fd4 and client_sock_fd6 */
static int separate_client_sockets;
/* Flag indicating that we have been initialised */
static int initialised=0;
/* ================================================== */
/* Forward prototypes */
static int prepare_socket(int family);
static void read_from_socket(void *anything);
/* ================================================== */
@@ -92,17 +99,14 @@ do_size_checks(void)
/* ================================================== */
static int
prepare_socket(int family)
prepare_socket(int family, int port_number, int client_only)
{
union sockaddr_in46 my_addr;
socklen_t my_addr_len;
int sock_fd;
unsigned short port_number;
IPAddr bind_address;
int on_off = 1;
port_number = CNF_GetNTPPort();
/* Open Internet domain UDP socket for NTP message transmissions */
#if 0
@@ -114,20 +118,22 @@ prepare_socket(int family)
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not open %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
return -1;
return INVALID_SOCK_FD;
}
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
/* Make the socket capable of re-using an old address */
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */
}
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set broadcast socket options");
/* Don't quit - we might survive anyway */
}
@@ -142,8 +148,9 @@ prepare_socket(int family)
if (family == AF_INET) {
#ifdef IP_PKTINFO
/* We want the local IP info too */
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
/* We want the local IP info on server sockets */
if (!client_only &&
setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request packet info using socket option");
/* Don't quit - we might survive anyway */
}
@@ -158,15 +165,17 @@ prepare_socket(int family)
}
#endif
if (!client_only) {
#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 request IPv6 packet info socket option");
}
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option");
}
#elif defined(IPV6_PKTINFO)
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option");
}
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPv6 packet info socket option");
}
#endif
}
}
#endif
@@ -179,7 +188,10 @@ prepare_socket(int family)
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
if (!client_only)
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
if (bind_address.family == IPADDR_INET4)
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
@@ -192,7 +204,10 @@ prepare_socket(int family)
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
if (!client_only)
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
if (bind_address.family == IPADDR_INET6)
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
@@ -213,7 +228,7 @@ prepare_socket(int family)
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
close(sock_fd);
return -1;
return INVALID_SOCK_FD;
}
/* Register handler for read events on the socket */
@@ -233,31 +248,114 @@ prepare_socket(int family)
/* ================================================== */
static int
connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
{
union sockaddr_in46 addr;
socklen_t addr_len;
memset(&addr, 0, sizeof (addr));
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
addr_len = sizeof (addr.in4);
addr.in4.sin_family = AF_INET;
addr.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4);
addr.in4.sin_port = htons(remote_addr->port);
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
addr_len = sizeof (addr.in6);
addr.in6.sin6_family = AF_INET6;
memcpy(addr.in6.sin6_addr.s6_addr, remote_addr->ip_addr.addr.in6,
sizeof (addr.in6.sin6_addr.s6_addr));
addr.in6.sin6_port = htons(remote_addr->port);
break;
#endif
default:
assert(0);
}
if (connect(sock_fd, &addr.u, addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
}
return 1;
}
/* ================================================== */
static void
close_socket(int sock_fd)
{
if (sock_fd == INVALID_SOCK_FD)
return;
SCH_RemoveInputFileHandler(sock_fd);
close(sock_fd);
}
/* ================================================== */
void
NIO_Initialise(int family)
{
int server_port, client_port;
assert(!initialised);
initialised = 1;
do_size_checks();
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = prepare_socket(AF_INET);
else
sock_fd4 = -1;
server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort();
/* Use separate connected sockets if client port is not set */
separate_client_sockets = client_port == 0;
server_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
#ifdef HAVE_IPV6
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
sock_fd6 = prepare_socket(AF_INET6);
else
sock_fd6 = -1;
server_sock_fd6 = INVALID_SOCK_FD;
client_sock_fd6 = INVALID_SOCK_FD;
#endif
if (sock_fd4 < 0
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (server_port)
server_sock_fd4 = prepare_socket(AF_INET, server_port, 0);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd4 = prepare_socket(AF_INET, client_port, 1);
else
client_sock_fd4 = server_sock_fd4;
}
}
#ifdef HAVE_IPV6
&& sock_fd6 < 0
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (server_port)
server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0);
if (!separate_client_sockets) {
if (client_port != server_port || !server_port)
client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1);
else
client_sock_fd6 = server_sock_fd6;
}
}
#endif
) {
LOG_FATAL(LOGF_NtpIO, "Could not open any NTP socket");
if ((server_port && server_sock_fd4 == INVALID_SOCK_FD
#ifdef HAVE_IPV6
&& server_sock_fd6 == INVALID_SOCK_FD
#endif
) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
#ifdef HAVE_IPV6
&& client_sock_fd6 == INVALID_SOCK_FD
#endif
)) {
LOG_FATAL(LOGF_NtpIO, "Could not open NTP sockets");
}
}
@@ -266,23 +364,101 @@ NIO_Initialise(int family)
void
NIO_Finalise(void)
{
if (sock_fd4 >= 0) {
SCH_RemoveInputFileHandler(sock_fd4);
close(sock_fd4);
}
sock_fd4 = -1;
if (server_sock_fd4 != client_sock_fd4)
close_socket(client_sock_fd4);
close_socket(server_sock_fd4);
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
#ifdef HAVE_IPV6
if (sock_fd6 >= 0) {
SCH_RemoveInputFileHandler(sock_fd6);
close(sock_fd6);
}
sock_fd6 = -1;
if (server_sock_fd6 != client_sock_fd6)
close_socket(client_sock_fd6);
close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif
initialised = 0;
}
/* ================================================== */
int
NIO_GetClientSocket(NTP_Remote_Address *remote_addr)
{
if (separate_client_sockets) {
int sock_fd;
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
sock_fd = prepare_socket(AF_INET, 0, 1);
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
sock_fd = prepare_socket(AF_INET6, 0, 1);
break;
#endif
default:
sock_fd = INVALID_SOCK_FD;
}
if (sock_fd == INVALID_SOCK_FD)
return INVALID_SOCK_FD;
if (!connect_socket(sock_fd, remote_addr)) {
close_socket(sock_fd);
return INVALID_SOCK_FD;
}
return sock_fd;
} else {
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
return client_sock_fd4;
#ifdef HAVE_IPV6
case IPADDR_INET6:
return client_sock_fd6;
#endif
default:
return INVALID_SOCK_FD;
}
}
}
/* ================================================== */
int
NIO_GetServerSocket(NTP_Remote_Address *remote_addr)
{
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
return server_sock_fd4;
#ifdef HAVE_IPV6
case IPADDR_INET6:
return server_sock_fd6;
#endif
default:
return INVALID_SOCK_FD;
}
}
/* ================================================== */
void
NIO_CloseClientSocket(int sock_fd)
{
if (separate_client_sockets)
close_socket(sock_fd);
}
/* ================================================== */
int
NIO_IsServerSocket(int sock_fd)
{
return sock_fd != INVALID_SOCK_FD &&
(sock_fd == server_sock_fd4
#ifdef HAVE_IPV6
|| sock_fd == server_sock_fd6
#endif
);
}
/* ================================================== */
@@ -296,9 +472,10 @@ read_from_socket(void *anything)
ReceiveBuffer message;
union sockaddr_in46 where_from;
unsigned int flags = 0;
struct timeval now, now_raw;
struct timeval now;
double now_err;
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr;
char cmsgbuf[256];
struct msghdr msg;
struct iovec iov;
@@ -306,7 +483,7 @@ read_from_socket(void *anything)
assert(initialised);
SCH_GetLastEventTime(&now, &now_err, &now_raw);
SCH_GetLastEventTime(&now, &now_err, NULL);
iov.iov_base = message.arbitrary;
iov.iov_len = sizeof(message);
@@ -329,8 +506,6 @@ read_from_socket(void *anything)
reponse on a subsequent recvfrom). */
if (status > 0) {
memset(&remote_addr, 0, sizeof (remote_addr));
switch (where_from.u.sa_family) {
case AF_INET:
remote_addr.ip_addr.family = IPADDR_INET4;
@@ -349,14 +524,17 @@ read_from_socket(void *anything)
assert(0);
}
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.sock_fd = sock_fd;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
#ifdef IP_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
remote_addr.local_ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
remote_addr.local_ip_addr.family = IPADDR_INET4;
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_spec_dst.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
}
#endif
@@ -365,9 +543,9 @@ read_from_socket(void *anything)
struct in6_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
memcpy(&remote_addr.local_ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (remote_addr.local_ip_addr.addr.in6));
remote_addr.local_ip_addr.family = IPADDR_INET6;
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
}
#endif
@@ -376,16 +554,15 @@ read_from_socket(void *anything)
struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
/* This should be more accurate than LCL_CookTime(&now_raw,...) */
UTI_AddDiffToTimeval(&now, &now_raw, &tv, &now);
LCL_CookTime(&tv, &now, &now_err);
}
#endif
}
if (status >= NTP_NORMAL_PACKET_SIZE && status <= sizeof(NTP_Packet)) {
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err, &remote_addr, status);
NSR_ProcessReceive((NTP_Packet *) &message.ntp_pkt, &now, now_err,
&remote_addr, &local_addr, status);
} else {
@@ -399,14 +576,13 @@ read_from_socket(void *anything)
/* Send a packet to given address */
static void
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
union sockaddr_in46 remote;
struct msghdr msg;
struct iovec iov;
char cmsgbuf[256];
int cmsglen;
int sock_fd;
socklen_t addrlen;
assert(initialised);
@@ -418,7 +594,6 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
remote.in4.sin_family = AF_INET;
remote.in4.sin_port = htons(remote_addr->port);
remote.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4);
sock_fd = sock_fd4;
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
@@ -428,15 +603,17 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
remote.in6.sin6_port = htons(remote_addr->port);
memcpy(&remote.in6.sin6_addr.s6_addr, &remote_addr->ip_addr.addr.in6,
sizeof (remote.in6.sin6_addr.s6_addr));
sock_fd = sock_fd6;
break;
#endif
default:
return;
}
if (sock_fd < 0)
if (local_addr->sock_fd == INVALID_SOCK_FD) {
DEBUG_LOG(LOGF_NtpIO, "No socket to send to %s:%d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
return;
}
iov.iov_base = packet;
iov.iov_len = packetlen;
@@ -450,7 +627,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
cmsglen = 0;
#ifdef IP_PKTINFO
if (remote_addr->local_ip_addr.family == IPADDR_INET4) {
if (local_addr->ip_addr.family == IPADDR_INET4) {
struct cmsghdr *cmsg;
struct in_pktinfo *ipi;
@@ -463,12 +640,12 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
ipi->ipi_spec_dst.s_addr = htonl(remote_addr->local_ip_addr.addr.in4);
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
}
#endif
#if defined(IPV6_PKTINFO) && defined(HAVE_IN6_PKTINFO)
if (remote_addr->local_ip_addr.family == IPADDR_INET6) {
if (local_addr->ip_addr.family == IPADDR_INET6) {
struct cmsghdr *cmsg;
struct in6_pktinfo *ipi;
@@ -481,7 +658,7 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memcpy(&ipi->ipi6_addr.s6_addr, &remote_addr->local_ip_addr.addr.in6,
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
sizeof(ipi->ipi6_addr.s6_addr));
}
#endif
@@ -496,15 +673,8 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
if (!cmsglen)
msg.msg_control = NULL;
if (sendmsg(sock_fd, &msg, 0) < 0 &&
#ifdef ENETUNREACH
errno != ENETUNREACH &&
#endif
#ifdef ENETDOWN
errno != ENETDOWN &&
#endif
!LOG_RateLimited()) {
LOG(LOGS_WARN, LOGF_NtpIO, "Could not send to %s:%d : %s",
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno));
}
}
@@ -513,18 +683,18 @@ send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
/* Send an unauthenticated packet to a given address */
void
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr)
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr);
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE, remote_addr, local_addr);
}
/* ================================================== */
/* Send an authenticated packet to a given address */
void
NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len)
NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len)
{
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr);
send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_addr, local_addr);
}
/* ================================================== */
@@ -533,7 +703,7 @@ NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
#define ECHO_PORT 7
void
NIO_SendEcho(NTP_Remote_Address *remote_addr)
NIO_SendEcho(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
{
unsigned long magic_message = 0xbe7ab1e7UL;
NTP_Remote_Address addr;
@@ -541,5 +711,5 @@ NIO_SendEcho(NTP_Remote_Address *remote_addr)
addr = *remote_addr;
addr.port = ECHO_PORT;
send_packet((void *) &magic_message, sizeof(unsigned long), &addr);
send_packet((void *) &magic_message, sizeof(unsigned long), &addr, local_addr);
}

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,13 +38,25 @@ extern void NIO_Initialise(int family);
/* Function to finalise the module */
extern void NIO_Finalise(void);
/* Function to obtain a socket for sending client packets */
extern int NIO_GetClientSocket(NTP_Remote_Address *remote_addr);
/* Function to obtain a socket for sending server/peer packets */
extern int NIO_GetServerSocket(NTP_Remote_Address *remote_addr);
/* Function to close a socket returned by NIO_GetClientSocket() */
extern void NIO_CloseClientSocket(int sock_fd);
/* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd);
/* Function to transmit a packet */
extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr);
extern void NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
/* Function to transmit an authenticated packet */
extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, int auth_len);
extern void NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len);
/* Function to send a datagram to a remote machine's UDP echo port. */
extern void NIO_SendEcho(NTP_Remote_Address *remote_addr);
extern void NIO_SendEcho(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
#endif /* GOT_NTP_IO_H */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012
* Copyright (C) Miroslav Lichvar 2011-2012, 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,7 +37,7 @@
#include "logging.h"
#include "local.h"
#include "memory.h"
#include "nameserv.h"
#include "nameserv_async.h"
#include "sched.h"
/* ================================================== */
@@ -62,6 +62,9 @@ static int n_sources;
/* The largest number of sources we want to have stored in the hash table */
#define MAX_SOURCES 64
/* Flag indicating new sources will be started automatically when added */
static int auto_start_sources = 0;
/* Source with unknown address (which may be resolved later) */
struct UnresolvedSource {
char *name;
@@ -71,18 +74,27 @@ struct UnresolvedSource {
struct UnresolvedSource *next;
};
#define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9
static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0;
static SCH_TimeoutID resolving_id;
static struct UnresolvedSource *resolving_source = NULL;
static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
/* ================================================== */
/* Forward prototypes */
static void resolve_sources(void *arg);
static void
slew_sources(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *anything);
/* ================================================== */
@@ -206,6 +218,8 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
n_sources++;
records[slot].data = NCR_GetInstance(remote_addr, type, params); /* Will need params passing through */
records[slot].remote_addr = NCR_GetRemoteAddress(records[slot].data);
if (auto_start_sources)
NCR_StartInstance(records[slot].data);
return NSR_Success;
}
}
@@ -213,44 +227,90 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, IPAddr *ip_addr, void *anything)
{
struct UnresolvedSource *us, **i, *next;
NTP_Remote_Address address;
us = (struct UnresolvedSource *)anything;
assert(us == resolving_source);
switch (status) {
case DNS_TryAgain:
break;
case DNS_Success:
DEBUG_LOG(LOGF_NtpSources, "%s resolved to %s", us->name, UTI_IPToString(ip_addr));
address.ip_addr = *ip_addr;
address.port = us->port;
NSR_AddSource(&address, us->type, &us->params);
break;
case DNS_Failure:
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
break;
default:
assert(0);
}
next = us->next;
if (status != DNS_TryAgain) {
/* Remove the source from the list */
for (i = &unresolved_sources; *i; i = &(*i)->next) {
if (*i == us) {
*i = us->next;
Free(us->name);
Free(us);
break;
}
}
}
resolving_source = next;
if (next) {
/* Continue with the next source in the list */
DEBUG_LOG(LOGF_NtpSources, "resolving %s", next->name);
DNS_Name2IPAddressAsync(next->name, name_resolve_handler, next);
} else {
/* This was the last source in the list. If some sources couldn't
be resolved, try again in exponentially increasing interval. */
if (unresolved_sources) {
if (resolving_interval < MIN_RESOLVE_INTERVAL)
resolving_interval = MIN_RESOLVE_INTERVAL;
else if (resolving_interval < MAX_RESOLVE_INTERVAL)
resolving_interval++;
resolving_id = SCH_AddTimeoutByDelay(RESOLVE_INTERVAL_UNIT *
(1 << resolving_interval), resolve_sources, NULL);
} else {
resolving_interval = 0;
}
/* This round of resolving is done */
if (resolving_end_handler)
(resolving_end_handler)();
}
}
/* ================================================== */
static void
resolve_sources(void *arg)
{
NTP_Remote_Address address;
struct UnresolvedSource *us, **i;
DNS_Status s;
struct UnresolvedSource *us;
memset(&address.local_ip_addr, 0, sizeof (address.local_ip_addr));
assert(!resolving_source);
DNS_Reload();
for (i = &unresolved_sources; *i; ) {
us = *i;
s = DNS_Name2IPAddress(us->name, &address.ip_addr);
if (s == DNS_TryAgain) {
i = &(*i)->next;
continue;
} else if (s == DNS_Success) {
address.port = us->port;
NSR_AddSource(&address, us->type, &us->params);
} else {
LOG(LOGS_WARN, LOGF_NtpSources, "Invalid host %s", us->name);
}
*i = us->next;
/* Start with the first source in the list, name_resolve_handler
will iterate over the rest */
us = unresolved_sources;
Free(us->name);
Free(us);
}
if (unresolved_sources) {
/* Try again later */
if (resolving_interval < 9)
resolving_interval++;
resolving_id = SCH_AddTimeoutByDelay(7 * (1 << resolving_interval), resolve_sources, NULL);
} else {
resolving_interval = 0;
}
resolving_source = us;
DEBUG_LOG(LOGF_NtpSources, "resolving %s", us->name);
DNS_Name2IPAddressAsync(us->name, name_resolve_handler, us);
}
/* ================================================== */
@@ -273,11 +333,14 @@ NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParame
for (i = &unresolved_sources; *i; i = &(*i)->next)
;
*i = us;
}
if (!resolving_interval) {
resolving_interval = 2;
resolving_id = SCH_AddTimeoutByDelay(7 * (1 << resolving_interval), resolve_sources, NULL);
}
/* ================================================== */
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
resolving_end_handler = handler;
}
/* ================================================== */
@@ -286,15 +349,44 @@ void
NSR_ResolveSources(void)
{
/* Try to resolve unresolved sources now */
if (resolving_interval) {
SCH_RemoveTimeout(resolving_id);
resolving_interval--;
resolve_sources(NULL);
if (unresolved_sources) {
/* Make sure no resolving is currently running */
if (!resolving_source) {
if (resolving_interval) {
SCH_RemoveTimeout(resolving_id);
resolving_interval--;
}
resolve_sources(NULL);
}
} else {
/* No unresolved sources, we are done */
if (resolving_end_handler)
(resolving_end_handler)();
}
}
/* ================================================== */
void NSR_StartSources(void)
{
int i;
for (i = 0; i < N_RECORDS; i++) {
if (!records[i].remote_addr)
continue;
NCR_StartInstance(records[i].data);
}
}
/* ================================================== */
void NSR_AutoStartSources(void)
{
auto_start_sources = 1;
}
/* ================================================== */
/* Procedure to remove a source. We don't bother whether the port
address is matched - we're only interested in removing a record for
the right IP address. Thus the caller can specify the port number
@@ -341,10 +433,25 @@ NSR_RemoveSource(NTP_Remote_Address *remote_addr)
/* ================================================== */
void
NSR_RemoveAllSources(void)
{
int i;
for (i = 0; i < N_RECORDS; i++) {
if (!records[i].remote_addr)
continue;
NCR_DestroyInstance(records[i].data);
records[i].remote_addr = NULL;
}
}
/* ================================================== */
/* This routine is called by ntp_io when a new packet arrives off the network,
possibly with an authentication tail */
void
NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int length)
NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
int slot, found;
@@ -358,9 +465,10 @@ NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP
find_slot(remote_addr, &slot, &found);
if (found == 2) { /* Must match IP address AND port number */
NCR_ProcessKnown(message, now, now_err, records[slot].data, length);
NCR_ProcessKnown(message, now, now_err, records[slot].data,
local_addr->sock_fd, length);
} else {
NCR_ProcessUnknown(message, now, now_err, remote_addr, length);
NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);
}
}
@@ -371,7 +479,7 @@ slew_sources(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *anything)
{
int i;
@@ -383,7 +491,11 @@ slew_sources(struct timeval *raw,
UTI_IPToString(&records[i].remote_addr->ip_addr), dfreq, doffset);
#endif
NCR_SlewTimes(records[i].data, cooked, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
NCR_ResetInstance(records[i].data);
} else {
NCR_SlewTimes(records[i].data, cooked, dfreq, doffset);
}
}
}

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -54,14 +55,30 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
until it succeeds or fails with a non-temporary error. */
extern void NSR_AddUnresolvedSource(char *name, int port, NTP_Source_Type type, SourceParameters *params);
/* Procedure to try resolve unresolved sources immediately. */
/* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */
typedef void (*NSR_SourceResolvingEndHandler)(void);
/* Set the handler, or NULL to disable the notification */
extern void NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler);
/* Procedure to start resolving unresolved sources */
extern void NSR_ResolveSources(void);
/* Procedure to start all sources */
extern void NSR_StartSources(void);
/* Start new sources automatically */
extern void NSR_AutoStartSources(void);
/* Procedure to remove a source */
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
/* Procedure to remove all sources */
extern void NSR_RemoveAllSources(void);
/* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, int length);
extern void NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
/* Initialisation function */
extern void NSR_Initialise(void);

View File

@@ -35,8 +35,8 @@
/* ================================================== */
int
PKL_CommandLength(CMD_Request *r)
static int
command_unpadded_length(CMD_Request *r)
{
int type;
type = ntohs(r->command);
@@ -157,6 +157,149 @@ PKL_CommandLength(CMD_Request *r)
}
/* ================================================== */
int
PKL_CommandLength(CMD_Request *r)
{
int command_length;
command_length = command_unpadded_length(r);
if (!command_length)
return 0;
return command_length + PKL_CommandPaddingLength(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;
if (r->version < PROTO_VERSION_PADDING)
return 0;
type = ntohs(r->command);
if (type < 0 || 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_LOGON:
return PADDING_LENGTH(data.logon.EOR, data.null.EOR);
case REQ_SETTIME:
return PADDING_LENGTH(data.settime.EOR, data.manual_timestamp.EOR);
case REQ_LOCAL:
return PADDING_LENGTH(data.local.EOR, data.null.EOR);
case REQ_MANUAL:
return PADDING_LENGTH(data.manual.EOR, data.null.EOR);
case REQ_N_SOURCES:
return PADDING_LENGTH(data.n_sources.EOR, data.n_sources.EOR);
case REQ_SOURCE_DATA:
return PADDING_LENGTH(data.source_data.EOR, data.source_data.EOR);
case REQ_REKEY:
return PADDING_LENGTH(data.rekey.EOR, data.null.EOR);
case REQ_ALLOW:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_ALLOWALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_DENY:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_DENYALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDALLOW:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDALLOWALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDDENY:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_CMDDENYALL:
return PADDING_LENGTH(data.allow_deny.EOR, data.null.EOR);
case REQ_ACCHECK:
return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR);
case REQ_CMDACCHECK:
return PADDING_LENGTH(data.ac_check.EOR, data.null.EOR);
case REQ_ADD_SERVER:
return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR);
case REQ_ADD_PEER:
return PADDING_LENGTH(data.ntp_source.EOR, data.null.EOR);
case REQ_DEL_SOURCE:
return PADDING_LENGTH(data.del_source.EOR, data.null.EOR);
case REQ_WRITERTC:
return PADDING_LENGTH(data.writertc.EOR, data.null.EOR);
case REQ_DFREQ:
return PADDING_LENGTH(data.dfreq.EOR, data.null.EOR);
case REQ_DOFFSET:
return PADDING_LENGTH(data.doffset.EOR, data.null.EOR);
case REQ_TRACKING:
return PADDING_LENGTH(data.tracking.EOR, data.tracking.EOR);
case REQ_SOURCESTATS:
return PADDING_LENGTH(data.sourcestats.EOR, data.sourcestats.EOR);
case REQ_RTCREPORT:
return PADDING_LENGTH(data.rtcreport.EOR, data.rtc.EOR);
case REQ_TRIMRTC:
return PADDING_LENGTH(data.trimrtc.EOR, data.null.EOR);
case REQ_CYCLELOGS:
return PADDING_LENGTH(data.cyclelogs.EOR, data.null.EOR);
case REQ_SUBNETS_ACCESSED:
case REQ_CLIENT_ACCESSES:
/* No longer supported */
return 0;
case REQ_CLIENT_ACCESSES_BY_INDEX:
return PADDING_LENGTH(data.client_accesses_by_index.EOR, data.client_accesses_by_index.EOR);
case REQ_MANUAL_LIST:
return PADDING_LENGTH(data.manual_list.EOR, data.manual_list.EOR);
case REQ_MANUAL_DELETE:
return PADDING_LENGTH(data.manual_delete.EOR, data.null.EOR);
case REQ_MAKESTEP:
return PADDING_LENGTH(data.make_step.EOR, data.null.EOR);
case REQ_ACTIVITY:
return PADDING_LENGTH(data.activity.EOR, data.activity.EOR);
case REQ_RESELECT:
return PADDING_LENGTH(data.reselect.EOR, data.null.EOR);
case REQ_RESELECTDISTANCE:
return PADDING_LENGTH(data.reselect_distance.EOR, data.null.EOR);
case REQ_MODIFY_MINSTRATUM:
return PADDING_LENGTH(data.modify_minstratum.EOR, data.null.EOR);
case REQ_MODIFY_POLLTARGET:
return PADDING_LENGTH(data.modify_polltarget.EOR, data.null.EOR);
default:
/* If we fall through the switch, it most likely means we've forgotten to implement a new case */
assert(0);
return 0;
}
}
/* ================================================== */
int

View File

@@ -33,6 +33,8 @@
extern int PKL_CommandLength(CMD_Request *r);
extern int PKL_CommandPaddingLength(CMD_Request *r);
extern int PKL_ReplyLength(CMD_Reply *r);
#endif /* GOT_PKTLENGTH_H */

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -42,6 +42,7 @@
extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver;
extern RefclockDriver RCL_PPS_driver;
extern RefclockDriver RCL_PHC_driver;
struct FilterSample {
double offset;
@@ -56,6 +57,7 @@ struct MedianFilter {
int last;
int avg_var_n;
double avg_var;
double max_var;
struct FilterSample *samples;
int *selected;
double *x_data;
@@ -73,6 +75,7 @@ struct RCL_Instance_Record {
int poll;
int leap_status;
int pps_rate;
int pps_active;
struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
@@ -94,11 +97,11 @@ static int valid_sample_time(RCL_Instance instance, struct timeval *tv);
static int pps_stratum(RCL_Instance instance, struct timeval *tv);
static void poll_timeout(void *arg);
static void slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, int is_step_change, void *anything);
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
static void filter_init(struct MedianFilter *filter, int length);
static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
static void filter_fini(struct MedianFilter *filter);
static void filter_reset(struct MedianFilter *filter);
static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
@@ -166,6 +169,9 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver = &RCL_PPS_driver;
inst->precision = 1e-9;
pps_source = 1;
} else if (strcmp(params->driver_name, "PHC") == 0) {
inst->driver = &RCL_PHC_driver;
inst->precision = 1e-9;
} else {
LOG_FATAL(LOGF_Refclock, "unknown refclock driver %s", params->driver_name);
return 0;
@@ -184,6 +190,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
@@ -239,14 +246,12 @@ RCL_AddRefclock(RefclockParameters *params)
return 0;
}
filter_init(&inst->filter, params->filter_length);
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_option, NULL);
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock added poll=%d dpoll=%d filter=%d",
inst->poll, inst->driver_poll, params->filter_length);
#endif
DEBUG_LOG(LOGF_Refclock, "refclock %s added poll=%d dpoll=%d filter=%d",
params->driver_name, inst->poll, inst->driver_poll, params->filter_length);
n_sources++;
Free(params->driver_name);
@@ -263,6 +268,7 @@ RCL_StartRefclocks(void)
RCL_Instance inst = &refclocks[i];
SRC_SetSelectable(inst->source);
SRC_SetActive(inst->source);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
if (inst->lock_ref) {
@@ -345,7 +351,7 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(sample_time, correction, &cooked_time);
dispersion += instance->precision + filter_get_avg_sample_dispersion(&instance->filter);
dispersion += instance->precision;
if (!valid_sample_time(instance, sample_time))
return 0;
@@ -363,6 +369,8 @@ RCL_AddSample(RCL_Instance instance, struct timeval *sample_time, double offset,
break;
}
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
/* for logging purposes */
@@ -378,10 +386,12 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
double correction, dispersion, offset;
struct timeval cooked_time;
int rate;
NTP_Leap leap;
leap = LEAP_Normal;
LCL_GetOffsetCorrection(pulse_time, &correction, &dispersion);
UTI_AddDoubleToTimeval(pulse_time, correction, &cooked_time);
dispersion += instance->precision + filter_get_avg_sample_dispersion(&instance->filter);
dispersion += instance->precision;
if (!valid_sample_time(instance, pulse_time))
return 0;
@@ -403,12 +413,19 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
double sample_diff, ref_offset, ref_dispersion, shift;
if (!filter_get_last_sample(&refclocks[instance->lock_ref].filter,
&ref_sample_time, &ref_offset, &ref_dispersion))
&ref_sample_time, &ref_offset, &ref_dispersion)) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored no ref sample");
return 0;
}
ref_dispersion += filter_get_avg_sample_dispersion(&refclocks[instance->lock_ref].filter);
UTI_DiffTimevalsToDouble(&sample_diff, &cooked_time, &ref_sample_time);
if (fabs(sample_diff) >= 2.0 / rate)
if (fabs(sample_diff) >= 2.0 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored samplediff=%.9f",
sample_diff);
return 0;
}
/* Align the offset to the reference sample */
if ((ref_offset - offset) >= 0.0)
@@ -418,18 +435,20 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
offset += shift;
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate)
if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_offset - offset, ref_dispersion, dispersion);
return 0;
}
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
leap = refclocks[instance->lock_ref].leap_status;
DEBUG_LOG(LOGF_Refclock, "refclock pulse second=%.9f offset=%.9f offdiff=%.9f samplediff=%.9f",
second, offset, ref_offset - offset, sample_diff);
#endif
} else {
struct timeval ref_time;
int is_synchronised, stratum;
double root_delay, root_dispersion, distance;
NTP_Leap leap;
uint32_t ref_id;
/* Ignore the pulse if we are not well synchronized */
@@ -439,10 +458,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
distance = fabs(root_delay) / 2 + root_dispersion;
if (!is_synchronised || distance >= 0.5 / rate) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "refclock pulse dropped second=%.9f sync=%d dist=%.9f",
DEBUG_LOG(LOGF_Refclock, "refclock pulse ignored second=%.9f sync=%d dist=%.9f",
second, is_synchronised, distance);
#endif
/* Drop also all stored samples */
filter_reset(&instance->filter);
return 0;
@@ -450,7 +467,8 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
}
filter_add_sample(&instance->filter, &cooked_time, offset, dispersion);
instance->leap_status = LEAP_Normal;
instance->leap_status = leap;
instance->pps_active = 1;
log_sample(instance, &cooked_time, 0, 1, offset + correction - instance->offset, offset, dispersion);
@@ -461,6 +479,15 @@ RCL_AddPulse(RCL_Instance instance, struct timeval *pulse_time, double second)
return 1;
}
static double
poll_interval(int poll)
{
if (poll >= 0)
return 1 << poll;
else
return 1.0 / (1 << -poll);
}
static int
valid_sample_time(RCL_Instance instance, struct timeval *tv)
{
@@ -469,8 +496,11 @@ valid_sample_time(RCL_Instance instance, struct timeval *tv)
LCL_ReadRawTime(&raw_time);
UTI_DiffTimevalsToDouble(&diff, &raw_time, tv);
if (diff < 0.0 || diff > 1 << (instance->poll + 1))
if (diff < 0.0 || diff > poll_interval(instance->poll + 1)) {
DEBUG_LOG(LOGF_Refclock, "refclock sample not valid age=%.6f tv=%s",
diff, UTI_TimevalToString(tv));
return 0;
}
return 1;
}
@@ -494,7 +524,7 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
/* Or the current source is another PPS refclock */
for (i = 0; i < n_sources; i++) {
if (refclocks[i].ref_id == ref_id &&
refclocks[i].pps_rate && refclocks[i].lock_ref == -1)
refclocks[i].pps_active && refclocks[i].lock_ref == -1)
return stratum - 1;
}
@@ -504,7 +534,6 @@ pps_stratum(RCL_Instance instance, struct timeval *tv)
static void
poll_timeout(void *arg)
{
double next;
int poll;
RCL_Instance inst = (RCL_Instance)arg;
@@ -526,7 +555,7 @@ poll_timeout(void *arg)
inst->driver_polled = 0;
if (sample_ok) {
if (inst->pps_rate && inst->lock_ref == -1)
if (inst->pps_active && inst->lock_ref == -1)
/* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time);
else
@@ -535,6 +564,7 @@ poll_timeout(void *arg)
SRC_UpdateReachability(inst->source, 1);
SRC_AccumulateSample(inst->source, &sample_time, offset,
inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
SRC_SelectSource(inst->source);
log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
} else {
@@ -542,22 +572,21 @@ poll_timeout(void *arg)
}
}
if (poll >= 0)
next = 1 << poll;
else
next = 1.0 / (1 << -poll);
inst->timeout_id = SCH_AddTimeoutByDelay(next, poll_timeout, arg);
inst->timeout_id = SCH_AddTimeoutByDelay(poll_interval(poll), poll_timeout, arg);
}
static void
slew_samples(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, int is_step_change, void *anything)
double doffset, LCL_ChangeType change_type, void *anything)
{
int i;
for (i = 0; i < n_sources; i++)
filter_slew_samples(&refclocks[i].filter, cooked, dfreq, doffset);
for (i = 0; i < n_sources; i++) {
if (change_type == LCL_ChangeUnknownStep)
filter_reset(&refclocks[i].filter);
else
filter_slew_samples(&refclocks[i].filter, cooked, dfreq, doffset);
}
}
static void
@@ -600,7 +629,7 @@ log_sample(RCL_Instance instance, struct timeval *sample_time, int filtered, int
}
static void
filter_init(struct MedianFilter *filter, int length)
filter_init(struct MedianFilter *filter, int length, double max_dispersion)
{
if (length < 1)
length = 1;
@@ -612,6 +641,7 @@ filter_init(struct MedianFilter *filter, int length)
/* set first estimate to system precision */
filter->avg_var_n = 0;
filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
filter->max_var = max_dispersion * max_dispersion;
filter->samples = MallocArray(struct FilterSample, filter->length);
filter->selected = MallocArray(int, filter->length);
filter->x_data = MallocArray(double, filter->length);
@@ -654,6 +684,9 @@ filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
filter->samples[filter->index].sample_time = *sample_time;
filter->samples[filter->index].offset = offset;
filter->samples[filter->index].dispersion = dispersion;
DEBUG_LOG(LOGF_Refclock, "filter sample %d t=%s offset=%.9f dispersion=%.9f",
filter->index, UTI_TimevalToString(sample_time), offset, dispersion);
}
static int
@@ -802,8 +835,6 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
y /= n;
e /= n;
e -= sqrt(filter->avg_var);
if (n >= 4) {
double b0, b1, s2, sb0, sb1;
@@ -836,6 +867,13 @@ filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, doub
d = sqrt(var);
}
/* drop the sample if variance is larger than allowed maximum */
if (filter->max_var > 0.0 && var > filter->max_var) {
DEBUG_LOG(LOGF_Refclock, "filter dispersion too large disp=%.9f max=%.9f",
sqrt(var), sqrt(filter->max_var));
return 0;
}
prev_avg_var = filter->avg_var;
/* update exponential moving average of the variance */

View File

@@ -43,6 +43,7 @@ typedef struct {
double offset;
double delay;
double precision;
double max_dispersion;
SRC_SelectOption sel_option;
} RefclockParameters;
@@ -58,7 +59,6 @@ extern void RCL_Initialise(void);
extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timeval *now);
/* functions used by drivers */

193
refclock_phc.c Normal file
View File

@@ -0,0 +1,193 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013
*
* 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.
*
**********************************************************************
=======================================================================
PTP hardware clock (PHC) refclock driver.
*/
#include "config.h"
#include "refclock.h"
#ifdef FEAT_PHC
#include "sysincl.h"
#include <linux/ptp_clock.h>
#include "refclock.h"
#include "logging.h"
#include "util.h"
/* From linux/include/linux/posix-timers.h */
#define CPUCLOCK_MAX 3
#define CLOCKFD CPUCLOCK_MAX
#define CLOCKFD_MASK (CPUCLOCK_PERTHREAD_MASK|CPUCLOCK_CLOCK_MASK)
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
#define NUM_READINGS 10
static int no_sys_offset_ioctl = 0;
struct phc_reading {
struct timespec sys_ts1;
struct timespec phc_ts;;
struct timespec sys_ts2;
};
static double diff_ts(struct timespec *ts1, struct timespec *ts2)
{
return (ts1->tv_sec - ts2->tv_sec) + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
}
static int read_phc_ioctl(struct phc_reading *readings, int phc_fd, int n)
{
#if defined(PTP_SYS_OFFSET) && NUM_READINGS <= PTP_MAX_SAMPLES
struct ptp_sys_offset sys_off;
int i;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = n;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
LOG(LOGS_ERR, LOGF_Refclock, "ioctl(PTP_SYS_OFFSET) failed : %s", strerror(errno));
return 0;
}
for (i = 0; i < n; i++) {
readings[i].sys_ts1.tv_sec = sys_off.ts[i * 2].sec;
readings[i].sys_ts1.tv_nsec = sys_off.ts[i * 2].nsec;
readings[i].phc_ts.tv_sec = sys_off.ts[i * 2 + 1].sec;
readings[i].phc_ts.tv_nsec = sys_off.ts[i * 2 + 1].nsec;
readings[i].sys_ts2.tv_sec = sys_off.ts[i * 2 + 2].sec;
readings[i].sys_ts2.tv_nsec = sys_off.ts[i * 2 + 2].nsec;
}
return 1;
#else
/* Not available */
return 0;
#endif
}
static int read_phc_user(struct phc_reading *readings, int phc_fd, int n)
{
clockid_t phc_id;
int i;
phc_id = FD_TO_CLOCKID(phc_fd);
for (i = 0; i < n; i++) {
if (clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts1) ||
clock_gettime(phc_id, &readings[i].phc_ts) ||
clock_gettime(CLOCK_REALTIME, &readings[i].sys_ts2)) {
LOG(LOGS_ERR, LOGF_Refclock, "clock_gettime() failed : %s", strerror(errno));
return 0;
}
}
return 1;
}
static int phc_initialise(RCL_Instance instance)
{
struct ptp_clock_caps caps;
int phc_fd;
char *path;
path = RCL_GetDriverParameter(instance);
phc_fd = open(path, O_RDONLY);
if (phc_fd < 0) {
LOG_FATAL(LOGF_Refclock, "open() failed on %s", path);
return 0;
}
/* Make sure it is a PHC */
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
LOG_FATAL(LOGF_Refclock, "ioctl(PTP_CLOCK_GETCAPS) failed : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(phc_fd);
RCL_SetDriverData(instance, (void *)(long)phc_fd);
return 1;
}
static void phc_finalise(RCL_Instance instance)
{
close((long)RCL_GetDriverData(instance));
}
static int phc_poll(RCL_Instance instance)
{
struct phc_reading readings[NUM_READINGS];
struct timeval tv;
double offset = 0.0, delay, best_delay = 0.0;
int i, phc_fd, best;
phc_fd = (long)RCL_GetDriverData(instance);
if (!no_sys_offset_ioctl) {
if (!read_phc_ioctl(readings, phc_fd, NUM_READINGS)) {
no_sys_offset_ioctl = 1;
return 0;
}
} else {
if (!read_phc_user(readings, phc_fd, NUM_READINGS))
return 0;
}
/* Find the fastest reading */
for (i = 0; i < NUM_READINGS; i++) {
delay = diff_ts(&readings[i].sys_ts2, &readings[i].sys_ts1);
if (!i || best_delay > delay) {
best = i;
best_delay = delay;
}
}
offset = diff_ts(&readings[best].phc_ts, &readings[best].sys_ts2) + best_delay / 2.0;
tv.tv_sec = readings[best].sys_ts2.tv_sec;
tv.tv_usec = readings[best].sys_ts2.tv_nsec / 1000;
DEBUG_LOG(LOGF_Refclock, "PHC offset: %+.9f delay: %.9f", offset, best_delay);
return RCL_AddSample(instance, &tv, offset, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {
phc_initialise,
phc_finalise,
phc_poll
};
#else
RefclockDriver RCL_PHC_driver = { NULL, NULL, NULL };
#endif

View File

@@ -134,9 +134,7 @@ static int pps_poll(RCL_Instance instance)
ts.tv_nsec = 0;
if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "time_pps_fetch error");
#endif
LOG(LOGS_ERR, LOGF_Refclock, "time_pps_fetch() failed : %s", strerror(errno));
return 0;
}
@@ -149,6 +147,8 @@ static int pps_poll(RCL_Instance instance)
}
if (seq == pps->last_seq || (ts.tv_sec == 0 && ts.tv_nsec == 0)) {
DEBUG_LOG(LOGF_Refclock, "PPS sample ignored seq=%lu ts=%lu.%09lu",
seq, ts.tv_sec, ts.tv_nsec);
return 0;
}

View File

@@ -100,9 +100,8 @@ static int shm_poll(RCL_Instance instance)
if ((t.mode == 1 && t.count != shm->count) ||
!(t.mode == 0 || t.mode == 1) || !t.valid) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "sample ignored mode: %d count: %d valid: %d", t.mode, t.count, t.valid);
#endif
DEBUG_LOG(LOGF_Refclock, "SHM sample ignored mode=%d count=%d valid=%d",
t.mode, t.count, t.valid);
return 0;
}

View File

@@ -57,23 +57,20 @@ static void read_sample(void *anything)
s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 0) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "Error reading from SOCK socket : %s", strerror(errno));
#endif
LOG(LOGS_ERR, LOGF_Refclock, "Could not read SOCK sample : %s",
strerror(errno));
return;
}
if (s != sizeof (sample)) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %d", s, sizeof (sample));
#endif
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected length of SOCK sample : %d != %ld",
s, sizeof (sample));
return;
}
if (sample.magic != SOCK_MAGIC) {
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x", sample.magic, SOCK_MAGIC);
#endif
LOG(LOGS_WARN, LOGF_Refclock, "Unexpected magic number in SOCK sample : %x != %x",
sample.magic, SOCK_MAGIC);
return;
}

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2013
* Copyright (C) Miroslav Lichvar 2009-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -67,6 +67,9 @@ static double correction_time_ratio;
/* Flag indicating that we are initialised */
static int initialised = 0;
/* Current operating mode */
static REF_Mode mode;
/* Threshold and update limit for stepping clock */
static int make_step_limit;
static double make_step_threshold;
@@ -86,6 +89,9 @@ static int do_mail_change;
static double mail_change_threshold;
static char *mail_change_user;
/* Handler for mode ending */
static REF_ModeEndHandler mode_end_handler = NULL;
/* Filename of the drift file. */
static char *drift_file=NULL;
static double drift_file_age;
@@ -138,11 +144,16 @@ handle_slew(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *anything)
{
if (is_step_change) {
UTI_AddDoubleToTimeval(&last_ref_update, -doffset, &last_ref_update);
double delta;
if (change_type == LCL_ChangeUnknownStep) {
last_ref_update.tv_sec = 0;
last_ref_update.tv_usec = 0;
} else if (last_ref_update.tv_sec) {
UTI_AdjustTimeval(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
}
}
@@ -155,6 +166,7 @@ REF_Initialise(void)
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
mode = REF_ModeNormal;
are_we_synchronised = 0;
our_leap_status = LEAP_Unsynchronised;
our_leap_sec = 0;
@@ -266,6 +278,29 @@ REF_Finalise(void)
/* ================================================== */
void REF_SetMode(REF_Mode new_mode)
{
mode = new_mode;
}
/* ================================================== */
REF_Mode
REF_GetMode(void)
{
return mode;
}
/* ================================================== */
void
REF_SetModeEndHandler(REF_ModeEndHandler handler)
{
mode_end_handler = handler;
}
/* ================================================== */
static double
Sqr(double x)
{
@@ -290,6 +325,7 @@ update_drift_file(double freq_ppm, double skew)
struct stat buf;
char *temp_drift_file;
FILE *out;
int r1, r2;
/* Create a temporary file with a '.tmp' extension. */
@@ -311,8 +347,9 @@ update_drift_file(double freq_ppm, double skew)
}
/* Write the frequency and skew parameters in ppm */
if ((fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew) < 0) |
fclose(out)) {
r1 = fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_drift_file);
LOG(LOGS_WARN, LOGF_Reference, "Could not write to temporary driftfile %s.tmp",
drift_file);
@@ -322,10 +359,12 @@ update_drift_file(double freq_ppm, double skew)
/* Clone the file attributes from the existing file if there is one. */
if (!stat(drift_file,&buf)) {
if (chown(temp_drift_file,buf.st_uid,buf.st_gid)) {
LOG(LOGS_WARN, LOGF_Reference, "Could not change ownership of temporary driftfile %s.tmp", drift_file);
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
chmod(temp_drift_file,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_Reference,
"Could not change ownership or permissions of temporary driftfile %s.tmp",
drift_file);
}
chmod(temp_drift_file,buf.st_mode&0777);
}
/* Rename the temporary file to the correct location (see rename(2) for details). */
@@ -454,6 +493,18 @@ schedule_fb_drift(struct timeval *now)
/* ================================================== */
static void
end_ref_mode(int result)
{
mode = REF_ModeIgnore;
/* Dispatch the handler */
if (mode_end_handler)
(mode_end_handler)(result);
}
/* ================================================== */
#define BUFLEN 255
#define S_MAX_USER_LEN "128"
@@ -506,15 +557,15 @@ maybe_log_offset(double offset, time_t now)
/* ================================================== */
static void
maybe_make_step()
static int
is_step_limit_reached(double offset, double offset_correction)
{
if (make_step_limit == 0) {
return;
return 0;
} else if (make_step_limit > 0) {
make_step_limit--;
}
LCL_MakeStep(make_step_threshold);
return fabs(offset - offset_correction) > make_step_threshold;
}
/* ================================================== */
@@ -534,9 +585,9 @@ is_offset_ok(double offset)
if (offset > max_offset) {
LOG(LOGS_WARN, LOGF_Reference,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
if (!max_offset_ignore)
SCH_QuitProgram();
end_ref_mode(0);
else if (max_offset_ignore > 0)
max_offset_ignore--;
return 0;
@@ -546,6 +597,15 @@ is_offset_ok(double offset)
/* ================================================== */
static int
is_leap_second_day(struct tm *stm) {
/* Allow leap second only on the last day of June and December */
return (stm->tm_mon == 5 && stm->tm_mday == 30) ||
(stm->tm_mon == 11 && stm->tm_mday == 31);
}
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when)
{
@@ -563,12 +623,7 @@ get_tz_leap(time_t when)
stm = *gmtime(&when);
/* Check for leap second only in the latter half of June and December */
if (stm.tm_mon == 5 && stm.tm_mday > 14)
stm.tm_mday = 30;
else if (stm.tm_mon == 11 && stm.tm_mday > 14)
stm.tm_mday = 31;
else
if (!is_leap_second_day(&stm))
return tz_leap;
/* Temporarily switch to the timezone containing leap seconds */
@@ -610,7 +665,6 @@ get_tz_leap(time_t when)
static void
update_leap_status(NTP_Leap leap, time_t now)
{
struct tm stm;
int leap_sec;
leap_sec = 0;
@@ -619,20 +673,16 @@ update_leap_status(NTP_Leap leap, time_t now)
leap = get_tz_leap(now);
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Insert/delete leap second only on June 30 or December 31
and in other months ignore the leap status completely */
/* Check that leap second is allowed today */
stm = *gmtime(&now);
if (stm.tm_mon != 5 && stm.tm_mon != 11) {
leap = LEAP_Normal;
} else if ((stm.tm_mon == 5 && stm.tm_mday == 30) ||
(stm.tm_mon == 11 && stm.tm_mday == 31)) {
if (is_leap_second_day(gmtime(&now))) {
if (leap == LEAP_InsertSecond) {
leap_sec = 1;
} else {
leap_sec = -1;
}
} else {
leap = LEAP_Normal;
}
}
@@ -662,6 +712,62 @@ write_log(struct timeval *ref_time, char *ref, int stratum, NTP_Leap leap,
/* ================================================== */
static void
special_mode_sync(int valid, double offset)
{
int step;
switch (mode) {
case REF_ModeInitStepSlew:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for initstepslew");
end_ref_mode(0);
break;
}
step = fabs(offset) >= CNF_GetInitStepThreshold();
LOG(LOGS_INFO, LOGF_Reference,
"System's initial offset : %.6f seconds %s of true (%s)",
fabs(offset), offset >= 0 ? "fast" : "slow", step ? "step" : "slew");
if (step)
LCL_ApplyStepOffset(offset);
else
LCL_AccumulateOffset(offset, 0.0);
end_ref_mode(1);
break;
case REF_ModeUpdateOnce:
case REF_ModePrintOnce:
if (!valid) {
LOG(LOGS_WARN, LOGF_Reference, "No suitable source for synchronisation");
end_ref_mode(0);
break;
}
step = mode == REF_ModeUpdateOnce;
LOG(LOGS_INFO, LOGF_Reference, "System clock wrong by %.6f seconds (%s)",
-offset, step ? "step" : "ignored");
if (step)
LCL_ApplyStepOffset(offset);
end_ref_mode(1);
break;
case REF_ModeIgnore:
/* Do nothing until the mode is changed */
break;
default:
assert(0);
}
}
/* ================================================== */
void
REF_SetReference(int stratum,
NTP_Leap leap,
@@ -688,11 +794,17 @@ REF_SetReference(int stratum,
double update_interval;
double elapsed;
double correction_rate;
double uncorrected_offset;
struct timeval now, raw_now, ev_now, ev_raw_now;
double uncorrected_offset, accumulate_offset, step_offset;
struct timeval now, raw_now;
assert(initialised);
/* Special modes are implemented elsewhere */
if (mode != REF_ModeNormal) {
special_mode_sync(1, offset);
return;
}
/* Guard against dividing by zero */
if (skew < MIN_SKEW)
skew = MIN_SKEW;
@@ -716,10 +828,7 @@ REF_SetReference(int stratum,
}
LCL_ReadRawTime(&raw_now);
/* This is cheaper than calling LCL_CookTime */
SCH_GetLastEventTime(&ev_now, NULL, &ev_raw_now);
UTI_DiffTimevalsToDouble(&uncorrected_offset, &ev_now, &ev_raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimeval(&raw_now, uncorrected_offset, &now);
UTI_DiffTimevalsToDouble(&elapsed, &now, ref_time);
@@ -763,6 +872,16 @@ REF_SetReference(int stratum,
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
if (is_step_limit_reached(our_offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
accumulate_offset = uncorrected_offset;
step_offset = our_offset - uncorrected_offset;
} else {
accumulate_offset = our_offset;
step_offset = 0.0;
}
/* Eliminate updates that are based on totally unreliable frequency
information. Ignore this limit with manual reference. */
@@ -797,21 +916,23 @@ REF_SetReference(int stratum,
our_residual_freq = new_freq - our_frequency;
LCL_AccumulateFrequencyAndOffset(our_frequency, our_offset, correction_rate);
LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
} else {
DEBUG_LOG(LOGF_Reference, "Skew %f too large to track, offset=%f", skew, accumulate_offset);
#if 0
LOG(LOGS_INFO, LOGF_Reference, "Skew %f too large to track, offset=%f", skew, our_offset);
#endif
LCL_AccumulateOffset(our_offset, correction_rate);
LCL_AccumulateOffset(accumulate_offset, correction_rate);
our_residual_freq = frequency;
}
update_leap_status(leap, raw_now.tv_sec);
maybe_log_offset(our_offset, raw_now.tv_sec);
maybe_make_step();
if (step_offset != 0.0) {
LCL_ApplyStepOffset(step_offset);
LOG(LOGS_WARN, LOGF_Reference, "System clock was stepped by %.6f seconds", -step_offset);
}
abs_freq_ppm = LCL_ReadAbsoluteFrequency();
@@ -884,9 +1005,15 @@ REF_SetUnsynchronised(void)
assert(initialised);
/* This is cheaper than calling LCL_CookTime */
SCH_GetLastEventTime(&now, NULL, &now_raw);
UTI_DiffTimevalsToDouble(&uncorrected_offset, &now, &now_raw);
/* Special modes are implemented elsewhere */
if (mode != REF_ModeNormal) {
special_mode_sync(0, 0.0);
return;
}
LCL_ReadRawTime(&now_raw);
LCL_GetOffsetCorrection(&now_raw, &uncorrected_offset, NULL);
UTI_AddDoubleToTimeval(&now_raw, uncorrected_offset, &now);
if (fb_drifts) {
schedule_fb_drift(&now);
@@ -1084,5 +1211,3 @@ REF_GetTrackingReport(RPT_TrackingReport *rep)
}
}
/* ================================================== */

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -40,6 +41,26 @@ extern void REF_Initialise(void);
/* Fini function */
extern void REF_Finalise(void);
typedef enum {
REF_ModeNormal,
REF_ModeInitStepSlew,
REF_ModeUpdateOnce,
REF_ModePrintOnce,
REF_ModeIgnore,
} REF_Mode;
/* Set reference update mode */
extern void REF_SetMode(REF_Mode mode);
/* Get reference update mode */
extern REF_Mode REF_GetMode(void);
/* Function type for handlers to be called back when mode ends */
typedef void (*REF_ModeEndHandler)(int result);
/* Set the handler for being notified of mode ending */
extern void REF_SetModeEndHandler(REF_ModeEndHandler handler);
/* Function which takes a local cooked time and returns the estimated
time of the reference. It also returns the other parameters
required for forming the outgoing NTP packet.
@@ -129,7 +150,7 @@ extern void REF_SetManualReference
extern void
REF_SetUnsynchronised(void);
/* Return the current stratum of this host or zero if the host is not
/* Return the current stratum of this host or 16 if the host is not
synchronised */
extern int REF_GetOurStratum(void);

View File

@@ -209,8 +209,6 @@ n_runs_from_residuals(double *resid, int n)
/* Return a boolean indicating whether we had enough points for
regression */
#define MIN_SAMPLES_FOR_REGRESS 3
int
RGR_FindBestRegression
(double *x, /* independent variable */
@@ -255,7 +253,7 @@ RGR_FindBestRegression
int start, resid_start, nruns, npoints;
int i;
assert(n <= MAX_POINTS);
assert(n <= MAX_POINTS && m >= 0);
assert(n * REGRESS_RUNS_RATIO < sizeof (critical_runs) / sizeof (critical_runs[0]));
if (n < MIN_SAMPLES_FOR_REGRESS) {
@@ -392,7 +390,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, int *flags)
l = u + 1;
r = v;
do {
while (x[l] < piv && l < v) l++;
while (l < v && x[l] < piv) l++;
while (x[r] > piv) r--;
if (r <= l) break;
EXCH(x[l], x[r]);

View File

@@ -66,6 +66,9 @@ extern double RGR_GetChi2Coef(int dof);
points */
#define REGRESS_RUNS_RATIO 2
/* Minimum number of samples for regression */
#define MIN_SAMPLES_FOR_REGRESS 3
/* Return a status indicating whether there were enough points to
carry out the regression */

4
rtc.c
View File

@@ -84,7 +84,7 @@ RTC_Initialise(void)
file_name = CNF_GetRtcFile();
if (file_name) {
if (CNF_GetRTCSync()) {
if (CNF_GetRtcSync()) {
LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync");
}
@@ -144,7 +144,7 @@ RTC_TimeInit(void (*after_hook)(void *), void *anything)
/* ================================================== */
/* Do an initial read of the RTC and set the system time to it. This
is analogous to what /sbin/clock -s -u would do on Linux. */
is analogous to what /sbin/hwclock -s would do on Linux. */
void
RTC_TimePreInit(void)

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012
* Copyright (C) Miroslav Lichvar 2012-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -38,6 +38,7 @@
#include "local.h"
#include "util.h"
#include "sys_linux.h"
#include "reference.h"
#include "regress.h"
#include "rtc.h"
#include "rtc_linux.h"
@@ -67,9 +68,7 @@ static int fd = -1;
#define LOWEST_MEASUREMENT_PERIOD 15
#define HIGHEST_MEASUREMENT_PERIOD 480
/* Try to avoid doing regression after _every_ sample we accumulate */
#define N_SAMPLES_PER_REGRESSION 4
#define N_SAMPLES_PER_REGRESSION 1
static int measurement_period = LOWEST_MEASUREMENT_PERIOD;
@@ -125,6 +124,9 @@ static double coef_gain_rate;
RTC data file once we have reacquired its offset after the step */
static double saved_coef_gain_rate;
/* Threshold for automatic RTC trimming in seconds, zero when disabled */
static double autotrim_threshold;
/* Filename supplied by config file where RTC coefficients are
stored. */
static char *coefs_file_name;
@@ -263,13 +265,19 @@ static void
slew_samples
(struct timeval *raw, struct timeval *cooked,
double dfreq,
double doffset, int is_step_change,
double doffset,
LCL_ChangeType change_type,
void *anything)
{
int i;
double delta_time;
double old_seconds_fast, old_gain_rate;
if (change_type == LCL_ChangeUnknownStep) {
/* Drop all samples. */
n_samples = 0;
}
for (i=0; i<n_samples; i++) {
UTI_AdjustTimeval(system_times + i, cooked, system_times + i, &delta_time,
dfreq, doffset);
@@ -280,18 +288,13 @@ slew_samples
if (coefs_valid) {
coef_seconds_fast += doffset;
coef_gain_rate = (1.0 + dfreq) * (1.0 + coef_gain_rate) - 1.0;
coef_gain_rate += dfreq * (1.0 - coef_gain_rate);
}
#if 0
LOG(LOGS_INFO, LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
DEBUG_LOG(LOGF_RtcLinux, "dfreq=%.8f doffset=%.6f old_fast=%.6f old_rate=%.3f new_fast=%.6f new_rate=%.3f",
dfreq, doffset,
old_seconds_fast, 1.0e6 * old_gain_rate,
coef_seconds_fast, 1.0e6 * coef_gain_rate);
#else
(void)old_seconds_fast; (void)old_gain_rate;
#endif
}
/* ================================================== */
@@ -369,14 +372,55 @@ t_from_rtc(struct tm *stm) {
/* ================================================== */
static void
read_hwclock_file(const char *hwclock_file)
{
FILE *in;
char line[256];
int i;
if (!hwclock_file)
return;
in = fopen(hwclock_file, "r");
if (!in) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open hwclockfile %s",
hwclock_file);
return;
}
/* Read third line from the file. */
for (i = 0; i < 3; i++) {
if (!fgets(line, sizeof(line), in))
break;
}
fclose(in);
if (i == 3 && !strncmp(line, "LOCAL", 5)) {
rtc_on_utc = 0;
} else if (i == 3 && !strncmp(line, "UTC", 3)) {
rtc_on_utc = 1;
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read LOCAL/UTC setting from hwclockfile %s",
hwclock_file);
}
}
/* ================================================== */
static void
setup_config(void)
{
if (CNF_GetRTCOnUTC()) {
if (CNF_GetRtcOnUtc()) {
rtc_on_utc = 1;
} else {
rtc_on_utc = 0;
}
read_hwclock_file(CNF_GetHwclockFile());
autotrim_threshold = CNF_GetRtcAutotrim();
}
/* ================================================== */
@@ -429,6 +473,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
struct stat buf;
char *temp_coefs_file_name;
FILE *out;
int r1, r2;
/* Create a temporary file with a '.tmp' extension. */
@@ -450,9 +495,10 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
}
/* Gain rate is written out in ppm */
if ((fprintf(out, "%1d %ld %.6f %.3f\n",
valid,ref_time, offset, 1.0e6 * rate) < 0) |
fclose(out)) {
r1 = fprintf(out, "%1d %ld %.6f %.3f\n",
valid, ref_time, offset, 1.0e6 * rate);
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_coefs_file_name);
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not write to temporary RTC file %s.tmp",
coefs_file_name);
@@ -462,10 +508,12 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
/* Clone the file attributes from the existing file if there is one. */
if (!stat(coefs_file_name,&buf)) {
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid)) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not change ownership of temporary RTC file %s.tmp", coefs_file_name);
if (chown(temp_coefs_file_name,buf.st_uid,buf.st_gid) ||
chmod(temp_coefs_file_name,buf.st_mode & 0777)) {
LOG(LOGS_WARN, LOGF_RtcLinux,
"Could not change ownership or permissions of temporary RTC file %s.tmp",
coefs_file_name);
}
chmod(temp_coefs_file_name,buf.st_mode&0777);
}
/* Rename the temporary file to the correct location (see rename(2) for details). */
@@ -670,6 +718,7 @@ handle_relock_after_trim(void)
time_t ref;
double fast, slope;
valid = 0;
run_regression(1, &valid, &ref, &fast, &slope);
if (valid) {
@@ -687,8 +736,25 @@ handle_relock_after_trim(void)
/* ================================================== */
/* Day number of 1 Jan 1970 */
#define MJD_1970 40587
static void
maybe_autotrim(void)
{
/* Trim only when in normal mode, the coefficients are fresh, the current
offset is above the threshold and the system clock is synchronized */
if (operating_mode != OM_NORMAL || !coefs_valid || n_samples_since_regression)
return;
if (autotrim_threshold <= 0.0 || fabs(coef_seconds_fast) < autotrim_threshold)
return;
if (REF_GetOurStratum() >= 16)
return;
RTC_Linux_Trim();
}
/* ================================================== */
static void
process_reading(time_t rtc_time, struct timeval *system_time)
@@ -700,9 +766,10 @@ process_reading(time_t rtc_time, struct timeval *system_time)
switch (operating_mode) {
case OM_NORMAL:
if (n_samples_since_regression >= /* 4 */ 1 ) {
if (n_samples_since_regression >= N_SAMPLES_PER_REGRESSION) {
run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
n_samples_since_regression = 0;
maybe_autotrim();
}
break;
@@ -753,7 +820,6 @@ read_from_device(void *any)
/* This looks like a bad error : the file descriptor was indicating it was
* ready to read but we couldn't read anything. Give up. */
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
error = 1;
SCH_RemoveInputFileHandler(fd);
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
close(fd);
@@ -905,7 +971,7 @@ RTC_Linux_WriteParameters(void)
/* ================================================== */
/* Try to set the system clock from the RTC, in the same manner as
/sbin/clock -s -u would do. We're not as picky about OS version
/sbin/hwclock -s would do. We're not as picky about OS version
etc in this case, since we have fewer requirements regarding the
RTC behaviour than we do for the rest of the module. */
@@ -913,7 +979,7 @@ void
RTC_Linux_TimePreInit(void)
{
int fd, status;
struct rtc_time rtc_raw;
struct rtc_time rtc_raw, rtc_raw_retry;
struct tm rtc_tm;
time_t rtc_t, estimated_correct_rtc_t;
long interval;
@@ -931,7 +997,15 @@ RTC_Linux_TimePreInit(void)
return; /* Can't open it, and won't be able to later */
}
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
/* Retry reading the rtc until both read attempts give the same sec value.
This way the race condition is prevented that the RTC has updated itself
during the first read operation. */
do {
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
if (status >= 0) {
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
}
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
if (status >= 0) {
/* Convert to seconds since 1970 */

65
sched.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011
* Copyright (C) Miroslav Lichvar 2011, 2013-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -123,7 +123,7 @@ handle_slew(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *anything);
/* ================================================== */
@@ -503,14 +503,14 @@ handle_slew(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *anything)
{
TimerQueueEntry *ptr;
double delta;
int i;
if (is_step_change) {
if (change_type != LCL_ChangeAdjust) {
/* If a step change occurs, just shift all raw time stamps by the offset */
for (ptr = timer_queue.next; ptr != &timer_queue; ptr = ptr->next) {
@@ -529,30 +529,33 @@ handle_slew(struct timeval *raw,
/* ================================================== */
/* Try to handle unexpected backward time jump */
#define JUMP_DETECT_THRESHOLD 10
static void
recover_backjump(struct timeval *raw, struct timeval *cooked, int timeout)
static int
check_current_time(struct timeval *raw, int timeout)
{
double diff, err;
double diff;
UTI_DiffTimevalsToDouble(&diff, &last_select_ts_raw, raw);
if (last_select_ts_raw.tv_sec > raw->tv_sec + JUMP_DETECT_THRESHOLD) {
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!");
} else if (n_timer_queue_entries > 0 &&
timer_queue.next->tv.tv_sec + JUMP_DETECT_THRESHOLD < raw->tv_sec) {
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!");
} else {
return 1;
}
if (n_timer_queue_entries > 0) {
UTI_DiffTimevalsToDouble(&err, &(timer_queue.next->tv), &last_select_ts_raw);
} else {
err = 0.0;
}
if (timeout) {
assert(n_timer_queue_entries > 0);
UTI_DiffTimevalsToDouble(&diff, &timer_queue.next->tv, raw);
} else {
UTI_DiffTimevalsToDouble(&diff, &last_select_ts_raw, raw);
}
diff += err;
/* Cooked time may no longer be valid after dispatching the handlers */
LCL_NotifyExternalTimeStep(raw, raw, diff, fabs(diff));
if (timeout) {
err = 1.0;
}
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected! (correction %.1f +- %.1f seconds)", diff, err);
LCL_NotifyExternalTimeStep(raw, cooked, diff, err);
return 0;
}
/* ================================================== */
@@ -569,13 +572,13 @@ SCH_MainLoop(void)
assert(initialised);
while (!need_to_exit) {
/* Copy current set of read file descriptors */
memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
/* Dispatch timeouts and fill now with current raw time */
dispatch_timeouts(&now);
/* The timeout handlers may request quit */
if (need_to_exit)
break;
/* Check whether there is a timeout and set it up */
if (n_timer_queue_entries > 0) {
@@ -591,15 +594,19 @@ SCH_MainLoop(void)
timeout set, this is clearly ridiculous, so stop the run */
assert(ptv || n_read_fds);
/* Copy current set of read file descriptors */
memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
status = select(one_highest_fd, &rd, NULL, NULL, ptv);
errsv = errno;
LCL_ReadRawTime(&now);
LCL_CookTime(&now, &cooked, &err);
/* Check if time didn't jump backwards */
if (last_select_ts_raw.tv_sec > now.tv_sec + 1) {
recover_backjump(&now, &cooked, status == 0);
/* Check if the time didn't jump unexpectedly */
if (!check_current_time(&now, status == 0)) {
/* Cook the time again after handling the step */
LCL_CookTime(&now, &cooked, &err);
}
last_select_ts_raw = now;

176
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2013
* Copyright (C) Miroslav Lichvar 2011-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -45,6 +45,7 @@
#include "nameserv.h"
#include "mkdirpp.h"
#include "sched.h"
#include "regress.h"
/* ================================================== */
/* Flag indicating that we are initialised */
@@ -90,11 +91,14 @@ struct SRC_Instance_Record {
/* Flag indicating that we can use this source as a reference */
int selectable;
/* Flag indicating that the source is updating reachability */
int active;
/* Reachability register */
int reachability;
/* Flag indicating that only few samples were accumulated so far */
int beginning;
/* Number of set bits in the reachability register */
int reachability_size;
/* Updates left before allowing combining */
int outlier;
@@ -135,9 +139,6 @@ static int selected_source_index; /* Which source index is currently
selected (set to INVALID_SOURCE
if no current valid reference) */
/* Keep reachability status for last 8 samples */
#define REACH_BITS 8
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
@@ -153,7 +154,7 @@ static double combine_limit;
static void
slew_sources(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, int is_step_change, void *anything);
double doffset, LCL_ChangeType change_type, void *anything);
static void
add_dispersion(double dispersion, void *anything);
static char *
@@ -217,9 +218,10 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, SRC_SelectOpt
result->leap_status = LEAP_Normal;
result->ref_id = ref_id;
result->ip_addr = addr;
result->active = 0;
result->selectable = 0;
result->reachability = 0;
result->beginning = 1;
result->reachability_size = 0;
result->outlier = 0;
result->status = SRC_BAD_STATS;
result->type = type;
@@ -307,17 +309,29 @@ void SRC_AccumulateSample
inst->leap_status = leap_status;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
DEBUG_LOG(LOGF_Sources, "ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
source_to_string(inst), UTI_TimevalToString(sample_time), -offset, root_delay, root_dispersion, stratum);
#endif
/* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
IS FLIPPED */
SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
SST_DoNewRegression(inst->stats);
/* And redo clock selection */
SRC_SelectSource(inst->ref_id);
}
/* ================================================== */
void
SRC_SetActive(SRC_Instance inst)
{
inst->active = 1;
}
/* ================================================== */
void
SRC_UnsetActive(SRC_Instance inst)
{
inst->active = 0;
}
/* ================================================== */
@@ -327,9 +341,7 @@ SRC_SetSelectable(SRC_Instance inst)
{
inst->selectable = 1;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Sources, "%s", source_to_string(inst));
#endif
DEBUG_LOG(LOGF_Sources, "%s", source_to_string(inst));
/* Don't do selection at this point, though - that will come about
in due course when we get some useful data from the source */
@@ -342,35 +354,60 @@ SRC_UnsetSelectable(SRC_Instance inst)
{
inst->selectable = 0;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Sources, "%s%s", source_to_string(inst),
DEBUG_LOG(LOGF_Sources, "%s%s", source_to_string(inst),
(inst->index == selected_source_index) ? "(REF)":"");
#endif
/* If this was the previous reference source, we have to reselect! */
if (inst->index == selected_source_index) {
SRC_SelectSource(0);
SRC_SelectSource(NULL);
}
}
/* ================================================== */
static int
special_mode_end(void)
{
int i;
for (i = 0; i < n_sources; i++) {
/* No updates from inactive sources */
if (!sources[i]->active)
continue;
/* Don't expect more updates than from an offline iburst NTP source */
if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1)
continue;
/* Check if the source could still have enough samples to be selectable */
if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size +
SRC_Samples(sources[i]) >= MIN_SAMPLES_FOR_REGRESS)
return 0;
}
return 1;
}
void
SRC_UpdateReachability(SRC_Instance inst, int reachable)
{
inst->reachability <<= 1;
inst->reachability |= !!reachable;
inst->reachability &= ~(-1 << REACH_BITS);
inst->reachability &= ~(-1 << SOURCE_REACH_BITS);
/* The beginning is over when the first sample is at the end of the register */
if (inst->reachability & (1 << (REACH_BITS - 1)))
inst->beginning = 0;
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
if (!reachable && inst->index == selected_source_index) {
/* Try to select a better source */
SRC_SelectSource(0);
SRC_SelectSource(NULL);
}
/* Check if special reference update mode failed */
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
REF_SetUnsynchronised();
}
}
@@ -383,12 +420,23 @@ SRC_ResetReachability(SRC_Instance inst)
a peer selected even when not reachable */
#if 0
inst->reachability = 0;
inst->reachability_size = 0;
SRC_UpdateReachability(inst, 0);
#endif
}
/* ================================================== */
static void
log_selection_message(char *format, char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
LOG(LOGS_INFO, LOGF_Sources, format, arg);
}
/* ================================================== */
static int
compare_sort_elements(const void *a, const void *b)
{
@@ -459,7 +507,9 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
(reselect_distance + sources[selected_source_index]->sel_info.root_distance) ||
fabs(*frequency - src_frequency) >
combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) {
sources[index]->outlier = !sources[index]->beginning ? OUTLIER_PENALTY : 1;
/* Use a smaller penalty in first few updates */
sources[index]->outlier = sources[index]->reachability_size >= SOURCE_REACH_BITS ?
OUTLIER_PENALTY : 1;
} else if (sources[index]->outlier) {
sources[index]->outlier--;
}
@@ -472,10 +522,8 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / src_skew;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Sources, "combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
DEBUG_LOG(LOGF_Sources, "combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
#endif
sum_offset_weight += offset_weight;
sum_offset += offset_weight * src_offset;
@@ -495,10 +543,8 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
*frequency = sum_frequency / sum_frequency_weight;
*skew = 1.0 / sqrt(inv_sum2_skew);
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e",
DEBUG_LOG(LOGF_Sources, "combined result offset=%e sd=%e freq=%e skew=%e",
*offset, *offset_sd, *frequency, *skew);
#endif
return combined;
}
@@ -511,9 +557,9 @@ combine_sources(int n_sel_sources, struct timeval *ref_time, double *offset,
or match_refid is equal to the selected reference source refid */
void
SRC_SelectSource(uint32_t match_refid)
SRC_SelectSource(SRC_Instance updated_inst)
{
int i, j, index, old_selected_index;
int i, j, index, old_selected_index, sel_prefer;
struct timeval now, ref_time;
double src_offset, src_offset_sd, src_frequency, src_skew;
double src_root_delay, src_root_dispersion;
@@ -535,7 +581,7 @@ SRC_SelectSource(uint32_t match_refid)
if (n_sources == 0) {
/* In this case, we clearly cannot synchronise to anything */
if (selected_source_index != INVALID_SOURCE) {
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no sources");
log_selection_message("Can't synchronise: no sources", NULL);
selected_source_index = INVALID_SOURCE;
REF_SetUnsynchronised();
}
@@ -708,7 +754,7 @@ SRC_SelectSource(uint32_t match_refid)
*/
if (selected_source_index != INVALID_SOURCE) {
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no majority");
log_selection_message("Can't synchronise: no majority", NULL);
}
selected_source_index = INVALID_SOURCE;
@@ -790,7 +836,6 @@ SRC_SelectSource(uint32_t match_refid)
if (index != INVALID_SOURCE) {
sources[index]->status = SRC_SELECTABLE;
sel_sources[j++] = sel_sources[i];
index++;
}
}
n_sel_sources = j;
@@ -822,6 +867,9 @@ SRC_SelectSource(uint32_t match_refid)
}
if (j > 0) {
n_sel_sources = j;
sel_prefer = 1;
} else {
sel_prefer = 0;
}
/* Now find minimum stratum. If none are left now,
@@ -854,7 +902,8 @@ SRC_SelectSource(uint32_t match_refid)
for (i = 0; i < n_sources; i++) {
/* Reset score for non-selectable sources */
if (sources[i]->status != SRC_SELECTABLE) {
if (sources[i]->status != SRC_SELECTABLE ||
(sel_prefer && sources[i]->sel_option != SRC_SelectPrefer)) {
sources[i]->sel_score = 1.0;
sources[i]->outlier = OUTLIER_PENALTY;
continue;
@@ -869,8 +918,8 @@ SRC_SelectSource(uint32_t match_refid)
/* Update score, but only for source pairs where one source
has a new sample */
if (sources[i]->ref_id == match_refid ||
sources[selected_source_index]->ref_id == match_refid) {
if (sources[i] == updated_inst ||
sources[selected_source_index] == updated_inst) {
sources[i]->sel_score *= sel_src_distance / distance;
@@ -888,8 +937,9 @@ SRC_SelectSource(uint32_t match_refid)
}
#if 0
LOG(LOGS_INFO, LOGF_Sources, "select score=%f refid=%lx match_refid=%lx status=%d dist=%f",
sources[i]->sel_score, sources[i]->ref_id, match_refid, sources[i]->status, distance);
LOG(LOGS_INFO, LOGF_Sources, "select score=%f refid=%x match_refid=%x status=%d dist=%f",
sources[i]->sel_score, sources[i]->ref_id, updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
#endif
if (max_score < sources[i]->sel_score) {
@@ -910,7 +960,7 @@ SRC_SelectSource(uint32_t match_refid)
/* We have to elect a new synchronisation source */
selected_source_index = max_score_index;
LOG(LOGS_INFO, LOGF_Sources, "Selected source %s",
log_selection_message("Selected source %s",
source_to_string(sources[selected_source_index]));
#if 0
@@ -926,10 +976,10 @@ SRC_SelectSource(uint32_t match_refid)
sources[selected_source_index]->status = SRC_SYNC;
/* Update local reference only when a new source was selected or a new
sample was received (i.e. match_refid is equal to selected refid) */
/* Update local reference only when a new source was selected
or the selected source has a new sample */
if (selected_source_index != old_selected_index ||
match_refid == sources[selected_source_index]->ref_id) {
updated_inst == sources[selected_source_index]) {
/* Now just use the statistics of the selected source combined with
the other selectable sources for trimming the local clock */
@@ -958,7 +1008,7 @@ SRC_SelectSource(uint32_t match_refid)
} else {
if (selected_source_index != INVALID_SOURCE) {
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no selectable sources");
log_selection_message("Can't synchronise: no selectable sources", NULL);
}
selected_source_index = INVALID_SOURCE;
}
@@ -967,7 +1017,7 @@ SRC_SelectSource(uint32_t match_refid)
} else {
/* No sources provided valid endpoints */
if (selected_source_index != INVALID_SOURCE) {
LOG(LOGS_INFO, LOGF_Sources, "Can't synchronise: no reachable sources");
log_selection_message("Can't synchronise: no reachable sources", NULL);
}
selected_source_index = INVALID_SOURCE;
}
@@ -985,7 +1035,7 @@ void
SRC_ReselectSource(void)
{
selected_source_index = INVALID_SOURCE;
SRC_SelectSource(0);
SRC_SelectSource(NULL);
}
/* ================================================== */
@@ -1037,15 +1087,23 @@ slew_sources(struct timeval *raw,
struct timeval *cooked,
double dfreq,
double doffset,
int is_step_change,
LCL_ChangeType change_type,
void *anything)
{
int i;
for (i=0; i<n_sources; i++) {
SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
SST_ResetInstance(sources[i]->stats);
} else {
SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset);
}
}
if (change_type == LCL_ChangeUnknownStep) {
/* After resetting no source is selectable, set reference unsynchronised */
SRC_SelectSource(NULL);
}
}
/* ================================================== */
@@ -1162,6 +1220,20 @@ SRC_ReadNumberOfSources(void)
/* ================================================== */
int
SRC_ActiveSources(void)
{
int i, r;
for (i = r = 0; i < n_sources; i++)
if (sources[i]->active)
r++;
return r;
}
/* ================================================== */
int
SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now)
{

View File

@@ -3,6 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -34,6 +35,9 @@
#include "ntp.h"
#include "reports.h"
/* Size of the source reachability register */
#define SOURCE_REACH_BITS 8
/* This datatype is used to hold information about sources. The
instance must be passed when calling many of the interface
functions */
@@ -114,6 +118,12 @@ extern void SRC_GetFrequencyRange(SRC_Instance instance, double *lo, double *hi)
extern void SRC_AccumulateSample(SRC_Instance instance, struct timeval *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
/* This routine sets the source as receiving reachability updates */
extern void SRC_SetActive(SRC_Instance inst);
/* This routine sets the source as not receiving reachability updates */
extern void SRC_UnsetActive(SRC_Instance inst);
/* This routine indicates that packets with valid headers are being
received from the designated source */
extern void SRC_SetSelectable(SRC_Instance instance);
@@ -131,11 +141,11 @@ extern void SRC_ResetReachability(SRC_Instance inst);
/* This routine is used to select the best source from amongst those
we currently have valid data on, and use it as the tracking base
for the local time. Updates are only made to the local reference
if a new source is selected or match_addr is equal to the selected
reference source address. (This avoids updating the frequency
if a new source is selected or updated_inst is the selected
reference source. (This avoids updating the frequency
tracking for every sample from other sources - only the ones from
the selected reference make a difference) */
extern void SRC_SelectSource(uint32_t match_refid);
extern void SRC_SelectSource(SRC_Instance updated_inst);
/* Force reselecting the best source */
extern void SRC_ReselectSource(void);
@@ -162,6 +172,7 @@ extern void SRC_ReloadSources(void);
extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timeval *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timeval *now);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012
* Copyright (C) Miroslav Lichvar 2011-2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -188,6 +188,26 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr)
inst = MallocNew(struct SST_Stats_Record);
inst->refid = refid;
inst->ip_addr = addr;
SST_ResetInstance(inst);
return inst;
}
/* ================================================== */
/* This function deletes an instance of the statistics handler. */
void
SST_DeleteInstance(SST_Stats inst)
{
Free(inst);
}
/* ================================================== */
void
SST_ResetInstance(SST_Stats inst)
{
inst->n_samples = 0;
inst->runs_samples = 0;
inst->last_sample = 0;
@@ -203,16 +223,6 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr)
inst->offset_time.tv_usec = 0;
inst->variance = 16.0;
inst->nruns = 0;
return inst;
}
/* ================================================== */
/* This function deletes an instance of the statistics handler. */
void
SST_DeleteInstance(SST_Stats inst)
{
Free(inst);
}
/* ================================================== */
@@ -258,7 +268,7 @@ SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time,
UTI_CompareTimevals(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
LOG(LOGS_WARN, LOGF_SourceStats, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
prune_register(inst, inst->n_samples);
SST_ResetInstance(inst);
}
n = inst->last_sample = (inst->last_sample + 1) %
@@ -569,10 +579,8 @@ SST_GetSelectionData(SST_Stats inst, struct timeval *now,
*select_ok = inst->regression_ok;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_SourceStats, "n=%d off=%f dist=%f var=%f selok=%d",
DEBUG_LOG(LOGF_SourceStats, "n=%d off=%f dist=%f var=%f selok=%d",
inst->n_samples, offset, *root_distance, *variance, *select_ok);
#endif
}
/* ================================================== */
@@ -586,6 +594,8 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
int i, j;
double elapsed_sample;
assert(inst->n_samples > 0);
i = get_runsbuf_index(inst, inst->best_single_sample);
j = get_buf_index(inst, inst->best_single_sample);
@@ -599,10 +609,8 @@ SST_GetTrackingData(SST_Stats inst, struct timeval *ref_time,
UTI_DiffTimevalsToDouble(&elapsed_sample, &inst->offset_time, &inst->sample_times[i]);
*root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
DEBUG_LOG(LOGF_SourceStats, "n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew, *average_offset, *offset_sd, *root_dispersion);
#endif
}
@@ -626,13 +634,10 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
prev_offset = inst->offsets[i];
inst->offsets[i] += delta_time;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_SourceStats, "i=%d old_st=[%s] new_st=[%s] old_off=%f new_off=%f",
DEBUG_LOG(LOGF_SourceStats, "i=%d old_st=[%s] new_st=[%s] old_off=%f new_off=%f",
i, UTI_TimevalToString(&prev), UTI_TimevalToString(sample),
prev_offset, inst->offsets[i]);
#else
(void)prev_offset;
#endif
}
/* Do a half-baked update to the regression estimates */
@@ -644,14 +649,10 @@ SST_SlewSamples(SST_Stats inst, struct timeval *when, double dfreq, double doffs
inst->estimated_offset += delta_time;
inst->estimated_frequency -= dfreq;
#ifdef TRACEON
LOG(LOGS_INFO, LOGF_SourceStats, "old_off_time=[%s] new=[%s] old_off=%f new_off=%f old_freq=%.3fppm new_freq=%.3fppm",
DEBUG_LOG(LOGF_SourceStats, "old_off_time=[%s] new=[%s] old_off=%f new_off=%f old_freq=%.3fppm new_freq=%.3fppm",
UTI_TimevalToString(&prev), UTI_TimevalToString(&(inst->offset_time)),
prev_offset, inst->estimated_offset,
1.0e6*prev_freq, 1.0e6*inst->estimated_frequency);
#else
(void)prev; (void)prev_freq;
#endif
}
/* ================================================== */
@@ -781,8 +782,11 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
unsigned long sec, usec;
double weight;
assert(!inst->n_samples);
if (fgets(line, sizeof(line), in) &&
(sscanf(line, "%u", &inst->n_samples) == 1) && inst->n_samples <= MAX_SAMPLES) {
sscanf(line, "%d", &inst->n_samples) == 1 &&
inst->n_samples > 0 && inst->n_samples <= MAX_SAMPLES) {
line_number = 2;

View File

@@ -43,6 +43,9 @@ extern SST_Stats SST_CreateInstance(uint32_t refid, IPAddr *addr);
/* This function deletes an instance of the statistics handler. */
extern void SST_DeleteInstance(SST_Stats inst);
/* This function resets an instance */
extern void SST_ResetInstance(SST_Stats inst);
/* This function accumulates a single sample into the statistics handler
sample_time is the epoch at which the sample is to be considered to
@@ -67,11 +70,6 @@ extern void SST_AccumulateSample(SST_Stats inst, struct timeval *sample_time, do
down to that number of samples. */
extern void SST_DoNewRegression(SST_Stats inst);
/* This function does a simple regression on what is in the register,
without trying to optimise the error bounds on the frequency by
deleting old samples */
extern void SST_DoUpdateRegression(SST_Stats inst);
/* Return the assumed worst case range of values that this source's
frequency lies within. Frequency is defined as the amount of time
the local clock gains relative to the source per unit local clock

314
sys_generic.c Normal file
View File

@@ -0,0 +1,314 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Generic driver functions to complete system-specific drivers
*/
#include "config.h"
#include "sysincl.h"
#include "sys_generic.h"
#include "conf.h"
#include "local.h"
#include "localp.h"
#include "logging.h"
#include "sched.h"
#include "util.h"
/* ================================================== */
/* System clock frequency drivers */
static lcl_ReadFrequencyDriver drv_read_freq;
static lcl_SetFrequencyDriver drv_set_freq;
/* Current frequency as requested by the local module (in ppm) */
static double base_freq;
/* Maximum frequency that can be set by drv_set_freq (in ppm) */
static double max_freq;
/* Maximum expected delay in the actual frequency change (e.g. kernel ticks)
in local time */
static double max_freq_change_delay;
/* Maximum allowed frequency offset relative to the base frequency */
static double max_corr_freq;
/* Amount of outstanding offset to process */
static double offset_register;
/* Minimum offset to correct */
#define MIN_OFFSET_CORRECTION 1.0e-9
/* Current frequency offset between base_freq and the real clock frequency
as set by drv_set_freq (not in ppm) */
static double slew_freq;
/* Time (raw) of last update of slewing frequency and offset */
static struct timeval slew_start;
/* Limits for the slew timeout */
#define MIN_SLEW_TIMEOUT 1.0
#define MAX_SLEW_TIMEOUT 1.0e4
/* Scheduler timeout ID and flag if the timer is currently running */
static SCH_TimeoutID slew_timeout_id;
static int slew_timer_running;
/* Suggested offset correction rate (correction time * offset) */
static double correction_rate;
/* Maximum expected offset correction error caused by delayed change in the
real frequency of the clock */
static double slew_error;
/* ================================================== */
static void handle_end_of_slew(void *anything);
static void update_slew(void);
/* ================================================== */
/* Adjust slew_start on clock step */
static void
handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type == LCL_ChangeUnknownStep) {
/* Reset offset and slewing */
slew_start = *raw;
offset_register = 0.0;
update_slew();
} else if (change_type == LCL_ChangeStep) {
UTI_AddDoubleToTimeval(&slew_start, -doffset, &slew_start);
}
}
/* ================================================== */
/* End currently running slew and start a new one */
static void
update_slew(void)
{
struct timeval now, end_of_slew;
double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */
if (slew_timer_running)
SCH_RemoveTimeout(slew_timeout_id);
LCL_ReadRawTime(&now);
/* Adjust the offset register by achieved slew */
UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
offset_register -= slew_freq * duration;
/* Estimate how long should the next slew take */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
duration = MAX_SLEW_TIMEOUT;
} else {
duration = correction_rate / fabs(offset_register);
if (duration < MIN_SLEW_TIMEOUT)
duration = MIN_SLEW_TIMEOUT;
}
/* Get frequency offset needed to slew the offset in the duration
and clamp it to the allowed maximum */
corr_freq = offset_register / duration;
if (corr_freq < -max_corr_freq)
corr_freq = -max_corr_freq;
else if (corr_freq > max_corr_freq)
corr_freq = max_corr_freq;
/* Get the new real frequency and clamp it */
total_freq = base_freq + corr_freq * (1.0e6 - base_freq);
if (total_freq > max_freq)
total_freq = max_freq;
else if (total_freq < -max_freq)
total_freq = -max_freq;
/* Set the new frequency (the actual frequency returned by the call may be
slightly different from the requested frequency due to rounding) */
total_freq = (*drv_set_freq)(total_freq);
/* Compute the new slewing frequency, it's relative to the real frequency to
make the calculation in offset_convert() cheaper */
old_slew_freq = slew_freq;
slew_freq = (total_freq - base_freq) / (1.0e6 - total_freq);
/* Compute the dispersion introduced by changing frequency and add it
to all statistics held at higher levels in the system */
slew_error = fabs((old_slew_freq - slew_freq) * max_freq_change_delay);
if (slew_error >= MIN_OFFSET_CORRECTION)
lcl_InvokeDispersionNotifyHandlers(slew_error);
/* Compute the duration of the slew and clamp it. If the slewing frequency
is zero or has wrong sign (e.g. due to rounding in the frequency driver or
when base_freq is larger than max_freq), use maximum timeout and try again
on the next update. */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
offset_register * slew_freq <= 0.0) {
duration = MAX_SLEW_TIMEOUT;
} else {
duration = offset_register / slew_freq;
if (duration < MIN_SLEW_TIMEOUT)
duration = MIN_SLEW_TIMEOUT;
else if (duration > MAX_SLEW_TIMEOUT)
duration = MAX_SLEW_TIMEOUT;
}
/* 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,
duration, slew_error);
}
/* ================================================== */
static void
handle_end_of_slew(void *anything)
{
slew_timer_running = 0;
update_slew();
}
/* ================================================== */
static double
read_frequency(void)
{
return base_freq;
}
/* ================================================== */
static double
set_frequency(double freq_ppm)
{
base_freq = freq_ppm;
update_slew();
return base_freq;
}
/* ================================================== */
static void
accrue_offset(double offset, double corr_rate)
{
offset_register += offset;
correction_rate = corr_rate;
update_slew();
}
/* ================================================== */
/* Determine the correction to generate the cooked time for given raw time */
static void
offset_convert(struct timeval *raw,
double *corr, double *err)
{
double duration;
UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
*corr = slew_freq * duration - offset_register;
if (err)
*err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0;
}
/* ================================================== */
/* Positive means currently fast of true time, i.e. jump backwards */
static void
apply_step_offset(double offset)
{
struct timeval old_time, new_time;
double err;
LCL_ReadRawTime(&old_time);
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
if (settimeofday(&new_time, NULL) < 0) {
LOG_FATAL(LOGF_SysGeneric, "settimeofday() failed");
}
LCL_ReadRawTime(&old_time);
UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
lcl_InvokeDispersionNotifyHandlers(fabs(err));
}
/* ================================================== */
void
SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay,
lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_freq,
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
lcl_SetLeapDriver sys_set_leap)
{
max_freq = max_set_freq_ppm;
max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
drv_read_freq = sys_read_freq;
drv_set_freq = sys_set_freq;
base_freq = (*drv_read_freq)();
slew_freq = 0.0;
offset_register = 0.0;
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
accrue_offset, sys_apply_step_offset ?
sys_apply_step_offset : apply_step_offset,
offset_convert, sys_set_leap);
LCL_AddParameterChangeHandler(handle_step, NULL);
}
/* ================================================== */
void
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;
}
(*drv_set_freq)(base_freq);
}
/* ================================================== */

42
sys_generic.h Normal file
View File

@@ -0,0 +1,42 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for generic driver
*/
#ifndef GOT_SYS_GENERIC_H
#define GOT_SYS_GENERIC_H
#include "localp.h"
/* Register a completed driver that implements offset functions on top of
provided frequency functions */
extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay,
lcl_ReadFrequencyDriver sys_read_freq,
lcl_SetFrequencyDriver sys_set_freq,
lcl_ApplyStepOffsetDriver sys_apply_step_offset,
lcl_SetLeapDriver sys_set_leap);
extern void SYS_Generic_Finalise(void);
#endif /* GOT_SYS_GENERIC_H */

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2011
* Copyright (C) Miroslav Lichvar 2011, 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -35,6 +35,9 @@
#include "sched.h"
#include "tempcomp.h"
/* Sanity limit (in ppm) */
#define MAX_COMP 10.0
static SCH_TimeoutID timeout_id;
static LOG_FileID logfileid;
@@ -54,8 +57,7 @@ read_timeout(void *arg)
if (f && fscanf(f, "%lf", &temp) == 1) {
comp = k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2;
/* Don't allow corrections above 10 ppm */
if (fabs(comp) < 10.0) {
if (fabs(comp) <= MAX_COMP) {
comp = LCL_SetTempComp(comp);
if (logfileid != -1) {
@@ -65,7 +67,14 @@ read_timeout(void *arg)
LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
UTI_TimeToLogForm(now.tv_sec), temp, comp);
}
} else {
LOG(LOGS_WARN, LOGF_TempComp,
"Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
comp, MAX_COMP);
}
} else {
LOG(LOGS_WARN, LOGF_TempComp, "Could not read temperature from %s",
filename);
}
if (f)

13
test/simulation/001-defaults Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
. test.common
test_start "default test settings"
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

@@ -0,0 +1,22 @@
#!/bin/bash
. test.common
test_start "large network"
time_rms_limit=5e-4
server_strata=3
servers=4
clients=5
client_start=2000
min_sync_time=2100
max_sync_time=2300
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
test_pass

View File

@@ -0,0 +1,19 @@
#!/bin/bash
. test.common
test_start "large frequency offset"
max_sync_time=1000
for freq_offset in -5e-2 -5e-3 5e-3 5e-2; do
# Adjust offset so it's close to 0 on first clock update
time_offset=$(awk "BEGIN {print -($freq_offset * 130)}")
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

View File

@@ -0,0 +1,18 @@
#!/bin/bash
. test.common
test_start "large time offset"
min_sync_time=1300
max_sync_time=1400
for time_offset in -1e2 1e2; do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

View File

@@ -0,0 +1,46 @@
#!/bin/bash
. test.common
test_start "external time step"
min_sync_time=1500
max_sync_time=1550
for step in -1e2 1e2; do
# Make one step in 150th second
client_step="(* $step (equal 0.1 (sum 1.0) 150))"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
min_sync_time=5120
max_sync_time=6200
client_conf="makestep 1 -1"
for step in -1e8 -1e5 1e5 1e8; do
# Make one step in 5000th second
client_step="(* $step (equal 0.1 (sum 1.0) 5000))"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
min_sync_time=$default_min_sync_time
max_sync_time=$default_max_sync_time
time_max_limit=2e4
time_rms_limit=8e3
for step in -1e4 1e4; do
# Make a step every 500 seconds
client_step="(* $step (equal 0.1 (% (sum 1.0) 500) 0))"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

21
test/simulation/006-largejitter Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
. test.common
test_start "large jitter"
time_offset=1e0
jitter=1e-1
time_max_limit=5e-1
freq_max_limit=2e-1
time_rms_limit=1e-1
freq_rms_limit=5e-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

20
test/simulation/007-largewander Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
. test.common
test_start "large wander"
wander=1e-7
time_max_limit=5e-3
freq_max_limit=5e-3
time_rms_limit=1e-3
freq_rms_limit=1e-4
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

28
test/simulation/101-poll Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
. test.common
test_start "minpoll/maxpoll options"
wander=0.0
jitter=1e-6
time_max_limit=1e-5
freq_max_limit=1e-5
time_rms_limit=5e-6
freq_rms_limit=5e-6
client_conf="makestep 1e-2 1"
for poll in $(seq 2 14); do
client_server_options="minpoll $poll maxpoll $poll"
limit=$[2**$poll * 10]
min_sync_time=$[2**$poll * 2]
max_sync_time=$[2**$poll * 21 / 10 + 1]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

23
test/simulation/102-iburst Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
. test.common
test_start "iburst option"
freq_offset=1e-4
client_conf="makestep 1e-2 1
driftfile tmp/drift"
client_server_options="iburst"
min_sync_time=4
max_sync_time=6
echo "100 1.0" > tmp/drift
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

@@ -0,0 +1,32 @@
#!/bin/bash
. test.common
test_start "initstepslew directive"
freq_offset=0.0
wander=0.0
time_rms_limit=1e-3
limit=100
client_conf="initstepslew 5 192.168.123.1"
min_sync_time=6
max_sync_time=35
for time_offset in -2.0 -0.2 0.2 2.0; do
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
min_sync_time=5
max_sync_time=5
for time_offset in -1e8 -1e2 1e2 1e8; do
run_test || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

23
test/simulation/104-driftfile Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
. test.common
test_start "driftfile directive"
servers=0
time_offset=0.0
wander=0.0
limit=10
freq_max_limit=1e-9
min_sync_time=1
max_sync_time=1
client_conf="driftfile tmp/drift"
for freq_offset in -5e-2 -5e-4 -5e-6 5e-6 5e-4 5e-2; do
awk "BEGIN {printf \"%.9e 1\", 1e6 - 1 / (1 + $freq_offset) * 1e6}" > tmp/drift
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

42
test/simulation/105-ntpauth Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
. test.common
test_start "NTP authentication"
server_conf="keyfile tmp/keys"
client_conf="keyfile tmp/keys"
cat > tmp/keys <<-EOF
1 $(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24)
2 ASCII:$(tr -c -d 'a-zA-Z0-9' < /dev/urandom 2> /dev/null | head -c 24)
3 MD5 ASCII:$(tr -c -d 'a-zA-Z' < /dev/urandom 2> /dev/null | head -c 24)
4 MD5 HEX:$(tr -c -d '0-9A-F' < /dev/urandom 2> /dev/null | head -c 32)
EOF
for key in 1 2 3 4; do
client_server_options="key $key"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
server_conf=""
run_test || test_fail
check_chronyd_exit || test_fail
# This check must fail as the server doesn't know the key
check_sync && test_fail
check_packet_interval || test_fail
server_conf="keyfile tmp/keys"
client_conf=""
run_test || test_fail
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
test_pass

18
test/simulation/106-refclock Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
. test.common
test_start "SHM refclock"
servers=0
limit=1000
refclock_jitter=$jitter
min_sync_time=45
max_sync_time=70
client_conf="refclock SHM 0"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
test_pass

46
test/simulation/107-allowdeny Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/bash
. test.common
test_start "allow/deny directives"
limit=500
# Note that start_client in clknetsim.bash always adds allow to the config
for server_conf in \
"deny" \
"deny all" \
"deny 192.168.0.0/16" \
"deny 192.168.123" \
"deny 192.168.123.2" \
"deny all
allow 192.168.124.0/24"
do
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
done
for server_conf in \
"deny all
allow" \
"deny all
allow all" \
"deny all
allow 192.168.123" \
"deny all
allow 192.168.123/24" \
"deny 192.168.124.0/24"
do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

33
test/simulation/108-peer Executable file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
. test.common
test_start "NTP peers"
# Allow and drop packets to the server in 1000 second intervals, so only one
# client has access to it and the other is forced to switch to the peer.
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 from 2)
(equal 0.1 to 1)
(equal 0.1 (min (% time 2000) 1000) 1000))
(* -1
(equal 0.1 from 3)
(equal 0.1 to 1)
(equal 0.1 (max (% time 2000) 1000) 1000)))
EOF
)
clients=2
peers=2
max_sync_time=1000
client_server_options="minpoll 6 maxpoll 6"
client_peer_options="minpoll 6 maxpoll 6"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
test_pass

41
test/simulation/109-makestep Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
. test.common
test_start "makestep directive"
client_conf="makestep 0 -1
corrtimeratio 1e10"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
limit=200
jitter=1e-5
client_conf="makestep 2 1"
min_sync_time=130
max_sync_time=150
for time_offset in -1.0 -0.1 0.1 1.0; do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
min_sync_time=120
max_sync_time=140
for time_offset in -1e8 -1e2 1e2 1e8; do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
test_pass

37
test/simulation/110-chronyc Executable file
View File

@@ -0,0 +1,37 @@
#!/bin/bash
. test.common
test_start "chronyc"
chronyc_conf="tracking
sources
sourcestats"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^Reference ID : 192\.168\.123\.1 \(192\.168\.123\.1\)
Stratum : 2
Ref time \(UTC\) : Fri Jan 1 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time
Last offset : [+-]0\.000...... seconds
RMS offset : 0\.000...... seconds
Frequency : (99|100)\.... ppm fast
Residual freq : [+-][0-9]\.... ppm
Skew : [0-9]\.... ppm
Root delay : 0\.000... seconds
Root dispersion : 0\.000... seconds
Update interval : [0-9]+\.. seconds
Leap status : Normal
210 Number of sources = 1
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
210 Number of sources = 1
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s$" \
|| test_fail
test_pass

17
test/simulation/111-knownclient Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
. test.common
test_start "reply to client configured as server"
server_conf="server 192.168.123.2
acquisitionport 123"
client_conf="acquisitionport 123"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_port || test_fail
check_sync || test_fail
test_pass

55
test/simulation/112-port Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
. test.common
test_start "port and acquisitionport directives"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
# This check is expected to fail
check_packet_port && test_fail
client_conf="acquisitionport 123"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_port || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_conf=""
for server_conf in \
"port 0" \
"acquisitionport 123
port 0"
do
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_port || test_fail
check_packet_interval || test_fail
# These checks are expected to fail
check_source_selection && test_fail
check_sync && test_fail
done
server_conf="port 124
acquisitionport 123"
client_server_options="port 124"
for client_conf in \
"acquisitionport 0" \
"acquisitionport 123" \
"acquisitionport 124"
do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
# This check is expected to fail
check_packet_port && test_fail
done
test_pass

21
test/simulation/113-leapsecond Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
. test.common
test_start "leap second"
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
limit=$[4 * 24 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
server_step="(* 1.0 (equal 0.1 (sum 1.0) $[2 * 24 * 3600 + 1]))"
client_step="(* 1.0 (equal 0.1 (sum 1.0) $[2 * 24 * 3600 + 1]))"
refclock_jitter=1e-9
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

@@ -0,0 +1,35 @@
#!/bin/bash
. test.common
# Test fix in commit 60d0fa299307076143da94d36deb7b908fa9bdb7
test_start "frequency accumulation"
time_offset=100.0
jitter=1e-6
base_delay=1e-6
wander=0.0
limit=180
time_max_limit=1e-5
freq_max_limit=1e-7
time_rms_limit=1e-5
freq_rms_limit=1e-7
min_sync_time=120
max_sync_time=140
client_server_options="minpoll 6 maxpoll 6"
client_conf="driftfile tmp/drift
makestep 1 1"
for freq_offset in -5e-2 -5e-4 5e-4 5e-2; do
for drift in -1e+4 -1e+2 1e+2 1e+4; do
echo "$drift 100000" > tmp/drift
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
done
done
test_pass

12
test/simulation/README Normal file
View File

@@ -0,0 +1,12 @@
This is a collection of simulation tests. They use clknetsim to simulate
multiple systems connected in a network. It's available at
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 tests are written in bash and they can be run directly. The ./run script
runs all tests.

20
test/simulation/run Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
. test.common
passed=() failed=()
[ $# -gt 0 ] && tests=($@) || tests=([0-9]*-*[^_])
for test in "${tests[@]}"; do
echo "$test ($[${#passed[@]} + ${#failed[@]} + 1]/${#tests[@]})"
./$test && passed=(${passed[@]} $test) || failed=(${failed[@]} $test)
echo
done
echo "SUMMARY:"
echo " TOTAL $[${#passed[@]} + ${#failed[@]}]"
echo " PASSED ${#passed[@]}"
echo " FAILED ${#failed[@]} (${failed[@]})"
[ ${#failed} -eq 0 ]

437
test/simulation/test.common Normal file
View File

@@ -0,0 +1,437 @@
# Copyright (C) 2013-2014 Miroslav Lichvar <mlichvar@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
export LC_ALL=C
export PATH=../../:$PATH
export CLKNETSIM_PATH=clknetsim
# Known working clknetsim revision
clknetsim_revision=326b619e36858c1f88240e17317595509e3d00de
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
fi
. $CLKNETSIM_PATH/clknetsim.bash
# Default test testings
default_limit=10000
default_time_offset=1e-1
default_freq_offset=1e-4
default_base_delay=1e-4
default_jitter=1e-4
default_wander=1e-9
default_refclock_jitter=""
default_update_interval=0
default_shift_pll=2
default_server_strata=1
default_servers=1
default_clients=1
default_peers=0
default_server_start=0.0
default_client_start=0.0
default_chronyc_start=1000.0
default_server_step=""
default_client_step=""
default_server_server_options=""
default_client_server_options=""
default_server_peer_options=""
default_client_peer_options=""
default_server_conf=""
default_client_conf=""
default_chronyc_conf=""
default_chronyd_options=""
default_time_max_limit=1e-3
default_freq_max_limit=5e-4
default_time_rms_limit=2e-4
default_freq_rms_limit=1e-5
default_min_sync_time=120
default_max_sync_time=210
# Initialize test settings from their defaults
for defopt in $(declare | grep '^default_'); do
defoptname=${defopt%%=*}
optname=${defoptname#default_}
eval "[ -z \"\${$optname:+a}\" ] && $optname=\"\$$defoptname\""
done
test_start() {
rm -f tmp/*
echo "Testing $@:"
}
test_pass() {
echo "PASS"
exit 0
}
test_fail() {
echo "FAIL"
exit 1
}
test_ok() {
pad_line
echo -e "\tOK"
return 0
}
test_bad() {
pad_line
echo -e "\tBAD"
return 1
}
test_error() {
pad_line
echo -e "\tERROR"
return 1
}
msg_length=0
pad_line() {
local line_length=56
[ $msg_length -lt $line_length ] && \
printf "%$[$line_length - $msg_length]s" ""
msg_length=0
}
# Print aligned message
test_message() {
local level=$1 eol=$2
shift 2
local msg="$*"
while [ $level -gt 0 ]; do
echo -n " "
level=$[$level - 1]
msg_length=$[$msg_length + 2]
done
echo -n "$msg"
msg_length=$[$msg_length + ${#msg}]
if [ $eol -ne 0 ]; then
echo
msg_length=0
fi
}
get_wander_expr() {
local scaled_wander
scaled_wander=$(awk "BEGIN {print $wander / \
sqrt($update_interval < 0 ? 2^-($update_interval) : 1)}")
echo "(+ $freq_offset (sum (* $scaled_wander (normal))))"
}
get_delay_expr() {
echo "(+ $base_delay (* $jitter (exponential)))"
}
get_refclock_expr() {
echo "(* $refclock_jitter (normal))"
}
get_chronyd_nodes() {
echo $[$servers * $server_strata + $clients]
}
get_chronyd_conf() {
local i stratum=$1 peer=$2
if [ $stratum -eq 1 ]; then
echo "local stratum 1"
echo "$server_conf"
elif [ $stratum -le $server_strata ]; then
for i in $(seq 1 $servers); do
echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $server_server_options"
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"
done
echo "$server_conf"
else
for i in $(seq 1 $servers); do
echo "server 192.168.123.$[$servers * ($stratum - 2) + $i] $client_server_options"
done
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"
done
echo "$client_conf"
fi
}
# Check if the clock was well synchronized
check_sync() {
local i sync_time max_time_error max_freq_error ret=0
local rms_time_error rms_freq_error
test_message 2 1 "checking clock sync time, max/rms time/freq error:"
for i in $(seq 1 $(get_chronyd_nodes)); do
[ $i -gt $[$servers * $server_strata] ] || continue
sync_time=$(find_sync tmp/log.offset tmp/log.freq $i \
$time_max_limit $freq_max_limit 1.0)
max_time_error=$(get_stat 'Maximum absolute offset' $i)
max_freq_error=$(get_stat 'Maximum absolute frequency' $i)
rms_time_error=$(get_stat 'RMS offset' $i)
rms_freq_error=$(get_stat 'RMS frequency' $i)
test_message 3 0 "node $i: $sync_time $(printf '%.2e %.2e %.2e %.2e' \
$max_time_error $max_freq_error $rms_time_error $rms_freq_error)"
check_stat $sync_time $min_sync_time $max_sync_time && \
check_stat $max_time_error 0.0 $time_max_limit && \
check_stat $max_freq_error 0.0 $freq_max_limit && \
check_stat $rms_time_error 0.0 $time_rms_limit && \
check_stat $rms_freq_error 0.0 $freq_rms_limit && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check if chronyd exited properly
check_chronyd_exit() {
local i ret=0
test_message 2 1 "checking chronyd exit:"
for i in $(seq 1 $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check for problems in source selection
check_source_selection() {
local i ret=0
test_message 2 1 "checking source selection:"
for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
! grep -q 'no majority\|no reachable sources' tmp/log.$i && \
grep -q 'Selected source' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check if incoming and outgoing packet intervals are sane
check_packet_interval() {
local i ret=0 mean_in_interval mean_out_interval min_in_interval min_out_interval
test_message 2 1 "checking mean/min incoming/outgoing packet interval:"
for i in $(seq 1 $(get_chronyd_nodes)); do
mean_in_interval=$(get_stat 'Mean incoming packet interval' $i)
mean_out_interval=$(get_stat 'Mean outgoing packet interval' $i)
min_in_interval=$(get_stat 'Minimum incoming packet interval' $i)
min_out_interval=$(get_stat 'Minimum outgoing packet interval' $i)
test_message 3 0 "node $i: $(printf '%.2e %.2e %.2e %.2e' \
$mean_in_interval $mean_out_interval $min_in_interval $min_out_interval)"
# Check that the mean intervals are non-zero and shorter than
# limit, incoming is not longer than outgoing for stratum 1
# servers, outgoing is not longer than incoming for clients,
# and the minimum outgoing interval is not shorter than the NTP
# sampling separation or iburst interval for clients
nodes=$[$servers * $server_strata + $clients]
check_stat $mean_in_interval 0.1 inf && \
check_stat $mean_out_interval 0.1 inf && \
([ $i -gt $servers ] || \
check_stat $mean_in_interval 0.0 $mean_out_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $mean_out_interval 0.0 $mean_in_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $min_out_interval \
$([ $servers -gt 1 ] && echo 0.18 || echo 1.8) inf) && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Compare chronyc output with specified pattern
check_chronyc_output() {
local i ret=0 pattern=$1
test_message 2 1 "checking chronyc output:"
for i in $(seq $[$(get_chronyd_nodes) + 1] $[$(get_chronyd_nodes) + $clients]); do
test_message 3 0 "node $i:"
[[ "$(cat tmp/log.$i)" =~ $pattern ]] && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check if only NTP port (123) was used
check_packet_port() {
local i ret=0 port=123
test_message 2 1 "checking port numbers in packet log:"
for i in $(seq 1 $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
grep -E -q " $port [0-9]+\$" tmp/log.packets && \
! grep -E "^[0-9e.+-]+ $i " tmp/log.packets | \
grep -E -q -v " $port [0-9]+\$" && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Print test settings which differ from default value
print_nondefaults() {
local defopt defoptname optname
test_message 2 1 "non-default settings:"
declare | grep '^default_*' | while read defopt; do
defoptname=${defopt%%=*}
optname=${defoptname#default_}
eval "[ \"\$$optname\" = \"\$$defoptname\" ]" || \
test_message 3 1 $(eval "echo $optname=\$$optname")
done
}
run_simulation() {
local nodes=$1
test_message 2 0 "running simulation:"
start_server $nodes \
-o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
-R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
-r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
-l $(awk "BEGIN {print $limit * 2^$update_interval}") && test_ok || test_error
}
run_test() {
local i j n stratum node nodes step start freq offset conf
test_message 1 1 "network with $servers*$server_strata servers and $clients clients:"
print_nondefaults
nodes=$(get_chronyd_nodes)
[ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
for i in $(seq 1 $nodes); do
echo "node${i}_shift_pll = $shift_pll"
for j in $(seq 1 $nodes); do
[ $i -eq $j ] && continue
echo "node${i}_delay${j} = $(get_delay_expr)"
echo "node${j}_delay${i} = $(get_delay_expr)"
done
done > tmp/conf
node=1
for stratum in $(seq 1 $[$server_strata + 1]); do
[ $stratum -le $server_strata ] && n=$servers || n=$clients
for i in $(seq 1 $n); do
test_message 2 0 "starting node $node:"
if [ $stratum -eq 1 ]; then
step=$server_step
start=$server_start
freq=""
offset=0.0
elif [ $stratum -le $server_strata ]; then
step=$server_step
start=$server_start
freq=$(get_wander_expr)
offset=0.0
else
step=$client_step
start=$client_start
freq=$(get_wander_expr)
offset=$time_offset
fi
conf=$(get_chronyd_conf $stratum $i $n)
[ -z "$freq" ] || echo "node${node}_freq = $freq" >> tmp/conf
[ -z "$step" ] || echo "node${node}_step = $step" >> tmp/conf
[ -z "$refclock_jitter" ] || \
echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf
echo "node${node}_offset = $offset" >> tmp/conf
echo "node${node}_start = $start" >> tmp/conf
start_client $node chronyd "$conf" "" "$chronyd_options" && \
test_ok || test_error
[ $? -ne 0 ] && return 1
node=$[$node + 1]
done
done
for i in $(seq 1 $[$nodes - $node + 1]); do
test_message 2 0 "starting node $node:"
echo "node${node}_start = $chronyc_start" >> tmp/conf
start_client $node chronyc "$chronyc_conf" "" \
"-n -h 192.168.123.$[$node - $clients]" && \
test_ok || test_error
[ $? -ne 0 ] && return 1
node=$[$node + 1]
done
run_simulation $nodes
}

6
util.c
View File

@@ -647,7 +647,7 @@ UTI_FloatHostToNetwork(double x)
/* ================================================== */
void
int
UTI_FdSetCloexec(int fd)
{
int flags;
@@ -655,8 +655,10 @@ UTI_FdSetCloexec(int fd)
flags = fcntl(fd, F_GETFD);
if (flags != -1) {
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
return !fcntl(fd, F_SETFD, flags);
}
return 0;
}
/* ================================================== */

2
util.h
View File

@@ -108,7 +108,7 @@ extern double UTI_FloatNetworkToHost(Float x);
extern Float UTI_FloatHostToNetwork(double x);
/* Set FD_CLOEXEC on descriptor */
extern void UTI_FdSetCloexec(int fd);
extern int UTI_FdSetCloexec(int fd);
extern int UTI_GenerateNTPAuth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len, unsigned char *auth, int auth_len);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2002
* Copyright (C) Miroslav Lichvar 2011-2012
* Copyright (C) Miroslav Lichvar 2011-2012, 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,26 +37,30 @@
static int status = 0;
int
TMX_SetTick(long tick)
TMX_ResetOffset(void)
{
struct timex txc;
txc.modes = ADJ_TICK;
txc.tick = tick;
return adjtimex(&txc);
}
int
TMX_ApplyOffset(long *offset)
{
struct timex txc;
int result;
/* Reset adjtime() offset */
txc.modes = ADJ_OFFSET_SINGLESHOT;
txc.offset = *offset;
result = adjtimex(&txc);
*offset = txc.offset;
return result;
txc.offset = 0;
if (adjtimex(&txc) < 0)
return -1;
/* Reset PLL offset */
txc.modes = ADJ_OFFSET | ADJ_STATUS;
txc.status = STA_PLL;
txc.offset = 0;
if (adjtimex(&txc) < 0)
return -1;
/* Set status back */
txc.modes = ADJ_STATUS;
txc.modes = status;
if (adjtimex(&txc) < 0)
return -1;
return 0;
}
int
@@ -93,17 +97,6 @@ TMX_GetFrequency(double *freq, long *tick)
return result;
}
int
TMX_GetOffsetLeft(long *offset)
{
struct timex txc;
int result;
txc.modes = ADJ_OFFSET_SS_READ;
result = adjtimex(&txc);
*offset = txc.offset;
return result;
}
int
TMX_ReadCurrentParams(struct tmx_params *params)
{
@@ -184,47 +177,6 @@ int TMX_SetSync(int sync)
return adjtimex(&txc);
}
int
TMX_EnableNanoPLL(void)
{
struct timex txc;
int result;
txc.modes = ADJ_STATUS | ADJ_OFFSET | ADJ_TIMECONST | ADJ_NANO;
txc.status = STA_PLL | STA_FREQHOLD;
txc.offset = 0;
txc.constant = 0;
result = adjtimex(&txc);
if (result < 0 || !(txc.status & STA_NANO) || txc.offset || txc.constant)
return -1;
status |= STA_PLL | STA_FREQHOLD;
return result;
}
int
TMX_ApplyPLLOffset(long offset, long constant)
{
struct timex txc;
txc.modes = ADJ_OFFSET | ADJ_TIMECONST | ADJ_NANO;
txc.offset = offset;
txc.constant = constant;
return adjtimex(&txc);
}
int
TMX_GetPLLOffsetLeft(long *offset)
{
struct timex txc;
int result;
txc.modes = 0;
result = adjtimex(&txc);
*offset = txc.offset;
return result;
}
int
TMX_TestStepOffset(void)
{
@@ -240,7 +192,7 @@ TMX_TestStepOffset(void)
if (adjtimex(&txc) < 0 || txc.maxerror != 0)
return -1;
txc.modes = ADJ_SETOFFSET;
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
txc.time.tv_sec = 0;
txc.time.tv_usec = 0;
if (adjtimex(&txc) < 0 || txc.maxerror < 100000)
@@ -254,21 +206,13 @@ TMX_ApplyStepOffset(double offset)
{
struct timex txc;
txc.modes = ADJ_SETOFFSET;
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
if (offset >= 0) {
txc.time.tv_sec = offset;
} else {
txc.time.tv_sec = offset - 1;
}
/* ADJ_NANO changes the status even with ADJ_SETOFFSET, use it only when
STA_NANO is already enabled */
if (status & STA_PLL) {
txc.modes |= ADJ_NANO;
txc.time.tv_usec = 1e9 * (offset - txc.time.tv_sec);
} else {
txc.time.tv_usec = 1e6 * (offset - txc.time.tv_sec);
}
txc.time.tv_usec = 1.0e9 * (offset - txc.time.tv_sec);
return adjtimex(&txc);
}

View File

@@ -64,17 +64,12 @@ struct tmx_params {
long stbcnt;
};
int TMX_SetTick(long tick);
int TMX_ApplyOffset(long *offset);
int TMX_ResetOffset(void);
int TMX_SetFrequency(double *freq, long tick);
int TMX_GetFrequency(double *freq, long *tick);
int TMX_GetOffsetLeft(long *offset);
int TMX_ReadCurrentParams(struct tmx_params *params);
int TMX_SetLeap(int leap);
int TMX_SetSync(int sync);
int TMX_EnableNanoPLL(void);
int TMX_ApplyPLLOffset(long offset, long constant);
int TMX_GetPLLOffsetLeft(long *offset);
int TMX_TestStepOffset(void);
int TMX_ApplyStepOffset(double offset);