Compare commits

...

294 Commits

Author SHA1 Message Date
Miroslav Lichvar
aabb564320 doc: update NEWS 2016-01-19 08:15:15 +01:00
Miroslav Lichvar
df46e5ca5d ntp: restrict authentication of server/peer to specified key
When a server/peer was specified with a key number to enable
authentication with a symmetric key, packets received from the
server/peer were accepted if they were authenticated with any of
the keys contained in the key file and not just the specified key.

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

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

This issue was discovered by Matt Street of Cisco ASIG.
2016-01-19 08:15:12 +01:00
Miroslav Lichvar
370ba5e8fc doc: warn that unauthenticated peers are vulnerable to DoS attack 2016-01-19 08:14:46 +01:00
Miroslav Lichvar
463093803d doc: fix CVE-ID in NEWS
CVE-2015-1853 is for chrony, CVE-2015-1799 is for ntp.
2015-04-08 08:47:13 +02:00
Miroslav Lichvar
c4bedce1f4 doc: update NEWS 2015-04-07 16:35:16 +02:00
Miroslav Lichvar
79eacdb7e6 cmdmon: fix initialization of allocated reply slots
When allocating memory to save unacknowledged replies to authenticated
command requests, the last "next" pointer was not initialized to NULL.
When all allocated reply slots were used, the next reply could be
written to an invalid memory instead of allocating a new slot for it.

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

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

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

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

[1] https://www.eecis.udel.edu/~mills/onwire.html
2015-04-07 16:34:42 +02:00
Miroslav Lichvar
ebab36e859 doc: update NEWS 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
3988a1e9a8 doc: mention that directives are not case-sensitive 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
949ef3e1dc doc: add section to FAQ on improving accuracy with NTP 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
dd12303276 doc: remove minpoll and maxpoll options from configuration example 2014-09-10 17:00:54 +02:00
Miroslav Lichvar
f1379a6574 sched: fix Clang warning 2014-09-10 17:00:50 +02:00
Miroslav Lichvar
ad58384760 client: describe error when could not open config or keyfile 2014-09-10 11:34:48 +02:00
Miroslav Lichvar
0e786f5907 Ignore measurements around leap second
When current time is within 5 seconds of a leap second, don't accumulate
new samples or update the leap second status to increase the chances of
getting through safely.
2014-09-09 17:08:30 +02:00
Miroslav Lichvar
e1accce498 ntp: print warning 10 years before supported time ends 2014-09-09 17:08:30 +02:00
Miroslav Lichvar
28db0fdde9 configure: check if pkg-config is available
This is needed with some shells to prevent "pkg-config: not found"
errors from being displayed.
2014-09-09 17:08:26 +02:00
Miroslav Lichvar
584bf9382b Fix compiler warnings on NetBSD 2014-09-09 11:48:09 +02:00
Miroslav Lichvar
0168b405a3 examples: add NetworkManager dispatcher script 2014-09-04 17:43:27 +02:00
Miroslav Lichvar
b5e0d76337 examples: add systemd services 2014-09-04 17:30:36 +02:00
Miroslav Lichvar
c924fba4fa examples: add logrotate configuration 2014-09-04 17:28:32 +02:00
Miroslav Lichvar
8ec43a39af Move chrony.spec to examples 2014-09-04 17:25:56 +02:00
Miroslav Lichvar
9f16445464 sys: fix typo in prctl() error message 2014-08-25 17:25:14 +02:00
Miroslav Lichvar
1a795b04ee util: fix compiler warning with 32-bit time_t 2014-08-21 14:06:46 +02:00
Miroslav Lichvar
b862f3e64d Update NEWS 2014-08-21 10:06:09 +02:00
Miroslav Lichvar
4e66b5ce8a ntp: don't stop online burst when sending fails
Don't stop online burst for unreachable sources until sending succeeds.
This is mainly useful with iburst when chronyd is started before the
network is configured.
2014-08-20 16:54:26 +02:00
Miroslav Lichvar
d446950c6a ntp: don't adjust polling interval when sending fails 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
e3c77f9b4b ntp: return with status from functions sending packets 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
090ec985f3 doc: clarify description of -s option 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
e63bd490b0 sched: improve time jump detection
To detect forward time jumps, use a timestamp made before calling
select() instead of the first timeout in the queue. Also, if the timeout
value is modified by select() (e.g. on Linux) use it to get a more
accurate estimate of the elapsed time.
2014-08-20 16:54:26 +02:00
Miroslav Lichvar
badf97d4ba ntp: restart timer when poll interval changes on reset 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
ba283e6b6e ntp: add function to restart transmit timer 2014-08-20 16:54:26 +02:00
Miroslav Lichvar
0bdac2c7b3 sched: make sure scheduler parameter change handler is first
This is needed to allow other handlers to add new timers.
2014-08-20 16:54:26 +02:00
Miroslav Lichvar
58b211d707 local: use common function to invoke parameter change handlers
This was missing in commit b69b648d.
2014-08-19 11:30:32 +02:00
Miroslav Lichvar
068ce237af reference: always update driftfile on exit
This is useful with the new fallback function of the -s option to
restore the system time at which chronyd was previously stopped.
2014-08-19 10:46:35 +02:00
Miroslav Lichvar
a5e9e5d0df rtc: set clock to mtime of driftfile when RTC preinit fails
When the RTC preinit function fails, set the system clock to the time of
the last modification of the driftfile if it's in the future. This makes
the -s option somewhat useful on systems where RTC is not supported or
missing.

This is similar to the functionality implemented in the fake-hwclock
script.
2014-08-19 10:46:35 +02:00
Miroslav Lichvar
e0af8069c1 rtc: don't try to open rtcfile when not set 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
696b05d6e6 rtc: use fscanf() to read coefficients 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
7e1a699616 rtc: return status from preinit function 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
716d73d982 rtc: use LCL functions to read and step system clock 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
38ac081114 rtc: improve accuracy of preinit step 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
5fce101f85 rtc: minor cleanup in RTC_Linux_TimePreInit() 2014-08-19 10:46:35 +02:00
Miroslav Lichvar
c6e064200d rtc: move preinit call to RTC_Initialise() 2014-08-19 10:46:32 +02:00
Miroslav Lichvar
c52e9085d1 rtc: cleanup in error messages 2014-08-18 17:21:26 +02:00
Miroslav Lichvar
d0fb17d70c test: add 115-cmdmontime 2014-08-18 16:06:28 +02:00
Miroslav Lichvar
713153b610 util: update functions converting cmdmon timestamps 2014-08-18 16:06:28 +02:00
Miroslav Lichvar
09d039fba6 cmdmon: convert LOGON timestamp only with LOGON message
Avoid always calling UTI_TimevalNetworkToHost() and make the code more
readable.
2014-08-18 16:06:28 +02:00
Miroslav Lichvar
07f7f28058 sched: check that added file descriptor fits in fd_set 2014-08-15 16:52:37 +02:00
Miroslav Lichvar
a2b40f527d sched: use FD_SETSIZE if defined 2014-08-15 16:51:15 +02:00
Miroslav Lichvar
6d8ffeefd6 test: add 114-presend 2014-08-15 16:51:15 +02:00
Miroslav Lichvar
9ce25bab04 ntp: add debug message for received packet 2014-08-15 16:51:10 +02:00
Miroslav Lichvar
cd5105b1db ntp: use NTP instead of echo for presend
Switch to NTP for presend as the echo service (RFC 862) is rarely
enabled. When presend is active, send an NTP client packet to the
server/peer and ignore the reply.

This also fixes presend with separate client sockets. The destination
port can't be changed on connected sockets, so the echo packet was sent
to the NTP port instead of the echo port.
2014-08-15 16:49:45 +02:00
Miroslav Lichvar
ff4abc69c3 Check for truncated source address when receiving packets 2014-08-15 16:44:43 +02:00
Miroslav Lichvar
192f74f0a1 test: fix check in run script 2014-08-15 11:18:44 +02:00
Miroslav Lichvar
be203d9af0 test: add 008-ntpera 2014-08-15 11:18:44 +02:00
Miroslav Lichvar
f8af299321 test: allow tests to be skipped 2014-08-15 11:18:44 +02:00
Miroslav Lichvar
474b2af1a6 util: add support for other NTP eras
NTP timestamps use only 32 bits to count seconds and the current NTP era
ends in 2036. Add support for converting NTP timestamps from other NTP
eras on systems with 64-bit time_t.

The earliest assumed NTP time is set by the configure script (by default
to 50 years before the date of the build) and earlier NTP timestamps
underflow to the following NTP era.
2014-08-15 11:18:40 +02:00
Miroslav Lichvar
cb88cea3c4 ntp: move packet size asserts to ntp_core 2014-08-15 10:58:52 +02:00
Miroslav Lichvar
fc2892fbb0 util: fix printing of timestamps when time_t is longer than long 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
36b25cbd2b configure: check for 64-bit time_t 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
d18c071849 ntp: use one socket with random port when acquisitionport is 0 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
61b629fdad conf: return port numbers as int 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
29647c8280 cmdmon: allow binding to address that doesn't exist yet 2014-08-15 10:58:44 +02:00
Miroslav Lichvar
97b15cb3ae ntp: allow binding to address that doesn't exist yet 2014-08-15 10:58:43 +02:00
Miroslav Lichvar
f725921dfb ntp: remove disabled code in prepare_socket() 2014-08-15 10:58:43 +02:00
Miroslav Lichvar
a4b4d0c0d8 ntp: bind socket only when port or address was specified
This removes an unnecessary system call when getting new connected
client socket.
2014-08-15 10:58:43 +02:00
Miroslav Lichvar
f59ade7f80 ntp: remove socket reconnecting
As new client socket is now created for each request, there is no need
to reconnect the socket. This is a partial revert of commit 43cca04c.
2014-08-15 10:58:43 +02:00
Miroslav Lichvar
a9b9e7befe ntp: create new socket for each client request
Create a new connected client socket before each request and close it
when a valid reply is received.

This is useful when the network configuration is changed and the client
socket should be reconnected, but the old bound address remains valid
and sendmsg() doesn't return with an error.
2014-08-15 10:58:43 +02:00
Miroslav Lichvar
ead9394a31 Regenerate getdate.c 2014-08-15 10:58:43 +02:00
Miroslav Lichvar
80129fa9ab makefile: regenerate getdate.c when missing 2014-08-14 14:51:24 +02:00
Joachim Wiedorn
18796a3c18 doc: fix small typo 2014-08-13 10:59:13 +02:00
Miroslav Lichvar
f632b6d4cb configure: remove ncurses_lib from first readline test 2014-08-13 10:59:13 +02:00
Miroslav Lichvar
7799e14770 test: increase default time rms limit
This is improves testing reliability with new default corrtimeratio.
2014-08-06 15:46:32 +02:00
Miroslav Lichvar
20aab86e12 test: require latest clknetsim 2014-08-06 15:26:35 +02:00
Miroslav Lichvar
b7766478a6 ntp: close socket when duplicating fails 2014-08-06 15:25:58 +02:00
Miroslav Lichvar
3d57b7a44d refclock: fix compiler warning in SOCK error message 2014-08-05 17:13:08 +02:00
Miroslav Lichvar
51a2b436f4 logging: move exit call from LOG_Message to LOG_FATAL 2014-08-05 15:15:15 +02:00
Miroslav Lichvar
88015081f2 ntp: shorten setsockopt error messages 2014-08-05 12:57:36 +02:00
Miroslav Lichvar
20cc1f6550 ntp: move debug message after sendmsg()
This should reduce the delay before sending the packet when debugging is
enabled.
2014-08-05 12:57:36 +02:00
Miroslav Lichvar
43cca04c33 ntp: reconnect client sockets
With separate client sockets, allow the initial connect() to fail (e.g.
when the network is not reachable yet) and try to connect later when
sending the packet.

Also, reconnect the socket when the local address has changed.
2014-08-05 12:57:36 +02:00
Miroslav Lichvar
17d944c333 doc: mention linuxcaps support in FAQ 2014-07-29 18:10:40 +02:00
Miroslav Lichvar
6789b5165c doc: update FAQ 2014-07-01 14:49:00 +02:00
Miroslav Lichvar
d631d7e81f doc: use iburst option in examples 2014-07-01 12:56:23 +02:00
Miroslav Lichvar
c6245dc616 doc: update NEWS 2014-06-30 17:31:39 +02:00
Miroslav Lichvar
4b36799ce1 doc: update README 2014-06-30 17:20:06 +02:00
Miroslav Lichvar
d26bb9b4eb doc: update initstepslew description 2014-06-30 17:19:40 +02:00
Miroslav Lichvar
698404b02f doc: update FAQ 2014-06-30 16:45:32 +02:00
Miroslav Lichvar
d46d7ad947 Update configuration examples 2014-06-30 14:20:32 +02:00
Miroslav Lichvar
7c6630905d sched: exit with fatal message when there is nothing to do
With cmdport 0 and port 0, it's now possible that there is no descriptor
watched or timer running, i.e. chronyd doing nothing and only waiting to
be terminated. Replace the assertion with LOG_FATAL to exit properly.
2014-06-30 12:54:04 +02:00
Miroslav Lichvar
129aa587c6 cmdmon: don't create socket when cmdport is 0 2014-06-30 12:40:18 +02:00
Miroslav Lichvar
cc1c6c94e3 makefile: remove faq.php rule 2014-06-27 17:26:54 +02:00
Miroslav Lichvar
41266cbaa0 make_release: generate FAQ from chrony.txt 2014-06-27 16:20:10 +02:00
Miroslav Lichvar
fbfd261da6 make_release: generate INSTALL from chrony.txt 2014-06-27 16:20:10 +02:00
Miroslav Lichvar
71602b8ee6 make_release: add testing mode 2014-06-27 16:06:49 +02:00
Miroslav Lichvar
14cae239f6 doc: include faq.txt in main document 2014-06-27 16:06:49 +02:00
Miroslav Lichvar
2e9e309a0d doc: update installation chapter 2014-06-27 13:25:14 +02:00
Miroslav Lichvar
3fba33d5f5 doc: drop porting guide
Most of the information provided in this section looks obsolete.
Comments in the source code should be a better source.
2014-06-27 12:17:03 +02:00
Miroslav Lichvar
77a7162361 util: print timevals for debug messages as numbers 2014-06-27 11:42:22 +02:00
Miroslav Lichvar
75efa5174c Convert disabled log messages to debug or remove them 2014-06-27 10:17:35 +02:00
Miroslav Lichvar
c62afbe77b cmdmon: remove disabled code 2014-06-26 17:19:45 +02:00
Miroslav Lichvar
a6f0688f46 keys: fix determine_hash_delay() declaration 2014-06-26 17:12:32 +02:00
Miroslav Lichvar
5762d33e38 test: require latest clknetsim revision 2014-06-25 17:35:59 +02:00
Miroslav Lichvar
9c6d1c214f ntp: don't set address for sendmsg() with connected sockets 2014-06-25 15:36:14 +02:00
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
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
106 changed files with 6026 additions and 5106 deletions

2
.gitignore vendored
View File

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

95
INSTALL
View File

@@ -1,95 +0,0 @@
The software is distributed as source code which has to be compiled.
PARTS OF THE SOFTWARE ARE HIGHLY SYSTEM-SPECIFIC AND NON-PORTABLE.
UNLESS YOU ARE RUNNING A SUPPORTED SYSTEM, BE PREPARED FOR SOME
PROGRAMMING!
After unpacking the source code, change directory into it, and type
./configure
This is a shell script that automatically determines the system type.
There is a single optional parameter, --prefix which indicates the
directory tree where the software should be installed. For example,
./configure --prefix=/opt/free
will install the chronyd daemon into /opt/free/sbin and the chronyc
control program into /opt/free/bin. The default value for the prefix
is /usr/local.
The configure script assumes you want to use gcc as your compiler.
If you want to use a different compiler, you can configure this way:
CC=cc CFLAGS=-O ./configure --prefix=/opt/free
for Bourne-family shells, or
setenv CC cc
setenv CFLAGS -O
./configure --prefix=/opt/free
for C-family shells.
If the software cannot (yet) be built on your system, an error message
will be shown. Otherwise, `Makefile' will be generated.
If editline or readline library is available, chronyc will be built
with line editing support. If you don't want this, specify the
--disable-readline flag to configure. Please refer to 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.
Now type
make
to build the programs.
If you want to build the manual in plain text, HTML and info versions, type
make docs
Once the programs have been successfully compiled, they need to be
installed in their target locations. This step normally needs to be
performed by the superuser, and requires the following command to be
entered.
make install
This will install the binaries, plain text manual and manpages.
To install the HTML and info versions of the manual as well, enter the command
make install-docs
If you want chrony to appear in the top level info directory listing, you need
to run the install-info command manually after this step. install-info takes 2
arguments. The first is the path to the chrony.info file you have just
installed. This will be the argument you gave to --prefix when you configured
(/usr/local by default), with /share/info/chrony.info on the end. The second
argument is the location of the file called 'dir'. This will typically be
/usr/share/info/dir. So the typical command line would be
install-info /usr/local/share/info/chrony.info /usr/share/info/dir
Now that the software is successfully installed, the next step is to
set up a configuration file. The contents of this depend on the
network environment in which the computer operates. Typical scenarios
are described in the manual. The simplest case is for a computer with
a permanent Internet connection - suppose you want to use public NTP
servers from the pool.ntp.org project as your time reference. You would
create an /etc/chrony.conf file containing
server 0.pool.ntp.org
server 1.pool.ntp.org
server 2.pool.ntp.org
driftfile /var/lib/chrony/drift
and then run /usr/local/sbin/chronyd.

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 $<
@@ -85,14 +85,17 @@ clean :
-rm -f *.o *.s chronyc chronyd core *~ chrony.info chrony.html chrony.txt
-rm -rf .deps
getdate.c : ;
getdate.c :
bison -o getdate.c getdate.y
# This can be used to force regeneration of getdate.c
getdate :
bison -o getdate.c getdate.y
# 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 +132,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
@@ -151,10 +157,6 @@ chrony.html : chrony.texi
chrony.info : chrony.texi
makeinfo chrony.texi
# This is only relevant if you're maintaining the website!
faq.php : faq.txt faqgen.pl
perl faqgen.pl < faq.txt > faq.php
.deps:
@mkdir .deps

92
NEWS
View File

@@ -1,3 +1,95 @@
New in version 1.31.2
=====================
Security fixes
--------------
* Restrict authentication of NTP server/peer to specified key (CVE-2016-1567)
New in version 1.31.1
=====================
Security fixes
--------------
* Protect authenticated symmetric NTP associations against DoS attacks
(CVE-2015-1853)
* Fix access configuration with subnet size indivisible by 4 (CVE-2015-1821)
* Fix initialization of reply slots for authenticated commands (CVE-2015-1822)
New in version 1.31
===================
Enhancements
------------
* Support operation in other NTP eras (next era begins in 2036),
NTP time is mapped to [-50, +86] years around build date by default
* Restore time from driftfile with -s when RTC is missing/unsupported
* Close connected client sockets when not waiting for reply
* Use one client socket with random port when acquisitionport is 0
* Use NTP packets instead of UDP echo for presend
* Don't adjust polling interval when sending fails
* Allow binding to addresses that don't exist yet
* Ignore measurements around leap second
* Improve detection of unexpected time jumps
* Include example of logrotate configuration, systemd services and
NetworkManager dispatcher script
Bug fixes
---------
* Reconnect client sockets for each request to follow changes
in network configuration automatically
* Restart timer when polling interval is changed on reset
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 accuracy
* 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
* Don't create cmdmon sockets when cmdport is 0
* 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
=====================

13
README
View File

@@ -2,6 +2,7 @@ This is the README for chrony.
What is chrony?
===============
Chrony is a pair of programs for maintaining the accuracy of computer
clocks.
@@ -61,7 +62,7 @@ Chrony can be successfully built and run on
Any other system will require a porting exercise. You would need to
start from one of the existing system-specific drivers and look into
the quirks of certain system calls and the kernel on your target
system. (This is described in the manual).
system.
How do I set it up?
===================
@@ -94,8 +95,7 @@ by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org
These messages will be copied to chrony-users (see below). New versions
are announced also on Freshmeat (http://freshmeat.net/).
These messages will be copied to chrony-users (see below).
How can I get support for chrony?
and where can I discuss new features, possible bugs etc?
@@ -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

@@ -199,7 +199,10 @@ set_subnet(TableNode *start_node,
/* How many subnet entries to set : 1->8, 2->4, 3->2 */
N = 1 << (NBITS-bits_to_go);
subnet = get_subnet(ip, bits_consumed);
subnet = get_subnet(ip, bits_consumed) & ~(N - 1);
assert(subnet + N <= TABLE_SIZE);
if (!(node->extended)) {
open_node(node);
}

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,

View File

@@ -518,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;
@@ -637,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/

View File

@@ -22,16 +22,19 @@ as a minimum
However, you will probably want to include some of the other directives
described in detail in the documentation supplied with the distribution
(\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives will be
particularly useful : `driftfile', `commandkey', `keyfile'. The smallest
useful configuration file would look something like
(\fIchrony.txt\fR and \fIchrony.texi\fR). The following directives may be
particularly useful : `driftfile', `generatecommandkey', `keyfile', `makestep'.
Also, the `iburst' server option is useful to speed up the initial
synchronization. The smallest useful configuration file would look something
like
server a.b.c
server d.e.f
server g.h.i
server a.b.c iburst
server d.e.f iburst
server g.h.i iburst
keyfile @SYSCONFDIR@/chrony.keys
commandkey 1
generatecommandkey
driftfile @CHRONYVARDIR@/drift
makestep 10 3
.SH "SEE ALSO"

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
@@ -72,35 +76,45 @@ should not be used.
.B \-R
When this option is used, the \fIinitstepslew\fR directive and the
\fImakestep\fR directive used with a positive limit will be ignored. This
option is useful when restarting \fBchronyd\fR and can be used in conjuction
option is useful when restarting \fBchronyd\fR and can be used in conjunction
with the \fB-r\fR option.
.TP
.B \-s
This option will set the system clock from the computer's real-time
clock. This is analogous to supplying the \fI-s\fR flag to the
\fI/sbin/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
documentation supplied with the distribution.
If \fBchronyd\fR cannot support the real time clock on your computer,
this option cannot be used and a warning message will be logged to
the syslog.
If used in conjunction with the \fB-r\fR flag, \fBchronyd\fR will attempt
to preserve the old samples after setting the system clock from
the real time clock. This can be used to allow \fBchronyd\fR to
the real time clock (RTC). This can be used to allow \fBchronyd\fR to
perform long term averaging of the gain or loss rate across system
reboots, and is useful for dial-up systems that are shut down when
not in use. For this to work well, it relies on \fBchronyd\fR having
been able to determine accurate statistics for the difference
between the real time clock and system clock last time the
computer was on.
between the RTC and system clock last time the computer was on.
If \fBchronyd\fR doesn't support the RTC on your computer or there is no RTC
installed, the system clock will be set with this option forward to the time of
the last modification of the drift file (specified by the \fIdriftfile\fR
directive) to restore the system time at which \fBchronyd\fR was previously
stopped.
.TP
\fB\-u\fR \fIuser\fR
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 +133,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 +141,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>

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);
@@ -1368,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;
@@ -1375,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));
@@ -1436,10 +1465,9 @@ submit_request(CMD_Request *request, CMD_Reply *reply, int *reply_auth_ok)
/* 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));
@@ -1669,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)
{
@@ -1693,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 */
@@ -1863,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(" ");
@@ -1952,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);
@@ -1961,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);
@@ -2576,7 +2616,7 @@ authenticate_from_config(const char *filename)
in = fopen(filename, "r");
if (!in) {
fprintf(stderr, "Could not open file %s\n", filename);
fprintf(stderr, "Could not open file %s : %s\n", filename, strerror(errno));
return 0;
}
@@ -2601,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 : %s\n", keyfile, strerror(errno));
return 0;
}
@@ -2675,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",
@@ -2689,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 */
@@ -2718,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);
@@ -2749,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) {

153
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
@@ -171,22 +171,19 @@ static ADF_AuthTable access_auth_table;
/* ================================================== */
/* Forward prototypes */
static int prepare_socket(int family);
static void read_from_cmd_socket(void *anything);
/* ================================================== */
static int
prepare_socket(int family)
prepare_socket(int family, int port_number)
{
int port_number, sock_fd;
int sock_fd;
socklen_t my_addr_len;
union sockaddr_in46 my_addr;
IPAddr bind_address;
int on_off = 1;
port_number = CNF_GetCommandPort();
sock_fd = socket(family, SOCK_DGRAM, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not open %s command socket : %s",
@@ -202,6 +199,14 @@ prepare_socket(int family)
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set reuseaddr socket options");
/* Don't quit - we might survive anyway */
}
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_CmdMon, "Could not set free bind socket option");
}
#endif
#ifdef HAVE_IPV6
if (family == AF_INET6) {
#ifdef IPV6_V6ONLY
@@ -265,7 +270,7 @@ prepare_socket(int family)
void
CAM_Initialise(int family)
{
int i;
int i, port_number;
assert(!initialised);
initialised = 1;
@@ -293,18 +298,20 @@ CAM_Initialise(int family)
free_replies = NULL;
kept_replies.next = NULL;
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = prepare_socket(AF_INET);
port_number = CNF_GetCommandPort();
if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET4))
sock_fd4 = prepare_socket(AF_INET, port_number);
else
sock_fd4 = -1;
#ifdef HAVE_IPV6
if (family == IPADDR_UNSPEC || family == IPADDR_INET6)
sock_fd6 = prepare_socket(AF_INET6);
if (port_number && (family == IPADDR_UNSPEC || family == IPADDR_INET6))
sock_fd6 = prepare_socket(AF_INET6, port_number);
else
sock_fd6 = -1;
#endif
if (sock_fd4 < 0
if (port_number && sock_fd4 < 0
#ifdef HAVE_IPV6
&& sock_fd6 < 0
#endif
@@ -559,6 +566,7 @@ get_more_replies(void)
for (i=1; i<REPLY_EXTEND_QUANTUM; i++) {
new_replies[i-1].next = new_replies + i;
}
new_replies[REPLY_EXTEND_QUANTUM - 1].next = NULL;
free_replies = new_replies;
}
}
@@ -667,46 +675,6 @@ token_acknowledged(unsigned long token, struct timeval *now)
/* ================================================== */
#if 0
/* These two routines are not legal if the program is operating as a daemon, since
stderr is no longer open */
static void
print_command_packet(CMD_Request *pkt, int length)
{
unsigned char *x;
int i;
x = (unsigned char *) pkt;
for (i=0; i<length; i++) {
fprintf(stderr, "%02x ", x[i]);
if (i%16 == 15) {
fprintf(stderr, "\n");
}
}
fprintf(stderr, "\n");
}
/* ================================================== */
static void
print_reply_packet(CMD_Reply *pkt)
{
unsigned char *x;
int i;
x = (unsigned char *) pkt;
for (i=0; i<sizeof(CMD_Reply); i++) {
fprintf(stderr, "%02x ", x[i]);
if (i%16 == 15) {
fprintf(stderr, "\n");
}
}
fprintf(stderr, "\n");
}
#endif
/* ================================================== */
static void
transmit_reply(CMD_Reply *msg, union sockaddr_in46 *where_to, int auth_len)
{
@@ -734,7 +702,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;
@@ -755,7 +723,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);
}
}
@@ -1276,7 +1244,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);
@@ -1324,7 +1291,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);
@@ -1380,8 +1346,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);
@@ -1591,7 +1557,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);
}
@@ -1631,20 +1597,6 @@ handle_reselect(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_SUCCESS);
}
/* ================================================== */
#if 0
/* ================================================== */
static void
handle_(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int status;
}
#endif
/* ================================================== */
/* Read a packet and process it */
@@ -1693,6 +1645,9 @@ read_from_cmd_socket(void *anything)
return;
}
if (from_length > sizeof (where_from))
LOG_FATAL(LOGF_CmdMon, "Truncated source address");
read_length = status;
LCL_ReadRawTime(&now);
@@ -1758,9 +1713,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
@@ -1769,10 +1724,7 @@ 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);
}
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);
CLG_LogCommandAccess(&remote_ip, CLG_CMD_BAD_PKT, cooked_now.tv_sec);
@@ -1784,9 +1736,7 @@ read_from_cmd_socket(void *anything)
}
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);
}
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);
@@ -1796,9 +1746,7 @@ 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);
}
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);
@@ -1881,8 +1829,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;
}
@@ -1896,20 +1844,22 @@ read_from_cmd_socket(void *anything)
}
valid_ts = 0;
issue_token = 0;
if (auth_ok) {
struct timeval ts;
UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts);
if ((utoken_ok && token_ok) ||
((ntohl(rx_message.utoken) == SPECIAL_UTOKEN) &&
(rx_command == REQ_LOGON) &&
(valid_ts = ts_is_unique_and_not_stale(&ts, &now))))
if (utoken_ok && token_ok) {
issue_token = 1;
else
issue_token = 0;
} else {
issue_token = 0;
} else if (rx_command == REQ_LOGON &&
ntohl(rx_message.utoken) == SPECIAL_UTOKEN) {
struct timeval ts;
UTI_TimevalNetworkToHost(&rx_message.data.logon.ts, &ts);
valid_ts = ts_is_unique_and_not_stale(&ts, &now);
if (valid_ts) {
issue_token = 1;
}
}
}
authenticated = auth_ok & utoken_ok & token_ok;
@@ -1936,8 +1886,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:
@@ -1959,6 +1907,7 @@ read_from_cmd_socket(void *anything)
break;
default:
assert(0);
allowed = 0;
}
if (allowed) {
@@ -2010,8 +1959,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;
@@ -203,7 +204,7 @@ CPS_NormalizeLine(char *line)
/* Remove white-space at beginning and replace white-spaces with space char */
for (p = q = line; *p; p++) {
if (isspace(*p)) {
if (isspace((unsigned char)*p)) {
if (!space)
*q++ = ' ';
space = 1;
@@ -233,15 +234,15 @@ CPS_SplitWord(char *line)
char *p = line, *q = line;
/* Skip white-space before the word */
while (*q && isspace(*q))
while (*q && isspace((unsigned char)*q))
q++;
/* Move the word to the beginning */
while (*q && !isspace(*q))
while (*q && !isspace((unsigned char)*q))
*p++ = *q++;
/* Find the next word */
while (*q && isspace(*q))
while (*q && isspace((unsigned char)*q))
q++;
*p = '\0';

917
conf.c

File diff suppressed because it is too large Load Diff

23
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,15 +35,15 @@ 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 int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(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 */

132
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,17 @@ 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-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-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]
@@ -155,6 +160,13 @@ add_def () {
fi
}
#}}}
#{{{ pkg_config
pkg_config () {
type pkg-config > /dev/null 2> /dev/null || return 1
pkg-config $@ 2> /dev/null
}
#}}}
# ======================================================================
@@ -170,7 +182,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 +196,22 @@ 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
ntp_era_split=""
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 +270,27 @@ 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-ntp-era=* )
ntp_era_split=`echo $option | sed -e 's/^.*=//;'`
;;
--with-user=* )
default_user=`echo $option | sed -e 's/^.*=//;'`
;;
--with-sendmail=* )
mail_program=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -310,11 +339,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
@@ -359,6 +389,40 @@ case $SYSTEM in
;;
esac
if test_code '64-bit time_t' 'time.h' '' '' '
char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
return x[0];'
then
add_def HAVE_LONG_TIME_T 1
if [ "x$ntp_era_split" != "x" ]; then
split_seconds=$ntp_era_split
split_days=0
else
split_seconds=`date '+%s'`
if [ "x$split_seconds" = "" ]; then
echo "Could not get current time, --with-ntp-era option is needed"
exit 1
fi
split_days=$((50 * 365))
fi
add_def NTP_ERA_SPLIT "(${split_seconds}LL - $split_days * 24 * 3600)"
date_format='+%Y-%m-%dT%H:%M:%SZ'
# Print the full NTP interval if a suitable date is found
if [ "x`date -u -d '1970-01-01 UTC 9 days ago 5 seconds 3 seconds' \
$date_format 2> /dev/null`" = "x1969-12-23T00:00:08Z" ]
then
time1="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds" \
$date_format`"
time2="`date -u -d "1970-01-01 UTC $split_days days ago $split_seconds seconds 4294967296 seconds" \
$date_format`"
echo "NTP time mapped to $time1/$time2"
fi
fi
MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));'
if test_code 'math' 'math.h' '' '' "$MATHCODE"; then
LIBS=""
@@ -401,6 +465,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 +495,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 +511,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 +522,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()' \
@@ -487,12 +582,12 @@ if [ $feat_readline = "1" ]; then
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code readline 'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib $ncurses_lib -lreadline" \
"$readline_inc" "$readline_lib -lreadline" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
READLINE_COMPILE="$readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
@@ -507,6 +602,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 +611,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`"
test_link="`pkg_config --libs-only-L nss` -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 +620,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 +632,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 +692,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 +709,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 +724,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

View File

@@ -0,0 +1,17 @@
[Unit]
Description=Wait for chrony to synchronize system clock
After=chronyd.service
Requires=chronyd.service
Before=time-sync.target
Wants=time-sync.target
[Service]
Type=oneshot
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc waitsync 60 0.1
RemainAfterExit=yes
StandardOutput=null
[Install]
WantedBy=multi-user.target

View File

@@ -106,6 +106,9 @@ keyfile /etc/chrony.keys
commandkey 1
# With this directive a random password will be generated automatically.
generatecommandkey
# chronyd can save the measurement history for the servers to files when
# it it exits. This is useful in 2 situations:
#

View File

@@ -0,0 +1,8 @@
/var/log/chrony/*.log {
missingok
nocreate
sharedscripts
postrotate
/usr/bin/chronyc -a cyclelogs > /dev/null 2>&1 || true
endscript
}

View File

@@ -0,0 +1,17 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
# online/offline when a default route is configured/removed on the system.
export LC_ALL=C
if [ "$2" = "up" ]; then
/sbin/ip route list dev "$1" | grep -q '^default' &&
/usr/bin/chronyc -a online > /dev/null 2>&1
fi
if [ "$2" = "down" ]; then
/sbin/ip route list | grep -q '^default' ||
/usr/bin/chronyc -a offline > /dev/null 2>&1
fi
exit 0

13
examples/chronyd.service Normal file
View File

@@ -0,0 +1,13 @@
[Unit]
Description=NTP client/server
After=ntpdate.service sntp.service ntpd.service
Conflicts=ntpd.service systemd-timesyncd.service
[Service]
Type=forking
PIDFile=/var/run/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
[Install]
WantedBy=multi-user.target

286
faq.txt
View File

@@ -1,286 +0,0 @@
@@PROLOGUE
<html>
<head>
<title>Frequently asked questions</title>
<meta name="description" content="Chrony FAQ (frequently asked questions)">
<meta name="keywords" content="chrony,network time protocol,NTP,RFC 1305,dial-up connection,real time clock,RTC,Linux,FAQ,frequently asked questns">
<?php
$root = ".";
include "$root/styles.php";
?>
</head>
<body>
<?php
include 'main_banner.php';
include 'header.php';
?>
<?php pretty_h1("Introduction") ?>
<p>
This is a set of questions and answers to common problems and issues.
<p>
As we receive more emails about the software, we will add new questions
to this page.
<hr>
<p>
The developers can be reached via the chrony-dev mailing list. See
<a href="#question_1.4">question 1.4.</a> for details.
<hr>
<br clear=all>
@@ENDPROLOGUE
S: Administrative issues
Q: Where can I get chrony source code?
Tarballs are available via the <b>Download</b> link on the Chrony
Web site. For the current development from the developers' version control
system see the <b>Git</b> link on the Web site.
Q: Are there any packaged versions of chrony?
We are aware of packages for Debian, Fedora, Gentoo, Mandriva, Slackware,
and Ubuntu. We are not involved with how these are built or distributed.
Q: Where is the home page?
It is currently at <a href="http://chrony.tuxfamily.org/">http://chrony.tuxfamily.org/</a>.
Q: Is there a mailing list?
Yes, it's currently at chrony-users@chrony.tuxfamily.org. There is a low-volume
list called chrony-announce which is just for announcements of new releases or
similar matters of high importance. You can join the lists by sending a
message with the subject subscribe to <a href="mailto:chrony-users-request@chrony.tuxfamily.org">chrony-users-request@chrony.tuxfamily.org</a> or
<a href="mailto:chrony-announce-request@chrony.tuxfamily.org">chrony-announce-request@chrony.tuxfamily.org</a> respectively.
For those who want to contribute to the development of chrony, there is a
developers' mailing list. You can subscribe by sending mail with the
subject subscribe to
<a href="mailto:chrony-dev-request@chrony.tuxfamily.org">chrony-dev-request@chrony.tuxfamily.org</a>.
Q: What licence is applied to chrony?
Starting from version 1.15, chrony is licensed under the GNU General Public
License, Version 2. Versions prior to 1.15 were licensed under a custom BSD-like
license.
S: Chrony compared to other programs
Q: How does chrony compare to xntpd?
If your computer is permenently connected, or connected for long periods (that
is, for the several hours it takes xntpd to settle down), or you need to
support hardware reference clocks to your computer, then xntpd will work fine.
Apart from not supporting hardware clocks, chrony will work fine too.
If your computer connects to the 'net for 5 minutes once a day (or something
like that), or you turn your Linux computer off when you're not using
it, or you want to use NTP on an isolated network with no hardware clocks in
sight, chrony will work much better for you.
The reason I wrote chrony was that I could not get xntpd to do
anything sensible on my PC at home, which is connected to the 'net for
about 5 minutes once or twice a day, mainly to upload/download email
and news. Nowadays it is also turned off for 22-23 hours a day, when
not in use. I wanted a program which would :
- slew the time to correct it when I go online and NTP servers become visible
- determine the rate at which the computer gains or loses time and use this
information to keep it reasonably correct between connects to the 'net. This
has to be done using a method that does not care about the intermittent
availability of the references or the fact the computer is turned off between
groups of measurements..
- maintain the time across reboots, by working out the error and drift rate of
the computer's real-time clock and using this information to set the system
clock correctly at boot up. (In the last few months, it became impossible for
me to leave my computer powered permanently.)
Also, when working with isolated networks with no true time references
at all, I found xntpd gave me no help with managing the local clock's
gain/loss rate on the NTP master node (which I set from my watch). I
added some automated support in chrony to deal with this.
S: Selection of NTP servers
Q: I have several computers on a LAN. Should I make one the master, or make them all clients of an external server?
I think the best configuration is to make one computer the master, with the
others as clients of it. Add a 'local' directive to the master's chrony.conf
file. This configuration will be better because
* the load on the external connection is less
* the load on the external NTP server(s) is less
* if your external connection goes down, the computers on the LAN will maintain
a common time with each other.
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.
Q: Behind a firewall?
If there is a firewall between you and the NTP server you're trying to use,
the packets may be blocked. Try using a tool like etherfind or tcpdump to see
if you're getting responses from the server. If you have an external modem,
see if the receive light blinks straight after the transmit light (when the
link is quiet apart from the NTP traffic.) Try adding 'log measurements' to
the chrony.conf file and look in the measurements.log file after chrony has
been running for a short period. See if any measurements appear.
Most people run chronyd on the firewall itself, to avoid all issues of UDP
packet forwarding and/or masquerading.
Q: Do you have a non-permanant (i.e. intermittent) Internet connection?
Check that you're using chronyc's 'online' and 'offline' commands
appropriately. Again, check in measurements.log to see if you're getting any
data back from the server.
Q: In measurements.log, do the '7' and '8' flag columns always show zero?
Do you have a 'local stratum X' directive in the chrony.conf file? If X is
lower than the stratum of the server you're trying to use, this situation will
arise. You should always make X quite high (e.g. 10) in this directive.
S: Issues with chronyc
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
isn't necessary for localhost.
Perhaps chronyd is not running. Try using the ps command (e.g. on Linux, 'ps
-auxw') to see if it's running. Or try 'netstat -a' and see if the ports
123/udp and 323/udp are listening. If chronyd is not running, you may have a
problem with the way you are trying to start it (e.g. at boot time).
Perhaps you have a firewall set up in a way that blocks packets on port
323/udp. You need to amend the firewall configuration in this case.
Q: Is the chronyc&lt;-&gt;chronyd protocol documented anywhere?
Only by the source code :-) See cmdmon.c (chronyd side) and client.c (chronyc
side).
S: Real-time clock issues.
Q: What is the real-time clock (RTC)?
This is the clock which keeps the time even when your computer is turned off.
It works with 1 second resolution. chronyd can monitor the rate at which the
real-time clock gains or loses time, and compensate for it when you set the
system time from it at the next reboot. See the documentation for details.
Q: I want to use chronyd's real-time clock support. Must I disable hwclock?
The hwclock program is often set-up by default in the boot and shutdown scripts
with many Linux installations. If you want to use chronyd's real-time clock
support, the important thing is to disable hwclock in the <b>shutdown</b>
procedure. If you don't, it will over-write the RTC with a new value, unknown
to chronyd. At the next reboot, chronyd will compensate this (wrong) time with
its estimate of how far the RTC has drifted whilst the power was off, giving a
meaningless initial system time.
There is no need to remove hwclock from the boot process, as long as chronyd is
started after it has run.
Q: I just keep getting the '513 RTC driver not running' message
For the real time clock support to work, you need the following three things:
* a kernel that is supported (e.g. 2.2 onwards)
* enhanced RTC support compiled into the kernel
* an 'rtcfile' directive in your chrony.conf file.
S: Microsoft Windows
Q: Does chrony support Windows?
No. The chronyc program (the command-line client used for configuring
chronyd while it is running) has been successfully built and run under Cygwin
in the past. chronyd is not portable, because part of it is very
system-dependent. It needs adapting to work with Windows' equivalent of the
adjtimex() call, and it needs to be made to work as an NT service.
Q: Are there any plans to support Windows?
We have no plans to do this. Anyone is welcome to pick this work up and
contribute it back to the project.
Q: What alternative NTP clients are there for Windows?
Some of the names we've seen mentioned are
- Automachron
- NetTime (nettime.sourceforge.net)
S: NTP-specific issues
Q: Can chrony be driven from broadcast NTP servers?
No. I remember looking at how they worked when I was first writing chrony.
Since the 'target market' then was dial-up systems, broadcast packets were not
relevant so I didn't bother working out how to deal with the complexities of
doing the delay estimation.
I no longer have root access to a LAN environment to develop and test broadcast
server support. Neither have I the time to work on this. I would be very
happy to accept a patch from anyone who can develop, test and debug the
necessary changes!
Q: Can chronyd transmit broadcast NTP packets (e.g. to synchronise other computers on a private LAN)?
Yes. Starting from version 1.17, chrony has this capability.
Q: Can chrony keep the system clock a fixed offset away from real time?
I have not experimented much, but I don't believe this would be possible as
the program currently stands.
Q: What happens if the network connection is dropped without using chronyc's 'offline' command first?
In this case chronyd will keep trying to access the server(s) that it thinks
are online. Eventually it will decide that they are unreachable and no longer
consider itself synchronised to them. If you have other computers on your LAN
accessing the computer that is affected this way, they too will become
'unsynchronised', unless you have the 'local' directive set up on the master
computer.
The 'auto_offline' option to the 'server' entry in the chrony.conf file may be
useful to avoid this situation.
S: Development
Q: Can I get the source via git from anywhere?
Yes. See the Git link at <a
href="http://chrony.tuxfamily.org/">http://chrony.tuxfamily.org</a> for
information.
S: Linux-specific issues
Q: Why does the source code include kernel header files?
The program needs to see the definitions of structures used to interact with
the real time clock (via /dev/rtc) and with the adjtimex() system call. Sadly
this has led to a number of compilation problems with newer kernels which have
been increasingly hard to fix in a way that makes the code compilable on all
Linux kernel versions. Hopefully
the situation will not deteriorate further with future kernel versions.
Q: I get "Could not open /dev/rtc, Device or resource busy" in my syslog file.
Check that you haven't accidentally got two copies of chronyd running (perhaps
defined in different start-up scripts.)
S: Solaris-specific issues
Q: On Solaris 2.8, I get an error message about not being able to open kvm to change dosynctodr.
(The dosynctodr variable controls whether Solaris couples the equivalent of its
BIOS clock into its system clock at regular intervals). The Solaris port of
chrony was developed in the Solaris 2.5 era. Some aspect of the Solaris kernel
has changed which prevents the same technique working. I no longer have root
access to any Solaris machines to work on this, and am reliant on somebody
developing the patch and testing it. A good starting point would be to see if
xntpd has been modified to work for Solaris 2.8.
@@EPILOGUE
<hr>
Back to
<a href="mailto:rc@rc0.org.uk?subject=chrony">the author</a>'s
<a href="http://www.rc0.org.uk/">main page</a>
</body>
</html>
@@ENDEPILOGUE

140
faqgen.pl
View File

@@ -1,140 +0,0 @@
#!/usr/bin/env perl
# $Header
# Copyright 2001 Richard P. Curnow
# LICENCE
# A script to generate an HTML FAQ page from a text input file. The input is assumed to consist of the following:
# Lines starting with 'S:'. These introduce sections.
# Lines starting with 'Q:'. These are the topics of questions.
# Body text (either as an introduction to the sections, or as answers to the questions.
# The body text is set as pre-formatted.
$| = 1;
@prologue = ();
@epilogue = ();
@sections=(); # section titles
@sect_text=(); # introductory text in sections
@questions=(); # questions in sections
@answers=(); # answers to questions
$sn = -1;
$had_q = 0;
#{{{ Parse input
while (<>) {
if (m/\@\@PROLOG/o) {
while (<>) {
last if (m/^\@\@ENDPROLOG/);
push (@prologue, $_);
}
} elsif (m/\@\@EPILOG/o) {
while (<>) {
last if (m/^\@\@ENDEPILOG/);
push (@epilogue, $_);
}
} elsif (m/^[sS]:[ \t]*(.*)$/) {
chomp;
$qn = -1;
++$sn;
$sections[$sn] = &guard($1);
$sect_text[$sn] = "";
$questions[$sn] = [ ];
$answers[$sn] = [ ];
$had_q = 0;
} elsif (/^[qQ]:[ \t]*(.*)$/) {
chomp;
die unless ($sn >= 0);
++$qn;
$questions[$sn]->[$qn] = &guard($1);
$had_q = 1;
} else {
if ($had_q) {
if ($qn >= 0) {
$answers[$sn]->[$qn] .= $_;
}
} else {
if ($sect_text[$sn] ne "" || $_ !~ /^\s*$/) {
$sect_text[$sn] .= $_;
}
}
}
}
#}}}
# Emit file header
if ($#prologue >= 0) {
print @prologue;
} else {
print <<EOF;
<html>
<head>
<title>
Chrony Frequently Asked Questions
</title>
</head>
<body>
<font face=\"arial,helvetica\" size=+4><b>Table of contents</b></font>
EOF
}
# Emit table of contents
print "<ul>\n";
for $sn (0 .. $#sections) {
print "<b><li> <a href=\"#section_".($sn+1)."\">".($sn+1).".</a> ".$sections[$sn]."</b>\n";
print " <ul>\n";
for $qn (0 .. $#{$questions[$sn]}) {
$sq = ($sn+1).".".($qn+1);
print " <li> <a href=\"#question_".$sq."\">".$sq.".</a> ".$questions[$sn]->[$qn]."\n";
#print " <li> ".$sq.". ".$questions[$sn]->[$qn]."\n";
}
print " </ul>\n";
}
print "</ul>\n";
# Emit main sections
for $sn (0 .. $#sections) {
print "<hr>\n";
print "<a name=section_".($sn+1).">\n";
#print "<b><font size=+2 face=\"arial,helvetica\">".($sn+1).". ".$sections[$sn]."</font></b>\n";
print "<?php pretty_h2(\"".($sn+1).". ".$sections[$sn]."\"); ?>\n";
if ($sect_text[$sn] ne "") {
print "<pre>\n";
print $sect_text[$sn];
print "</pre>\n";
}
for $qn (0 .. $#{$questions[$sn]}) {
$sq = ($sn+1).".".($qn+1);
print "<p>\n";
print "<a name=question_".$sq.">\n";
print "<font size=+1 face=\"arial,helvetica\">".$sq.". ".$questions[$sn]->[$qn]."</font>\n";
print "<pre>\n";
print $answers[$sn]->[$qn];
print "</pre>\n";
}
}
# Print footer
if ($#epilogue >= 0) {
print @epilogue;
} else {
print <<EOF;
</body>
</html>
EOF
}
#{{{ sub guard {
sub guard {
# Hide wierd tags etc
my ($x) = @_;
return $x;
}
#}}}

864
getdate.c

File diff suppressed because it is too large Load Diff

View File

@@ -711,7 +711,7 @@ LookupWord (buff)
/* Make it lowercase. */
for (p = buff; *p; p++)
if (ISUPPER ((unsigned char) *p))
*p = tolower (*p);
*p = tolower ((unsigned char) *p);
if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
{

49
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
@@ -142,7 +142,7 @@ KEY_Finalise(void)
/* ================================================== */
static int
determine_hash_delay(int key_id)
determine_hash_delay(unsigned long key_id)
{
NTP_Packet pkt;
struct timeval before, after;
@@ -162,12 +162,12 @@ determine_hash_delay(int key_id)
}
}
#if 0
LOG(LOGS_INFO, LOGF_Keys, "authentication delay for key %lu: %d useconds", key_id, min_usecs);
#endif
/* Add on a bit extra to allow for copying, conversions etc */
return min_usecs + (min_usecs >> 4);
min_usecs += min_usecs >> 4;
DEBUG_LOG(LOGF_Keys, "authentication delay for key %lu: %ld useconds", key_id, min_usecs);
return min_usecs;
}
/* ================================================== */
@@ -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;
}
/* ================================================== */

93
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,28 @@ void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *
/* ================================================== */
int
LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
{
return change_list.next->handler == handler;
}
/* ================================================== */
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 +395,6 @@ LCL_ReadAbsoluteFrequency(void)
void
LCL_SetAbsoluteFrequency(double afreq_ppm)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
double dfreq;
@@ -382,15 +407,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 +424,6 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
void
LCL_AccumulateDeltaFrequency(double dfreq)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
double old_freq_ppm;
@@ -411,20 +433,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 +451,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 +462,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 +470,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 +481,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 +490,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);
}
@@ -494,7 +501,6 @@ LCL_NotifyExternalTimeStep(struct timeval *raw, struct timeval *cooked,
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *ptr;
struct timeval raw, cooked;
double old_freq_ppm;
@@ -508,25 +514,19 @@ 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);
/* Dispatch to all handlers */
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
(ptr->handler)(&raw, &cooked, dfreq, doffset, 0, ptr->anything);
}
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
}
/* ================================================== */
@@ -561,17 +561,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 +577,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;
}

17
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
);
@@ -86,6 +92,9 @@ extern void LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, vo
/* Remove a handler */
extern void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler, void *anything);
/* Check if a handler is invoked first when dispatching */
extern int LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler);
/* Function type for handlers to be called back when an indeterminate
offset is introduced into the local time. This situation occurs
when the frequency must be adjusted to effect a clock slew and
@@ -183,7 +192,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,

146
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,94 @@ 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);
}
/* ================================================== */
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);
/* 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);
}
}
break;
default:
assert(0);
}
#endif
}
/* ================================================== */
@@ -194,6 +207,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 +232,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,41 @@
#ifndef GOT_LOGGING_H
#define GOT_LOGGING_H
#include "sysincl.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, ...) \
do { \
LOG_Message(LOGS_FATAL, facility, __LINE__, __FILE__, FUNCTION_NAME, __VA_ARGS__); \
exit(1); \
} while (0)
/* 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 +87,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 +109,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 +130,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);

143
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
@@ -371,16 +452,12 @@ int main
* be done *AFTER* the daemon-creation fork() */
write_lockfile();
if (do_init_rtc) {
RTC_TimePreInit();
}
LCL_Initialise();
SCH_Initialise();
SYS_Initialise();
NIO_Initialise(address_family);
CAM_Initialise(address_family);
RTC_Initialise();
RTC_Initialise(do_init_rtc);
SRC_Initialise();
RCL_Initialise();
KEY_Initialise();
@@ -400,7 +477,7 @@ int main
if (!user) {
user = CNF_GetUser();
}
if (user) {
if (user && strcmp(user, "root")) {
SYS_DropRoot(user);
}
@@ -412,13 +489,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

@@ -9,6 +9,7 @@ if [ $# -ne 1 ]; then
fi
version=$1
tag=$version
subdir=chrony-${version}
mandate=$(date +'%B %Y')
@@ -21,20 +22,24 @@ fi
[ -d RELEASES ] || mkdir RELEASES
git tag -s $version || exit 1
rm -rf RELEASES/$subdir
git archive --format=tar --prefix=RELEASES/${subdir}/ $version | \
if [ $version != test ]; then
git tag -s $tag || exit 1
else
tag=HEAD
fi
git archive --format=tar --prefix=RELEASES/${subdir}/ $tag | \
tar xf - || exit 1
cd RELEASES/$subdir || exit 1
echo $version > version.txt
sed -e "s%@@VERSION@@%${version}%" < chrony.spec.sample > chrony.spec
sed -i -e "s%@@VERSION@@%${version}%" examples/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 +50,29 @@ mv chrony.txt chrony.txt_
make distclean
mv chrony.txt_ chrony.txt
rm -f faqgen.pl make_release chrony.spec.sample .gitignore
awk '/^[1-9] Installation$/{p=1}
/^[1-9]\.. Support for line editing/{exit}; p' chrony.txt | \
tail -n +4 > INSTALL
if [ $(wc -l < INSTALL) -gt 100 -o $(wc -l < INSTALL) -lt 85 ]; then
echo "INSTALL generated incorrectly?"
exit 3
fi
awk '/^[1-9] Frequently asked questions$/{p=1}
/^Appendix A GNU General Public License$/{exit}; p' chrony.txt | \
tail -n +4 | sed 's/^[1-9]\.\([1-9]\)/\1/' | sed 's/^----/--/' | \
sed 's/^====/==/' > FAQ
if [ $(wc -l < FAQ) -gt 400 -o $(wc -l < FAQ) -lt 200 ]; then
echo "FAQ generated incorrectly?"
exit 3
fi
rm -f config.h config.log make_release .gitignore
cd ..
tar cvf - $subdir | gzip -9 > ${subdir}.tar.gz
gpg -b -a -o ${subdir}-tar-gz-asc.txt ${subdir}.tar.gz
tar cv --owner root --group root $subdir | gzip -9 > ${subdir}.tar.gz
[ $version != test ] && \
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 */
@@ -88,8 +85,8 @@ struct NCR_Instance_Record {
received packets) */
int presend_minpoll; /* If the current polling interval is
at least this, an echo datagram
will be send some time before every
at least this, an extra client packet
will be send some time before normal
transmit. This ensures that both
us and the server/peer have an ARP
entry for each other ready, which
@@ -188,9 +185,13 @@ 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
/* Time to wait after sending echo to 'warm up' link */
/* Number of samples in initial burst */
#define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
/* Time to wait after sending packet to 'warm up' link */
#define WARM_UP_DELAY 4.0
/* The NTP protocol version that we support */
@@ -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;
@@ -220,12 +229,79 @@ static ADF_AuthTable access_auth_table;
/* Forward prototypes */
static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
/* ================================================== */
static void
do_size_checks(void)
{
/* Assertions to check the sizes of certain data types
and the positions of certain record fields */
/* Check that certain invariants are true */
assert(sizeof(NTP_int32) == 4);
assert(sizeof(NTP_int64) == 8);
/* Check offsets of all fields in the NTP packet format */
assert(offsetof(NTP_Packet, lvm) == 0);
assert(offsetof(NTP_Packet, stratum) == 1);
assert(offsetof(NTP_Packet, poll) == 2);
assert(offsetof(NTP_Packet, precision) == 3);
assert(offsetof(NTP_Packet, root_delay) == 4);
assert(offsetof(NTP_Packet, root_dispersion) == 8);
assert(offsetof(NTP_Packet, reference_id) == 12);
assert(offsetof(NTP_Packet, reference_ts) == 16);
assert(offsetof(NTP_Packet, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40);
}
/* ================================================== */
static void
do_time_checks(void)
{
struct timeval now;
time_t warning_advance = 3600 * 24 * 365 * 10; /* 10 years */
#ifdef HAVE_LONG_TIME_T
/* Check that time before NTP_ERA_SPLIT underflows correctly */
struct timeval tv1 = {NTP_ERA_SPLIT, 1}, tv2 = {NTP_ERA_SPLIT - 1, 1};
NTP_int64 ntv1, ntv2;
int r;
UTI_TimevalToInt64(&tv1, &ntv1, 0);
UTI_TimevalToInt64(&tv2, &ntv2, 0);
UTI_Int64ToTimeval(&ntv1, &tv1);
UTI_Int64ToTimeval(&ntv2, &tv2);
r = tv1.tv_sec == NTP_ERA_SPLIT &&
tv1.tv_sec + (1ULL << 32) - 1 == tv2.tv_sec;
assert(r);
LCL_ReadRawTime(&now);
if (tv2.tv_sec - now.tv_sec < warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "Assumed NTP time ends at %s!",
UTI_TimeToLogForm(tv2.tv_sec));
#else
LCL_ReadRawTime(&now);
if (now.tv_sec > 0x7fffffff - warning_advance)
LOG(LOGS_WARN, LOGF_NtpCore, "System time ends at %s!",
UTI_TimeToLogForm(0x7fffffff));
#endif
}
/* ================================================== */
void
NCR_Initialise(void)
{
do_size_checks();
do_time_checks();
logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements",
" Date (UTC) Time IP Address L St 1234 abc 5678 LP RP Score Offset Peer del. Peer disp. Root del. Root disp.")
: -1;
@@ -245,11 +321,20 @@ NCR_Finalise(void)
/* ================================================== */
static void
start_initial_timeout(NCR_Instance inst)
restart_timeout(NCR_Instance inst, double delay)
{
/* Check if we can transmit */
if (inst->tx_suspended) {
assert(!inst->timer_running);
return;
}
/* Start timer for first transmission */
inst->timeout_id = SCH_AddTimeoutInClass(INITIAL_DELAY, SAMPLING_SEPARATION,
/* Stop old timer if running */
if (inst->timer_running)
SCH_RemoveTimeout(inst->timeout_id);
/* Start new timer for transmission */
inst->timeout_id = SCH_AddTimeoutInClass(delay, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
@@ -258,6 +343,32 @@ start_initial_timeout(NCR_Instance inst)
/* ================================================== */
static void
start_initial_timeout(NCR_Instance inst)
{
if (!inst->timer_running) {
/* This will be the first transmission after mode change */
/* Mark source active */
SRC_SetActive(inst->source);
}
restart_timeout(inst, INITIAL_DELAY);
}
/* ================================================== */
static void
close_client_socket(NCR_Instance inst)
{
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;
}
}
/* ================================================== */
static void
take_offline(NCR_Instance inst)
{
@@ -269,6 +380,13 @@ take_offline(NCR_Instance inst)
/* Mark source unreachable */
SRC_ResetReachability(inst->source);
/* And inactive */
SRC_UnsetActive(inst->source);
close_client_socket(inst);
NCR_ResetInstance(inst);
}
/* ================================================== */
@@ -281,11 +399,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 sending request */
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 +416,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 +439,28 @@ 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;
result->local_poll = result->minpoll;
NCR_ResetInstance(result);
if (params->iburst) {
NCR_InitiateSampleBurst(result, IBURST_GOOD_SAMPLES, IBURST_TOTAL_SAMPLES);
}
return result;
}
/* ================================================== */
@@ -358,23 +469,60 @@ 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->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;
if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll;
/* The timer was set with a longer poll interval, restart it */
if (instance->timer_running)
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
}
}
/* ================================================== */
static int
check_packet_auth(NTP_Packet *pkt, unsigned long keyid, int auth_len)
{
@@ -520,8 +668,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:
@@ -534,7 +681,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
/* ================================================== */
static void
static int
transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
int my_poll, /* The log2 of the local poll interval */
int version, /* The NTP version to be set in the packet */
@@ -551,11 +698,12 @@ 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;
int leap;
int leap, ret;
struct timeval local_transmit;
/* Parameters read from reference module */
@@ -639,12 +787,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,
ret = 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 0;
}
} else {
UTI_TimevalToInt64(&local_transmit, &message.transmit_ts, ts_fuzz);
NIO_SendNormalPacket(&message, where_to);
ret = NIO_SendNormalPacket(&message, where_to, from);
}
if (local_tx) {
@@ -655,6 +808,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
*local_ntp_tx = message.transmit_ts;
}
return ret;
}
/* ================================================== */
@@ -664,8 +818,7 @@ static void
transmit_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance) arg;
double timeout_delay;
int do_auth;
int sent;
inst->timer_running = 0;
@@ -686,13 +839,18 @@ 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
/* Open new client socket */
if (inst->mode == MODE_CLIENT) {
close_client_socket(inst);
assert(inst->local_addr.sock_fd == INVALID_SOCK_FD);
inst->local_addr.sock_fd = NIO_GetClientSocket(&inst->remote_addr);
}
/* 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
sending an NTP exchange to ensure both ends' ARP caches are
primed. On loaded systems this might also help ensure that bits
of the program are paged in properly before we start. */
@@ -700,33 +858,42 @@ transmit_timeout(void *arg)
(inst->presend_minpoll <= inst->local_poll) &&
!inst->presend_done) {
/* Send */
NIO_SendEcho(&inst->remote_addr);
/* Send a client packet, don't store the local tx values
as the reply will be ignored */
transmit_packet(MODE_CLIENT, inst->local_poll, NTP_VERSION, 0, 0,
&inst->remote_orig, &inst->local_rx, NULL, NULL,
&inst->remote_addr, &inst->local_addr);
inst->presend_done = 1;
/* Requeue timeout */
inst->timer_running = 1;
inst->timeout_id = SCH_AddTimeoutInClass(WARM_UP_DELAY, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
restart_timeout(inst, WARM_UP_DELAY);
return;
}
inst->presend_done = 0; /* Reset for next time */
sent = transmit_packet(inst->mode, inst->local_poll,
NTP_VERSION,
inst->do_auth, inst->auth_key_id,
&inst->remote_orig,
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
&inst->remote_addr,
&inst->local_addr);
++inst->tx_count;
/* If the source loses connectivity, back off the sampling rate to reduce
wasted sampling. If it's the source to which we are currently locked,
back off slower. */
/* If the source loses connectivity and our packets are still being sent,
back off the sampling rate to reduce the network traffic. If it's the
source to which we are currently locked, back off slowly. */
if (inst->tx_count >= 2) {
/* Implies we have missed at least one transmission */
adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25);
if (sent) {
adjust_poll(inst, SRC_IsSyncPeer(inst->source) ? 0.1 : 0.25);
}
SRC_UpdateReachability(inst->source, 0);
@@ -735,21 +902,12 @@ 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->remote_orig,
&inst->local_rx, &inst->local_tx, &inst->local_ntp_tx,
&inst->remote_addr);
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
/* When not reachable, don't stop online burst until sending succeeds */
if (!sent && !SRC_IsReachable(inst->source))
break;
/* Fall through */
case MD_BURST_WAS_OFFLINE:
--inst->burst_total_samples_to_go;
break;
@@ -758,12 +916,7 @@ transmit_timeout(void *arg)
}
/* Restart timer for this message */
timeout_delay = get_transmit_delay(inst, 1, 0.0);
inst->timer_running = 1;
inst->timeout_id = SCH_AddTimeoutInClass(timeout_delay, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
}
@@ -852,9 +1005,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* ==================== */
/* Save local receive timestamp */
inst->local_rx = *now;
pkt_leap = (message->lvm >> 6) & 0x3;
if (pkt_leap == 0x3) {
source_is_synchronized = 0;
@@ -886,14 +1036,6 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
test2 = 1; /* Success */
}
/* Regardless of any validity checks we apply, we are required to
save this field from the packet into the ntp source
instance record. See RFC1305 section 3.4.4, peer.org <- pkt.xmt
& peer.peerpoll <- pkt.poll. Note we can't do this assignment
before test1 has been carried out!! */
inst->remote_orig = message->transmit_ts;
/* Test 3 requires that pkt.org != 0 and pkt.rec != 0. If
either of these are true it means the association is not properly
'up'. */
@@ -998,11 +1140,8 @@ 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) &&
auth_key_id == inst->auth_key_id;
} else {
/* If we expect authenticated info from this peer/server and the packet
doesn't have it, it's got to fail */
@@ -1070,6 +1209,14 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
kod_rate = 1;
}
/* The transmit timestamp and local receive timestamp must not be saved when
the authentication test failed to prevent denial-of-service attacks on
symmetric associations using authentication */
if (test5) {
inst->remote_orig = message->transmit_ts;
inst->local_rx = *now;
}
valid_kod = test1 && test2 && test5;
valid_data = test1 && test2 && test3 && test4 && test4a && test4b;
@@ -1080,55 +1227,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 +1314,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
@@ -1191,6 +1346,10 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
adjust_poll(inst, 0.1);
}
/* If in client mode, no more packets are expected to be coming from the
server and the socket can be closed */
close_client_socket(inst);
requeue_transmit = 1;
}
@@ -1205,11 +1364,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins
/* Get rid of old timeout and start a new one */
assert(inst->timer_running);
SCH_RemoveTimeout(inst->timeout_id);
inst->timeout_id = SCH_AddTimeoutInClass(delay_time, SAMPLING_SEPARATION,
SAMPLING_RANDOMNESS,
SCH_NtpSamplingClass,
transmit_timeout, (void *)inst);
restart_timeout(inst, delay_time);
}
/* Do measurement logging */
@@ -1259,18 +1414,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 +1470,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;
@@ -1383,6 +1507,9 @@ NCR_ProcessKnown
break;
case MODE_SERVER:
/* Ignore presend reply */
if (inst->presend_done)
break;
switch(inst->mode) {
case MODE_ACTIVE:
@@ -1452,7 +1579,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 +1587,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 +1640,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 +1655,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 +1676,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 +1695,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 +1717,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 +1737,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 +1750,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 +1830,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);

528
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,87 +49,108 @@ 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);
/* ================================================== */
static void
do_size_checks(void)
{
/* Assertions to check the sizes of certain data types
and the positions of certain record fields */
/* Check that certain invariants are true */
assert(sizeof(NTP_int32) == 4);
assert(sizeof(NTP_int64) == 8);
/* Check offsets of all fields in the NTP packet format */
assert(offsetof(NTP_Packet, lvm) == 0);
assert(offsetof(NTP_Packet, stratum) == 1);
assert(offsetof(NTP_Packet, poll) == 2);
assert(offsetof(NTP_Packet, precision) == 3);
assert(offsetof(NTP_Packet, root_delay) == 4);
assert(offsetof(NTP_Packet, root_dispersion) == 8);
assert(offsetof(NTP_Packet, reference_id) == 12);
assert(offsetof(NTP_Packet, reference_ts) == 16);
assert(offsetof(NTP_Packet, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40);
}
/* ================================================== */
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
sock_fd = socket(family, SOCK_DGRAM, IPPROTO_UDP);
#else
sock_fd = socket(family, SOCK_DGRAM, 0);
#endif
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) {
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
switch (family) {
case AF_INET:
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);
else if (port_number)
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
else
break;
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
my_addr_len = sizeof (my_addr.in4);
break;
#ifdef HAVE_IPV6
case AF_INET6:
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,
sizeof (my_addr.in6.sin6_addr.s6_addr));
else if (port_number)
my_addr.in6.sin6_addr = in6addr_any;
else
break;
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
my_addr_len = sizeof (my_addr.in6);
break;
#endif
default:
assert(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 */
}
@@ -140,11 +163,20 @@ prepare_socket(int family)
}
#endif
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set free bind socket option");
}
#endif
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) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request packet info using socket option");
/* 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 set packet info socket option");
/* Don't quit - we might survive anyway */
}
#endif
@@ -154,110 +186,164 @@ prepare_socket(int family)
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not request IPV6_V6ONLY socket option");
LOG(LOGS_ERR, LOGF_NtpIO, "Could not set IPV6_V6ONLY socket option");
}
#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 set 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 set IPv6 packet info socket option");
}
#endif
}
}
#endif
/* Bind the port */
memset(&my_addr, 0, sizeof (my_addr));
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
close(sock_fd);
return INVALID_SOCK_FD;
}
/* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
return sock_fd;
}
/* ================================================== */
static int
prepare_separate_client_socket(int family)
{
switch (family) {
case AF_INET:
my_addr_len = sizeof (my_addr.in4);
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
case IPADDR_INET4:
return prepare_socket(AF_INET, 0, 1);
#ifdef HAVE_IPV6
case IPADDR_INET6:
return prepare_socket(AF_INET6, 0, 1);
#endif
default:
return INVALID_SOCK_FD;
}
}
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
/* ================================================== */
if (bind_address.family == IPADDR_INET4)
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
else
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
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 AF_INET6:
my_addr_len = sizeof (my_addr.in6);
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
if (bind_address.family == IPADDR_INET6)
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
sizeof (my_addr.in6.sin6_addr.s6_addr));
else
my_addr.in6.sin6_addr = in6addr_any;
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 0
LOG(LOGS_INFO, LOGF_NtpIO, "Initialising, socket fd=%d", sock_fd);
#endif
if (bind(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not bind %s NTP socket : %s",
family == AF_INET ? "IPv4" : "IPv6", strerror(errno));
close(sock_fd);
return -1;
if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
}
/* Register handler for read events on the socket */
SCH_AddInputFileHandler(sock_fd, read_from_socket, (void *)(long)sock_fd);
#if 0
if (fcntl(sock_fd, F_SETFL, O_NONBLOCK | O_NDELAY) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not make socket non-blocking");
}
if (ioctl(sock_fd, I_SETSIG, S_INPUT) < 0) {
LOG(LOGS_ERR, LOGF_NtpIO, "Could not enable signal");
}
#endif
return sock_fd;
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();
server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort();
if (family == IPADDR_UNSPEC || family == IPADDR_INET4)
sock_fd4 = prepare_socket(AF_INET);
else
sock_fd4 = -1;
/* Use separate connected sockets if client port is negative */
separate_client_sockets = client_port < 0;
if (client_port < 0)
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 +352,88 @@ 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 = prepare_separate_client_socket(remote_addr->ip_addr.family);
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 +447,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 +458,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,7 +481,8 @@ read_from_socket(void *anything)
reponse on a subsequent recvfrom). */
if (status > 0) {
memset(&remote_addr, 0, sizeof (remote_addr));
if (msg.msg_namelen > sizeof (where_from))
LOG_FATAL(LOGF_NtpIO, "Truncated source address");
switch (where_from.u.sa_family) {
case AF_INET:
@@ -349,14 +502,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 +521,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 +532,22 @@ 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 > 0) {
DEBUG_LOG(LOGF_NtpIO, "Received %d bytes from %s:%d to %s fd %d",
status,
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd);
}
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 {
@@ -398,50 +560,62 @@ read_from_socket(void *anything)
/* ================================================== */
/* Send a packet to given address */
static void
send_packet(void *packet, int packetlen, NTP_Remote_Address *remote_addr)
static int
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;
socklen_t addrlen = 0;
assert(initialised);
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 0;
}
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
/* Don't set address with connected socket */
if (local_addr->sock_fd != server_sock_fd4 && separate_client_sockets)
break;
memset(&remote.in4, 0, sizeof (remote.in4));
addrlen = sizeof (remote.in4);
remote.in4.sin_family = AF_INET;
remote.in4.sin_port = htons(remote_addr->port);
remote.in4.sin_addr.s_addr = htonl(remote_addr->ip_addr.addr.in4);
sock_fd = sock_fd4;
break;
#ifdef HAVE_IPV6
case IPADDR_INET6:
/* Don't set address with connected socket */
if (local_addr->sock_fd != server_sock_fd6 && separate_client_sockets)
break;
memset(&remote.in6, 0, sizeof (remote.in6));
addrlen = sizeof (remote.in6);
remote.in6.sin6_family = AF_INET6;
remote.in6.sin6_port = htons(remote_addr->port);
memcpy(&remote.in6.sin6_addr.s6_addr, &remote_addr->ip_addr.addr.in6,
sizeof (remote.in6.sin6_addr.s6_addr));
sock_fd = sock_fd6;
break;
#endif
default:
return;
return 0;
}
if (sock_fd < 0)
return;
if (addrlen) {
msg.msg_name = &remote.u;
msg.msg_namelen = addrlen;
} else {
msg.msg_name = NULL;
msg.msg_namelen = 0;
}
iov.iov_base = packet;
iov.iov_len = packetlen;
msg.msg_name = &remote.u;
msg.msg_namelen = addrlen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
@@ -450,7 +624,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 +637,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,65 +655,45 @@ 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
#if 0
LOG(LOGS_INFO, LOGF_NtpIO, "sending to %s:%d from %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPToString(&remote_addr->local_ip_addr));
#endif
msg.msg_controllen = cmsglen;
/* This is apparently required on some systems */
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",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, strerror(errno));
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG(LOGF_NtpIO, "Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno));
return 0;
}
DEBUG_LOG(LOGF_NtpIO, "Sent to %s:%d from %s fd %d",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
return 1;
}
/* ================================================== */
/* Send an unauthenticated packet to a given address */
void
NIO_SendNormalPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr)
int
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);
return 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)
int
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);
}
/* ================================================== */
/* We ought to use getservbyname, but I can't really see this changing */
#define ECHO_PORT 7
void
NIO_SendEcho(NTP_Remote_Address *remote_addr)
{
unsigned long magic_message = 0xbe7ab1e7UL;
NTP_Remote_Address addr;
addr = *remote_addr;
addr.port = ECHO_PORT;
send_packet((void *) &magic_message, sizeof(unsigned long), &addr);
return send_packet((void *) packet, NTP_NORMAL_PACKET_SIZE + auth_len, remote_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,22 @@ 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 int 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);
/* Function to send a datagram to a remote machine's UDP echo port. */
extern void NIO_SendEcho(NTP_Remote_Address *remote_addr);
extern int NIO_SendAuthenticatedPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int auth_len);
#endif /* GOT_NTP_IO_H */

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);
/* ================================================== */
@@ -188,10 +200,6 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParam
assert(initialised);
#if 0
LOG(LOGS_INFO, LOGF_NtpSources, "IP=%s port=%d", UTI_IPToString(&remote_addr->ip_addr), remote_addr->port);
#endif
/* Find empty bin & check that we don't have the address already */
find_slot(remote_addr, &slot, &found);
if (found) {
@@ -206,6 +214,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 +223,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 +329,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 +345,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,26 +429,36 @@ 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;
assert(initialised);
#if 0
LOG(LOGS_INFO, LOGF_NtpSources, "from (%s,%d) at %s",
UTI_IPToString(&remote_addr->ip_addr),
remote_addr->port, UTI_TimevalToString(now));
#endif
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,22 +469,20 @@ 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_RECORDS; i++) {
if (records[i].remote_addr) {
#if 0
LOG(LOGS_INFO, LOGF_Sources, "IP=%s dfreq=%f doff=%f",
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

@@ -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 */
@@ -870,20 +908,13 @@ static void
filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset)
{
int i;
double delta_time, prev_offset;
double delta_time;
struct timeval *sample;
for (i = 0; i < filter->used; i++) {
sample = &filter->samples[i].sample_time;
UTI_AdjustTimeval(sample, when, sample, &delta_time, dfreq, doffset);
prev_offset = filter->samples[i].offset;
filter->samples[i].offset -= delta_time;
#if 0
LOG(LOGS_INFO, LOGF_Refclock, "i=%d old_off=%.9f new_off=%.9f",
i, prev_offset, filter->samples[i].offset);
#else
(void)prev_offset;
#endif
}
}

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, (long)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;
@@ -255,7 +267,7 @@ REF_Finalise(void)
LCL_SetLeap(0);
}
if (drift_file && drift_file_age > 0.0) {
if (drift_file) {
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
}
@@ -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). */
@@ -387,10 +426,8 @@ update_fb_drifts(double freq_ppm, double update_interval)
(freq_ppm - fb_drifts[i].freq);
}
#if 0
LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
DEBUG_LOG(LOGF_Reference, "Fallback drift %d updated: %f ppm %f seconds",
i + fb_drift_min, fb_drifts[i].freq, fb_drifts[i].secs);
#endif
}
}
@@ -437,23 +474,31 @@ schedule_fb_drift(struct timeval *now)
if (c > next_fb_drift) {
LCL_SetAbsoluteFrequency(fb_drifts[c - fb_drift_min].freq);
next_fb_drift = c;
#if 0
LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d set", c);
#endif
DEBUG_LOG(LOGF_Reference, "Fallback drift %d set", c);
}
if (i <= fb_drift_max) {
next_fb_drift = i;
UTI_AddDoubleToTimeval(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
#if 0
LOG(LOGS_INFO, LOGF_Reference, "Fallback drift %d scheduled", i);
#endif
DEBUG_LOG(LOGF_Reference, "Fallback drift %d scheduled", i);
}
}
/* ================================================== */
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 +551,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 +579,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 +591,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 +617,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 +659,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,24 +667,20 @@ 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;
}
}
if (leap_sec != our_leap_sec) {
if (leap_sec != our_leap_sec && !REF_IsLeapSecondClose()) {
LCL_SetLeap(leap_sec);
our_leap_sec = leap_sec;
}
@@ -662,6 +706,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 +788,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 +822,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 +866,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 +910,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 +999,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);
@@ -1000,9 +1121,6 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{
max_update_skew = new_max_update_skew * 1.0e-6;
#if 0
LOG(LOGS_INFO, LOGF_Reference, "New max update skew = %.3fppm", new_max_update_skew);
#endif
}
/* ================================================== */
@@ -1032,6 +1150,31 @@ REF_IsLocalActive(void)
/* ================================================== */
#define LEAP_SECOND_CLOSE 5
int REF_IsLeapSecondClose(void)
{
struct timeval now, now_raw;
time_t t;
if (!our_leap_sec)
return 0;
SCH_GetLastEventTime(&now, NULL, &now_raw);
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
return 0;
}
/* ================================================== */
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
@@ -1084,5 +1227,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);
@@ -140,6 +161,10 @@ extern void REF_EnableLocal(int stratum);
extern void REF_DisableLocal(void);
extern int REF_IsLocalActive(void);
/* Check if current raw or cooked time is close to a leap second
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
#endif /* GOT_REFERENCE_H */

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]);
@@ -572,11 +570,6 @@ RGR_FindBestRobustRegression
b = X / V;
a = my - b*mx;
#if 0
printf("my=%20.12f mx=%20.12f a=%20.12f b=%20.12f\n", my, mx, a, b);
#endif
s2 = 0.0;
for (i=start; i<n; i++) {
resid = y[i] - a - b * x[i];

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

76
rtc.c
View File

@@ -28,6 +28,7 @@
#include "sysincl.h"
#include "rtc.h"
#include "local.h"
#include "logging.h"
#include "conf.h"
@@ -42,7 +43,7 @@ static int driver_initialised = 0;
static struct {
int (*init)(void);
void (*fini)(void);
void (*time_pre_init)(void);
int (*time_pre_init)(void);
void (*time_init)(void (*after_hook)(void*), void *anything);
void (*start_measurements)(void);
int (*write_parameters)(void);
@@ -71,42 +72,68 @@ static struct {
#endif
};
/* ================================================== */
/* Set the system clock to the time of last modification of driftfile
if it's in the future */
static void
fallback_time_init(void)
{
struct timeval now;
struct stat buf;
char *drift_file;
drift_file = CNF_GetDriftFile();
if (!drift_file)
return;
if (stat(drift_file, &buf))
return;
LCL_ReadCookedTime(&now, NULL);
if (now.tv_sec < buf.st_mtime) {
LCL_ApplyStepOffset(now.tv_sec - buf.st_mtime);
LOG(LOGS_INFO, LOGF_Rtc,
"System clock set from driftfile %s", drift_file);
}
}
/* ================================================== */
void
RTC_Initialise(void)
RTC_Initialise(int initial_set)
{
char *file_name;
int ok;
/* Do an initial read of the RTC and set the system time to it. This
is analogous to what /sbin/hwclock -s would do on Linux. If that fails
or RTC is not supported, set the clock to the time of the last
modification of driftfile, so we at least get closer to the truth. */
if (initial_set) {
if (!driver.time_pre_init || !driver.time_pre_init()) {
fallback_time_init();
}
}
driver_initialised = 0;
/* This is how we tell whether the user wants to load the RTC
driver, if he is on a machine where it is an option. */
file_name = CNF_GetRtcFile();
if (file_name) {
if (CNF_GetRTCSync()) {
if (CNF_GetRtcSync()) {
LOG_FATAL(LOGF_Rtc, "rtcfile directive cannot be used with rtcsync");
}
if (driver.init) {
if ((driver.init)()) {
ok = 1;
} else {
ok = 0;
driver_initialised = 1;
}
} else {
ok = 0;
LOG(LOGS_ERR, LOGF_Rtc, "RTC not supported on this operating system");
}
if (ok) {
driver_initialised = 1;
} else {
driver_initialised = 0;
LOG(LOGS_ERR, LOGF_Rtc, "Real time clock not supported on this operating system");
}
} else {
driver_initialised = 0;
}
}
@@ -137,23 +164,10 @@ RTC_TimeInit(void (*after_hook)(void *), void *anything)
if (driver_initialised) {
(driver.time_init)(after_hook, anything);
} else {
LOG(LOGS_ERR, LOGF_Rtc, "Can't initialise from real time clock, driver not loaded");
(after_hook)(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. */
void
RTC_TimePreInit(void)
{
if (driver.time_pre_init) {
(driver.time_pre_init)();
}
}
/* ================================================== */
/* Start the RTC measurement process */

3
rtc.h
View File

@@ -28,9 +28,8 @@
#include "reports.h"
extern void RTC_Initialise(void);
extern void RTC_Initialise(int initial_set);
extern void RTC_Finalise(void);
extern void RTC_TimePreInit(void);
extern void RTC_TimeInit(void (*after_hook)(void *), void *anything);
extern void RTC_StartMeasurements(void);
extern int RTC_GetReport(RPT_RTC_Report *report);

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();
}
/* ================================================== */
@@ -387,7 +431,6 @@ static void
read_coefs_from_file(void)
{
FILE *in;
char line[256];
if (!tried_to_load_coefs) {
@@ -395,26 +438,17 @@ read_coefs_from_file(void)
tried_to_load_coefs = 1;
in = fopen(coefs_file_name, "r");
if (in) {
if (fgets(line, sizeof(line), in)) {
if (sscanf(line, "%d%ld%lf%lf",
&valid_coefs_from_file,
&file_ref_time,
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not parse coefficients line from RTC file %s",
coefs_file_name);
}
if (coefs_file_name && (in = fopen(coefs_file_name, "r"))) {
if (fscanf(in, "%d%ld%lf%lf",
&valid_coefs_from_file,
&file_ref_time,
&file_ref_offset,
&file_rate_ppm) == 4) {
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read first line from RTC file %s",
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not read coefficients from RTC file %s",
coefs_file_name);
}
fclose(in);
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not open RTC file %s for reading",
coefs_file_name);
}
}
}
@@ -429,6 +463,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 +485,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 +498,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). */
@@ -502,7 +540,8 @@ RTC_Linux_Initialise(void)
fd = open (CNF_GetRtcDevice(), O_RDWR);
if (fd < 0) {
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open %s, %s", CNF_GetRtcDevice(), strerror(errno));
LOG(LOGS_ERR, LOGF_RtcLinux, "Could not open RTC device %s : %s",
CNF_GetRtcDevice(), strerror(errno));
return 0;
}
@@ -670,6 +709,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 +727,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 +757,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 +811,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,19 +962,18 @@ 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. */
void
int
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;
double accumulated_error = 0.0;
time_t rtc_t;
double accumulated_error, sys_offset;
struct timeval new_sys_time, old_sys_time;
coefs_file_name = CNF_GetRtcFile();
@@ -928,10 +984,23 @@ RTC_Linux_TimePreInit(void)
fd = open(CNF_GetRtcDevice(), O_RDONLY);
if (fd < 0) {
return; /* Can't open it, and won't be able to later */
return 0; /* 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);
/* Read system clock */
LCL_ReadCookedTime(&old_sys_time, NULL);
close(fd);
if (status >= 0) {
/* Convert to seconds since 1970 */
@@ -949,37 +1018,35 @@ RTC_Linux_TimePreInit(void)
/* Work out approximatation to correct time (to about the
nearest second) */
if (valid_coefs_from_file) {
interval = rtc_t - file_ref_time;
accumulated_error = file_ref_offset + (double)(interval) * 1.0e-6 * file_rate_ppm;
/* Correct time */
estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
accumulated_error = file_ref_offset +
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
} else {
estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error);
accumulated_error = 0.0;
}
new_sys_time.tv_sec = estimated_correct_rtc_t;
new_sys_time.tv_usec = 0;
/* Correct time */
new_sys_time.tv_sec = rtc_t;
/* Average error in the RTC reading */
new_sys_time.tv_usec = 500000;
UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time);
UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time);
/* Set system time only if the step is larger than 1 second */
if (!(gettimeofday(&old_sys_time, NULL) < 0) &&
(old_sys_time.tv_sec - new_sys_time.tv_sec > 1 ||
old_sys_time.tv_sec - new_sys_time.tv_sec < -1)) {
if (fabs(sys_offset) >= 1.0) {
LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f",
accumulated_error);
/* Tough luck if this fails */
if (settimeofday(&new_sys_time, NULL) < 0) {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not settimeofday");
}
LCL_ApplyStepOffset(sys_offset);
}
} else {
LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970");
return 0;
}
}
close(fd);
return 1;
}
/* ================================================== */

View File

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

115
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
@@ -57,18 +57,18 @@ static unsigned int n_read_fds;
/* One more than the highest file descriptor that is registered */
static unsigned int one_highest_fd;
/* This assumes that fd_set is implemented as a fixed size array of
bits, possibly embedded inside a record. It might therefore
somewhat non-portable. */
#define FD_SET_SIZE (sizeof(fd_set) * 8)
#ifndef FD_SETSIZE
/* If FD_SETSIZE is not defined, assume that fd_set is implemented
as a fixed size array of bits, possibly embedded inside a record */
#define FD_SETSIZE (sizeof(fd_set) * 8)
#endif
typedef struct {
SCH_FileHandler handler;
SCH_ArbitraryArgument arg;
} FileHandlerEntry;
static FileHandlerEntry file_handlers[FD_SET_SIZE];
static FileHandlerEntry file_handlers[FD_SETSIZE];
/* Timestamp when last select() returned */
static struct timeval last_select_ts, last_select_ts_raw;
@@ -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);
/* ================================================== */
@@ -169,6 +169,9 @@ SCH_AddInputFileHandler
assert(initialised);
if (fd >= FD_SETSIZE)
LOG_FATAL(LOGF_Scheduler, "Too many file descriptors");
/* Don't want to allow the same fd to register a handler more than
once without deleting a previous association - this suggests
a bug somewhere else in the program. */
@@ -503,14 +506,18 @@ 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) {
/* Make sure this handler is invoked first in order to not shift new timers
added from other handlers */
assert(LCL_IsFirstParameterChangeHandler(handle_slew));
/* 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 +536,54 @@ 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 *prev_raw, struct timeval *raw, int timeout,
struct timeval *orig_select_tv,
struct timeval *rem_select_tv)
{
double diff, err;
struct timeval elapsed_min, elapsed_max;
double step, elapsed;
UTI_DiffTimevalsToDouble(&diff, &last_select_ts_raw, raw);
/* Get an estimate of the time spent waiting in the select() call. On some
systems (e.g. Linux) the timeout timeval is modified to return the
remaining time, use that information. */
if (timeout) {
elapsed_max = elapsed_min = *orig_select_tv;
} else if (rem_select_tv && rem_select_tv->tv_sec >= 0 &&
rem_select_tv->tv_sec <= orig_select_tv->tv_sec &&
(rem_select_tv->tv_sec != orig_select_tv->tv_sec ||
rem_select_tv->tv_usec != orig_select_tv->tv_usec)) {
UTI_DiffTimevals(&elapsed_min, orig_select_tv, rem_select_tv);
elapsed_max = elapsed_min;
} else {
if (rem_select_tv)
elapsed_max = *orig_select_tv;
else
UTI_DiffTimevals(&elapsed_max, raw, prev_raw);
elapsed_min.tv_sec = 0;
elapsed_min.tv_usec = 0;
}
if (n_timer_queue_entries > 0) {
UTI_DiffTimevalsToDouble(&err, &(timer_queue.next->tv), &last_select_ts_raw);
} else {
err = 0.0;
}
if (last_select_ts_raw.tv_sec + elapsed_min.tv_sec >
raw->tv_sec + JUMP_DETECT_THRESHOLD) {
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected!");
} else if (prev_raw->tv_sec + elapsed_max.tv_sec + JUMP_DETECT_THRESHOLD <
raw->tv_sec) {
LOG(LOGS_WARN, LOGF_Scheduler, "Forward time jump detected!");
} else {
return 1;
}
diff += err;
UTI_DiffTimevalsToDouble(&step, &last_select_ts_raw, raw);
UTI_TimevalToDouble(&elapsed_min, &elapsed);
step += elapsed;
if (timeout) {
err = 1.0;
}
/* Cooked time may no longer be valid after dispatching the handlers */
LCL_NotifyExternalTimeStep(raw, raw, step, fabs(step));
LOG(LOGS_WARN, LOGF_Scheduler, "Backward time jump detected! (correction %.1f +- %.1f seconds)", diff, err);
LCL_NotifyExternalTimeStep(raw, cooked, diff, err);
return 0;
}
/* ================================================== */
@@ -562,34 +593,43 @@ SCH_MainLoop(void)
{
fd_set rd;
int status, errsv;
struct timeval tv, *ptv;
struct timeval now, cooked;
struct timeval tv, saved_tv, *ptv;
struct timeval now, saved_now, cooked;
double err;
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);
saved_now = 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) {
UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
ptv = &tv;
assert(tv.tv_sec > 0 || tv.tv_usec > 0);
saved_tv = tv;
} else {
ptv = NULL;
/* This is needed to fix a compiler warning */
saved_tv.tv_sec = 0;
}
/* if there are no file descriptors being waited on and no
timeout set, this is clearly ridiculous, so stop the run */
assert(ptv || n_read_fds);
if (!ptv && !n_read_fds) {
LOG_FATAL(LOGF_Scheduler, "Nothing to do");
}
/* 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;
@@ -597,9 +637,10 @@ SCH_MainLoop(void)
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(&saved_now, &now, status == 0, &saved_tv, ptv)) {
/* Cook the time again after handling the step */
LCL_CookTime(&now, &cooked, &err);
}
last_select_ts_raw = now;

236
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,34 @@ 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
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, LOGF_Sources, "Dropping sample around leap second");
return;
}
/* 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 +346,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 +359,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 +425,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 +512,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 +527,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 +548,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 +562,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 +586,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();
}
@@ -564,13 +615,6 @@ SRC_SelectSource(uint32_t match_refid)
&(si->variance),
&(si->select_ok));
#if 0
LOG(LOGS_INFO, LOGF_Sources, "%s dist=%f lo=%f hi=%f",
source_to_string(sources[i]),
si->root_distance,
si->lo_limit, si->hi_limit);
#endif
if (si->select_ok) {
++n_sel_sources;
@@ -609,10 +653,8 @@ SRC_SelectSource(uint32_t match_refid)
}
}
#if 0
LOG(LOGS_INFO, LOGF_Sources, "badstat_sources=%d sel_sources=%d badstat_reach=%x sel_reach=%x",
DEBUG_LOG(LOGF_Sources, "badstat_sources=%d sel_sources=%d badstat_reach=%x sel_reach=%x",
n_badstats_sources, n_sel_sources, max_badstat_reach, max_sel_reach);
#endif
/* Wait for the next call if we have no source selected and there is
a source with bad stats (has less than 3 samples) with reachability
@@ -626,10 +668,6 @@ SRC_SelectSource(uint32_t match_refid)
return;
}
#if 0
LOG(LOGS_INFO, LOGF_Sources, "n_endpoints=%d", n_endpoints);
#endif
/* Now sort the endpoint list */
if (n_endpoints > 0) {
@@ -663,10 +701,6 @@ SRC_SelectSource(uint32_t match_refid)
best_lo = best_hi = 0.0;
for (i=0; i<n_endpoints; i++) {
#if 0
LOG(LOGS_INFO, LOGF_Sources, "i=%d t=%f tag=%d addr=%s", i, sort_list[i].offset, sort_list[i].tag,
source_to_string(sources[sort_list[i].index]));
#endif
switch(sort_list[i].tag) {
case LOW:
depth++;
@@ -690,11 +724,6 @@ SRC_SelectSource(uint32_t match_refid)
}
}
#if 0
LOG(LOGS_INFO, LOGF_Sources, "best_depth=%d best_lo=%f best_hi=%f",
best_depth, best_lo, best_hi);
#endif
if (best_depth <= n_sel_sources/2) {
/* Could not even get half the reachable sources to agree -
clearly we can't synchronise.
@@ -708,7 +737,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;
@@ -737,14 +766,8 @@ SRC_SelectSource(uint32_t match_refid)
(sources[i]->sel_info.hi_limit <= best_hi))) {
sel_sources[n_sel_sources++] = i;
#if 0
LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is valid", i, source_to_string(sources[i]));
#endif
} else {
sources[i]->status = SRC_FALSETICKER;
#if 0
LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s is a falseticker", i, source_to_string(sources[i]));
#endif
}
}
}
@@ -765,10 +788,6 @@ SRC_SelectSource(uint32_t match_refid)
}
}
#if 0
LOG(LOGS_INFO, LOGF_Sources, "min_distance=%f", min_distance);
#endif
/* Now go through and prune any NTP sources that have excessive
variance */
for (i=0; i<n_sel_sources; i++) {
@@ -777,9 +796,6 @@ SRC_SelectSource(uint32_t match_refid)
sqrt(sources[index]->sel_info.variance) > min_distance) {
sel_sources[i] = INVALID_SOURCE;
sources[index]->status = SRC_JITTERY;
#if 0
LOG(LOGS_INFO, LOGF_Sources, "i=%d addr=%s has too much variance", i, source_to_string(sources[i]));
#endif
}
}
#endif
@@ -790,7 +806,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 +837,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,
@@ -836,10 +854,6 @@ SRC_SelectSource(uint32_t match_refid)
if (stratum < min_stratum) min_stratum = stratum;
}
#if 0
LOG(LOGS_INFO, LOGF_Sources, "min_stratum=%d", min_stratum);
#endif
/* Update scores and find source with maximum score */
max_score_index = INVALID_SOURCE;
@@ -854,7 +868,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 +884,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;
@@ -887,10 +902,9 @@ SRC_SelectSource(uint32_t match_refid)
sources[i]->sel_score = 1.0 / distance;
}
#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);
#endif
DEBUG_LOG(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);
if (max_score < sources[i]->sel_score) {
max_score = sources[i]->sel_score;
@@ -910,13 +924,9 @@ 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
LOG(LOGS_INFO, LOGF_Sources, "new_sel_index=%d", selected_source_index);
#endif
/* New source has been selected, reset all scores */
for (i=0; i < n_sources; i++) {
sources[i]->sel_score = 1.0;
@@ -926,10 +936,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 +968,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 +977,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 +995,7 @@ void
SRC_ReselectSource(void)
{
selected_source_index = INVALID_SOURCE;
SRC_SelectSource(0);
SRC_SelectSource(NULL);
}
/* ================================================== */
@@ -1037,15 +1047,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);
}
}
/* ================================================== */
@@ -1154,6 +1172,14 @@ SRC_IsSyncPeer(SRC_Instance inst)
/* ================================================== */
int
SRC_IsReachable(SRC_Instance inst)
{
return inst->reachability != 0;
}
/* ================================================== */
int
SRC_ReadNumberOfSources(void)
{
@@ -1162,6 +1188,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);
@@ -161,7 +171,9 @@ extern void SRC_DumpSources(void);
extern void SRC_ReloadSources(void);
extern int SRC_IsSyncPeer(SRC_Instance inst);
extern int SRC_IsReachable(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) %
@@ -353,10 +363,6 @@ find_best_sample_index(SST_Stats inst, double *times_back)
assert(best_index >= 0);
inst->best_single_sample = best_index;
#if 0
LOG(LOGS_INFO, LOGF_SourceStats, "n=%d best_index=%d", n, best_index);
#endif
}
/* ================================================== */
@@ -490,9 +496,6 @@ SST_DoNewRegression(SST_Stats inst)
times_back_start = inst->runs_samples + best_start;
prune_register(inst, best_start);
} else {
#if 0
LOG(LOGS_INFO, LOGF_SourceStats, "too few points (%d) for regression", inst->n_samples);
#endif
inst->estimated_frequency = 0.0;
inst->skew = WORST_CASE_FREQ_BOUND;
times_back_start = 0;
@@ -569,10 +572,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 +587,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 +602,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 +627,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 +642,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
}
/* ================================================== */
@@ -733,9 +727,8 @@ SST_IsGoodSample(SST_Stats inst, double offset, double delay,
if (fabs(offset) - delay_increase > allowed_increase)
return 1;
#if 0
LOG(LOGS_INFO, LOGF_SourceStats, "bad sample: offset=%f delay=%f incr_delay=%f allowed=%f", offset, delay, allowed_increase, delay_increase);
#endif
DEBUG_LOG(LOGF_SourceStats, "Bad sample: offset=%f delay=%f incr_delay=%f allowed=%f",
offset, delay, allowed_increase, delay_increase);
return 0;
}
@@ -781,8 +774,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

@@ -281,7 +281,6 @@ SYS_NetBSD_Initialise(void)
};
kvm_t *kt;
FILE *fp;
kt = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
if (!kt) {

View File

@@ -409,11 +409,6 @@ set_dosynctodr(unsigned long on_off)
kvm_close(kt);
assert(read_back == on_off);
#if 0
LOG(LOGS_INFO, LOGF_SysSolaris, "Set value of dosynctodr to %d", on_off);
#endif
}
/* ================================================== */

View File

@@ -364,11 +364,6 @@ setup_kernel(unsigned long on_off)
}
kvm_close(kt);
#if 0
LOG(LOGS_INFO, LOGF_SysSunOS, "Set value of _dosynctodr to %d", on_off);
#endif
}
/* ================================================== */

View File

@@ -62,10 +62,10 @@
#include <syslog.h>
#include <time.h>
#if HAS_STDINT_H
#include <stdint.h>
#elif defined(HAS_INTTYPES_H)
#ifdef HAS_INTTYPES_H
#include <inttypes.h>
#elif HAS_STDINT_H
#include <stdint.h>
#else
/* Tough */
#endif

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

40
test/simulation/008-ntpera Executable file
View File

@@ -0,0 +1,40 @@
#!/bin/bash
. test.common
test_start "NTP eras"
# Assume NTP_ERA_SPLIT is between years 1960 and 1990
# Set date to 500 seconds before NTP second overflows, this should
# work correctly with both 32-bit and 64-bit time_t
export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
# The following tests need 64-bit time_t
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
for year in 1990 2090; do
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
for year in 1950 2130; do
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
# This check is expected to fail
check_sync && test_fail
done
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

22
test/simulation/114-presend Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
. test.common
test_start "presend option"
client_server_options="presend 6"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
base_delay=5
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

24
test/simulation/115-cmdmontime Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
. test.common
test_start "cmdmon timestamps"
# The following tests need 64-bit time_t
grep -q 'HAVE_LONG_TIME_T 1' ../../config.h || test_skip
limit=2
client_server_options="noselect"
client_conf="local stratum 1"
chronyc_start="0.5"
chronyc_conf="tracking"
for year in `seq 1850 100 2300`; do
date="Jan 1 00:00:00 $year"
export CLKNETSIM_START_DATE=$(date -d "$date UTC" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*Ref time \(UTC\).*$date.*$" || test_fail
done
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.

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