mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-04 02:25:07 -05:00
Compare commits
196 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a03206222 | ||
|
|
b86c50bb9f | ||
|
|
36f9b24dfe | ||
|
|
e0b75b87bf | ||
|
|
6661a61486 | ||
|
|
bc76291750 | ||
|
|
2aefadd129 | ||
|
|
123cb497b9 | ||
|
|
0c38e4a6ca | ||
|
|
0db30fd0b1 | ||
|
|
b90d2c084f | ||
|
|
ab8da7ecb9 | ||
|
|
05809e937c | ||
|
|
8265fe2e30 | ||
|
|
c11a052955 | ||
|
|
109970f687 | ||
|
|
ca10b9e072 | ||
|
|
19da1d95a8 | ||
|
|
61da7d0913 | ||
|
|
105f1f90c1 | ||
|
|
c9d791e02d | ||
|
|
de678ff780 | ||
|
|
e16bcca617 | ||
|
|
b57d7040b3 | ||
|
|
c80858f738 | ||
|
|
81bf7cdcdc | ||
|
|
b8b3830dc4 | ||
|
|
d4738e1259 | ||
|
|
5ba42cee45 | ||
|
|
b2dac47c82 | ||
|
|
6a6161dc0f | ||
|
|
a4eb5be8ea | ||
|
|
3050e29b1d | ||
|
|
fb1af6e55b | ||
|
|
47a13ae88c | ||
|
|
a8496658a0 | ||
|
|
6ea1082a72 | ||
|
|
4f674539fd | ||
|
|
68d3fb4af8 | ||
|
|
3c24f2c2ed | ||
|
|
0189dac7d8 | ||
|
|
4a11399c2e | ||
|
|
cf98551ea1 | ||
|
|
5508b01bd8 | ||
|
|
907accec87 | ||
|
|
a511029cc2 | ||
|
|
0845df7684 | ||
|
|
2f961ab36a | ||
|
|
a0cf7f7f12 | ||
|
|
a5f1a113f0 | ||
|
|
5160f14fdc | ||
|
|
b0a2ad2535 | ||
|
|
ecdde75f8f | ||
|
|
2d80be9541 | ||
|
|
ab776ed9d8 | ||
|
|
ccebec3eb6 | ||
|
|
3ea3e0efd7 | ||
|
|
c3e4e3e47a | ||
|
|
e949e1d991 | ||
|
|
c8649ccb7e | ||
|
|
39ff7ceeca | ||
|
|
06945d927b | ||
|
|
caf82b1a45 | ||
|
|
f99b2f633b | ||
|
|
6270a3eb7c | ||
|
|
1daa40a2f7 | ||
|
|
a1406eded3 | ||
|
|
1eb8994c00 | ||
|
|
221e5fb501 | ||
|
|
ecfbde9872 | ||
|
|
dec07aa844 | ||
|
|
5b3d4dfe76 | ||
|
|
dc0f0cd134 | ||
|
|
bd37efa52e | ||
|
|
c71185a0e5 | ||
|
|
f149b7b758 | ||
|
|
883b0dde94 | ||
|
|
9cba9c8585 | ||
|
|
88e711ad9a | ||
|
|
badaa83c31 | ||
|
|
bbeec7361c | ||
|
|
6fba5a4a7f | ||
|
|
26889a8cb7 | ||
|
|
cd278d1826 | ||
|
|
3877734814 | ||
|
|
19f2ab9e09 | ||
|
|
3260dc82fe | ||
|
|
1a98c5ffa9 | ||
|
|
8247b8525f | ||
|
|
8901293be8 | ||
|
|
e789b0817f | ||
|
|
d0fd04c0a2 | ||
|
|
7122321249 | ||
|
|
b328c8c348 | ||
|
|
7b97668319 | ||
|
|
6f5df7e4a4 | ||
|
|
5a39074e01 | ||
|
|
c8e57f4350 | ||
|
|
b1230efac3 | ||
|
|
4e1ce88981 | ||
|
|
790a336eb2 | ||
|
|
cc706b50b9 | ||
|
|
73042494bd | ||
|
|
ec89739d50 | ||
|
|
4baf999cc3 | ||
|
|
9afd19c29b | ||
|
|
5dd173c050 | ||
|
|
5caf0ad187 | ||
|
|
17d2291a84 | ||
|
|
a6179261a7 | ||
|
|
098e0c43fc | ||
|
|
7b197953e8 | ||
|
|
9dcace0fc4 | ||
|
|
a07ac38331 | ||
|
|
166e43b13e | ||
|
|
b84d6759f9 | ||
|
|
f323c814af | ||
|
|
19b47dcbc9 | ||
|
|
5edeadcbd9 | ||
|
|
d91ae2094f | ||
|
|
30a5845098 | ||
|
|
0f367efac5 | ||
|
|
24c011d4a6 | ||
|
|
0c2cdd2fb1 | ||
|
|
cd1a666e1b | ||
|
|
070b4f69d0 | ||
|
|
851c823b42 | ||
|
|
df80274644 | ||
|
|
bb2d68ddf9 | ||
|
|
685d8f725b | ||
|
|
4234732b08 | ||
|
|
a16094adfb | ||
|
|
a4349b13df | ||
|
|
3556dadea1 | ||
|
|
220e6d1907 | ||
|
|
a738037705 | ||
|
|
7daf34675a | ||
|
|
de598c2310 | ||
|
|
91cc4dbb12 | ||
|
|
0ae6f2485b | ||
|
|
52ec694d2b | ||
|
|
e2e07af8a4 | ||
|
|
2ed88c31c7 | ||
|
|
af8e4a5115 | ||
|
|
f503a9a490 | ||
|
|
9c64fbb9c4 | ||
|
|
b428f901c7 | ||
|
|
09b7f77f9a | ||
|
|
c23c0b8484 | ||
|
|
d530055917 | ||
|
|
f41d09e19f | ||
|
|
46030d9d3e | ||
|
|
02ccd3a3c7 | ||
|
|
9cc609c4b0 | ||
|
|
a0a496dcb4 | ||
|
|
8d08486edf | ||
|
|
a3b376cf0a | ||
|
|
e66f1df89d | ||
|
|
35220aac9d | ||
|
|
5b04f3ca90 | ||
|
|
beb1c36136 | ||
|
|
da3495c472 | ||
|
|
356771c0c3 | ||
|
|
fca8966ada | ||
|
|
25f80a1a9d | ||
|
|
1219f99935 | ||
|
|
33a1fe7a9c | ||
|
|
eed0a0de56 | ||
|
|
07600cbd71 | ||
|
|
f2e341b5ed | ||
|
|
55717c1ccd | ||
|
|
d5e645eb38 | ||
|
|
3196630fb9 | ||
|
|
663dde1ad7 | ||
|
|
62757cda49 | ||
|
|
af6ae9186b | ||
|
|
4c29f8888c | ||
|
|
d06ae4a60e | ||
|
|
f9af2f9733 | ||
|
|
43ae0131cd | ||
|
|
8bb8f15a7d | ||
|
|
e55f174bd3 | ||
|
|
5bd13c8d59 | ||
|
|
759580aa6f | ||
|
|
b61cbed689 | ||
|
|
2ac2247756 | ||
|
|
55f48b14b7 | ||
|
|
3dfac33858 | ||
|
|
d5f2401421 | ||
|
|
fb0570cc73 | ||
|
|
43936ba0d1 | ||
|
|
f2ba20f293 | ||
|
|
fcd384523b | ||
|
|
48bce351bf | ||
|
|
25f93875d9 | ||
|
|
ebc610fcb3 |
@@ -35,7 +35,7 @@ LDFLAGS = @LDFLAGS@
|
|||||||
|
|
||||||
EXTRA_OBJS = @EXTRA_OBJS@
|
EXTRA_OBJS = @EXTRA_OBJS@
|
||||||
|
|
||||||
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
|
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
|
||||||
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
|
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
|
||||||
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
|
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
|
||||||
|
|
||||||
|
|||||||
51
NEWS
51
NEWS
@@ -1,3 +1,49 @@
|
|||||||
|
New in version 4.4
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add support for AES-GCM-SIV with Nettle >= 3.9 to shorten NTS
|
||||||
|
cookies to avoid some length-specific blocking of NTP on Internet
|
||||||
|
* Add support for multiple refclocks using extpps option on one PHC
|
||||||
|
* Add maxpoll option to hwtimestamp directive to improve PHC tracking
|
||||||
|
with low packet rates
|
||||||
|
* Add hwtstimeout directive to configure timeout for late timestamps
|
||||||
|
* Handle late hardware transmit timestamps of NTP requests on all sockets
|
||||||
|
* Handle mismatched 32/64-bit time_t in SOCK refclock samples
|
||||||
|
* Improve source replacement
|
||||||
|
* Log important changes made by command requests (chronyc)
|
||||||
|
* Refresh address of NTP sources periodically
|
||||||
|
* Set DSCP for IPv6 packets
|
||||||
|
* Shorten NTS-KE retry interval when network is down
|
||||||
|
* Update seccomp filter for musl
|
||||||
|
* Warn if loading keys from file with unexpected permissions
|
||||||
|
* Warn if source selection fails or falseticker is detected
|
||||||
|
* Add selectopts command to modify source-specific selection options
|
||||||
|
* Add timestamp sources to serverstats report and make its fields 64-bit
|
||||||
|
* Add -e option to chronyc to indicate end of response
|
||||||
|
|
||||||
|
New in version 4.3
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add local option to refclock directive to stabilise system clock
|
||||||
|
with more stable free-running clock (e.g. TCXO, OCXO)
|
||||||
|
* Add maxdelayquant option to server/pool/peer directive to replace
|
||||||
|
maxdelaydevratio filter with long-term quantile-based filtering
|
||||||
|
* Add selection option to log directive
|
||||||
|
* Allow external PPS in PHC refclock without configurable pin
|
||||||
|
* Don't accept first interleaved response to minimise error in delay
|
||||||
|
* Don't use arc4random on Linux to avoid server performance loss
|
||||||
|
* Improve filter option to better handle missing NTP samples
|
||||||
|
* Improve stability with hardware timestamping and PHC refclock
|
||||||
|
* Update seccomp filter
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Fix waitsync command to reconnect when not getting response
|
||||||
|
|
||||||
New in version 4.2
|
New in version 4.2
|
||||||
==================
|
==================
|
||||||
|
|
||||||
@@ -16,6 +62,11 @@ Bug fixes
|
|||||||
---------
|
---------
|
||||||
* Fix RTC support with 64-bit time_t on 32-bit Linux
|
* Fix RTC support with 64-bit time_t on 32-bit Linux
|
||||||
* Fix seccomp filter to work correctly with bind*device directives
|
* Fix seccomp filter to work correctly with bind*device directives
|
||||||
|
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
* Switch Solaris support to illumos
|
||||||
|
|
||||||
New in version 4.1
|
New in version 4.1
|
||||||
==================
|
==================
|
||||||
|
|||||||
6
README
6
README
@@ -28,7 +28,7 @@ What will chrony run on?
|
|||||||
========================
|
========================
|
||||||
|
|
||||||
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
|
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
|
||||||
Solaris. Closely related systems may work too. Any other system will
|
illumos. Closely related systems may work too. Any other system will
|
||||||
likely require a porting exercise.
|
likely require a porting exercise.
|
||||||
|
|
||||||
How do I set it up?
|
How do I set it up?
|
||||||
@@ -108,6 +108,7 @@ Erik Bryer <ebryer@spots.ab.ca>
|
|||||||
Jonathan Cameron <jic23@cam.ac.uk>
|
Jonathan Cameron <jic23@cam.ac.uk>
|
||||||
Bryan Christianson <bryan@whatroute.net>
|
Bryan Christianson <bryan@whatroute.net>
|
||||||
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
||||||
|
Dan Drown <dan-ntp@drown.org>
|
||||||
Kamil Dudka <kdudka@redhat.com>
|
Kamil Dudka <kdudka@redhat.com>
|
||||||
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
||||||
Paul Elliott <pelliott@io.com>
|
Paul Elliott <pelliott@io.com>
|
||||||
@@ -121,6 +122,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
|
|||||||
John Hasler <john@dhh.gt.org>
|
John Hasler <john@dhh.gt.org>
|
||||||
Tjalling Hattink <t.hattink@fugro.nl>
|
Tjalling Hattink <t.hattink@fugro.nl>
|
||||||
Liam Hatton <me@liamhatton.com>
|
Liam Hatton <me@liamhatton.com>
|
||||||
|
Holger Hoffstätte <holger@applied-asynchrony.com>
|
||||||
Jachym Holecek <jakym@volny.cz>
|
Jachym Holecek <jakym@volny.cz>
|
||||||
Håkan Johansson <f96hajo@chalmers.se>
|
Håkan Johansson <f96hajo@chalmers.se>
|
||||||
Jim Knoble <jmknoble@pobox.com>
|
Jim Knoble <jmknoble@pobox.com>
|
||||||
@@ -136,9 +138,11 @@ Victor Moroz <vim@prv.adlum.ru>
|
|||||||
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
|
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
|
||||||
Frank Otto <sandwichmacher@web.de>
|
Frank Otto <sandwichmacher@web.de>
|
||||||
Denny Page <dennypage@me.com>
|
Denny Page <dennypage@me.com>
|
||||||
|
Rupesh Patel <rupatel@redhat.com>
|
||||||
Chris Perl <cperl@janestreet.com>
|
Chris Perl <cperl@janestreet.com>
|
||||||
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
||||||
Andreas Piesk <apiesk@virbus.de>
|
Andreas Piesk <apiesk@virbus.de>
|
||||||
|
Mike Ryan <msr@hsilop.net>
|
||||||
Baruch Siach <baruch@tkos.co.il>
|
Baruch Siach <baruch@tkos.co.il>
|
||||||
Foster Snowhill <forst@forstwoof.ru>
|
Foster Snowhill <forst@forstwoof.ru>
|
||||||
Andreas Steinmetz <ast@domdv.de>
|
Andreas Steinmetz <ast@domdv.de>
|
||||||
|
|||||||
15
array.c
15
array.c
@@ -116,6 +116,21 @@ ARR_AppendElement(ARR_Instance array, void *element)
|
|||||||
memcpy(e, element, array->elem_size);
|
memcpy(e, element, array->elem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ARR_RemoveElement(ARR_Instance array, unsigned int index)
|
||||||
|
{
|
||||||
|
void *e, *l;
|
||||||
|
|
||||||
|
e = ARR_GetElement(array, index);
|
||||||
|
l = ARR_GetElement(array, array->used - 1);
|
||||||
|
|
||||||
|
if (e < l)
|
||||||
|
memmove(e, (char *)e + array->elem_size, (char *)l - (char *)e);
|
||||||
|
array->used--;
|
||||||
|
|
||||||
|
realloc_array(array, array->used);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ARR_SetSize(ARR_Instance array, unsigned int size)
|
ARR_SetSize(ARR_Instance array, unsigned int size)
|
||||||
{
|
{
|
||||||
|
|||||||
3
array.h
3
array.h
@@ -47,6 +47,9 @@ extern void *ARR_GetElements(ARR_Instance array);
|
|||||||
/* Add a new element to the end of the array */
|
/* Add a new element to the end of the array */
|
||||||
extern void ARR_AppendElement(ARR_Instance array, void *element);
|
extern void ARR_AppendElement(ARR_Instance array, void *element);
|
||||||
|
|
||||||
|
/* Remove element with given index */
|
||||||
|
extern void ARR_RemoveElement(ARR_Instance array, unsigned int index);
|
||||||
|
|
||||||
/* Set the size of the array */
|
/* Set the size of the array */
|
||||||
extern void ARR_SetSize(ARR_Instance array, unsigned int size);
|
extern void ARR_SetSize(ARR_Instance array, unsigned int size);
|
||||||
|
|
||||||
|
|||||||
57
candm.h
57
candm.h
@@ -109,7 +109,8 @@
|
|||||||
#define REQ_SELECT_DATA 69
|
#define REQ_SELECT_DATA 69
|
||||||
#define REQ_RELOAD_SOURCES 70
|
#define REQ_RELOAD_SOURCES 70
|
||||||
#define REQ_DOFFSET2 71
|
#define REQ_DOFFSET2 71
|
||||||
#define N_REQUEST_TYPES 72
|
#define REQ_MODIFY_SELECTOPTS 72
|
||||||
|
#define N_REQUEST_TYPES 73
|
||||||
|
|
||||||
/* Structure used to exchange timespecs independent of time_t size */
|
/* Structure used to exchange timespecs independent of time_t size */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -121,6 +122,12 @@ typedef struct {
|
|||||||
/* This is used in tv_sec_high for 32-bit timestamps */
|
/* This is used in tv_sec_high for 32-bit timestamps */
|
||||||
#define TV_NOHIGHSEC 0x7fffffff
|
#define TV_NOHIGHSEC 0x7fffffff
|
||||||
|
|
||||||
|
/* Structure for 64-bit integers (not requiring 64-bit alignment) */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t high;
|
||||||
|
uint32_t low;
|
||||||
|
} Integer64;
|
||||||
|
|
||||||
/* 32-bit floating-point format consisting of 7-bit signed exponent
|
/* 32-bit floating-point format consisting of 7-bit signed exponent
|
||||||
and 25-bit signed coefficient without hidden bit.
|
and 25-bit signed coefficient without hidden bit.
|
||||||
The result is calculated as: 2^(exp - 25) * coef */
|
The result is calculated as: 2^(exp - 25) * coef */
|
||||||
@@ -296,7 +303,8 @@ typedef struct {
|
|||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
int32_t filter_length;
|
int32_t filter_length;
|
||||||
uint32_t cert_set;
|
uint32_t cert_set;
|
||||||
uint32_t reserved[2];
|
Float max_delay_quant;
|
||||||
|
uint32_t reserved[1];
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} REQ_NTP_Source;
|
} REQ_NTP_Source;
|
||||||
|
|
||||||
@@ -370,6 +378,15 @@ typedef struct {
|
|||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} REQ_SelectData;
|
} REQ_SelectData;
|
||||||
|
|
||||||
|
/* Mask and options reuse the REQ_ADDSRC flags */
|
||||||
|
typedef struct {
|
||||||
|
IPAddr address;
|
||||||
|
uint32_t ref_id;
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t options;
|
||||||
|
int32_t EOR;
|
||||||
|
} REQ_Modify_SelectOpts;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
#define PKT_TYPE_CMD_REQUEST 1
|
#define PKT_TYPE_CMD_REQUEST 1
|
||||||
@@ -476,6 +493,7 @@ typedef struct {
|
|||||||
REQ_NTPSourceName ntp_source_name;
|
REQ_NTPSourceName ntp_source_name;
|
||||||
REQ_AuthData auth_data;
|
REQ_AuthData auth_data;
|
||||||
REQ_SelectData select_data;
|
REQ_SelectData select_data;
|
||||||
|
REQ_Modify_SelectOpts modify_select_opts;
|
||||||
} data; /* Command specific parameters */
|
} data; /* Command specific parameters */
|
||||||
|
|
||||||
/* Padding used to prevent traffic amplification. It only defines the
|
/* Padding used to prevent traffic amplification. It only defines the
|
||||||
@@ -518,7 +536,8 @@ typedef struct {
|
|||||||
#define RPY_SERVER_STATS2 22
|
#define RPY_SERVER_STATS2 22
|
||||||
#define RPY_SELECT_DATA 23
|
#define RPY_SELECT_DATA 23
|
||||||
#define RPY_SERVER_STATS3 24
|
#define RPY_SERVER_STATS3 24
|
||||||
#define N_REPLY_TYPES 25
|
#define RPY_SERVER_STATS4 25
|
||||||
|
#define N_REPLY_TYPES 26
|
||||||
|
|
||||||
/* Status codes */
|
/* Status codes */
|
||||||
#define STT_SUCCESS 0
|
#define STT_SUCCESS 0
|
||||||
@@ -653,17 +672,24 @@ typedef struct {
|
|||||||
} RPY_ClientAccessesByIndex;
|
} RPY_ClientAccessesByIndex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t ntp_hits;
|
Integer64 ntp_hits;
|
||||||
uint32_t nke_hits;
|
Integer64 nke_hits;
|
||||||
uint32_t cmd_hits;
|
Integer64 cmd_hits;
|
||||||
uint32_t ntp_drops;
|
Integer64 ntp_drops;
|
||||||
uint32_t nke_drops;
|
Integer64 nke_drops;
|
||||||
uint32_t cmd_drops;
|
Integer64 cmd_drops;
|
||||||
uint32_t log_drops;
|
Integer64 log_drops;
|
||||||
uint32_t ntp_auth_hits;
|
Integer64 ntp_auth_hits;
|
||||||
uint32_t ntp_interleaved_hits;
|
Integer64 ntp_interleaved_hits;
|
||||||
uint32_t ntp_timestamps;
|
Integer64 ntp_timestamps;
|
||||||
uint32_t ntp_span_seconds;
|
Integer64 ntp_span_seconds;
|
||||||
|
Integer64 ntp_daemon_rx_timestamps;
|
||||||
|
Integer64 ntp_daemon_tx_timestamps;
|
||||||
|
Integer64 ntp_kernel_rx_timestamps;
|
||||||
|
Integer64 ntp_kernel_tx_timestamps;
|
||||||
|
Integer64 ntp_hw_rx_timestamps;
|
||||||
|
Integer64 ntp_hw_tx_timestamps;
|
||||||
|
Integer64 reserved[4];
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} RPY_ServerStats;
|
} RPY_ServerStats;
|
||||||
|
|
||||||
@@ -733,7 +759,8 @@ typedef struct {
|
|||||||
uint32_t total_tx_count;
|
uint32_t total_tx_count;
|
||||||
uint32_t total_rx_count;
|
uint32_t total_rx_count;
|
||||||
uint32_t total_valid_count;
|
uint32_t total_valid_count;
|
||||||
uint32_t reserved[4];
|
uint32_t total_good_count;
|
||||||
|
uint32_t reserved[3];
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} RPY_NTPData;
|
} RPY_NTPData;
|
||||||
|
|
||||||
|
|||||||
313
client.c
313
client.c
@@ -71,6 +71,8 @@ static int source_names = 0;
|
|||||||
|
|
||||||
static int csv_mode = 0;
|
static int csv_mode = 0;
|
||||||
|
|
||||||
|
static int end_dot = 0;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Log a message. This is a minimalistic replacement of the logging.c
|
/* Log a message. This is a minimalistic replacement of the logging.c
|
||||||
implementation to avoid linking with it and other modules. */
|
implementation to avoid linking with it and other modules. */
|
||||||
@@ -283,6 +285,9 @@ open_io(void)
|
|||||||
close_io();
|
close_io();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Start from the first address if called again */
|
||||||
|
address_index = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -866,6 +871,17 @@ process_cmd_doffset(CMD_Request *msg, char *line)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
convert_addsrc_sel_options(int options)
|
||||||
|
{
|
||||||
|
return (options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
|
||||||
|
(options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
|
||||||
|
(options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
|
||||||
|
(options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_cmd_add_source(CMD_Request *msg, char *line)
|
process_cmd_add_source(CMD_Request *msg, char *line)
|
||||||
{
|
{
|
||||||
@@ -943,12 +959,11 @@ process_cmd_add_source(CMD_Request *msg, char *line)
|
|||||||
(data.params.nts ? REQ_ADDSRC_NTS : 0) |
|
(data.params.nts ? REQ_ADDSRC_NTS : 0) |
|
||||||
(data.params.copy ? REQ_ADDSRC_COPY : 0) |
|
(data.params.copy ? REQ_ADDSRC_COPY : 0) |
|
||||||
(data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
|
(data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
|
||||||
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
|
convert_addsrc_sel_options(data.params.sel_options));
|
||||||
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
|
|
||||||
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
|
|
||||||
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
|
|
||||||
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
|
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
|
||||||
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
|
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
|
||||||
|
msg->data.ntp_source.max_delay_quant =
|
||||||
|
UTI_FloatHostToNetwork(data.params.max_delay_quant);
|
||||||
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
|
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
|
||||||
|
|
||||||
result = 1;
|
result = 1;
|
||||||
@@ -1008,6 +1023,7 @@ give_help(void)
|
|||||||
"sources [-a] [-v]\0Display information about current sources\0"
|
"sources [-a] [-v]\0Display information about current sources\0"
|
||||||
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
|
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
|
||||||
"selectdata [-a] [-v]\0Display information about source selection\0"
|
"selectdata [-a] [-v]\0Display information about source selection\0"
|
||||||
|
"selectopts <address|refid> <+|-options>\0Modify selection options\0"
|
||||||
"reselect\0Force reselecting synchronisation source\0"
|
"reselect\0Force reselecting synchronisation source\0"
|
||||||
"reselectdist <dist>\0Modify reselection distance\0"
|
"reselectdist <dist>\0Modify reselection distance\0"
|
||||||
"\0\0"
|
"\0\0"
|
||||||
@@ -1122,8 +1138,8 @@ command_name_generator(const char *text, int state)
|
|||||||
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
|
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
|
||||||
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
|
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
|
||||||
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
|
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
|
||||||
"retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
|
"retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
|
||||||
"smoothtime", "sourcename", "sources", "sourcestats",
|
"shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
|
||||||
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
|
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
@@ -1151,7 +1167,7 @@ command_name_generator(const char *text, int state)
|
|||||||
|
|
||||||
while ((name = names[tab_complete_index][list_index++])) {
|
while ((name = names[tab_complete_index][list_index++])) {
|
||||||
if (strncmp(name, text, len) == 0) {
|
if (strncmp(name, text, len) == 0) {
|
||||||
return strdup(name);
|
return Strdup(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1457,24 +1473,24 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_seconds(unsigned long s)
|
print_seconds(uint32_t s)
|
||||||
{
|
{
|
||||||
unsigned long d;
|
uint32_t d;
|
||||||
|
|
||||||
if (s == (uint32_t)-1) {
|
if (s == (uint32_t)-1) {
|
||||||
printf(" -");
|
printf(" -");
|
||||||
} else if (s < 1200) {
|
} else if (s < 1200) {
|
||||||
printf("%4lu", s);
|
printf("%4"PRIu32, s);
|
||||||
} else if (s < 36000) {
|
} else if (s < 36000) {
|
||||||
printf("%3lum", s / 60);
|
printf("%3"PRIu32"m", s / 60);
|
||||||
} else if (s < 345600) {
|
} else if (s < 345600) {
|
||||||
printf("%3luh", s / 3600);
|
printf("%3"PRIu32"h", s / 3600);
|
||||||
} else {
|
} else {
|
||||||
d = s / 86400;
|
d = s / 86400;
|
||||||
if (d > 999) {
|
if (d > 999) {
|
||||||
printf("%3luy", d / 365);
|
printf("%3"PRIu32"y", d / 365);
|
||||||
} else {
|
} else {
|
||||||
printf("%3lud", d);
|
printf("%3"PRIu32"d", d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1605,8 +1621,9 @@ print_report(const char *format, ...)
|
|||||||
va_list ap;
|
va_list ap;
|
||||||
int i, field, sign, width, prec, spec;
|
int i, field, sign, width, prec, spec;
|
||||||
const char *string;
|
const char *string;
|
||||||
unsigned long long_uinteger;
|
|
||||||
unsigned int uinteger;
|
unsigned int uinteger;
|
||||||
|
uint64_t uinteger64;
|
||||||
|
uint32_t uinteger32;
|
||||||
int integer;
|
int integer;
|
||||||
struct timespec *ts;
|
struct timespec *ts;
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
@@ -1704,9 +1721,9 @@ print_report(const char *format, ...)
|
|||||||
spec == 'O' ? "seconds" : "ppm",
|
spec == 'O' ? "seconds" : "ppm",
|
||||||
(dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
|
(dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
|
||||||
break;
|
break;
|
||||||
case 'I': /* interval with unit */
|
case 'I': /* uint32_t interval with unit */
|
||||||
long_uinteger = va_arg(ap, unsigned long);
|
uinteger32 = va_arg(ap, uint32_t);
|
||||||
print_seconds(long_uinteger);
|
print_seconds(uinteger32);
|
||||||
break;
|
break;
|
||||||
case 'L': /* leap status */
|
case 'L': /* leap status */
|
||||||
integer = va_arg(ap, int);
|
integer = va_arg(ap, int);
|
||||||
@@ -1773,8 +1790,8 @@ print_report(const char *format, ...)
|
|||||||
print_freq_ppm(dbl);
|
print_freq_ppm(dbl);
|
||||||
break;
|
break;
|
||||||
case 'R': /* reference ID in hexdecimal */
|
case 'R': /* reference ID in hexdecimal */
|
||||||
long_uinteger = va_arg(ap, unsigned long);
|
uinteger32 = va_arg(ap, uint32_t);
|
||||||
printf("%08lX", long_uinteger);
|
printf("%08"PRIX32, uinteger32);
|
||||||
break;
|
break;
|
||||||
case 'S': /* offset with unit */
|
case 'S': /* offset with unit */
|
||||||
dbl = va_arg(ap, double);
|
dbl = va_arg(ap, double);
|
||||||
@@ -1791,14 +1808,18 @@ print_report(const char *format, ...)
|
|||||||
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
|
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
|
||||||
printf("%s", buf);
|
printf("%s", buf);
|
||||||
break;
|
break;
|
||||||
case 'U': /* unsigned long in decimal */
|
case 'U': /* uint32_t in decimal */
|
||||||
long_uinteger = va_arg(ap, unsigned long);
|
uinteger32 = va_arg(ap, uint32_t);
|
||||||
printf("%*lu", width, long_uinteger);
|
printf("%*"PRIu32, width, uinteger32);
|
||||||
break;
|
break;
|
||||||
case 'V': /* timespec as seconds since epoch */
|
case 'V': /* timespec as seconds since epoch */
|
||||||
ts = va_arg(ap, struct timespec *);
|
ts = va_arg(ap, struct timespec *);
|
||||||
printf("%s", UTI_TimespecToString(ts));
|
printf("%s", UTI_TimespecToString(ts));
|
||||||
break;
|
break;
|
||||||
|
case 'Q': /* uint64_t in decimal */
|
||||||
|
uinteger64 = va_arg(ap, uint64_t);
|
||||||
|
printf("%*"PRIu64, width, uinteger64);
|
||||||
|
break;
|
||||||
case 'b': /* unsigned int in binary */
|
case 'b': /* unsigned int in binary */
|
||||||
uinteger = va_arg(ap, unsigned int);
|
uinteger = va_arg(ap, unsigned int);
|
||||||
for (i = prec - 1; i >= 0; i--)
|
for (i = prec - 1; i >= 0; i--)
|
||||||
@@ -1958,7 +1979,7 @@ process_cmd_sources(char *line)
|
|||||||
IPAddr ip_addr;
|
IPAddr ip_addr;
|
||||||
uint32_t i, mode, n_sources;
|
uint32_t i, mode, n_sources;
|
||||||
char name[256], mode_ch, state_ch;
|
char name[256], mode_ch, state_ch;
|
||||||
int all, verbose;
|
int all, verbose, ref;
|
||||||
|
|
||||||
parse_sources_options(line, &all, &verbose);
|
parse_sources_options(line, &all, &verbose);
|
||||||
|
|
||||||
@@ -1995,9 +2016,8 @@ process_cmd_sources(char *line)
|
|||||||
if (!all && ip_addr.family == IPADDR_ID)
|
if (!all && ip_addr.family == IPADDR_ID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
format_name(name, sizeof (name), 25,
|
ref = mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4;
|
||||||
mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
|
format_name(name, sizeof (name), 25, ref, ref ? ip_addr.addr.in4 : 0, 1, &ip_addr);
|
||||||
ip_addr.addr.in4, 1, &ip_addr);
|
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case RPY_SD_MD_CLIENT:
|
case RPY_SD_MD_CLIENT:
|
||||||
@@ -2046,7 +2066,7 @@ process_cmd_sources(char *line)
|
|||||||
ntohs(reply.data.source_data.stratum),
|
ntohs(reply.data.source_data.stratum),
|
||||||
(int16_t)ntohs(reply.data.source_data.poll),
|
(int16_t)ntohs(reply.data.source_data.poll),
|
||||||
ntohs(reply.data.source_data.reachability),
|
ntohs(reply.data.source_data.reachability),
|
||||||
(unsigned long)ntohl(reply.data.source_data.since_sample),
|
ntohl(reply.data.source_data.since_sample),
|
||||||
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
|
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
|
||||||
UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
|
UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
|
||||||
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
|
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
|
||||||
@@ -2107,9 +2127,9 @@ process_cmd_sourcestats(char *line)
|
|||||||
|
|
||||||
print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
|
print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
|
||||||
name,
|
name,
|
||||||
(unsigned long)ntohl(reply.data.sourcestats.n_samples),
|
ntohl(reply.data.sourcestats.n_samples),
|
||||||
(unsigned long)ntohl(reply.data.sourcestats.n_runs),
|
ntohl(reply.data.sourcestats.n_runs),
|
||||||
(unsigned long)ntohl(reply.data.sourcestats.span_seconds),
|
ntohl(reply.data.sourcestats.span_seconds),
|
||||||
UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
|
UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
|
||||||
UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
|
UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
|
||||||
UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
|
UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
|
||||||
@@ -2157,7 +2177,7 @@ process_cmd_tracking(char *line)
|
|||||||
"Root dispersion : %.9f seconds\n"
|
"Root dispersion : %.9f seconds\n"
|
||||||
"Update interval : %.1f seconds\n"
|
"Update interval : %.1f seconds\n"
|
||||||
"Leap status : %L\n",
|
"Leap status : %L\n",
|
||||||
(unsigned long)ref_id, name,
|
ref_id, name,
|
||||||
ntohs(reply.data.tracking.stratum),
|
ntohs(reply.data.tracking.stratum),
|
||||||
&ref_time,
|
&ref_time,
|
||||||
UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
|
UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
|
||||||
@@ -2245,10 +2265,10 @@ process_cmd_authdata(char *line)
|
|||||||
|
|
||||||
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
|
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
|
||||||
name, mode_str,
|
name, mode_str,
|
||||||
(unsigned long)ntohl(reply.data.auth_data.key_id),
|
ntohl(reply.data.auth_data.key_id),
|
||||||
ntohs(reply.data.auth_data.key_type),
|
ntohs(reply.data.auth_data.key_type),
|
||||||
ntohs(reply.data.auth_data.key_length),
|
ntohs(reply.data.auth_data.key_length),
|
||||||
(unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
|
ntohl(reply.data.auth_data.last_ke_ago),
|
||||||
ntohs(reply.data.auth_data.ke_attempts),
|
ntohs(reply.data.auth_data.ke_attempts),
|
||||||
ntohs(reply.data.auth_data.nak),
|
ntohs(reply.data.auth_data.nak),
|
||||||
ntohs(reply.data.auth_data.cookies),
|
ntohs(reply.data.auth_data.cookies),
|
||||||
@@ -2341,18 +2361,18 @@ process_cmd_ntpdata(char *line)
|
|||||||
"RX timestamping : %N\n"
|
"RX timestamping : %N\n"
|
||||||
"Total TX : %U\n"
|
"Total TX : %U\n"
|
||||||
"Total RX : %U\n"
|
"Total RX : %U\n"
|
||||||
"Total valid RX : %U\n",
|
"Total valid RX : %U\n"
|
||||||
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
|
"Total good RX : %U\n",
|
||||||
|
UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
|
||||||
ntohs(reply.data.ntp_data.remote_port),
|
ntohs(reply.data.ntp_data.remote_port),
|
||||||
UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
|
UTI_IPToString(&local_addr), UTI_IPToRefid(&local_addr),
|
||||||
reply.data.ntp_data.leap, reply.data.ntp_data.version,
|
reply.data.ntp_data.leap, reply.data.ntp_data.version,
|
||||||
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
|
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
|
||||||
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
|
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
|
||||||
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
|
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
|
||||||
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
|
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
|
||||||
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
|
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
|
||||||
(unsigned long)ntohl(reply.data.ntp_data.ref_id),
|
ntohl(reply.data.ntp_data.ref_id), reply.data.ntp_data.stratum <= 1 ?
|
||||||
reply.data.ntp_data.stratum <= 1 ?
|
|
||||||
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
|
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
|
||||||
&ref_time,
|
&ref_time,
|
||||||
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
|
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
|
||||||
@@ -2366,9 +2386,10 @@ process_cmd_ntpdata(char *line)
|
|||||||
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
|
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
|
||||||
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
|
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
|
||||||
reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
|
reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
|
||||||
(unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
|
ntohl(reply.data.ntp_data.total_tx_count),
|
||||||
(unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
|
ntohl(reply.data.ntp_data.total_rx_count),
|
||||||
(unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
|
ntohl(reply.data.ntp_data.total_valid_count),
|
||||||
|
ntohl(reply.data.ntp_data.total_good_count),
|
||||||
REPORT_END);
|
REPORT_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2396,12 +2417,12 @@ process_cmd_selectdata(char *line)
|
|||||||
n_sources = ntohl(reply.data.n_sources.n_sources);
|
n_sources = ntohl(reply.data.n_sources.n_sources);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
|
printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
|
||||||
printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n");
|
printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
|
||||||
printf( "| x - falseticker, P - not preferred, U - waits for update,\n");
|
printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
|
||||||
printf( "| S - stale, O - orphan, + - combined, * - best.\n");
|
printf( "| U - waits for update,, x - falseticker, + - combined, * - best.\n");
|
||||||
printf( "| Effective options ------. (N - noselect, P - prefer\n");
|
printf( "| Effective options ---------. (N - noselect, P - prefer\n");
|
||||||
printf( "| Configured options -. \\ T - trust, R - require)\n");
|
printf( "| Configured options ----. \\ T - trust, R - require)\n");
|
||||||
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
|
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
|
||||||
printf( "| | | | |\n");
|
printf( "| | | | |\n");
|
||||||
}
|
}
|
||||||
@@ -2440,7 +2461,7 @@ process_cmd_selectdata(char *line)
|
|||||||
eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
|
eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
|
||||||
eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
|
eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
|
||||||
'-',
|
'-',
|
||||||
(unsigned long)ntohl(reply.data.select_data.last_sample_ago),
|
ntohl(reply.data.select_data.last_sample_ago),
|
||||||
UTI_FloatNetworkToHost(reply.data.select_data.score),
|
UTI_FloatNetworkToHost(reply.data.select_data.score),
|
||||||
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
|
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
|
||||||
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
|
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
|
||||||
@@ -2460,31 +2481,43 @@ process_cmd_serverstats(char *line)
|
|||||||
CMD_Reply reply;
|
CMD_Reply reply;
|
||||||
|
|
||||||
request.command = htons(REQ_SERVER_STATS);
|
request.command = htons(REQ_SERVER_STATS);
|
||||||
if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
|
if (!request_reply(&request, &reply, RPY_SERVER_STATS4, 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
print_report("NTP packets received : %U\n"
|
print_report("NTP packets received : %Q\n"
|
||||||
"NTP packets dropped : %U\n"
|
"NTP packets dropped : %Q\n"
|
||||||
"Command packets received : %U\n"
|
"Command packets received : %Q\n"
|
||||||
"Command packets dropped : %U\n"
|
"Command packets dropped : %Q\n"
|
||||||
"Client log records dropped : %U\n"
|
"Client log records dropped : %Q\n"
|
||||||
"NTS-KE connections accepted: %U\n"
|
"NTS-KE connections accepted: %Q\n"
|
||||||
"NTS-KE connections dropped : %U\n"
|
"NTS-KE connections dropped : %Q\n"
|
||||||
"Authenticated NTP packets : %U\n"
|
"Authenticated NTP packets : %Q\n"
|
||||||
"Interleaved NTP packets : %U\n"
|
"Interleaved NTP packets : %Q\n"
|
||||||
"NTP timestamps held : %U\n"
|
"NTP timestamps held : %Q\n"
|
||||||
"NTP timestamp span : %U\n",
|
"NTP timestamp span : %Q\n"
|
||||||
(unsigned long)ntohl(reply.data.server_stats.ntp_hits),
|
"NTP daemon RX timestamps : %Q\n"
|
||||||
(unsigned long)ntohl(reply.data.server_stats.ntp_drops),
|
"NTP daemon TX timestamps : %Q\n"
|
||||||
(unsigned long)ntohl(reply.data.server_stats.cmd_hits),
|
"NTP kernel RX timestamps : %Q\n"
|
||||||
(unsigned long)ntohl(reply.data.server_stats.cmd_drops),
|
"NTP kernel TX timestamps : %Q\n"
|
||||||
(unsigned long)ntohl(reply.data.server_stats.log_drops),
|
"NTP hardware RX timestamps : %Q\n"
|
||||||
(unsigned long)ntohl(reply.data.server_stats.nke_hits),
|
"NTP hardware TX timestamps : %Q\n",
|
||||||
(unsigned long)ntohl(reply.data.server_stats.nke_drops),
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hits),
|
||||||
(unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_drops),
|
||||||
(unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
|
UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_hits),
|
||||||
(unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
|
UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_drops),
|
||||||
(unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
|
UTI_Integer64NetworkToHost(reply.data.server_stats.log_drops),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.nke_hits),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.nke_drops),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_auth_hits),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_interleaved_hits),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_timestamps),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_span_seconds),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_daemon_rx_timestamps),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_daemon_tx_timestamps),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_kernel_rx_timestamps),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_kernel_tx_timestamps),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hw_rx_timestamps),
|
||||||
|
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hw_tx_timestamps),
|
||||||
REPORT_END);
|
REPORT_END);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -2566,7 +2599,7 @@ process_cmd_rtcreport(char *line)
|
|||||||
&ref_time,
|
&ref_time,
|
||||||
ntohs(reply.data.rtc.n_samples),
|
ntohs(reply.data.rtc.n_samples),
|
||||||
ntohs(reply.data.rtc.n_runs),
|
ntohs(reply.data.rtc.n_runs),
|
||||||
(unsigned long)ntohl(reply.data.rtc.span_seconds),
|
ntohl(reply.data.rtc.span_seconds),
|
||||||
UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast),
|
UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast),
|
||||||
UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
|
UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
|
||||||
REPORT_END);
|
REPORT_END);
|
||||||
@@ -2641,16 +2674,15 @@ process_cmd_clients(char *line)
|
|||||||
|
|
||||||
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
|
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
|
||||||
name,
|
name,
|
||||||
(unsigned long)ntohl(client->ntp_hits),
|
ntohl(client->ntp_hits),
|
||||||
(unsigned long)ntohl(client->ntp_drops),
|
ntohl(client->ntp_drops),
|
||||||
client->ntp_interval,
|
client->ntp_interval,
|
||||||
client->ntp_timeout_interval,
|
client->ntp_timeout_interval,
|
||||||
(unsigned long)ntohl(client->last_ntp_hit_ago),
|
ntohl(client->last_ntp_hit_ago),
|
||||||
(unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
|
ntohl(nke ? client->nke_hits : client->cmd_hits),
|
||||||
(unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
|
ntohl(nke ? client->nke_drops : client->cmd_drops),
|
||||||
nke ? client->nke_interval : client->cmd_interval,
|
nke ? client->nke_interval : client->cmd_interval,
|
||||||
(unsigned long)ntohl(nke ? client->last_nke_hit_ago :
|
ntohl(nke ? client->last_nke_hit_ago : client->last_cmd_hit_ago),
|
||||||
client->last_cmd_hit_ago),
|
|
||||||
REPORT_END);
|
REPORT_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2681,7 +2713,7 @@ process_cmd_manual_list(const char *line)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
n_samples = ntohl(reply.data.manual_list.n_samples);
|
n_samples = ntohl(reply.data.manual_list.n_samples);
|
||||||
print_info_field("210 n_samples = %lu\n", (unsigned long)n_samples);
|
print_info_field("210 n_samples = %"PRIu32"\n", n_samples);
|
||||||
|
|
||||||
print_header("# Date Time(UTC) Slewed Original Residual");
|
print_header("# Date Time(UTC) Slewed Original Residual");
|
||||||
|
|
||||||
@@ -2801,11 +2833,11 @@ process_cmd_activity(const char *line)
|
|||||||
"%U sources doing burst (return to online)\n"
|
"%U sources doing burst (return to online)\n"
|
||||||
"%U sources doing burst (return to offline)\n"
|
"%U sources doing burst (return to offline)\n"
|
||||||
"%U sources with unknown address\n",
|
"%U sources with unknown address\n",
|
||||||
(unsigned long)ntohl(reply.data.activity.online),
|
ntohl(reply.data.activity.online),
|
||||||
(unsigned long)ntohl(reply.data.activity.offline),
|
ntohl(reply.data.activity.offline),
|
||||||
(unsigned long)ntohl(reply.data.activity.burst_online),
|
ntohl(reply.data.activity.burst_online),
|
||||||
(unsigned long)ntohl(reply.data.activity.burst_offline),
|
ntohl(reply.data.activity.burst_offline),
|
||||||
(unsigned long)ntohl(reply.data.activity.unresolved),
|
ntohl(reply.data.activity.unresolved),
|
||||||
REPORT_END);
|
REPORT_END);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -2884,6 +2916,55 @@ process_cmd_reset(CMD_Request *msg, char *line)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_cmd_selectopts(CMD_Request *msg, char *line)
|
||||||
|
{
|
||||||
|
int mask, options, option;
|
||||||
|
uint32_t ref_id;
|
||||||
|
IPAddr ip_addr;
|
||||||
|
char *src, *opt;
|
||||||
|
|
||||||
|
src = line;
|
||||||
|
line = CPS_SplitWord(line);
|
||||||
|
ref_id = 0;
|
||||||
|
|
||||||
|
/* Don't allow hostnames to avoid conflicts with reference IDs */
|
||||||
|
if (!UTI_StringToIdIP(src, &ip_addr) && !UTI_StringToIP(src, &ip_addr)) {
|
||||||
|
ip_addr.family = IPADDR_UNSPEC;
|
||||||
|
if (CPS_ParseRefid(src, &ref_id) == 0) {
|
||||||
|
LOG(LOGS_ERR, "Invalid syntax for selectopts command");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = options = 0;
|
||||||
|
|
||||||
|
while (*line != '\0') {
|
||||||
|
opt = line;
|
||||||
|
line = CPS_SplitWord(line);
|
||||||
|
|
||||||
|
if ((opt[0] != '+' && opt[0] != '-') || (option = CPS_GetSelectOption(opt + 1)) == 0) {
|
||||||
|
LOG(LOGS_ERR, "Invalid syntax for selectopts command");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask |= option;
|
||||||
|
if (opt[0] == '+')
|
||||||
|
options |= option;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_select_opts.address);
|
||||||
|
msg->data.modify_select_opts.ref_id = htonl(ref_id);
|
||||||
|
msg->data.modify_select_opts.mask = htonl(mask);
|
||||||
|
msg->data.modify_select_opts.options = htonl(convert_addsrc_sel_options(options));
|
||||||
|
|
||||||
|
msg->command = htons(REQ_MODIFY_SELECTOPTS);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_cmd_waitsync(char *line)
|
process_cmd_waitsync(char *line)
|
||||||
{
|
{
|
||||||
@@ -2919,7 +3000,7 @@ process_cmd_waitsync(char *line)
|
|||||||
skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
|
skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
|
||||||
|
|
||||||
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
|
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
|
||||||
i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
|
i, ref_id, correction, skew_ppm, REPORT_END);
|
||||||
|
|
||||||
if ((ip_addr.family != IPADDR_UNSPEC ||
|
if ((ip_addr.family != IPADDR_UNSPEC ||
|
||||||
(ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
|
(ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
|
||||||
@@ -3186,6 +3267,8 @@ process_line(char *line)
|
|||||||
} else if (!strcmp(command, "selectdata")) {
|
} else if (!strcmp(command, "selectdata")) {
|
||||||
do_normal_submit = 0;
|
do_normal_submit = 0;
|
||||||
ret = process_cmd_selectdata(line);
|
ret = process_cmd_selectdata(line);
|
||||||
|
} else if (!strcmp(command, "selectopts")) {
|
||||||
|
do_normal_submit = process_cmd_selectopts(&tx_message, line);
|
||||||
} else if (!strcmp(command, "serverstats")) {
|
} else if (!strcmp(command, "serverstats")) {
|
||||||
do_normal_submit = 0;
|
do_normal_submit = 0;
|
||||||
ret = process_cmd_serverstats(line);
|
ret = process_cmd_serverstats(line);
|
||||||
@@ -3235,44 +3318,50 @@ process_line(char *line)
|
|||||||
if (do_normal_submit) {
|
if (do_normal_submit) {
|
||||||
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
|
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (end_dot) {
|
||||||
|
printf(".\n");
|
||||||
|
}
|
||||||
|
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
fflush(stdout);
|
|
||||||
|
if (fflush(stdout) != 0 || ferror(stdout) != 0) {
|
||||||
|
LOG(LOGS_ERR, "Could not write to stdout");
|
||||||
|
|
||||||
|
/* Return error for commands that print data */
|
||||||
|
if (!do_normal_submit)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
#define MAX_LINE_LENGTH 2048
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_args(int argc, char **argv, int multi)
|
process_args(int argc, char **argv, int multi)
|
||||||
{
|
{
|
||||||
int total_length, i, ret = 0;
|
char line[MAX_LINE_LENGTH];
|
||||||
char *line;
|
int i, l, ret = 0;
|
||||||
|
|
||||||
total_length = 0;
|
for (i = l = 0; i < argc; i++) {
|
||||||
for(i=0; i<argc; i++) {
|
l += snprintf(line + l, sizeof (line) - l, "%s ", argv[i]);
|
||||||
total_length += strlen(argv[i]) + 1;
|
if (l >= sizeof (line)) {
|
||||||
|
LOG(LOGS_ERR, "Command too long");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
line = (char *) Malloc((2 + total_length) * sizeof(char));
|
if (!multi && i + 1 < argc)
|
||||||
|
continue;
|
||||||
for (i = 0; i < argc; i++) {
|
|
||||||
line[0] = '\0';
|
|
||||||
if (multi) {
|
|
||||||
strcat(line, argv[i]);
|
|
||||||
} else {
|
|
||||||
for (; i < argc; i++) {
|
|
||||||
strcat(line, argv[i]);
|
|
||||||
if (i + 1 < argc)
|
|
||||||
strcat(line, " ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = process_line(line);
|
ret = process_line(line);
|
||||||
if (!ret || quit)
|
if (!ret || quit)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
Free(line);
|
l = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -3291,7 +3380,7 @@ static void
|
|||||||
display_gpl(void)
|
display_gpl(void)
|
||||||
{
|
{
|
||||||
printf("chrony version %s\n"
|
printf("chrony version %s\n"
|
||||||
"Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
|
"Copyright (C) 1997-2003, 2007, 2009-2022 Richard P. Curnow and others\n"
|
||||||
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\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"
|
"you are welcome to redistribute it under certain conditions. See the\n"
|
||||||
"GNU General Public License version 2 for details.\n\n",
|
"GNU General Public License version 2 for details.\n\n",
|
||||||
@@ -3310,6 +3399,7 @@ print_help(const char *progname)
|
|||||||
" -n\t\tDon't resolve hostnames\n"
|
" -n\t\tDon't resolve hostnames\n"
|
||||||
" -N\t\tPrint original source names\n"
|
" -N\t\tPrint original source names\n"
|
||||||
" -c\t\tEnable CSV format\n"
|
" -c\t\tEnable CSV format\n"
|
||||||
|
" -e\t\tEnd responses with dot\n"
|
||||||
#if DEBUG > 0
|
#if DEBUG > 0
|
||||||
" -d\t\tEnable debug messages\n"
|
" -d\t\tEnable debug messages\n"
|
||||||
#endif
|
#endif
|
||||||
@@ -3354,7 +3444,7 @@ main(int argc, char **argv)
|
|||||||
optind = 1;
|
optind = 1;
|
||||||
|
|
||||||
/* Parse short command-line options */
|
/* Parse short command-line options */
|
||||||
while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
|
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:v")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case '4':
|
case '4':
|
||||||
case '6':
|
case '6':
|
||||||
@@ -3372,6 +3462,9 @@ main(int argc, char **argv)
|
|||||||
log_min_severity = LOGS_DEBUG;
|
log_min_severity = LOGS_DEBUG;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
case 'e':
|
||||||
|
end_dot = 1;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
hostnames = optarg;
|
hostnames = optarg;
|
||||||
break;
|
break;
|
||||||
|
|||||||
57
clientlog.c
57
clientlog.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
|
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -126,7 +126,8 @@ static int active;
|
|||||||
/* RX and TX timestamp saved for clients using interleaved mode */
|
/* RX and TX timestamp saved for clients using interleaved mode */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t rx_ts;
|
uint64_t rx_ts;
|
||||||
uint16_t flags;
|
uint8_t flags;
|
||||||
|
uint8_t tx_ts_source;
|
||||||
uint16_t slew_epoch;
|
uint16_t slew_epoch;
|
||||||
int32_t tx_ts_offset;
|
int32_t tx_ts_offset;
|
||||||
} NtpTimestamps;
|
} NtpTimestamps;
|
||||||
@@ -155,12 +156,17 @@ static NtpTimestampMap ntp_ts_map;
|
|||||||
/* Maximum number of timestamps moved in the array to insert a new timestamp */
|
/* Maximum number of timestamps moved in the array to insert a new timestamp */
|
||||||
#define NTPTS_INSERT_LIMIT 64
|
#define NTPTS_INSERT_LIMIT 64
|
||||||
|
|
||||||
|
/* Maximum expected value of the timestamp source */
|
||||||
|
#define MAX_NTP_TS NTP_TS_HARDWARE
|
||||||
|
|
||||||
/* Global statistics */
|
/* Global statistics */
|
||||||
static uint32_t total_hits[MAX_SERVICES];
|
static uint64_t total_hits[MAX_SERVICES];
|
||||||
static uint32_t total_drops[MAX_SERVICES];
|
static uint64_t total_drops[MAX_SERVICES];
|
||||||
static uint32_t total_ntp_auth_hits;
|
static uint64_t total_ntp_auth_hits;
|
||||||
static uint32_t total_ntp_interleaved_hits;
|
static uint64_t total_ntp_interleaved_hits;
|
||||||
static uint32_t total_record_drops;
|
static uint64_t total_record_drops;
|
||||||
|
static uint64_t total_ntp_rx_timestamps[MAX_NTP_TS + 1];
|
||||||
|
static uint64_t total_ntp_tx_timestamps[MAX_NTP_TS + 1];
|
||||||
|
|
||||||
#define NSEC_PER_SEC 1000000000U
|
#define NSEC_PER_SEC 1000000000U
|
||||||
|
|
||||||
@@ -639,9 +645,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
CLG_LogAuthNtpRequest(void)
|
CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src, NTP_Timestamp_Source tx_ts_src)
|
||||||
{
|
{
|
||||||
|
if (auth)
|
||||||
total_ntp_auth_hits++;
|
total_ntp_auth_hits++;
|
||||||
|
if (rx_ts_src >= 0 && rx_ts_src <= MAX_NTP_TS)
|
||||||
|
total_ntp_rx_timestamps[rx_ts_src]++;
|
||||||
|
if (tx_ts_src >= 0 && tx_ts_src <= MAX_NTP_TS)
|
||||||
|
total_ntp_tx_timestamps[tx_ts_src]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -751,7 +762,7 @@ static void
|
|||||||
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
|
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
|
||||||
{
|
{
|
||||||
ntp_ts->hi = htonl(ts >> 32);
|
ntp_ts->hi = htonl(ts >> 32);
|
||||||
ntp_ts->lo = htonl(ts & ((1ULL << 32) - 1));
|
ntp_ts->lo = htonl(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -773,7 +784,8 @@ push_ntp_tss(uint32_t index)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
|
set_ntp_tx(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src)
|
||||||
{
|
{
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
|
||||||
@@ -792,12 +804,13 @@ set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
|
|||||||
|
|
||||||
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
|
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
|
||||||
tss->flags |= NTPTS_VALID_TX;
|
tss->flags |= NTPTS_VALID_TX;
|
||||||
|
tss->tx_ts_source = tx_src;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
|
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts, NTP_Timestamp_Source *tx_src)
|
||||||
{
|
{
|
||||||
int32_t offset = tss->tx_ts_offset;
|
int32_t offset = tss->tx_ts_offset;
|
||||||
NTP_int64 ntp_ts;
|
NTP_int64 ntp_ts;
|
||||||
@@ -814,12 +827,14 @@ get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
|
|||||||
} else {
|
} else {
|
||||||
UTI_ZeroTimespec(tx_ts);
|
UTI_ZeroTimespec(tx_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*tx_src = tss->tx_ts_source;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts, NTP_Timestamp_Source tx_src)
|
||||||
{
|
{
|
||||||
NtpTimestamps *tss;
|
NtpTimestamps *tss;
|
||||||
uint32_t i, index;
|
uint32_t i, index;
|
||||||
@@ -877,7 +892,7 @@ CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
|||||||
tss->rx_ts = rx;
|
tss->rx_ts = rx;
|
||||||
tss->flags = 0;
|
tss->flags = 0;
|
||||||
tss->slew_epoch = ntp_ts_map.slew_epoch;
|
tss->slew_epoch = ntp_ts_map.slew_epoch;
|
||||||
set_ntp_tx_offset(tss, rx_ts, tx_ts);
|
set_ntp_tx(tss, rx_ts, tx_ts, tx_src);
|
||||||
|
|
||||||
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
|
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
|
||||||
index, ntp_ts_map.first, ntp_ts_map.size);
|
index, ntp_ts_map.first, ntp_ts_map.size);
|
||||||
@@ -921,7 +936,8 @@ CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src)
|
||||||
{
|
{
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
|
|
||||||
@@ -931,13 +947,14 @@ CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
|||||||
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
|
set_ntp_tx(get_ntp_tss(index), rx_ts, tx_ts, tx_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source *tx_src)
|
||||||
{
|
{
|
||||||
NtpTimestamps *tss;
|
NtpTimestamps *tss;
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
@@ -953,7 +970,7 @@ CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
|||||||
if (tss->flags & NTPTS_DISABLED)
|
if (tss->flags & NTPTS_DISABLED)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
get_ntp_tx(tss, tx_ts);
|
get_ntp_tx(tss, tx_ts, tx_src);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1085,4 +1102,10 @@ CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
|
|||||||
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
|
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
|
||||||
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
|
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
|
||||||
get_ntp_tss(0)->rx_ts) >> 32 : 0;
|
get_ntp_tss(0)->rx_ts) >> 32 : 0;
|
||||||
|
report->ntp_daemon_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_DAEMON];
|
||||||
|
report->ntp_daemon_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_DAEMON];
|
||||||
|
report->ntp_kernel_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_KERNEL];
|
||||||
|
report->ntp_kernel_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_KERNEL];
|
||||||
|
report->ntp_hw_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_HARDWARE];
|
||||||
|
report->ntp_hw_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_HARDWARE];
|
||||||
}
|
}
|
||||||
|
|||||||
12
clientlog.h
12
clientlog.h
@@ -42,14 +42,18 @@ extern void CLG_Finalise(void);
|
|||||||
extern int CLG_GetClientIndex(IPAddr *client);
|
extern int CLG_GetClientIndex(IPAddr *client);
|
||||||
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
|
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
|
||||||
extern int CLG_LimitServiceRate(CLG_Service service, int index);
|
extern int CLG_LimitServiceRate(CLG_Service service, int index);
|
||||||
extern void CLG_LogAuthNtpRequest(void);
|
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
|
||||||
|
NTP_Timestamp_Source tx_ts_src);
|
||||||
extern int CLG_GetNtpMinPoll(void);
|
extern int CLG_GetNtpMinPoll(void);
|
||||||
|
|
||||||
/* Functions to save and retrieve timestamps for server interleaved mode */
|
/* Functions to save and retrieve timestamps for server interleaved mode */
|
||||||
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src);
|
||||||
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
||||||
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
NTP_Timestamp_Source tx_src);
|
||||||
|
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source *tx_src);
|
||||||
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
|
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
|
||||||
|
|
||||||
/* And some reporting functions, for use by chronyc. */
|
/* And some reporting functions, for use by chronyc. */
|
||||||
|
|||||||
101
cmdmon.c
101
cmdmon.c
@@ -144,6 +144,7 @@ static const char permissions[] = {
|
|||||||
PERMIT_AUTH, /* SELECT_DATA */
|
PERMIT_AUTH, /* SELECT_DATA */
|
||||||
PERMIT_AUTH, /* RELOAD_SOURCES */
|
PERMIT_AUTH, /* RELOAD_SOURCES */
|
||||||
PERMIT_AUTH, /* DOFFSET2 */
|
PERMIT_AUTH, /* DOFFSET2 */
|
||||||
|
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -321,7 +322,8 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
|
|||||||
|
|
||||||
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
||||||
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
||||||
if (message->local_addr.ip.family == IPADDR_INET4 && (sock_fd != sock_fd4 || bound_sock_fd4))
|
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
|
||||||
|
(sock_fd != sock_fd4 || bound_sock_fd4))
|
||||||
message->local_addr.ip.family = IPADDR_UNSPEC;
|
message->local_addr.ip.family = IPADDR_UNSPEC;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -702,6 +704,17 @@ handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
convert_addsrc_select_options(int flags)
|
||||||
|
{
|
||||||
|
return (flags & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
|
||||||
|
(flags & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
|
||||||
|
(flags & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
|
||||||
|
(flags & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||||
{
|
{
|
||||||
@@ -756,6 +769,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
|
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
|
||||||
params.max_delay_dev_ratio =
|
params.max_delay_dev_ratio =
|
||||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
|
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
|
||||||
|
params.max_delay_quant =
|
||||||
|
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
|
||||||
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
|
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
|
||||||
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
|
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
|
||||||
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
|
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
|
||||||
@@ -770,11 +785,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
|
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
|
||||||
params.ext_fields =
|
params.ext_fields =
|
||||||
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
|
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
|
||||||
params.sel_options =
|
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
|
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
|
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
|
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
|
|
||||||
|
|
||||||
status = NSR_AddSourceByName(name, port, pool, type, ¶ms, NULL);
|
status = NSR_AddSourceByName(name, port, pool, type, ¶ms, NULL);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@@ -1166,18 +1177,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
RPT_ServerStatsReport report;
|
RPT_ServerStatsReport report;
|
||||||
|
|
||||||
CLG_GetServerStatsReport(&report);
|
CLG_GetServerStatsReport(&report);
|
||||||
tx_message->reply = htons(RPY_SERVER_STATS3);
|
tx_message->reply = htons(RPY_SERVER_STATS4);
|
||||||
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
|
tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
|
||||||
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
|
tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
|
||||||
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
|
tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
|
||||||
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
|
tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
|
||||||
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
|
tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
|
||||||
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
|
tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
|
||||||
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
|
tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
|
||||||
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
|
tx_message->data.server_stats.ntp_auth_hits =
|
||||||
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
|
UTI_Integer64HostToNetwork(report.ntp_auth_hits);
|
||||||
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
|
tx_message->data.server_stats.ntp_interleaved_hits =
|
||||||
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
|
UTI_Integer64HostToNetwork(report.ntp_interleaved_hits);
|
||||||
|
tx_message->data.server_stats.ntp_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_span_seconds =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_span_seconds);
|
||||||
|
tx_message->data.server_stats.ntp_daemon_rx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_daemon_rx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_daemon_tx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_daemon_tx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_kernel_rx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_kernel_rx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_kernel_tx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_kernel_tx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_hw_rx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_hw_rx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_hw_tx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_hw_tx_timestamps);
|
||||||
|
memset(tx_message->data.server_stats.reserved, 0xff,
|
||||||
|
sizeof (tx_message->data.server_stats.reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1221,6 +1250,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
|
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
|
||||||
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
|
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
|
||||||
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
|
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
|
||||||
|
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
|
||||||
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
|
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1324,7 +1354,7 @@ handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static uint16_t
|
static uint16_t
|
||||||
convert_select_options(int options)
|
convert_sd_sel_options(int options)
|
||||||
{
|
{
|
||||||
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
|
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
|
||||||
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
|
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
|
||||||
@@ -1351,14 +1381,32 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
tx_message->data.select_data.state_char = report.state_char;
|
tx_message->data.select_data.state_char = report.state_char;
|
||||||
tx_message->data.select_data.authentication = report.authentication;
|
tx_message->data.select_data.authentication = report.authentication;
|
||||||
tx_message->data.select_data.leap = report.leap;
|
tx_message->data.select_data.leap = report.leap;
|
||||||
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
|
tx_message->data.select_data.conf_options = htons(convert_sd_sel_options(report.conf_options));
|
||||||
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
|
tx_message->data.select_data.eff_options = htons(convert_sd_sel_options(report.eff_options));
|
||||||
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
|
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
|
||||||
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
|
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
|
||||||
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
|
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
|
||||||
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
|
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||||
|
{
|
||||||
|
int mask, options;
|
||||||
|
uint32_t ref_id;
|
||||||
|
IPAddr ip_addr;
|
||||||
|
|
||||||
|
UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
|
||||||
|
ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
|
||||||
|
mask = ntohl(rx_message->data.modify_select_opts.mask);
|
||||||
|
options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
|
||||||
|
|
||||||
|
if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
|
||||||
|
tx_message->status = htons(STT_NOSUCHSOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Read a packet and process it */
|
/* Read a packet and process it */
|
||||||
|
|
||||||
@@ -1511,6 +1559,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
|
LOG_SetContext(LOGC_Command);
|
||||||
|
|
||||||
switch(rx_command) {
|
switch(rx_command) {
|
||||||
case REQ_NULL:
|
case REQ_NULL:
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
@@ -1753,11 +1803,17 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
|||||||
handle_reload_sources(&rx_message, &tx_message);
|
handle_reload_sources(&rx_message, &tx_message);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REQ_MODIFY_SELECTOPTS:
|
||||||
|
handle_modify_selectopts(&rx_message, &tx_message);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG_LOG("Unhandled command %d", rx_command);
|
DEBUG_LOG("Unhandled command %d", rx_command);
|
||||||
tx_message.status = htons(STT_FAILED);
|
tx_message.status = htons(STT_FAILED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_UnsetContext(LOGC_Command);
|
||||||
} else {
|
} else {
|
||||||
tx_message.status = htons(STT_UNAUTH);
|
tx_message.status = htons(STT_UNAUTH);
|
||||||
}
|
}
|
||||||
@@ -1791,6 +1847,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
|
|||||||
if (status == ADF_BADSUBNET) {
|
if (status == ADF_BADSUBNET) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (status == ADF_SUCCESS) {
|
} else if (status == ADF_SUCCESS) {
|
||||||
|
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
|
||||||
|
allow ? "Allowed" : "Denied", all ? " all" : "", "command",
|
||||||
|
UTI_IPSubnetToString(ip_addr, subnet_bits));
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
51
cmdparse.c
51
cmdparse.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2013-2014, 2016
|
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -44,7 +44,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
{
|
{
|
||||||
char *hostname, *cmd;
|
char *hostname, *cmd;
|
||||||
uint32_t ef_type;
|
uint32_t ef_type;
|
||||||
int n;
|
int n, sel_option;
|
||||||
|
|
||||||
src->port = SRC_DEFAULT_PORT;
|
src->port = SRC_DEFAULT_PORT;
|
||||||
src->params.minpoll = SRC_DEFAULT_MINPOLL;
|
src->params.minpoll = SRC_DEFAULT_MINPOLL;
|
||||||
@@ -72,6 +72,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
|
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
|
||||||
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
|
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
|
||||||
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
|
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
|
||||||
|
src->params.max_delay_quant = 0.0;
|
||||||
src->params.min_delay = 0.0;
|
src->params.min_delay = 0.0;
|
||||||
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
|
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
|
||||||
src->params.offset = 0.0;
|
src->params.offset = 0.0;
|
||||||
@@ -100,14 +101,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
src->params.iburst = 1;
|
src->params.iburst = 1;
|
||||||
} else if (!strcasecmp(cmd, "offline")) {
|
} else if (!strcasecmp(cmd, "offline")) {
|
||||||
src->params.connectivity = SRC_OFFLINE;
|
src->params.connectivity = SRC_OFFLINE;
|
||||||
} else if (!strcasecmp(cmd, "noselect")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_NOSELECT;
|
|
||||||
} else if (!strcasecmp(cmd, "prefer")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_PREFER;
|
|
||||||
} else if (!strcasecmp(cmd, "require")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_REQUIRE;
|
|
||||||
} else if (!strcasecmp(cmd, "trust")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_TRUST;
|
|
||||||
} else if (!strcasecmp(cmd, "certset")) {
|
} else if (!strcasecmp(cmd, "certset")) {
|
||||||
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
|
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -140,6 +133,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
|
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (!strcasecmp(cmd, "maxdelayquant")) {
|
||||||
|
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
|
||||||
|
return 0;
|
||||||
} else if (!strcasecmp(cmd, "maxpoll")) {
|
} else if (!strcasecmp(cmd, "maxpoll")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
|
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -183,6 +179,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
return 0;
|
return 0;
|
||||||
} else if (!strcasecmp(cmd, "xleave")) {
|
} else if (!strcasecmp(cmd, "xleave")) {
|
||||||
src->params.interleaved = 1;
|
src->params.interleaved = 1;
|
||||||
|
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
|
||||||
|
src->params.sel_options |= sel_option;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -193,6 +191,23 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CPS_GetSelectOption(char *option)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(option, "noselect")) {
|
||||||
|
return SRC_SELECT_NOSELECT;
|
||||||
|
} else if (!strcasecmp(option, "prefer")) {
|
||||||
|
return SRC_SELECT_PREFER;
|
||||||
|
} else if (!strcasecmp(option, "require")) {
|
||||||
|
return SRC_SELECT_REQUIRE;
|
||||||
|
} else if (!strcasecmp(option, "trust")) {
|
||||||
|
return SRC_SELECT_TRUST;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
|
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
|
||||||
{
|
{
|
||||||
@@ -392,3 +407,19 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CPS_ParseRefid(char *line, uint32_t *ref_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
|
||||||
|
if (i >= 4)
|
||||||
|
return 0;
|
||||||
|
*ref_id |= (uint32_t)line[i] << (24 - i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ typedef struct {
|
|||||||
/* Parse a command to add an NTP server or peer */
|
/* Parse a command to add an NTP server or peer */
|
||||||
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
|
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
|
||||||
|
|
||||||
|
/* Get an NTP/refclock select option */
|
||||||
|
extern int CPS_GetSelectOption(char *option);
|
||||||
|
|
||||||
/* Parse a command to allow/deny access */
|
/* Parse a command to allow/deny access */
|
||||||
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
|
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
|
||||||
|
|
||||||
@@ -54,4 +57,7 @@ extern char *CPS_SplitWord(char *line);
|
|||||||
/* Parse a key from keyfile */
|
/* Parse a key from keyfile */
|
||||||
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
|
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
|
||||||
|
|
||||||
|
/* Parse a refclock reference ID (returns number of characters) */
|
||||||
|
extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
|
||||||
|
|
||||||
#endif /* GOT_CMDPARSE_H */
|
#endif /* GOT_CMDPARSE_H */
|
||||||
|
|||||||
90
conf.c
90
conf.c
@@ -115,6 +115,7 @@ static int cmd_port = DEFAULT_CANDM_PORT;
|
|||||||
|
|
||||||
static int raw_measurements = 0;
|
static int raw_measurements = 0;
|
||||||
static int do_log_measurements = 0;
|
static int do_log_measurements = 0;
|
||||||
|
static int do_log_selection = 0;
|
||||||
static int do_log_statistics = 0;
|
static int do_log_statistics = 0;
|
||||||
static int do_log_tracking = 0;
|
static int do_log_tracking = 0;
|
||||||
static int do_log_rtc = 0;
|
static int do_log_rtc = 0;
|
||||||
@@ -251,6 +252,9 @@ static char *leapsec_tz = NULL;
|
|||||||
/* Name of the user to which will be dropped root privileges. */
|
/* Name of the user to which will be dropped root privileges. */
|
||||||
static char *user;
|
static char *user;
|
||||||
|
|
||||||
|
/* Address refresh interval */
|
||||||
|
static int refresh = 1209600; /* 2 weeks */
|
||||||
|
|
||||||
/* NTS server and client configuration */
|
/* NTS server and client configuration */
|
||||||
static char *nts_dump_dir = NULL;
|
static char *nts_dump_dir = NULL;
|
||||||
static char *nts_ntp_server = NULL;
|
static char *nts_ntp_server = NULL;
|
||||||
@@ -273,6 +277,9 @@ static int no_system_cert = 0;
|
|||||||
/* Array of CNF_HwTsInterface */
|
/* Array of CNF_HwTsInterface */
|
||||||
static ARR_Instance hwts_interfaces;
|
static ARR_Instance hwts_interfaces;
|
||||||
|
|
||||||
|
/* Timeout for resuming reading from sockets waiting for HW TX timestamp */
|
||||||
|
static double hwts_timeout = 0.001;
|
||||||
|
|
||||||
/* PTP event port (disabled by default) */
|
/* PTP event port (disabled by default) */
|
||||||
static int ptp_port = 0;
|
static int ptp_port = 0;
|
||||||
|
|
||||||
@@ -601,6 +608,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
|||||||
parse_string(p, &hwclock_file);
|
parse_string(p, &hwclock_file);
|
||||||
} else if (!strcasecmp(command, "hwtimestamp")) {
|
} else if (!strcasecmp(command, "hwtimestamp")) {
|
||||||
parse_hwtimestamp(p);
|
parse_hwtimestamp(p);
|
||||||
|
} else if (!strcasecmp(command, "hwtstimeout")) {
|
||||||
|
parse_double(p, &hwts_timeout);
|
||||||
} else if (!strcasecmp(command, "include")) {
|
} else if (!strcasecmp(command, "include")) {
|
||||||
parse_include(p);
|
parse_include(p);
|
||||||
} else if (!strcasecmp(command, "initstepslew")) {
|
} else if (!strcasecmp(command, "initstepslew")) {
|
||||||
@@ -696,6 +705,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
|||||||
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
|
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
|
||||||
} else if (!strcasecmp(command, "refclock")) {
|
} else if (!strcasecmp(command, "refclock")) {
|
||||||
parse_refclock(p);
|
parse_refclock(p);
|
||||||
|
} else if (!strcasecmp(command, "refresh")) {
|
||||||
|
parse_int(p, &refresh);
|
||||||
} else if (!strcasecmp(command, "reselectdist")) {
|
} else if (!strcasecmp(command, "reselectdist")) {
|
||||||
parse_double(p, &reselect_distance);
|
parse_double(p, &reselect_distance);
|
||||||
} else if (!strcasecmp(command, "rtcautotrim")) {
|
} else if (!strcasecmp(command, "rtcautotrim")) {
|
||||||
@@ -861,16 +872,16 @@ static void
|
|||||||
parse_refclock(char *line)
|
parse_refclock(char *line)
|
||||||
{
|
{
|
||||||
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
|
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
|
||||||
int max_lock_age, pps_forced, stratum, tai;
|
int local, max_lock_age, pps_forced, sel_option, stratum, tai;
|
||||||
uint32_t ref_id, lock_ref_id;
|
uint32_t ref_id, lock_ref_id;
|
||||||
double offset, delay, precision, max_dispersion, pulse_width;
|
double offset, delay, precision, max_dispersion, pulse_width;
|
||||||
char *p, *cmd, *name, *param;
|
char *p, *cmd, *name, *param;
|
||||||
unsigned char ref[5];
|
|
||||||
RefclockParameters *refclock;
|
RefclockParameters *refclock;
|
||||||
|
|
||||||
poll = 4;
|
poll = 4;
|
||||||
dpoll = 0;
|
dpoll = 0;
|
||||||
filter_length = 64;
|
filter_length = 64;
|
||||||
|
local = 0;
|
||||||
pps_forced = 0;
|
pps_forced = 0;
|
||||||
pps_rate = 0;
|
pps_rate = 0;
|
||||||
min_samples = SRC_DEFAULT_MINSAMPLES;
|
min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||||
@@ -910,13 +921,11 @@ parse_refclock(char *line)
|
|||||||
line = CPS_SplitWord(line);
|
line = CPS_SplitWord(line);
|
||||||
|
|
||||||
if (!strcasecmp(cmd, "refid")) {
|
if (!strcasecmp(cmd, "refid")) {
|
||||||
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
|
if ((n = CPS_ParseRefid(line, &ref_id)) == 0)
|
||||||
break;
|
break;
|
||||||
ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
|
||||||
} else if (!strcasecmp(cmd, "lock")) {
|
} else if (!strcasecmp(cmd, "lock")) {
|
||||||
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
|
if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
|
||||||
break;
|
break;
|
||||||
lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
|
||||||
} else if (!strcasecmp(cmd, "poll")) {
|
} else if (!strcasecmp(cmd, "poll")) {
|
||||||
if (sscanf(line, "%d%n", &poll, &n) != 1) {
|
if (sscanf(line, "%d%n", &poll, &n) != 1) {
|
||||||
break;
|
break;
|
||||||
@@ -929,6 +938,9 @@ parse_refclock(char *line)
|
|||||||
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
|
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (!strcasecmp(cmd, "local")) {
|
||||||
|
n = 0;
|
||||||
|
local = 1;
|
||||||
} else if (!strcasecmp(cmd, "rate")) {
|
} else if (!strcasecmp(cmd, "rate")) {
|
||||||
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
|
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
|
||||||
break;
|
break;
|
||||||
@@ -966,18 +978,9 @@ parse_refclock(char *line)
|
|||||||
} else if (!strcasecmp(cmd, "width")) {
|
} else if (!strcasecmp(cmd, "width")) {
|
||||||
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
|
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
|
||||||
break;
|
break;
|
||||||
} else if (!strcasecmp(cmd, "noselect")) {
|
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
|
||||||
n = 0;
|
n = 0;
|
||||||
sel_options |= SRC_SELECT_NOSELECT;
|
sel_options |= sel_option;
|
||||||
} else if (!strcasecmp(cmd, "prefer")) {
|
|
||||||
n = 0;
|
|
||||||
sel_options |= SRC_SELECT_PREFER;
|
|
||||||
} else if (!strcasecmp(cmd, "trust")) {
|
|
||||||
n = 0;
|
|
||||||
sel_options |= SRC_SELECT_TRUST;
|
|
||||||
} else if (!strcasecmp(cmd, "require")) {
|
|
||||||
n = 0;
|
|
||||||
sel_options |= SRC_SELECT_REQUIRE;
|
|
||||||
} else {
|
} else {
|
||||||
other_parse_error("Invalid refclock option");
|
other_parse_error("Invalid refclock option");
|
||||||
return;
|
return;
|
||||||
@@ -995,6 +998,7 @@ parse_refclock(char *line)
|
|||||||
refclock->driver_poll = dpoll;
|
refclock->driver_poll = dpoll;
|
||||||
refclock->poll = poll;
|
refclock->poll = poll;
|
||||||
refclock->filter_length = filter_length;
|
refclock->filter_length = filter_length;
|
||||||
|
refclock->local = local;
|
||||||
refclock->pps_forced = pps_forced;
|
refclock->pps_forced = pps_forced;
|
||||||
refclock->pps_rate = pps_rate;
|
refclock->pps_rate = pps_rate;
|
||||||
refclock->min_samples = min_samples;
|
refclock->min_samples = min_samples;
|
||||||
@@ -1027,6 +1031,8 @@ parse_log(char *line)
|
|||||||
raw_measurements = 1;
|
raw_measurements = 1;
|
||||||
} else if (!strcmp(log_name, "measurements")) {
|
} else if (!strcmp(log_name, "measurements")) {
|
||||||
do_log_measurements = 1;
|
do_log_measurements = 1;
|
||||||
|
} else if (!strcmp(log_name, "selection")) {
|
||||||
|
do_log_selection = 1;
|
||||||
} else if (!strcmp(log_name, "statistics")) {
|
} else if (!strcmp(log_name, "statistics")) {
|
||||||
do_log_statistics = 1;
|
do_log_statistics = 1;
|
||||||
} else if (!strcmp(log_name, "tracking")) {
|
} else if (!strcmp(log_name, "tracking")) {
|
||||||
@@ -1429,8 +1435,8 @@ static void
|
|||||||
parse_hwtimestamp(char *line)
|
parse_hwtimestamp(char *line)
|
||||||
{
|
{
|
||||||
CNF_HwTsInterface *iface;
|
CNF_HwTsInterface *iface;
|
||||||
|
int n, maxpoll_set = 0;
|
||||||
char *p, filter[5];
|
char *p, filter[5];
|
||||||
int n;
|
|
||||||
|
|
||||||
if (!*line) {
|
if (!*line) {
|
||||||
command_parse_error();
|
command_parse_error();
|
||||||
@@ -1460,6 +1466,10 @@ parse_hwtimestamp(char *line)
|
|||||||
} else if (!strcasecmp(p, "minpoll")) {
|
} else if (!strcasecmp(p, "minpoll")) {
|
||||||
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
|
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
|
||||||
break;
|
break;
|
||||||
|
} else if (!strcasecmp(p, "maxpoll")) {
|
||||||
|
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
|
||||||
|
break;
|
||||||
|
maxpoll_set = 1;
|
||||||
} else if (!strcasecmp(p, "minsamples")) {
|
} else if (!strcasecmp(p, "minsamples")) {
|
||||||
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
|
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
|
||||||
break;
|
break;
|
||||||
@@ -1495,6 +1505,9 @@ parse_hwtimestamp(char *line)
|
|||||||
|
|
||||||
if (*p)
|
if (*p)
|
||||||
command_parse_error();
|
command_parse_error();
|
||||||
|
|
||||||
|
if (!maxpoll_set)
|
||||||
|
iface->maxpoll = iface->minpoll + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1696,6 +1709,8 @@ reload_source_dirs(void)
|
|||||||
new_ids = ARR_GetElements(ntp_source_ids);
|
new_ids = ARR_GetElements(ntp_source_ids);
|
||||||
unresolved = 0;
|
unresolved = 0;
|
||||||
|
|
||||||
|
LOG_SetContext(LOGC_SourceFile);
|
||||||
|
|
||||||
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
|
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
|
||||||
|
|
||||||
for (i = j = 0; i < prev_size || j < new_size; ) {
|
for (i = j = 0; i < prev_size || j < new_size; ) {
|
||||||
@@ -1731,6 +1746,8 @@ reload_source_dirs(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_UnsetContext(LOGC_SourceFile);
|
||||||
|
|
||||||
for (i = 0; i < prev_size; i++)
|
for (i = 0; i < prev_size; i++)
|
||||||
Free(prev_sources[i].params.name);
|
Free(prev_sources[i].params.name);
|
||||||
Free(prev_sources);
|
Free(prev_sources);
|
||||||
@@ -1774,6 +1791,19 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
CNF_CheckReadOnlyAccess(void)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (keys_file)
|
||||||
|
UTI_CheckReadOnlyAccess(keys_file);
|
||||||
|
for (i = 0; i < ARR_GetSize(nts_server_key_files); i++)
|
||||||
|
UTI_CheckReadOnlyAccess(*(char **)ARR_GetElement(nts_server_key_files, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
CNF_AddInitSources(void)
|
CNF_AddInitSources(void)
|
||||||
{
|
{
|
||||||
@@ -1924,6 +1954,14 @@ CNF_GetLogMeasurements(int *raw)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CNF_GetLogSelection(void)
|
||||||
|
{
|
||||||
|
return do_log_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CNF_GetLogStatistics(void)
|
CNF_GetLogStatistics(void)
|
||||||
{
|
{
|
||||||
@@ -2484,6 +2522,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
double
|
||||||
|
CNF_GetHwTsTimeout(void)
|
||||||
|
{
|
||||||
|
return hwts_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CNF_GetPtpPort(void)
|
CNF_GetPtpPort(void)
|
||||||
{
|
{
|
||||||
@@ -2492,6 +2538,14 @@ CNF_GetPtpPort(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CNF_GetRefresh(void)
|
||||||
|
{
|
||||||
|
return refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
char *
|
char *
|
||||||
CNF_GetNtsDumpDir(void)
|
CNF_GetNtsDumpDir(void)
|
||||||
{
|
{
|
||||||
|
|||||||
7
conf.h
7
conf.h
@@ -44,6 +44,8 @@ extern void CNF_ParseLine(const char *filename, int number, char *line);
|
|||||||
|
|
||||||
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
|
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
|
||||||
|
|
||||||
|
extern void CNF_CheckReadOnlyAccess(void);
|
||||||
|
|
||||||
extern void CNF_AddInitSources(void);
|
extern void CNF_AddInitSources(void);
|
||||||
extern void CNF_AddSources(void);
|
extern void CNF_AddSources(void);
|
||||||
extern void CNF_AddBroadcasts(void);
|
extern void CNF_AddBroadcasts(void);
|
||||||
@@ -58,6 +60,7 @@ extern char *CNF_GetLogDir(void);
|
|||||||
extern char *CNF_GetDumpDir(void);
|
extern char *CNF_GetDumpDir(void);
|
||||||
extern int CNF_GetLogBanner(void);
|
extern int CNF_GetLogBanner(void);
|
||||||
extern int CNF_GetLogMeasurements(int *raw);
|
extern int CNF_GetLogMeasurements(int *raw);
|
||||||
|
extern int CNF_GetLogSelection(void);
|
||||||
extern int CNF_GetLogStatistics(void);
|
extern int CNF_GetLogStatistics(void);
|
||||||
extern int CNF_GetLogTracking(void);
|
extern int CNF_GetLogTracking(void);
|
||||||
extern int CNF_GetLogRtc(void);
|
extern int CNF_GetLogRtc(void);
|
||||||
@@ -141,6 +144,7 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
int minpoll;
|
int minpoll;
|
||||||
|
int maxpoll;
|
||||||
int min_samples;
|
int min_samples;
|
||||||
int max_samples;
|
int max_samples;
|
||||||
int nocrossts;
|
int nocrossts;
|
||||||
@@ -151,9 +155,12 @@ typedef struct {
|
|||||||
} CNF_HwTsInterface;
|
} CNF_HwTsInterface;
|
||||||
|
|
||||||
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
|
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
|
||||||
|
extern double CNF_GetHwTsTimeout(void);
|
||||||
|
|
||||||
extern int CNF_GetPtpPort(void);
|
extern int CNF_GetPtpPort(void);
|
||||||
|
|
||||||
|
extern int CNF_GetRefresh(void);
|
||||||
|
|
||||||
extern char *CNF_GetNtsDumpDir(void);
|
extern char *CNF_GetNtsDumpDir(void);
|
||||||
extern char *CNF_GetNtsNtpServer(void);
|
extern char *CNF_GetNtsNtpServer(void);
|
||||||
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
|
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
|
||||||
|
|||||||
59
configure
vendored
59
configure
vendored
@@ -128,6 +128,7 @@ For better control, use the options below.
|
|||||||
--without-seccomp Don't use seccomp even if it is available
|
--without-seccomp Don't use seccomp even if it is available
|
||||||
--disable-asyncdns Disable asynchronous name resolving
|
--disable-asyncdns Disable asynchronous name resolving
|
||||||
--disable-forcednsretry Don't retry on permanent DNS error
|
--disable-forcednsretry Don't retry on permanent DNS error
|
||||||
|
--without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
|
||||||
--without-clock-gettime Don't use clock_gettime() even if it is available
|
--without-clock-gettime Don't use clock_gettime() even if it is available
|
||||||
--disable-timestamping Disable support for SW/HW timestamping
|
--disable-timestamping Disable support for SW/HW timestamping
|
||||||
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
|
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
|
||||||
@@ -244,7 +245,9 @@ try_setsched=0
|
|||||||
try_lockmem=0
|
try_lockmem=0
|
||||||
feat_asyncdns=1
|
feat_asyncdns=1
|
||||||
feat_forcednsretry=1
|
feat_forcednsretry=1
|
||||||
|
try_aes_gcm_siv=1
|
||||||
try_clock_gettime=1
|
try_clock_gettime=1
|
||||||
|
try_arc4random=1
|
||||||
try_recvmmsg=1
|
try_recvmmsg=1
|
||||||
feat_timestamping=1
|
feat_timestamping=1
|
||||||
try_timestamping=0
|
try_timestamping=0
|
||||||
@@ -344,6 +347,9 @@ do
|
|||||||
--disable-forcednsretry)
|
--disable-forcednsretry)
|
||||||
feat_forcednsretry=0
|
feat_forcednsretry=0
|
||||||
;;
|
;;
|
||||||
|
--without-aes-gcm-siv)
|
||||||
|
try_aes_gcm_siv=0
|
||||||
|
;;
|
||||||
--without-clock-gettime)
|
--without-clock-gettime)
|
||||||
try_clock_gettime=0
|
try_clock_gettime=0
|
||||||
;;
|
;;
|
||||||
@@ -421,6 +427,7 @@ case $OPERATINGSYSTEM in
|
|||||||
try_setsched=1
|
try_setsched=1
|
||||||
try_lockmem=1
|
try_lockmem=1
|
||||||
try_phc=1
|
try_phc=1
|
||||||
|
try_arc4random=0
|
||||||
add_def LINUX
|
add_def LINUX
|
||||||
echo "Configuring for " $SYSTEM
|
echo "Configuring for " $SYSTEM
|
||||||
;;
|
;;
|
||||||
@@ -467,7 +474,7 @@ case $OPERATINGSYSTEM in
|
|||||||
;;
|
;;
|
||||||
SunOS)
|
SunOS)
|
||||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
||||||
LIBS="$LIBS -lsocket -lnsl -lresolv"
|
LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
|
||||||
try_setsched=1
|
try_setsched=1
|
||||||
try_lockmem=1
|
try_lockmem=1
|
||||||
add_def SOLARIS
|
add_def SOLARIS
|
||||||
@@ -479,7 +486,7 @@ case $OPERATINGSYSTEM in
|
|||||||
add_def FEAT_PRIVDROP
|
add_def FEAT_PRIVDROP
|
||||||
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
|
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
|
||||||
fi
|
fi
|
||||||
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
|
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
|
||||||
;;
|
;;
|
||||||
* )
|
* )
|
||||||
echo "error: $SYSTEM is not supported (yet?)"
|
echo "error: $SYSTEM is not supported (yet?)"
|
||||||
@@ -671,12 +678,12 @@ fi
|
|||||||
|
|
||||||
if [ $try_clock_gettime = "1" ]; then
|
if [ $try_clock_gettime = "1" ]; then
|
||||||
if test_code 'clock_gettime()' 'time.h' '' '' \
|
if test_code 'clock_gettime()' 'time.h' '' '' \
|
||||||
'clock_gettime(CLOCK_REALTIME, NULL);'
|
'clock_gettime(CLOCK_REALTIME, (void *)1);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CLOCK_GETTIME
|
add_def HAVE_CLOCK_GETTIME
|
||||||
else
|
else
|
||||||
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
|
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
|
||||||
'clock_gettime(CLOCK_REALTIME, NULL);'
|
'clock_gettime(CLOCK_REALTIME, (void *)1);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CLOCK_GETTIME
|
add_def HAVE_CLOCK_GETTIME
|
||||||
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
||||||
@@ -702,11 +709,14 @@ then
|
|||||||
use_pthread=1
|
use_pthread=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
|
if [ $try_arc4random = "1" ] && \
|
||||||
|
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
|
||||||
|
'arc4random_buf((void *)1, 1);'
|
||||||
|
then
|
||||||
add_def HAVE_ARC4RANDOM
|
add_def HAVE_ARC4RANDOM
|
||||||
else
|
else
|
||||||
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
|
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
|
||||||
'return getrandom(NULL, 256, 0);'; then
|
'return getrandom((void *)1, 1, 0);'; then
|
||||||
add_def HAVE_GETRANDOM
|
add_def HAVE_GETRANDOM
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -900,7 +910,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
|
|||||||
add_def FEAT_SECHASH
|
add_def FEAT_SECHASH
|
||||||
|
|
||||||
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
|
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
|
||||||
'cmac128_update(NULL, NULL, NULL, 0, NULL);'
|
'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CMAC
|
add_def HAVE_CMAC
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
|
||||||
@@ -925,7 +935,7 @@ fi
|
|||||||
|
|
||||||
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
|
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
|
||||||
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
|
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
|
||||||
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
|
'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
|
||||||
then
|
then
|
||||||
HASH_OBJ="hash_tomcrypt.o"
|
HASH_OBJ="hash_tomcrypt.o"
|
||||||
HASH_LINK="-ltomcrypt"
|
HASH_LINK="-ltomcrypt"
|
||||||
@@ -939,7 +949,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
|
|||||||
test_link="`pkg_config --libs gnutls`"
|
test_link="`pkg_config --libs gnutls`"
|
||||||
if test_code 'gnutls' 'gnutls/crypto.h' \
|
if test_code 'gnutls' 'gnutls/crypto.h' \
|
||||||
"$test_cflags" "$test_link" '
|
"$test_cflags" "$test_link" '
|
||||||
return gnutls_hash(NULL, NULL, 0);'
|
return gnutls_hash((void *)1, (void *)2, 1);'
|
||||||
then
|
then
|
||||||
HASH_OBJ="hash_gnutls.o"
|
HASH_OBJ="hash_gnutls.o"
|
||||||
HASH_LINK="$test_link"
|
HASH_LINK="$test_link"
|
||||||
@@ -947,7 +957,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
|
|||||||
add_def FEAT_SECHASH
|
add_def FEAT_SECHASH
|
||||||
|
|
||||||
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
|
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
|
||||||
'return gnutls_hmac_init(NULL, GNUTLS_MAC_AES_CMAC_128, NULL, 0);'
|
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CMAC
|
add_def HAVE_CMAC
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
|
||||||
@@ -970,27 +980,40 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
|||||||
fi
|
fi
|
||||||
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
|
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
|
||||||
"$test_cflags" "$test_link $LIBS" '
|
"$test_cflags" "$test_link $LIBS" '
|
||||||
return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
|
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
|
||||||
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
|
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
|
||||||
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
|
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
|
||||||
then
|
then
|
||||||
if test_code 'SIV in nettle' \
|
if test_code 'AES-SIV-CMAC in nettle' \
|
||||||
'nettle/siv-cmac.h' "" "$LIBS" \
|
'nettle/siv-cmac.h' "" "$LIBS" \
|
||||||
'siv_cmac_aes128_set_key(NULL, NULL);'
|
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||||
add_def HAVE_SIV
|
add_def HAVE_SIV
|
||||||
add_def HAVE_NETTLE_SIV_CMAC
|
add_def HAVE_NETTLE_SIV_CMAC
|
||||||
|
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
|
||||||
|
'nettle/siv-gcm.h' "" "$LIBS" \
|
||||||
|
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
|
||||||
|
(void *)4);'
|
||||||
|
then
|
||||||
|
add_def HAVE_NETTLE_SIV_GCM
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
|
if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
|
||||||
"$test_cflags" "$test_link $LIBS" '
|
"$test_cflags" "$test_link $LIBS" '
|
||||||
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
|
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
|
||||||
add_def HAVE_SIV
|
add_def HAVE_SIV
|
||||||
|
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
|
||||||
|
"$test_cflags" "$test_link $LIBS" '
|
||||||
|
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
|
||||||
|
then
|
||||||
|
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
|
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
|
||||||
'aes128_set_encrypt_key(NULL, NULL);'
|
'aes128_set_encrypt_key((void *)1, (void *)2);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||||
add_def HAVE_SIV
|
add_def HAVE_SIV
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// Copyright (C) Richard P. Curnow 1997-2003
|
// Copyright (C) Richard P. Curnow 1997-2003
|
||||||
// Copyright (C) Stephen Wadeley 2016
|
// Copyright (C) Stephen Wadeley 2016
|
||||||
// Copyright (C) Bryan Christianson 2017
|
// Copyright (C) Bryan Christianson 2017
|
||||||
// Copyright (C) Miroslav Lichvar 2009-2021
|
// Copyright (C) Miroslav Lichvar 2009-2022
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -63,16 +63,26 @@ source. The client-server relationship is strictly hierarchical: a client might
|
|||||||
synchronise its system time to that of the server, but the server's system time
|
synchronise its system time to that of the server, but the server's system time
|
||||||
will never be influenced by that of a client.
|
will never be influenced by that of a client.
|
||||||
+
|
+
|
||||||
|
The server can be specified by its hostname or IP address. If the hostname cannot
|
||||||
|
be resolved on start, *chronyd* will try it again in increasing intervals, and
|
||||||
|
also when the <<chronyc.adoc#online,*online*>> command is issued in *chronyc*.
|
||||||
|
+
|
||||||
|
The DNS record can change over time. The used address will be replaced with a
|
||||||
|
newly resolved address when the server becomes unreachable (i.e. no valid
|
||||||
|
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
|
||||||
|
agree with a majority of other sources), or the root distance is too large (the
|
||||||
|
limit can be configured by the <<maxdistance,*maxdistance*>> directive). The
|
||||||
|
automatic replacement happens at most once per 30 minutes.
|
||||||
|
+
|
||||||
This directive can be used multiple times to specify multiple servers.
|
This directive can be used multiple times to specify multiple servers.
|
||||||
+
|
+
|
||||||
The directive is immediately followed by either the name of the
|
The directive supports the following options:
|
||||||
server, or its IP address. It supports the following options:
|
|
||||||
+
|
+
|
||||||
*minpoll* _poll_:::
|
*minpoll* _poll_:::
|
||||||
This option specifies the minimum interval between requests sent to the server
|
This option specifies the minimum interval between requests sent to the server
|
||||||
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
|
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
|
||||||
polling interval should not drop below 32 seconds. The default is 6 (64
|
polling interval should not drop below 32 seconds. The default is 6 (64
|
||||||
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
|
seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
|
||||||
months). Note that intervals shorter than 6 (64 seconds) should generally not
|
months). Note that intervals shorter than 6 (64 seconds) should generally not
|
||||||
be used with public servers on the Internet, because it might be considered
|
be used with public servers on the Internet, because it might be considered
|
||||||
abuse. A sub-second interval will be enabled only when the server is reachable
|
abuse. A sub-second interval will be enabled only when the server is reachable
|
||||||
@@ -82,7 +92,7 @@ should be in a local network.
|
|||||||
This option specifies the maximum interval between requests sent to the server
|
This option specifies the maximum interval between requests sent to the server
|
||||||
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
|
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
|
||||||
interval should stay at or below 9 (512 seconds). The default is 10 (1024
|
interval should stay at or below 9 (512 seconds). The default is 10 (1024
|
||||||
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
|
seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
|
||||||
months).
|
months).
|
||||||
*iburst*:::
|
*iburst*:::
|
||||||
With this option, *chronyd* will start with a burst of 4-8 requests in order to
|
With this option, *chronyd* will start with a burst of 4-8 requests in order to
|
||||||
@@ -138,18 +148,33 @@ behind a lot of packets related to a download of some sort).
|
|||||||
If the user knows that round trip delays above a certain level should cause the
|
If the user knows that round trip delays above a certain level should cause the
|
||||||
measurement to be ignored, this level can be defined with the *maxdelay*
|
measurement to be ignored, this level can be defined with the *maxdelay*
|
||||||
option. For example, *maxdelay 0.3* would indicate that measurements with a
|
option. For example, *maxdelay 0.3* would indicate that measurements with a
|
||||||
round-trip delay of 0.3 seconds or more should be ignored. The default value is
|
round-trip delay greater than 0.3 seconds should be ignored. The default value
|
||||||
3 seconds and the maximum value is 1000 seconds.
|
is 3 seconds and the maximum value is 1000 seconds.
|
||||||
*maxdelayratio* _ratio_:::
|
*maxdelayratio* _ratio_:::
|
||||||
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
|
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
|
||||||
of the minimum round-trip delay amongst the previous measurements that it has
|
of the minimum round-trip delay amongst the previous measurements that it has
|
||||||
buffered. If a measurement has a round trip delay that is greater than the
|
buffered. If a measurement has a round-trip delay that is greater than the
|
||||||
specified ratio times the minimum delay, it will be rejected.
|
specified ratio times the minimum delay, it will be rejected. By default, this
|
||||||
|
test is disabled.
|
||||||
*maxdelaydevratio* _ratio_:::
|
*maxdelaydevratio* _ratio_:::
|
||||||
If a measurement has a ratio of the increase in the round-trip delay from the
|
If a measurement has a ratio of the increase in the round-trip delay from the
|
||||||
minimum delay amongst the previous measurements to the standard deviation of
|
minimum delay amongst the previous measurements to the standard deviation of
|
||||||
the previous measurements that is greater than the specified ratio, it will be
|
the previous measurements that is greater than the specified ratio, it will be
|
||||||
rejected. The default is 10.0.
|
rejected. The default is 10.0.
|
||||||
|
*maxdelayquant* _p_:::
|
||||||
|
This option disables the *maxdelaydevratio* test and specifies the maximum
|
||||||
|
acceptable delay as a quantile of the round-trip delay instead of a function of
|
||||||
|
the minimum delay amongst the buffered measurements. If a measurement has a
|
||||||
|
round-trip delay that is greater than a long-term estimate of the _p_-quantile,
|
||||||
|
it will be rejected.
|
||||||
|
+
|
||||||
|
The specified _p_ value should be between 0.05 and 0.95. For example,
|
||||||
|
*maxdelayquant 0.2* would indicate that only measurements with the lowest 20
|
||||||
|
percent of round-trip delays should be accepted. Note that it can take many
|
||||||
|
measurements for the estimated quantile to reach the expected value. This
|
||||||
|
option is intended for synchronisation in mostly static local networks with
|
||||||
|
very short polling intervals and possibly combined with the *filter* option.
|
||||||
|
By default, this test is disabled in favour of the *maxdelaydevratio* test.
|
||||||
*mindelay* _delay_:::
|
*mindelay* _delay_:::
|
||||||
This option specifies a fixed minimum round-trip delay to be used instead of
|
This option specifies a fixed minimum round-trip delay to be used instead of
|
||||||
the minimum amongst the previous measurements. This can be useful in networks
|
the minimum amongst the previous measurements. This can be useful in networks
|
||||||
@@ -176,11 +201,12 @@ Set the minimum number of samples kept for this source. This overrides the
|
|||||||
*maxsamples* _samples_:::
|
*maxsamples* _samples_:::
|
||||||
Set the maximum number of samples kept for this source. This overrides the
|
Set the maximum number of samples kept for this source. This overrides the
|
||||||
<<maxsamples,*maxsamples*>> directive.
|
<<maxsamples,*maxsamples*>> directive.
|
||||||
*filter* _samples_:::
|
*filter* _polls_:::
|
||||||
This option enables a median filter to reduce noise in NTP measurements. The
|
This option enables a median filter to reduce noise in NTP measurements. The
|
||||||
filter will reduce the specified number of samples to a single sample. It is
|
filter will process samples collected in the specified number of polls
|
||||||
intended to be used with very short polling intervals in local networks where
|
into a single sample. It is intended to be used with very short polling
|
||||||
it is acceptable to generate a lot of NTP traffic.
|
intervals in local networks where it is acceptable to generate a lot of NTP
|
||||||
|
traffic.
|
||||||
*offline*:::
|
*offline*:::
|
||||||
If the server will not be reachable when *chronyd* is started, the *offline*
|
If the server will not be reachable when *chronyd* is started, the *offline*
|
||||||
option can be specified. *chronyd* will not try to poll the server until it is
|
option can be specified. *chronyd* will not try to poll the server until it is
|
||||||
@@ -325,12 +351,6 @@ This option sets the desired number of sources to be used from the pool.
|
|||||||
sources responding to requests. The default value is 4 and the maximum value is
|
sources responding to requests. The default value is 4 and the maximum value is
|
||||||
16.
|
16.
|
||||||
+
|
+
|
||||||
{blank}::
|
|
||||||
When an NTP source is unreachable,
|
|
||||||
marked as a falseticker, or has a distance larger than the limit set by the
|
|
||||||
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
|
|
||||||
source with a newly resolved address of the name.
|
|
||||||
+
|
|
||||||
An example of the *pool* directive is
|
An example of the *pool* directive is
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
@@ -451,16 +471,41 @@ instead.
|
|||||||
Examples:
|
Examples:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
refclock PPS /dev/pps0 lock NMEA refid GPS
|
refclock PPS /dev/pps0 lock NMEA refid GPS1
|
||||||
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
|
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.2 refid NMEA noselect
|
||||||
refclock PPS /dev/pps1:clear refid GPS2
|
refclock PPS /dev/pps1:clear refid GPS2
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
|
*SOCK*:::
|
||||||
|
Unix domain socket driver. This driver uses a datagram socket to receive
|
||||||
|
samples from another application running on the system. The parameter is the
|
||||||
|
path to the socket, which *chronyd* will create on start. The format of the
|
||||||
|
messages is described in the _refclock_sock.c_ file in the chrony source code.
|
||||||
|
+
|
||||||
|
An application which supports the SOCK protocol is the *gpsd* daemon. It can
|
||||||
|
provide accurate measurements using the receiver's PPS signal, and since
|
||||||
|
version 3.25 also (much less accurate) measurements based on the timing of
|
||||||
|
serial data (e.g. NMEA), which can be useful when the receiver does not provide
|
||||||
|
a PPS signal, or it cannot be connected to the computer. The paths where *gpsd*
|
||||||
|
expects the sockets to be created by *chronyd* are described in the *gpsd(8)*
|
||||||
|
man page. Note that *gpsd* needs to be started after *chronyd* in order to
|
||||||
|
connect to the socket.
|
||||||
|
+
|
||||||
|
Examples:
|
||||||
|
+
|
||||||
|
----
|
||||||
|
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS1 poll 2 filter 4
|
||||||
|
refclock SOCK /var/run/chrony.clk.ttyUSB0.sock refid GPS2 offset 0.2 delay 0.1
|
||||||
|
----
|
||||||
|
+
|
||||||
*SHM*:::
|
*SHM*:::
|
||||||
NTP shared memory driver. This driver uses a shared memory segment to receive
|
NTP shared memory driver. This driver implements the protocol of the *ntpd*
|
||||||
samples from another process (e.g. *gpsd*). The parameter is the number of the
|
driver type 28. It is functionally similar to the SOCK driver, but uses a
|
||||||
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
|
shared memory segment instead of a socket. The parameter is the unit number,
|
||||||
supports the following option:
|
typically a small number like 0, 1, 2, or 3, from which is derived the key of
|
||||||
|
the memory segment as 0x4e545030 + unit.
|
||||||
|
+
|
||||||
|
The driver supports the following option:
|
||||||
+
|
+
|
||||||
*perm*=_mode_::::
|
*perm*=_mode_::::
|
||||||
This option specifies the permissions of the shared memory segment created by
|
This option specifies the permissions of the shared memory segment created by
|
||||||
@@ -468,6 +513,16 @@ This option specifies the permissions of the shared memory segment created by
|
|||||||
(read-write access for owner only).
|
(read-write access for owner only).
|
||||||
{blank}:::
|
{blank}:::
|
||||||
+
|
+
|
||||||
|
Unlike with the SOCK driver, there is no prescribed order of starting *chronyd*
|
||||||
|
and the program providing measurements. Both are expected to create the memory
|
||||||
|
segment if it does not exist. *chronyd* will attach to an existing segment even
|
||||||
|
if it has a different owner than root or different permissions than the
|
||||||
|
permissions specified by the *perm* option. The segment needs to be created
|
||||||
|
before untrusted applications or users can execute code to prevent an attacker
|
||||||
|
from feeding *chronyd* with false measurements. The owner and permissions of
|
||||||
|
the segment can be verified with the *ipcs -m* command. For this reason, the
|
||||||
|
SHM driver is deprecated in favor of SOCK.
|
||||||
|
+
|
||||||
Examples:
|
Examples:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
@@ -475,23 +530,6 @@ refclock SHM 0 poll 3 refid GPS1
|
|||||||
refclock SHM 1:perm=0644 refid GPS2
|
refclock SHM 1:perm=0644 refid GPS2
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
*SOCK*:::
|
|
||||||
Unix domain socket driver. It is similar to the SHM driver, but samples are
|
|
||||||
received from a Unix domain socket instead of shared memory and the messages
|
|
||||||
have a different format. The parameter is the path to the socket, which
|
|
||||||
*chronyd* creates on start. An advantage over the SHM driver is that SOCK does
|
|
||||||
not require polling and it can receive PPS samples with incomplete time. The
|
|
||||||
format of the messages is described in the _refclock_sock.c_ file in the chrony
|
|
||||||
source code.
|
|
||||||
+
|
|
||||||
An application which supports the SOCK protocol is the *gpsd* daemon. The path
|
|
||||||
where *gpsd* expects the socket to be created is described in the *gpsd(8)* man
|
|
||||||
page. For example:
|
|
||||||
+
|
|
||||||
----
|
|
||||||
refclock SOCK /var/run/chrony.ttyS0.sock
|
|
||||||
----
|
|
||||||
+
|
|
||||||
*PHC*:::
|
*PHC*:::
|
||||||
PTP hardware clock (PHC) driver. The parameter is the path to the device of
|
PTP hardware clock (PHC) driver. The parameter is the path to the device of
|
||||||
the PTP clock which should be used as a time source. If the clock is kept in
|
the PTP clock which should be used as a time source. If the clock is kept in
|
||||||
@@ -511,8 +549,10 @@ Note that some PTP clocks cannot be configured to timestamp only assert or
|
|||||||
clear events, and it is necessary to use the *width* option to filter wrong
|
clear events, and it is necessary to use the *width* option to filter wrong
|
||||||
PPS samples.
|
PPS samples.
|
||||||
*pin*=_index_::::
|
*pin*=_index_::::
|
||||||
This option specifies the index of the pin to which is connected the PPS
|
This option specifies the index of the pin which should be enabled for the
|
||||||
signal. The default value is 0.
|
PPS timestamping. If the PHC does not have configurable pins (i.e. the channel
|
||||||
|
function is fixed), the index needs to be set to -1 to disable the pin
|
||||||
|
configuration. The default value is 0.
|
||||||
*channel*=_index_::::
|
*channel*=_index_::::
|
||||||
This option specifies the index of the channel for the PPS mode. The default
|
This option specifies the index of the channel for the PPS mode. The default
|
||||||
value is 0.
|
value is 0.
|
||||||
@@ -600,7 +640,9 @@ noise in the measurements. With each poll about 40 percent of the stored
|
|||||||
samples are discarded and one final sample is calculated as an average of the
|
samples are discarded and one final sample is calculated as an average of the
|
||||||
remaining samples. If the length is 4 or more, at least 4 samples have to be
|
remaining samples. If the length is 4 or more, at least 4 samples have to be
|
||||||
collected between polls. For lengths below 4, the filter has to be full. The
|
collected between polls. For lengths below 4, the filter has to be full. The
|
||||||
default is 64.
|
default is 64. With drivers that perform their own polling (PPS, PHC, SHM), the
|
||||||
|
maximum value is adjusted to the number of driver polls per source poll, i.e.
|
||||||
|
2^(_poll_ - _dpoll_).
|
||||||
*prefer*:::
|
*prefer*:::
|
||||||
Prefer this source over sources without the prefer option.
|
Prefer this source over sources without the prefer option.
|
||||||
*noselect*:::
|
*noselect*:::
|
||||||
@@ -624,6 +666,13 @@ and that *chronyd* should correct its offset by the current TAI-UTC offset. The
|
|||||||
<<leapsectz,*leapsectz*>> directive must be used with this option and the
|
<<leapsectz,*leapsectz*>> directive must be used with this option and the
|
||||||
database must be kept up to date in order for this correction to work as
|
database must be kept up to date in order for this correction to work as
|
||||||
expected. This option does not make sense with PPS refclocks.
|
expected. This option does not make sense with PPS refclocks.
|
||||||
|
*local*:::
|
||||||
|
This option specifies that the reference clock is an unsynchronised clock which
|
||||||
|
is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
|
||||||
|
it should be used as a local standard to stabilise the system clock. The
|
||||||
|
refclock will bypass the source selection. There should be at most one refclock
|
||||||
|
specified with this option and it should have the shortest polling interval
|
||||||
|
among all configured sources.
|
||||||
*minsamples* _samples_:::
|
*minsamples* _samples_:::
|
||||||
Set the minimum number of samples kept for this source. This overrides the
|
Set the minimum number of samples kept for this source. This overrides the
|
||||||
<<minsamples,*minsamples*>> directive.
|
<<minsamples,*minsamples*>> directive.
|
||||||
@@ -778,6 +827,10 @@ This directory is used also by the <<ntsdumpdir2,NTS server>> to save keys.
|
|||||||
This directive specifies the maximum interval between NTS-KE handshakes (in
|
This directive specifies the maximum interval between NTS-KE handshakes (in
|
||||||
seconds) in order to refresh the keys authenticating NTP packets. The default
|
seconds) in order to refresh the keys authenticating NTP packets. The default
|
||||||
value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
|
value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
|
||||||
|
+
|
||||||
|
The interval must be longer than polling intervals of all configured NTP
|
||||||
|
sources using NTS, otherwise the source with a longer polling interval will
|
||||||
|
refresh the keys on each poll and no NTP packets will be exchanged.
|
||||||
|
|
||||||
[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
|
[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
|
||||||
This directive specifies a file or directory containing certificates (in the
|
This directive specifies a file or directory containing certificates (in the
|
||||||
@@ -831,6 +884,19 @@ This would disable the time checks until the clock is updated for the first
|
|||||||
time, assuming the first update corrects the clock and later checks can work
|
time, assuming the first update corrects the clock and later checks can work
|
||||||
with correct time.
|
with correct time.
|
||||||
|
|
||||||
|
[[refresh]]*refresh* _interval_::
|
||||||
|
This directive specifies the interval (in seconds) between refreshing IP
|
||||||
|
addresses of NTP sources specified by hostname. If the hostname no longer
|
||||||
|
resolves to the currently used address, it will be replaced with one of the new
|
||||||
|
addresses to avoid using a server which is no longer intended for service, even
|
||||||
|
if it is still responding correctly and would not be replaced as unreachable.
|
||||||
|
Only one source is refreshed at a time. The default value is 1209600 (2 weeks)
|
||||||
|
and the maximum value is 2^31-1 (68 years). A value of 0 disables the periodic
|
||||||
|
refreshment.
|
||||||
|
+
|
||||||
|
The <<chronyc.adoc#refresh,*refresh*>> command can be used to refresh all
|
||||||
|
sources immediately.
|
||||||
|
|
||||||
=== Source selection
|
=== Source selection
|
||||||
|
|
||||||
[[authselectmode]]*authselectmode* _mode_::
|
[[authselectmode]]*authselectmode* _mode_::
|
||||||
@@ -892,7 +958,7 @@ As an example, the following configuration using the default *mix* mode:
|
|||||||
server foo.example.net nts
|
server foo.example.net nts
|
||||||
server bar.example.net nts
|
server bar.example.net nts
|
||||||
server baz.example.net
|
server baz.example.net
|
||||||
refclock SHM 0
|
refclock SOCK /var/run/chrony.ttyS0.sock
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
is equivalent to the following configuration using the *ignore* mode:
|
is equivalent to the following configuration using the *ignore* mode:
|
||||||
@@ -902,7 +968,7 @@ authselectmode ignore
|
|||||||
server foo.example.net nts require trust
|
server foo.example.net nts require trust
|
||||||
server bar.example.net nts require trust
|
server bar.example.net nts require trust
|
||||||
server baz.example.net
|
server baz.example.net
|
||||||
refclock SHM 0 require trust
|
refclock /var/run/chrony.ttyS0.sock require trust
|
||||||
----
|
----
|
||||||
|
|
||||||
[[combinelimit]]*combinelimit* _limit_::
|
[[combinelimit]]*combinelimit* _limit_::
|
||||||
@@ -1099,7 +1165,11 @@ clock will be off for a longer time. On Linux with the default
|
|||||||
*ignore*:::
|
*ignore*:::
|
||||||
No correction is applied to the clock for the leap second. The clock will be
|
No correction is applied to the clock for the leap second. The clock will be
|
||||||
corrected later in normal operation when new measurements are made and the
|
corrected later in normal operation when new measurements are made and the
|
||||||
estimated offset includes the one second error.
|
estimated offset includes the one second error. This option is particularly
|
||||||
|
useful when multiple *chronyd* instances are running on the system, one
|
||||||
|
controlling the system clock and others started with the *-x* option, which
|
||||||
|
should rely on the first instance to correct the system clock and ignore it for
|
||||||
|
the correction of their own NTP clock running on top of the system clock.
|
||||||
{blank}::
|
{blank}::
|
||||||
+
|
+
|
||||||
When serving time to NTP clients that cannot be configured to correct their
|
When serving time to NTP clients that cannot be configured to correct their
|
||||||
@@ -1206,12 +1276,16 @@ This would step the system clock if the adjustment is larger than 0.1 seconds, b
|
|||||||
only in the first three clock updates.
|
only in the first three clock updates.
|
||||||
|
|
||||||
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
|
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
|
||||||
This directive sets the maximum allowed offset corrected on a clock update. The
|
This directive sets the maximum offset to be accepted on a clock update. The
|
||||||
check is performed only after the specified number of updates to allow a large
|
offset is measured relative to the current estimate of the true time, which is
|
||||||
initial adjustment of the system clock. When an offset larger than the
|
different from the system time if a previous slew did not finish.
|
||||||
specified maximum occurs, it will be ignored for the specified number of times
|
+
|
||||||
and then *chronyd* will give up and exit (a negative value can be used to never
|
The check is enabled after the specified number of clock updates to allow a
|
||||||
exit). In both cases a message is sent to syslog.
|
large initial offset to be corrected on start. Offsets larger than the
|
||||||
|
specified maximum will be ignored for the specified number of times. Another
|
||||||
|
large offset will cause *chronyd* to give up and exit. A negative value
|
||||||
|
can be used to disable the limit to ignore all large offsets. A syslog message
|
||||||
|
will be generated when an offset is ignored or it causes the exit.
|
||||||
+
|
+
|
||||||
An example of the use of this directive is:
|
An example of the use of this directive is:
|
||||||
+
|
+
|
||||||
@@ -1238,7 +1312,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
|
|||||||
system clock. It limits the frequency adjustment that *chronyd* is allowed to
|
system clock. It limits the frequency adjustment that *chronyd* is allowed to
|
||||||
use to correct the measured drift. It is an additional limit to the maximum
|
use to correct the measured drift. It is an additional limit to the maximum
|
||||||
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
|
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
|
||||||
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
|
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on illumos).
|
||||||
+
|
+
|
||||||
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
|
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
|
||||||
limited by the system driver rather than this directive.
|
limited by the system driver rather than this directive.
|
||||||
@@ -1263,10 +1337,9 @@ It should be noted that this is not the only means of protection against using
|
|||||||
unreliable estimates. At all times, *chronyd* keeps track of both the estimated
|
unreliable estimates. At all times, *chronyd* keeps track of both the estimated
|
||||||
gain or loss rate, and the error bound on the estimate. When a new estimate is
|
gain or loss rate, and the error bound on the estimate. When a new estimate is
|
||||||
generated following another measurement from one of the sources, a weighted
|
generated following another measurement from one of the sources, a weighted
|
||||||
combination algorithm is used to update the master estimate. So if *chronyd*
|
combination algorithm is used to update the existing estimate. If it has
|
||||||
has an existing highly-reliable master estimate and a new estimate is generated
|
significantly smaller error bounds than the new estimate, the existing estimate
|
||||||
which has large error bounds, the existing master estimate will dominate in the
|
will dominate in the new combined value.
|
||||||
new master estimate.
|
|
||||||
|
|
||||||
[[maxslewrate]]*maxslewrate* _rate-in-ppm_::
|
[[maxslewrate]]*maxslewrate* _rate-in-ppm_::
|
||||||
The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
|
The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
|
||||||
@@ -1277,7 +1350,7 @@ all supported systems with the exception of macOS 12 or earlier).
|
|||||||
+
|
+
|
||||||
For each system there is a maximum frequency offset of the clock that can be set
|
For each system there is a maximum frequency offset of the clock that can be set
|
||||||
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
|
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
|
||||||
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
|
is 5000 ppm, and on illumos it is 32500 ppm. Also, due to a kernel limitation,
|
||||||
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
|
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
|
||||||
ppm and 5000 ppm will effectively set it to 500 ppm.
|
ppm and 5000 ppm will effectively set it to 500 ppm.
|
||||||
+
|
+
|
||||||
@@ -1696,7 +1769,10 @@ save the keys to the _ntskeys_ file and will reload the keys from the file when
|
|||||||
the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
|
the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
|
||||||
be periodically copied from another server running *chronyd* (which does
|
be periodically copied from another server running *chronyd* (which does
|
||||||
not have *ntsrotate* set to 0) in order to have one or more servers dedicated
|
not have *ntsrotate* set to 0) in order to have one or more servers dedicated
|
||||||
to NTS-KE. The NTS-KE servers need to be configured with the
|
to NTS-KE. The file includes the subsequent key to which the NTS-KE server will
|
||||||
|
switch on the next rotation, i.e. the process copying and reloading the file
|
||||||
|
does not need to be timed precisely (it can be delayed by up to one rotation
|
||||||
|
interval). The NTS-KE servers need to be configured with the
|
||||||
<<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
|
<<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
|
||||||
server.
|
server.
|
||||||
+
|
+
|
||||||
@@ -2051,9 +2127,11 @@ from the example line above):
|
|||||||
. Stratum of remote computer. [2]
|
. Stratum of remote computer. [2]
|
||||||
. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
|
. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
|
||||||
. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
|
. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
|
||||||
. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio,
|
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
|
||||||
against defined parameters, and a test for synchronisation loop (1=pass,
|
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
|
||||||
0=fail) [1111]
|
0=fail). The first test from these four also checks the server precision,
|
||||||
|
response time, and whether an interleaved response is acceptable for
|
||||||
|
synchronisation. [1111]
|
||||||
. Local poll [10]
|
. Local poll [10]
|
||||||
. Remote poll [10]
|
. Remote poll [10]
|
||||||
. '`Score`' (an internal score within each polling level used to decide when to
|
. '`Score`' (an internal score within each polling level used to decide when to
|
||||||
@@ -2127,6 +2205,78 @@ from the example line above):
|
|||||||
the source is more variable than the delay of packets sent from the source
|
the source is more variable than the delay of packets sent from the source
|
||||||
back. [0.00, i.e. no correction for asymmetry]
|
back. [0.00, i.e. no correction for asymmetry]
|
||||||
+
|
+
|
||||||
|
*selection*:::
|
||||||
|
This option logs information about selection of sources for synchronisation to
|
||||||
|
a file called _selection.log_. Note that the rate of entries written to this
|
||||||
|
file grows quadratically with the number of specified sources (each measurement
|
||||||
|
triggers the selection for all sources). An example line (which actually
|
||||||
|
appears as a single line in the file) from the log file is shown below.
|
||||||
|
+
|
||||||
|
----
|
||||||
|
2022-05-01 02:01:20 203.0.113.15 * ----- 377 1.00 \
|
||||||
|
4.228e+01 -1.575e-04 1.239e-04
|
||||||
|
----
|
||||||
|
+
|
||||||
|
The columns are as follows (the quantities in square brackets are the values
|
||||||
|
from the example line above):
|
||||||
|
+
|
||||||
|
. Date [2022-05-01]
|
||||||
|
. Hour:Minute:Second. Note that the date-time pair is expressed in
|
||||||
|
UTC, not the local time zone. [02:01:20]
|
||||||
|
. IP address or reference ID of the source. [203.0.113.15]
|
||||||
|
. State of the source indicated with one of the following symbols. [*]
|
||||||
|
{blank}::::
|
||||||
|
Not considered selectable for synchronisation:
|
||||||
|
* _N_ - has the *noselect* option.
|
||||||
|
* _s_ - is not synchronised.
|
||||||
|
* _M_ - does not have enough measurements.
|
||||||
|
* _d_ - has a root distance larger than the maximum distance (configured by the
|
||||||
|
<<maxdistance,*maxdistance*>> directive).
|
||||||
|
* _~_ - has a jitter larger than the maximum jitter (configured by the
|
||||||
|
<<maxjitter,*maxjitter*>> directive).
|
||||||
|
* _w_ - waits for other sources to get out of the _M_ state.
|
||||||
|
* _S_ - has older measurements than other sources.
|
||||||
|
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
|
||||||
|
the <<local,*local*>> directive).
|
||||||
|
* _T_ - does not fully agree with sources that have the *trust* option.
|
||||||
|
* _x_ - does not agree with other sources (falseticker).
|
||||||
|
{blank}::::
|
||||||
|
Considered selectable for synchronisation, but not currently used:
|
||||||
|
* _W_ - waits for other sources to be selectable (required by the
|
||||||
|
<<minsources,*minsources*>> directive, or the *require* option of
|
||||||
|
another source).
|
||||||
|
* _P_ - another selectable source is preferred due to the *prefer* option.
|
||||||
|
* _U_ - waits for a new measurement (after selecting a different best source).
|
||||||
|
* _D_ - has, or recently had, a root distance which is too large to be combined
|
||||||
|
with other sources (configured by the <<combinelimit,*combinelimit*>>
|
||||||
|
directive).
|
||||||
|
{blank}::::
|
||||||
|
Used for synchronisation of the local clock:
|
||||||
|
* _+_ - combined with the best source.
|
||||||
|
* _*_ - selected as the best source to update the reference data (e.g. root
|
||||||
|
delay, root dispersion).
|
||||||
|
. Current effective selection options of the source. which can be different
|
||||||
|
from the configured options due to the authentication selection mode
|
||||||
|
(configured by the <<authselectmode,*authselectmode*>> directive). [-----]
|
||||||
|
* _N_ indicates the *noselect* option.
|
||||||
|
* _P_ indicates the *prefer* option.
|
||||||
|
* _T_ indicates the *trust* option.
|
||||||
|
* _R_ indicates the *require* option.
|
||||||
|
. Reachability register printed as an octal number. The register has 8 bits and
|
||||||
|
is updated on every received or missed packet from the source. A value of 377
|
||||||
|
indicates that a valid reply was received for all from the last eight
|
||||||
|
transmissions. [377]
|
||||||
|
. Current score against the source in the _*_ state. The scoring system avoids
|
||||||
|
frequent reselection when multiple sources have a similar root distance. A
|
||||||
|
value larger than 1 indicates this source was better than the _*_ source in
|
||||||
|
recent selections. If the score reaches 10, the best source will be reselected
|
||||||
|
and the scores will be reset to 1. [1.00]
|
||||||
|
. Interval since the last measurement of the source in seconds. [4.228e+01]
|
||||||
|
. Lower endpoint of the interval which was expected to contain the true offset
|
||||||
|
of the local clock determined by the root distance of the source. [-1.575e-04]
|
||||||
|
. Upper endpoint of the interval which was expected to contain the true offset
|
||||||
|
of the local clock determined by the root distance of the source. [1.239e-04]
|
||||||
|
+
|
||||||
*tracking*:::
|
*tracking*:::
|
||||||
This option logs changes to the estimate of the system's gain or loss rate, and
|
This option logs changes to the estimate of the system's gain or loss rate, and
|
||||||
any slews made, to a file called _tracking.log_. An example line (which
|
any slews made, to a file called _tracking.log_. An example line (which
|
||||||
@@ -2381,19 +2531,29 @@ be enabled by the *xleave* option in the <<server,*server*>> or the
|
|||||||
+
|
+
|
||||||
This directive is supported on Linux 3.19 and newer. The NIC must support HW
|
This directive is supported on Linux 3.19 and newer. The NIC must support HW
|
||||||
timestamping, which can be verified with the *ethtool -T* command. The list of
|
timestamping, which can be verified with the *ethtool -T* command. The list of
|
||||||
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
|
capabilities should include _hardware-raw-clock_, _hardware-transmit_, and
|
||||||
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
|
_hardware-receive_. The receive filter _all_, or _ntp_, is necessary for
|
||||||
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
|
timestamping of received NTP packets. Timestamping of packets received on
|
||||||
timestamping of received NTP packets. Timestamping of packets received on bridged
|
bridged and bonded interfaces is supported on Linux 4.13 and newer. If HW
|
||||||
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
|
timestamping does not work for received packets, *chronyd* will use kernel
|
||||||
running, no other process (e.g. a PTP daemon) should be working with the NIC
|
receive timestamps instead. Transmit-only HW timestamping can still be useful
|
||||||
clock.
|
to improve stability of the synchronisation.
|
||||||
|
+
|
||||||
|
*chronyd* does not synchronise the NIC clock. It assumes the clock is running
|
||||||
|
free. Multiple instances of *chronyd* can use the same interface with enabled
|
||||||
|
HW timestamping. Applications which need HW timestamping with a synchronised
|
||||||
|
clock (e.g. a PTP daemon) should use a virtual clock running on top of the
|
||||||
|
physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
|
||||||
|
feature is available on Linux 5.14 and newer.
|
||||||
+
|
+
|
||||||
If the kernel supports software timestamping, it will be enabled for all
|
If the kernel supports software timestamping, it will be enabled for all
|
||||||
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
|
interfaces automatically.
|
||||||
indicated in the _measurements.log_ file if enabled by the <<log,*log
|
+
|
||||||
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
|
The source of timestamps (i.e. hardware, kernel, or daemon) is indicated on the
|
||||||
*chronyc*.
|
client side in the _measurements.log_ file (if enabled by the <<log,*log*>>
|
||||||
|
directive) and the <<chronyc.adoc#ntpdata,*ntpdata*>> report. On the server
|
||||||
|
side, the number of served timestamps from each source is provided in the
|
||||||
|
<<chronyc.adoc#serverstats,*serverstats*>> report.
|
||||||
+
|
+
|
||||||
This directive can be used multiple times to enable HW timestamping on multiple
|
This directive can be used multiple times to enable HW timestamping on multiple
|
||||||
interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
|
interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
|
||||||
@@ -2403,10 +2563,15 @@ The *hwtimestamp* directive has the following options:
|
|||||||
+
|
+
|
||||||
*minpoll* _poll_:::
|
*minpoll* _poll_:::
|
||||||
This option specifies the minimum interval between readings of the NIC clock.
|
This option specifies the minimum interval between readings of the NIC clock.
|
||||||
It's defined as a power of two. It should correspond to the minimum polling
|
It's defined as a power of 2. It should correspond to the minimum polling
|
||||||
interval of all NTP sources and the minimum expected polling interval of NTP
|
interval of all NTP sources and the minimum expected polling interval of NTP
|
||||||
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
|
clients. The default value is 0 (1 second), the minimum value is -6 (1/64th
|
||||||
of a second).
|
of a second), and the maximum value is 20 (about 12 days).
|
||||||
|
*maxpoll* _poll_:::
|
||||||
|
This option specifies the maximum interval between readings of the NIC clock,
|
||||||
|
as a power of 2. The default value is *minpoll* + 1, i.e. 1 (2 seconds) with
|
||||||
|
the default *minpoll* of 0. The minimum and maximum values are the same as with
|
||||||
|
the *minpoll* option.
|
||||||
*minsamples* _samples_:::
|
*minsamples* _samples_:::
|
||||||
This option specifies the minimum number of readings kept for tracking of the
|
This option specifies the minimum number of readings kept for tracking of the
|
||||||
NIC clock. The default value is 2.
|
NIC clock. The default value is 2.
|
||||||
@@ -2445,7 +2610,8 @@ is selected by default. Some NICs can timestamp PTP packets only. By default,
|
|||||||
they will be configured with the _none_ filter and expected to provide hardware
|
they will be configured with the _none_ filter and expected to provide hardware
|
||||||
timestamps for transmitted packets only. Timestamping of PTP packets is useful
|
timestamps for transmitted packets only. Timestamping of PTP packets is useful
|
||||||
with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
|
with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
|
||||||
directive. Forcing timestamping of all packets with the _all_ filter could be
|
directive, or when another application is receiving PTP packets on the
|
||||||
|
interface. Forcing timestamping of all packets with the _all_ filter could be
|
||||||
useful if the NIC supported both the _all_ and _ntp_ filters, and it should
|
useful if the NIC supported both the _all_ and _ntp_ filters, and it should
|
||||||
timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
|
timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
|
||||||
{blank}::
|
{blank}::
|
||||||
@@ -2458,6 +2624,27 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
|
|||||||
hwtimestamp *
|
hwtimestamp *
|
||||||
----
|
----
|
||||||
|
|
||||||
|
[[hwtstimeout]]*hwtstimeout* _timeout_::
|
||||||
|
If hardware timestamping is used with a close NTP server, or the NIC or its
|
||||||
|
driver is slow in providing the transmit timestamp of NTP requests, a response
|
||||||
|
from the server can be received before the transmit timestamp of the request.
|
||||||
|
To avoid calculating the offset with a less accurate transmit timestamp,
|
||||||
|
*chronyd* can save the response for later processing and wait for the hardware
|
||||||
|
transmit timestamp. There is no guarantee that the timestamp will be provided
|
||||||
|
(NICs typically have a limited rate of transmit timestamping). This directive
|
||||||
|
configures how long should *chronyd* wait for the timestamp after receiving a
|
||||||
|
valid response from the server. If a second valid response is received from the
|
||||||
|
server while waiting for the timestamp, they will be both processed
|
||||||
|
immediately.
|
||||||
|
+
|
||||||
|
The default value is 0.001 seconds, which should be sufficient with most
|
||||||
|
hardware. If you frequently see kernel transmit timestamps in the
|
||||||
|
_measurements.log_ file or <<chronyc.adoc#ntpdata,*ntpdata*>> report, and it is
|
||||||
|
not a server handling a high rate of requests in the interleaved mode on the
|
||||||
|
same interface (which would compete with timestamping of the server's own
|
||||||
|
requests), increasing the timeout to 0.01 or possibly even longer might help.
|
||||||
|
Note that the maximum timeout is limited by the NTP polling interval.
|
||||||
|
|
||||||
[[keyfile]]*keyfile* _file_::
|
[[keyfile]]*keyfile* _file_::
|
||||||
This directive is used to specify the location of the file containing symmetric
|
This directive is used to specify the location of the file containing symmetric
|
||||||
keys which are shared between NTP servers and clients, or peers, in order to
|
keys which are shared between NTP servers and clients, or peers, in order to
|
||||||
@@ -2491,7 +2678,7 @@ The type is a name of a cryptographic hash function or cipher which is used to
|
|||||||
generate and verify the MAC. The default type is *MD5*, which is always
|
generate and verify the MAC. The default type is *MD5*, which is always
|
||||||
supported.
|
supported.
|
||||||
If *chronyd* was built with enabled support for hashing using a crypto library
|
If *chronyd* was built with enabled support for hashing using a crypto library
|
||||||
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
|
(Nettle, GnuTLS, NSS, or LibTomCrypt), the following functions are available: *MD5*,
|
||||||
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
|
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
|
||||||
*chronyd* using, some of the following hash functions and ciphers may
|
*chronyd* using, some of the following hash functions and ciphers may
|
||||||
also be available:
|
also be available:
|
||||||
@@ -2524,7 +2711,7 @@ file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
|
|||||||
[[lock_all]]*lock_all*::
|
[[lock_all]]*lock_all*::
|
||||||
The *lock_all* directive will lock the *chronyd* process into RAM so that it
|
The *lock_all* directive will lock the *chronyd* process into RAM so that it
|
||||||
will never be paged out. This can result in lower and more consistent latency.
|
will never be paged out. This can result in lower and more consistent latency.
|
||||||
The directive is supported on Linux, FreeBSD, NetBSD, and Solaris.
|
The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
|
||||||
|
|
||||||
[[pidfile]]*pidfile* _file_::
|
[[pidfile]]*pidfile* _file_::
|
||||||
Unless *chronyd* is started with the *-Q* option, it writes its process ID
|
Unless *chronyd* is started with the *-Q* option, it writes its process ID
|
||||||
@@ -2564,7 +2751,7 @@ ptpport 319
|
|||||||
----
|
----
|
||||||
|
|
||||||
[[sched_priority]]*sched_priority* _priority_::
|
[[sched_priority]]*sched_priority* _priority_::
|
||||||
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will
|
On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
|
||||||
select the SCHED_FIFO real-time scheduler at the specified priority (which must
|
select the SCHED_FIFO real-time scheduler at the specified priority (which must
|
||||||
be between 0 and 100). On macOS, this option must have either a value of 0 (the
|
be between 0 and 100). On macOS, this option must have either a value of 0 (the
|
||||||
default) to disable the thread time constraint policy or 1 for the policy to be
|
default) to disable the thread time constraint policy or 1 for the policy to be
|
||||||
@@ -2590,7 +2777,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
|
|||||||
switch after start in order to drop root privileges.
|
switch after start in order to drop root privileges.
|
||||||
+
|
+
|
||||||
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
||||||
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
|
On macOS, FreeBSD, NetBSD and illumos *chronyd* forks into two processes.
|
||||||
The child process retains root privileges, but can only perform a very limited
|
The child process retains root privileges, but can only perform a very limited
|
||||||
range of privileged system calls on behalf of the parent.
|
range of privileged system calls on behalf of the parent.
|
||||||
+
|
+
|
||||||
|
|||||||
120
doc/chronyc.adoc
120
doc/chronyc.adoc
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Copyright (C) Richard P. Curnow 1997-2003
|
// Copyright (C) Richard P. Curnow 1997-2003
|
||||||
// Copyright (C) Stephen Wadeley 2016
|
// Copyright (C) Stephen Wadeley 2016
|
||||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020
|
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2022
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -89,6 +89,10 @@ format. Reverse DNS lookups will be disabled, time will be printed as number of
|
|||||||
seconds since the epoch, and values in seconds will not be converted to other
|
seconds since the epoch, and values in seconds will not be converted to other
|
||||||
units.
|
units.
|
||||||
|
|
||||||
|
*-e*::
|
||||||
|
With this option each *chronyc* response will end with a line containing a
|
||||||
|
single dot.
|
||||||
|
|
||||||
*-d*::
|
*-d*::
|
||||||
This option enables printing of debugging messages if *chronyc* was compiled
|
This option enables printing of debugging messages if *chronyc* was compiled
|
||||||
with debugging support.
|
with debugging support.
|
||||||
@@ -99,12 +103,15 @@ With this option multiple commands can be specified. Each argument will be
|
|||||||
interpreted as a whole command.
|
interpreted as a whole command.
|
||||||
|
|
||||||
*-h* _host_::
|
*-h* _host_::
|
||||||
This option allows the user to specify which host (or comma-separated list of
|
This option specifies the host to be contacted by *chronyc*. It can be
|
||||||
addresses) running the *chronyd* program is to be contacted. This allows for
|
specified with a hostname, IP address, or path to the local Unix domain socket.
|
||||||
remote monitoring, without having to connect over SSH to the other host first.
|
Multiple values can be specified as a comma-separated list to provide a
|
||||||
|
fallback.
|
||||||
+
|
+
|
||||||
The default is to contact *chronyd* running on the same host where
|
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
|
||||||
*chronyc* is being run.
|
where *chronyc* is being run. First, it tries to connect to the Unix domain
|
||||||
|
socket and if that fails (e.g. due to running under a non-root user), it
|
||||||
|
will try to connect to 127.0.0.1 and then ::1.
|
||||||
|
|
||||||
*-p* _port_::
|
*-p* _port_::
|
||||||
This option allows the user to specify the UDP port number which the target
|
This option allows the user to specify the UDP port number which the target
|
||||||
@@ -177,15 +184,19 @@ stratum-2 and is synchronised from a stratum-1).
|
|||||||
This is the time (UTC) at which the last measurement from the reference
|
This is the time (UTC) at which the last measurement from the reference
|
||||||
source was processed.
|
source was processed.
|
||||||
*System time*:::
|
*System time*:::
|
||||||
In normal operation, *chronyd* by default never steps the system clock, because
|
This is the current offset between the NTP clock and system clock. The NTP
|
||||||
any jump in the time can have adverse consequences for certain application
|
clock is a software (virtual) clock maintained by *chronyd*, which is
|
||||||
programs. Instead, any error in the system clock is corrected by slightly
|
synchronised to the configured time sources and provides time to NTP clients.
|
||||||
speeding up or slowing down the system clock until the error has been removed,
|
The system clock is synchronised to the NTP clock. To avoid steps in the
|
||||||
and then returning to the system clock's normal speed. A consequence of this is
|
system time, which might have adverse consequences for certain applications,
|
||||||
that there will be a period when the system clock (as read by other programs)
|
the system clock is normally corrected only by speeding up or slowing down (up
|
||||||
will be different from *chronyd*'s estimate of the current true time (which it
|
to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
|
||||||
reports to NTP clients when it is operating as a server). The value reported
|
directive). If the offset is too large, this correction will take a very long
|
||||||
on this line is the difference due to this effect.
|
time. A step can be forced by the <<makestep,*makestep*>> command, or the
|
||||||
|
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
|
||||||
|
+
|
||||||
|
Note that all other offsets reported by *chronyc* and most offsets in the log
|
||||||
|
files are relative to the NTP clock, not the system clock.
|
||||||
*Last offset*:::
|
*Last offset*:::
|
||||||
This is the estimated local offset on the last clock update. A positive value
|
This is the estimated local offset on the last clock update. A positive value
|
||||||
indicates the local time (as previously estimated true time) was ahead of the
|
indicates the local time (as previously estimated true time) was ahead of the
|
||||||
@@ -448,6 +459,7 @@ states are reported.
|
|||||||
The following states indicate the source is not considered selectable for
|
The following states indicate the source is not considered selectable for
|
||||||
synchronisation:
|
synchronisation:
|
||||||
* _N_ - has the *noselect* option.
|
* _N_ - has the *noselect* option.
|
||||||
|
* _s_ - is not synchronised.
|
||||||
* _M_ - does not have enough measurements.
|
* _M_ - does not have enough measurements.
|
||||||
* _d_ - has a root distance larger than the maximum distance (configured by the
|
* _d_ - has a root distance larger than the maximum distance (configured by the
|
||||||
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
|
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
|
||||||
@@ -492,8 +504,8 @@ This column displays the configured selection options of the source.
|
|||||||
This column displays the current effective selection options of the source,
|
This column displays the current effective selection options of the source,
|
||||||
which can be different from the configured options due to the authentication
|
which can be different from the configured options due to the authentication
|
||||||
selection mode (configured by the
|
selection mode (configured by the
|
||||||
<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
|
<<chrony.conf.adoc#authselectmode,*authselectmode*>> directive). The symbols
|
||||||
same as in the *COpts* column.
|
are the same as in the *COpts* column.
|
||||||
*Last*:::
|
*Last*:::
|
||||||
This column displays how long ago was the last measurement of the source made
|
This column displays how long ago was the last measurement of the source made
|
||||||
when the selection was performed.
|
when the selection was performed.
|
||||||
@@ -514,6 +526,23 @@ This column displays the current leap status of the source.
|
|||||||
* _-_ indicates that a leap second will be deleted at the end of the month.
|
* _-_ indicates that a leap second will be deleted at the end of the month.
|
||||||
* _?_ indicates the unknown status (i.e. no valid measurement was made).
|
* _?_ indicates the unknown status (i.e. no valid measurement was made).
|
||||||
|
|
||||||
|
[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
|
||||||
|
The *selectopts* command modifies the configured selection options of an NTP
|
||||||
|
source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
|
||||||
|
unknown addresses), or a reference clock specified by reference ID as a string.
|
||||||
|
+
|
||||||
|
The selection options can be added with the *+* symbol or removed with the *-*
|
||||||
|
symbol. The *selectdata* command can be used to verify the configuration. The
|
||||||
|
modified options will be applied in the next source selection, e.g. when a new
|
||||||
|
measurement is made, or the *reselect* command is executed.
|
||||||
|
+
|
||||||
|
An example of using this command is shown below.
|
||||||
|
+
|
||||||
|
----
|
||||||
|
selectopts 1.2.3.4 -noselect +prefer
|
||||||
|
selectopts GPS +trust
|
||||||
|
----
|
||||||
|
|
||||||
[[reselect]]*reselect*::
|
[[reselect]]*reselect*::
|
||||||
To avoid excessive switching between sources, *chronyd* can stay synchronised
|
To avoid excessive switching between sources, *chronyd* can stay synchronised
|
||||||
to a source even when it is not currently the best one among the available
|
to a source even when it is not currently the best one among the available
|
||||||
@@ -602,6 +631,7 @@ be reported:
|
|||||||
* 13: AES128
|
* 13: AES128
|
||||||
* 14: AES256
|
* 14: AES256
|
||||||
* 15: AEAD-AES-SIV-CMAC-256
|
* 15: AEAD-AES-SIV-CMAC-256
|
||||||
|
* 30: AEAD-AES-128-GCM-SIV
|
||||||
*KLen*:::
|
*KLen*:::
|
||||||
This column shows the length of the key in bits.
|
This column shows the length of the key in bits.
|
||||||
*Last*:::
|
*Last*:::
|
||||||
@@ -658,6 +688,7 @@ RX timestamping : Kernel
|
|||||||
Total TX : 24
|
Total TX : 24
|
||||||
Total RX : 24
|
Total RX : 24
|
||||||
Total valid RX : 24
|
Total valid RX : 24
|
||||||
|
Total good RX : 22
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The fields are explained as follows:
|
The fields are explained as follows:
|
||||||
@@ -695,7 +726,8 @@ packets sent to the source is more variable than the delay of packets sent
|
|||||||
from the source back.
|
from the source back.
|
||||||
*NTP tests*:::
|
*NTP tests*:::
|
||||||
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
|
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
|
||||||
delay, delay ratio, delay dev ratio, and synchronisation loop.
|
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
|
||||||
|
loop.
|
||||||
*Interleaved*:::
|
*Interleaved*:::
|
||||||
This shows if the response was in the interleaved mode.
|
This shows if the response was in the interleaved mode.
|
||||||
*Authenticated*:::
|
*Authenticated*:::
|
||||||
@@ -710,7 +742,10 @@ The number of packets sent to the source.
|
|||||||
*Total RX*:::
|
*Total RX*:::
|
||||||
The number of all packets received from the source.
|
The number of all packets received from the source.
|
||||||
*Total valid RX*:::
|
*Total valid RX*:::
|
||||||
The number of valid packets received from the source.
|
The number of packets which passed the first two groups of NTP tests.
|
||||||
|
*Total good RX*:::
|
||||||
|
The number of packets which passed all three groups of NTP tests, i.e. the NTP
|
||||||
|
measurement was accepted.
|
||||||
|
|
||||||
[[add_peer]]*add peer* _name_ [_option_]...::
|
[[add_peer]]*add peer* _name_ [_option_]...::
|
||||||
The *add peer* command allows a new NTP peer to be added whilst
|
The *add peer* command allows a new NTP peer to be added whilst
|
||||||
@@ -935,12 +970,17 @@ current set of sources. It is equivalent to the *polltarget* option in the
|
|||||||
|
|
||||||
[[refresh]]*refresh*::
|
[[refresh]]*refresh*::
|
||||||
The *refresh* command can be used to force *chronyd* to resolve the names of
|
The *refresh* command can be used to force *chronyd* to resolve the names of
|
||||||
configured sources to IP addresses again, e.g. after suspending and resuming
|
configured NTP sources to IP addresses again and replace any addresses missing
|
||||||
the machine in a different network.
|
in the list of resolved addresses.
|
||||||
+
|
+
|
||||||
Sources that stop responding will be replaced with newly resolved addresses
|
Sources that stop responding are replaced with newly resolved addresses
|
||||||
automatically after 8 polling intervals, but this command can still be useful
|
automatically after 8 polling intervals. This command can be used to replace
|
||||||
to replace them immediately and not wait until they are marked as unreachable.
|
them immediately, e.g. after suspending and resuming the machine in a different
|
||||||
|
network.
|
||||||
|
+
|
||||||
|
Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
|
||||||
|
addresses are included in a single DNS response (e.g. pool.ntp.org), this
|
||||||
|
command might replace the addresses even if they are still in the pool.
|
||||||
|
|
||||||
[[reload]]*reload* *sources*::
|
[[reload]]*reload* *sources*::
|
||||||
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
|
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
|
||||||
@@ -1123,6 +1163,12 @@ Authenticated NTP packets : 189
|
|||||||
Interleaved NTP packets : 43
|
Interleaved NTP packets : 43
|
||||||
NTP timestamps held : 44
|
NTP timestamps held : 44
|
||||||
NTP timestamp span : 120
|
NTP timestamp span : 120
|
||||||
|
NTP daemon RX timestamps : 0
|
||||||
|
NTP daemon TX timestamps : 1537
|
||||||
|
NTP kernel RX timestamps : 1590
|
||||||
|
NTP kernel TX timestamps : 43
|
||||||
|
NTP hardware RX timestamps : 0
|
||||||
|
NTP hardware TX timestamps : 0
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The fields have the following meaning:
|
The fields have the following meaning:
|
||||||
@@ -1157,10 +1203,24 @@ The number of pairs of receive and transmit timestamps that the server is
|
|||||||
currently holding in memory for clients using the interleaved mode.
|
currently holding in memory for clients using the interleaved mode.
|
||||||
*NTP timestamp span*:::
|
*NTP timestamp span*:::
|
||||||
The interval (in seconds) covered by the currently held NTP timestamps.
|
The interval (in seconds) covered by the currently held NTP timestamps.
|
||||||
{blank}::
|
*NTP daemon RX timestamps*:::
|
||||||
+
|
The number of NTP responses which included a receive timestamp captured by the
|
||||||
Note that the numbers reported by this overflow to zero after 4294967295
|
daemon.
|
||||||
(32-bit values).
|
*NTP daemon TX timestamps*:::
|
||||||
|
The number of NTP responses which included a transmit timestamp captured by the
|
||||||
|
daemon.
|
||||||
|
*NTP kernel RX timestamps*:::
|
||||||
|
The number of NTP responses which included a receive timestamp captured by the
|
||||||
|
kernel.
|
||||||
|
*NTP kernel TX timestamps*:::
|
||||||
|
The number of NTP responses (in the interleaved mode) which included a transmit
|
||||||
|
timestamp captured by the kernel.
|
||||||
|
*NTP hardware RX timestamps*:::
|
||||||
|
The number of NTP responses which included a receive timestamp captured by the
|
||||||
|
NIC.
|
||||||
|
*NTP hardware TX timestamps*:::
|
||||||
|
The number of NTP responses (in the interleaved mode) which included a transmit
|
||||||
|
timestamp captured by the NIC.
|
||||||
|
|
||||||
[[allow]]*allow* [*all*] [_subnet_]::
|
[[allow]]*allow* [*all*] [_subnet_]::
|
||||||
The effect of the allow command is identical to the
|
The effect of the allow command is identical to the
|
||||||
@@ -1199,8 +1259,8 @@ deny all
|
|||||||
*local* *off*::
|
*local* *off*::
|
||||||
The *local* command allows *chronyd* to be told that it is to appear as a
|
The *local* command allows *chronyd* to be told that it is to appear as a
|
||||||
reference source, even if it is not itself properly synchronised to an external
|
reference source, even if it is not itself properly synchronised to an external
|
||||||
source. (This can be used on isolated networks, to allow one computer to be a
|
source. This can be used on isolated networks, to allow a computer to be the
|
||||||
master time server with the other computers slaving to it.)
|
primary time server for other computers.
|
||||||
+
|
+
|
||||||
The first form enables the local reference mode on the host. The syntax is
|
The first form enables the local reference mode on the host. The syntax is
|
||||||
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
|
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
|
||||||
|
|||||||
@@ -88,8 +88,10 @@ will not detach from the terminal.
|
|||||||
|
|
||||||
*-Q*::
|
*-Q*::
|
||||||
This option is similar to the *-q* option, except it only prints the offset
|
This option is similar to the *-q* option, except it only prints the offset
|
||||||
without making any corrections of the clock and it allows *chronyd* to be
|
without making any corrections of the clock and disables server ports to allow
|
||||||
started without root privileges.
|
*chronyd* to be started without root privileges, assuming the configuration
|
||||||
|
does not have any directives which would require them (e.g. *refclock*,
|
||||||
|
*hwtimestamp*, *rtcfile*, etc).
|
||||||
|
|
||||||
*-r*::
|
*-r*::
|
||||||
This option will try to reload and then delete files containing sample
|
This option will try to reload and then delete files containing sample
|
||||||
@@ -100,7 +102,7 @@ directive in the configuration file. This option is useful if you want to stop
|
|||||||
and restart *chronyd* briefly for any reason, e.g. to install a new version.
|
and restart *chronyd* briefly for any reason, e.g. to install a new version.
|
||||||
However, it should be used only on systems where the kernel can maintain clock
|
However, it should be used only on systems where the kernel can maintain clock
|
||||||
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
|
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
|
||||||
Solaris, and macOS 10.13 or later).
|
illumos, and macOS 10.13 or later).
|
||||||
|
|
||||||
*-R*::
|
*-R*::
|
||||||
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
|
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
|
||||||
@@ -141,7 +143,7 @@ after start in order to drop root privileges. It overrides the
|
|||||||
_@DEFAULT_USER@_.
|
_@DEFAULT_USER@_.
|
||||||
+
|
+
|
||||||
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
||||||
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
|
On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
|
||||||
The child process retains root privileges, but can only perform a very limited
|
The child process retains root privileges, but can only perform a very limited
|
||||||
range of privileged system calls on behalf of the parent.
|
range of privileged system calls on behalf of the parent.
|
||||||
|
|
||||||
@@ -181,7 +183,7 @@ limited.
|
|||||||
The filters cannot be enabled with the *mailonchange* directive.
|
The filters cannot be enabled with the *mailonchange* directive.
|
||||||
|
|
||||||
*-P* _priority_::
|
*-P* _priority_::
|
||||||
On Linux, FreeBSD, NetBSD, and Solaris, this option will select the SCHED_FIFO
|
On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
|
||||||
real-time scheduler at the specified priority (which must be between 0 and
|
real-time scheduler at the specified priority (which must be between 0 and
|
||||||
100). On macOS, this option must have either a value of 0 to disable the thread
|
100). On macOS, this option must have either a value of 0 to disable the thread
|
||||||
time constraint policy or 1 for the policy to be enabled. Other systems do not
|
time constraint policy or 1 for the policy to be enabled. Other systems do not
|
||||||
@@ -189,7 +191,7 @@ support this option. The default value is 0.
|
|||||||
|
|
||||||
*-m*::
|
*-m*::
|
||||||
This option will lock *chronyd* into RAM so that it will never be paged out.
|
This option will lock *chronyd* into RAM so that it will never be paged out.
|
||||||
This mode is only supported on Linux, FreeBSD, NetBSD, and Solaris.
|
This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
|
||||||
|
|
||||||
*-x*::
|
*-x*::
|
||||||
This option disables the control of the system clock. *chronyd* will not try to
|
This option disables the control of the system clock. *chronyd* will not try to
|
||||||
|
|||||||
155
doc/faq.adoc
155
doc/faq.adoc
@@ -1,7 +1,7 @@
|
|||||||
// This file is part of chrony
|
// This file is part of chrony
|
||||||
//
|
//
|
||||||
// Copyright (C) Richard P. Curnow 1997-2003
|
// Copyright (C) Richard P. Curnow 1997-2003
|
||||||
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021
|
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2022
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -232,7 +232,7 @@ authenticated servers should be configured as trusted and required to not allow
|
|||||||
the unauthenticated servers to override the authenticated servers in the source
|
the unauthenticated servers to override the authenticated servers in the source
|
||||||
selection. Since `chrony` version 4.0, the selection options are enabled in
|
selection. Since `chrony` version 4.0, the selection options are enabled in
|
||||||
such a case automatically. This behaviour can be disabled or modified by the
|
such a case automatically. This behaviour can be disabled or modified by the
|
||||||
`authselmode` directive.
|
`authselectmode` directive.
|
||||||
|
|
||||||
An example of a client configuration limiting the impact of the attacks could
|
An example of a client configuration limiting the impact of the attacks could
|
||||||
be
|
be
|
||||||
@@ -345,6 +345,15 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
|
|||||||
hwtimestamp eth0 minpoll -6
|
hwtimestamp eth0 minpoll -6
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Since `chrony` version 4.3, the minimum `minpoll` is -7 and a filter using a
|
||||||
|
long-term estimate of a delay quantile can be enabled by the `maxdelayquant`
|
||||||
|
option to replace the default `maxdelaydevratio` filter, which is sensitive to
|
||||||
|
outliers corrupting the minimum delay. For example:
|
||||||
|
|
||||||
|
----
|
||||||
|
server ntp.local minpoll -7 maxpoll -7 filter 31 maxdelayquant 0.3 xleave
|
||||||
|
----
|
||||||
|
|
||||||
As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
|
As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
|
||||||
extension field containing an additional timestamp to enable frequency transfer
|
extension field containing an additional timestamp to enable frequency transfer
|
||||||
and significantly improve stability of synchronisation. It can be enabled by
|
and significantly improve stability of synchronisation. It can be enabled by
|
||||||
@@ -475,6 +484,72 @@ identically configured leap-smearing servers. Note that some clients can get
|
|||||||
leap seconds from other sources (e.g. with the `leapsectz` directive in
|
leap seconds from other sources (e.g. with the `leapsectz` directive in
|
||||||
`chrony`) and they will not work correctly with a leap smearing server.
|
`chrony`) and they will not work correctly with a leap smearing server.
|
||||||
|
|
||||||
|
=== How should `chronyd` be configuration with `gpsd`?
|
||||||
|
|
||||||
|
A GPS or other GNSS receiver can be used as a reference clock with `gpsd`. It
|
||||||
|
can work as one or two separate time sources for each connected receiver. The
|
||||||
|
first time source is based on timestamping of messages sent by the receiver.
|
||||||
|
Typically, it is accurate to milliseconds. The other source is much more
|
||||||
|
accurate. It is timestamping a pulse-per-second (PPS) signal, usually connected
|
||||||
|
to a serial port (e.g. DCD pin) or GPIO pin.
|
||||||
|
|
||||||
|
If the PPS signal is connected to the serial port which is receiving messages
|
||||||
|
from the GPS/GNSS receiver, `gpsd` should detect and use it automatically. If
|
||||||
|
it is connected to a GPIO pin, or another serial port, the PPS device needs to
|
||||||
|
be specified on the command line as an additional data source. On Linux, the
|
||||||
|
`ldattach` utility can be used to create a PPS device for a serial device.
|
||||||
|
|
||||||
|
The PPS-based time source provided by `gpsd` is available as a `SHM 1`
|
||||||
|
refclock, or other odd number if `gpsd` is configured with multiple receivers,
|
||||||
|
and also as `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
|
||||||
|
serial device (e.g. ttyS0).
|
||||||
|
|
||||||
|
The message-based time source is available as a `SHM 0` refclock (or other even
|
||||||
|
number) and since `gpsd` version 3.25 also as
|
||||||
|
`SOCK /var/run/chrony.clk.DEV.sock` where `DEV` is the name of the serial
|
||||||
|
device.
|
||||||
|
|
||||||
|
The SOCK refclocks should be preferred over SHM for better security
|
||||||
|
(the shared memory segment needs to be created by `chronyd` or `gpsd` with an
|
||||||
|
expected owner and permissions before an untrusted application or user has a
|
||||||
|
chance to create its own in order to feed `chronyd` with false measurements).
|
||||||
|
`gpsd` needs to be started after `chronyd` in order to connect to the socket.
|
||||||
|
|
||||||
|
With `chronyd` and `gpsd` both supporting PPS, there are two different
|
||||||
|
recommended configurations:
|
||||||
|
|
||||||
|
----
|
||||||
|
# First option
|
||||||
|
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
|
||||||
|
|
||||||
|
# Second option
|
||||||
|
refclock PPS /dev/pps0 lock NMEA refid GPS
|
||||||
|
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid NMEA noselect
|
||||||
|
----
|
||||||
|
|
||||||
|
They both have some advantages:
|
||||||
|
|
||||||
|
* `SOCK` can be more accurate than `PPS` if `gpsd` corrects for the
|
||||||
|
sawtooth error provided by the receiver in serial data
|
||||||
|
* `PPS` can be used with higher PPS rates (specified by the `rate` option),
|
||||||
|
but it requires a second refclock or another time source to pair pulses
|
||||||
|
with seconds, and the `SOCK` offset needs to be specified
|
||||||
|
<<using-pps-refclock,correctly>> to compensate for the message delay, while
|
||||||
|
`gpsd` can apply HW-specific information
|
||||||
|
|
||||||
|
If the PPS signal is not available, or cannot be used for some reason, the only
|
||||||
|
option is the message-based timing
|
||||||
|
|
||||||
|
----
|
||||||
|
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid GPS
|
||||||
|
----
|
||||||
|
|
||||||
|
or the SHM equivalent if using `gpsd` version before 3.25
|
||||||
|
|
||||||
|
----
|
||||||
|
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
|
||||||
|
----
|
||||||
|
|
||||||
=== Does `chrony` support PTP?
|
=== Does `chrony` support PTP?
|
||||||
|
|
||||||
No, the Precision Time Protocol (PTP) is not supported as a protocol for
|
No, the Precision Time Protocol (PTP) is not supported as a protocol for
|
||||||
@@ -498,6 +573,27 @@ transport for NTP messages (NTP over PTP) to enable hardware timestamping on
|
|||||||
hardware which can timestamp PTP packets only. It can be enabled by the
|
hardware which can timestamp PTP packets only. It can be enabled by the
|
||||||
`ptpport` directive.
|
`ptpport` directive.
|
||||||
|
|
||||||
|
=== Why are client log records dropped before reaching `clientloglimit`?
|
||||||
|
|
||||||
|
The number of dropped client log records reported by the `serverstats` command
|
||||||
|
can be increasing before the number of clients reported by the `clients` command
|
||||||
|
reaches the maximum value corresponding to the memory limit set by the
|
||||||
|
`clientloglimit` directive.
|
||||||
|
|
||||||
|
This is due to the design of the data structure keeping the client records. It
|
||||||
|
is a hash table which can store only up to 16 colliding addresses per slot. If
|
||||||
|
a slot has more collisions and the table already has the maximum size, the
|
||||||
|
oldest record will be dropped and replaced by the new client.
|
||||||
|
|
||||||
|
Note that the size of the table is always a power of two and it can only grow.
|
||||||
|
The limit set by the `clientloglimit` directive takes into account that two
|
||||||
|
copies of the table exist when it is being resized. This means the actual
|
||||||
|
memory usage reported by `top` and other utilities can be significantly smaller
|
||||||
|
than the limit even when the maximum number of records is used.
|
||||||
|
|
||||||
|
The absolute maximum number of client records kept at the same time is
|
||||||
|
16777216.
|
||||||
|
|
||||||
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
||||||
|
|
||||||
They were removed in version 2.2. Authentication is no longer supported in the
|
They were removed in version 2.2. Authentication is no longer supported in the
|
||||||
@@ -517,8 +613,9 @@ following questions.
|
|||||||
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
|
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
|
||||||
is zero, it means `chronyd` did not get any valid responses from the NTP server
|
is zero, it means `chronyd` did not get any valid responses from the NTP server
|
||||||
you are trying to use. If there is a firewall between you and the server, the
|
you are trying to use. If there is a firewall between you and the server, the
|
||||||
packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
|
requests sent to the UDP port 123 of the server or responses sent back from
|
||||||
if you are getting any responses from the server.
|
the port might be blocked. Try using a tool like `wireshark` or `tcpdump` to
|
||||||
|
see if you are getting any responses from the server.
|
||||||
|
|
||||||
When `chronyd` is receiving responses from the servers, the output of the
|
When `chronyd` is receiving responses from the servers, the output of the
|
||||||
`sources` command issued few minutes after `chronyd` start might look like
|
`sources` command issued few minutes after `chronyd` start might look like
|
||||||
@@ -624,6 +721,18 @@ was not shut down for too long and the server's certificate was not renewed too
|
|||||||
close to its expiration, it should be sufficient for the time checks to
|
close to its expiration, it should be sufficient for the time checks to
|
||||||
succeed.
|
succeed.
|
||||||
|
|
||||||
|
If you run your own server, you can use a self-signed certificate covering
|
||||||
|
all dates where the client can start (e.g. years 1970-2100). The certificate
|
||||||
|
needs to be installed on the client and specified with the `ntstrustedcerts`
|
||||||
|
directive. The server can have multiple names and certificates. To avoid
|
||||||
|
trusting a certificate for too long, a new certificate can be added to the
|
||||||
|
server periodically (e.g. once per year) and the client can have the server
|
||||||
|
name and trusted certificate updated automatically (e.g. using a package
|
||||||
|
repository, or a cron script downloading the files directly from the server
|
||||||
|
over HTTPS). A client that was shut down for years will still be able to
|
||||||
|
synchronise its clock and perform the update as long as the server keeps
|
||||||
|
the old certificate.
|
||||||
|
|
||||||
As a last resort, you can disable the time checks by the `nocerttimecheck`
|
As a last resort, you can disable the time checks by the `nocerttimecheck`
|
||||||
directive. This has some important security implications. To reduce the
|
directive. This has some important security implications. To reduce the
|
||||||
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
|
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
|
||||||
@@ -692,12 +801,14 @@ frequently, you can effectively disable the test by setting the
|
|||||||
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
|
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
|
||||||
recovery by increasing the clock error rate with the `maxclockerror` directive.
|
recovery by increasing the clock error rate with the `maxclockerror` directive.
|
||||||
|
|
||||||
|
[[using-pps-refclock]]
|
||||||
=== Using a PPS reference clock?
|
=== Using a PPS reference clock?
|
||||||
|
|
||||||
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
||||||
determine which second of UTC corresponds to each pulse. If it is another
|
determine which second of UTC corresponds to each pulse. If it is another
|
||||||
reference clock specified with the `lock` option in the `refclock` directive,
|
reference clock specified with the `lock` option in the `refclock` directive,
|
||||||
the offset between the two reference clocks must be smaller than 0.2 seconds in
|
the offset between the two reference clocks must be smaller than 0.4 seconds
|
||||||
|
(0.2 seconds with `chrony` versions before 4.1) in
|
||||||
order for the PPS reference clock to work. With NMEA reference clocks it is
|
order for the PPS reference clock to work. With NMEA reference clocks it is
|
||||||
common to have a larger offset. It needs to be corrected with the `offset`
|
common to have a larger offset. It needs to be corrected with the `offset`
|
||||||
option.
|
option.
|
||||||
@@ -716,7 +827,7 @@ foo.example.net 7 3 200 -2.991 16.141 -107us 492us
|
|||||||
|
|
||||||
the offset of the NMEA source would need to be increased by about 0.504
|
the offset of the NMEA source would need to be increased by about 0.504
|
||||||
seconds. It does not have to be very accurate. As long as the offset of the
|
seconds. It does not have to be very accurate. As long as the offset of the
|
||||||
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
|
NMEA reference clock stays below the limit, the PPS reference clock should be
|
||||||
able to determine the seconds corresponding to the pulses and allow the samples
|
able to determine the seconds corresponding to the pulses and allow the samples
|
||||||
to be used for synchronisation.
|
to be used for synchronisation.
|
||||||
|
|
||||||
@@ -772,6 +883,10 @@ in parentheses) on the `Reference ID` line.
|
|||||||
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
|
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
|
||||||
(`chronyc` side).
|
(`chronyc` side).
|
||||||
|
|
||||||
|
Note that this protocol is not compatible with the mode 6 or mode 7 protocol
|
||||||
|
supported by `ntpd`, i.e. the `ntpq` or `ntpdc` utility cannot be used to
|
||||||
|
monitor `chronyd`, and `chronyc` cannot be used to monitor `ntpd`.
|
||||||
|
|
||||||
== Real-time clock issues
|
== Real-time clock issues
|
||||||
|
|
||||||
=== What is the real-time clock (RTC)?
|
=== What is the real-time clock (RTC)?
|
||||||
@@ -898,6 +1013,34 @@ timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
|
|||||||
synchronisation of its own clock, which will cause the other computer to
|
synchronisation of its own clock, which will cause the other computer to
|
||||||
measure a significant offset.
|
measure a significant offset.
|
||||||
|
|
||||||
|
== Operation
|
||||||
|
|
||||||
|
=== What clocks does `chronyd` use?
|
||||||
|
|
||||||
|
There are several different clocks used by `chronyd`:
|
||||||
|
|
||||||
|
* *System clock:* software clock maintained by the kernel. It is the main clock
|
||||||
|
used by applications running on the computer. It is synchronised by `chronyd`
|
||||||
|
to its NTP clock, unless started with the *-x* option.
|
||||||
|
* *NTP clock:* software clock (virtual) based on the system clock and internal
|
||||||
|
to `chronyd`. It keeps the best estimate of the true time according to the
|
||||||
|
configured time sources, which is served to NTP clients unless time smoothing
|
||||||
|
is enabled by the *smoothtime* directive. The *System time* value in the
|
||||||
|
`tracking` report is the current offset between the system and NTP clock.
|
||||||
|
* *Real-time clock (RTC):* hardware clock keeping time even when the
|
||||||
|
computer is turned off. It is used by the kernel to initialise the system
|
||||||
|
clock on boot and also by `chronyd` to compensate for its measured drift if
|
||||||
|
configured with the `rtcfile` directive and started with the `-s` option.
|
||||||
|
The clock can be kept accurate only by stepping enabled by the `rtcsync` or
|
||||||
|
`rtcautotrim` directive.
|
||||||
|
* *Reference clock:* hardware clock used as a time source. It is specified by
|
||||||
|
the `refclock` directive.
|
||||||
|
* *NIC clock (also known as PTP hardware clock):* hardware clock timestamping
|
||||||
|
packets received and transmitted by a network device specified by the
|
||||||
|
*hwtimestamp* directive. The clock is expected to be running free. It is not
|
||||||
|
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
|
||||||
|
order to convert the hardware timestamps.
|
||||||
|
|
||||||
== Operating systems
|
== Operating systems
|
||||||
|
|
||||||
=== Does `chrony` support Windows?
|
=== Does `chrony` support Windows?
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ The following libraries with their development files, and programs, are needed
|
|||||||
to enable optional features:
|
to enable optional features:
|
||||||
|
|
||||||
* pkg-config: detection of development libraries
|
* pkg-config: detection of development libraries
|
||||||
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
|
* Nettle, GnuTLS, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
|
||||||
* libcap: dropping root privileges on Linux (`DROPROOT`)
|
* libcap: dropping root privileges on Linux (`DROPROOT`)
|
||||||
* libseccomp: system call filter on Linux (`SCFILTER`)
|
* libseccomp: system call filter on Linux (`SCFILTER`)
|
||||||
* GnuTLS and Nettle: Network Time Security (`NTS`)
|
* GnuTLS and Nettle: Network Time Security (`NTS`)
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ LockPersonality=yes
|
|||||||
MemoryDenyWriteExecute=yes
|
MemoryDenyWriteExecute=yes
|
||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
PrivateUsers=yes
|
PrivateUsers=yes
|
||||||
ProcSubset=pid
|
|
||||||
ProtectClock=yes
|
ProtectClock=yes
|
||||||
ProtectControlGroups=yes
|
ProtectControlGroups=yes
|
||||||
ProtectHome=yes
|
ProtectHome=yes
|
||||||
|
|||||||
@@ -11,3 +11,5 @@
|
|||||||
#1 MD5 AVeryLongAndRandomPassword
|
#1 MD5 AVeryLongAndRandomPassword
|
||||||
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
|
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
|
||||||
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
|
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
|
||||||
|
#4 AES128 HEX:2DA837C4B6573748CA692B8C828E4891
|
||||||
|
#5 AES256 HEX:2666B8099BFF2D5BA20876121788ED24D2BE59111B8FFB562F0F56AE6EC7246E
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# This is a NetworkManager dispatcher script for chronyd to update
|
# This is a NetworkManager dispatcher script for chronyd to update
|
||||||
# its NTP sources passed from DHCP options. Note that this script is
|
# its NTP sources with servers from DHCP options passed by NetworkManager
|
||||||
# specific to NetworkManager-dispatcher due to use of the
|
# in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
|
||||||
# DHCP4_NTP_SERVERS environment variable.
|
|
||||||
|
|
||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
|
|
||||||
@@ -10,17 +9,23 @@ interface=$1
|
|||||||
action=$2
|
action=$2
|
||||||
|
|
||||||
chronyc=/usr/bin/chronyc
|
chronyc=/usr/bin/chronyc
|
||||||
default_server_options=iburst
|
server_options=iburst
|
||||||
server_dir=/var/run/chrony-dhcp
|
server_dir=/var/run/chrony-dhcp
|
||||||
|
|
||||||
dhcp_server_file=$server_dir/$interface.sources
|
dhcp_server_file=$server_dir/$interface.sources
|
||||||
# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager.
|
dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
|
||||||
nm_dhcp_servers=$DHCP4_NTP_SERVERS
|
|
||||||
|
|
||||||
add_servers_from_dhcp() {
|
add_servers_from_dhcp() {
|
||||||
rm -f "$dhcp_server_file"
|
rm -f "$dhcp_server_file"
|
||||||
for server in $nm_dhcp_servers; do
|
for server in $dhcp_ntp_servers; do
|
||||||
echo "server $server $default_server_options" >> "$dhcp_server_file"
|
# Check for invalid characters (from the DHCPv6 NTP FQDN suboption)
|
||||||
|
len1=$(printf '%s' "$server" | wc -c)
|
||||||
|
len2=$(printf '%s' "$server" | tr -d -c 'A-Za-z0-9:.-' | wc -c)
|
||||||
|
if [ "$len1" -ne "$len2" ] || [ "$len2" -lt 1 ] || [ "$len2" -gt 255 ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'server %s %s\n' "$server" "$server_options" >> "$dhcp_server_file"
|
||||||
done
|
done
|
||||||
$chronyc reload sources > /dev/null 2>&1 || :
|
$chronyc reload sources > /dev/null 2>&1 || :
|
||||||
}
|
}
|
||||||
@@ -34,10 +39,11 @@ clear_servers_from_dhcp() {
|
|||||||
|
|
||||||
mkdir -p $server_dir
|
mkdir -p $server_dir
|
||||||
|
|
||||||
if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then
|
case "$action" in
|
||||||
add_servers_from_dhcp
|
up|dhcp4-change|dhcp6-change)
|
||||||
elif [ "$action" = "down" ]; then
|
add_servers_from_dhcp;;
|
||||||
clear_servers_from_dhcp
|
down)
|
||||||
fi
|
clear_servers_from_dhcp;;
|
||||||
|
esac
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -7,8 +7,20 @@ export LC_ALL=C
|
|||||||
|
|
||||||
chronyc=/usr/bin/chronyc
|
chronyc=/usr/bin/chronyc
|
||||||
|
|
||||||
# For NetworkManager consider only up/down events
|
# For NetworkManager consider only selected events
|
||||||
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
|
if [ $# -ge 2 ]; then
|
||||||
|
case "$2" in
|
||||||
|
up|down|connectivity-change)
|
||||||
|
;;
|
||||||
|
dhcp4-change|dhcp6-change)
|
||||||
|
# Actions "up" and "connectivity-change" in some cases do not
|
||||||
|
# guarantee that the interface has a route (e.g. a bond).
|
||||||
|
# dhcp(x)-change handles at least cases that use DHCP.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
|
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
|
||||||
|
|
||||||
|
|||||||
58
examples/chronyd-restricted.service
Normal file
58
examples/chronyd-restricted.service
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# This is a more restricted version of the chronyd service intended for
|
||||||
|
# minimal NTP/NTS client configurations. The daemon is started without root
|
||||||
|
# privileges and is allowed to write only to its own runtime, state, and log
|
||||||
|
# directories. It cannot bind to privileged ports in order to operate as an
|
||||||
|
# NTP server, or provide monitoring access over IPv4/IPv6. It cannot use
|
||||||
|
# reference clocks, HW timestamping, RTC tracking, and other features.
|
||||||
|
[Unit]
|
||||||
|
Description=NTP client (restricted)
|
||||||
|
Documentation=man:chronyd(8) man:chrony.conf(5)
|
||||||
|
After=chronyd.service ntpdate.service sntp.service ntpd.service
|
||||||
|
Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
|
||||||
|
ConditionCapability=CAP_SYS_TIME
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=forking
|
||||||
|
PIDFile=/run/chrony/chronyd.pid
|
||||||
|
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||||
|
ExecStart=/usr/sbin/chronyd -U $OPTIONS
|
||||||
|
|
||||||
|
User=chrony
|
||||||
|
LogsDirectory=chrony
|
||||||
|
LogsDirectoryMode=0750
|
||||||
|
RuntimeDirectory=chrony
|
||||||
|
RuntimeDirectoryMode=0750
|
||||||
|
RuntimeDirectoryPreserve=restart
|
||||||
|
StateDirectory=chrony
|
||||||
|
StateDirectoryMode=0750
|
||||||
|
|
||||||
|
AmbientCapabilities=CAP_SYS_TIME
|
||||||
|
CapabilityBoundingSet=CAP_SYS_TIME
|
||||||
|
DevicePolicy=closed
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
# This breaks adjtimex()
|
||||||
|
#PrivateUsers=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectHostname=yes
|
||||||
|
ProtectKernelLogs=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProtectSystem=strict
|
||||||
|
RemoveIPC=yes
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io
|
||||||
|
SystemCallFilter=~@reboot @resources @swap
|
||||||
|
UMask=0077
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -24,7 +24,6 @@ LockPersonality=yes
|
|||||||
MemoryDenyWriteExecute=yes
|
MemoryDenyWriteExecute=yes
|
||||||
NoNewPrivileges=yes
|
NoNewPrivileges=yes
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
ProcSubset=pid
|
|
||||||
ProtectControlGroups=yes
|
ProtectControlGroups=yes
|
||||||
ProtectHome=yes
|
ProtectHome=yes
|
||||||
ProtectHostname=yes
|
ProtectHostname=yes
|
||||||
|
|||||||
19
getdate.y
19
getdate.y
@@ -448,9 +448,9 @@ o_merid : /* NULL */
|
|||||||
the same signature as the function definition does. */
|
the same signature as the function definition does. */
|
||||||
#include "getdate.h"
|
#include "getdate.h"
|
||||||
|
|
||||||
extern struct tm *gmtime ();
|
extern struct tm *gmtime (const time_t *timep);
|
||||||
extern struct tm *localtime ();
|
extern struct tm *localtime (const time_t *timep);
|
||||||
extern time_t mktime ();
|
extern time_t mktime (struct tm *tm);
|
||||||
|
|
||||||
/* Month and day table. */
|
/* Month and day table. */
|
||||||
static TABLE const MonthDayTable[] = {
|
static TABLE const MonthDayTable[] = {
|
||||||
@@ -641,16 +641,13 @@ static TABLE const MilitaryTable[] = {
|
|||||||
|
|
||||||
/* ARGSUSED */
|
/* ARGSUSED */
|
||||||
static int
|
static int
|
||||||
yyerror (s)
|
yyerror (char *s ATTRIBUTE_UNUSED)
|
||||||
char *s ATTRIBUTE_UNUSED;
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ToHour (Hours, Meridian)
|
ToHour (int Hours, MERIDIAN Meridian)
|
||||||
int Hours;
|
|
||||||
MERIDIAN Meridian;
|
|
||||||
{
|
{
|
||||||
switch (Meridian)
|
switch (Meridian)
|
||||||
{
|
{
|
||||||
@@ -677,8 +674,7 @@ ToHour (Hours, Meridian)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ToYear (Year)
|
ToYear (int Year)
|
||||||
int Year;
|
|
||||||
{
|
{
|
||||||
if (Year < 0)
|
if (Year < 0)
|
||||||
Year = -Year;
|
Year = -Year;
|
||||||
@@ -694,8 +690,7 @@ ToYear (Year)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
LookupWord (buff)
|
LookupWord (char *buff)
|
||||||
char *buff;
|
|
||||||
{
|
{
|
||||||
register char *p;
|
register char *p;
|
||||||
register char *q;
|
register char *q;
|
||||||
|
|||||||
111
hwclock.c
111
hwclock.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
* Copyright (C) Miroslav Lichvar 2016-2018, 2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "local.h"
|
#include "local.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "quantiles.h"
|
||||||
#include "regress.h"
|
#include "regress.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -43,6 +44,13 @@
|
|||||||
/* Maximum acceptable frequency offset of the clock */
|
/* Maximum acceptable frequency offset of the clock */
|
||||||
#define MAX_FREQ_OFFSET (2.0 / 3.0)
|
#define MAX_FREQ_OFFSET (2.0 / 3.0)
|
||||||
|
|
||||||
|
/* Quantiles for filtering readings by delay */
|
||||||
|
#define DELAY_QUANT_MIN_K 1
|
||||||
|
#define DELAY_QUANT_MAX_K 2
|
||||||
|
#define DELAY_QUANT_Q 10
|
||||||
|
#define DELAY_QUANT_REPEAT 7
|
||||||
|
#define DELAY_QUANT_MIN_STEP 1.0e-9
|
||||||
|
|
||||||
struct HCL_Instance_Record {
|
struct HCL_Instance_Record {
|
||||||
/* HW and local reference timestamp */
|
/* HW and local reference timestamp */
|
||||||
struct timespec hw_ref;
|
struct timespec hw_ref;
|
||||||
@@ -64,12 +72,18 @@ struct HCL_Instance_Record {
|
|||||||
/* Minimum interval between samples */
|
/* Minimum interval between samples */
|
||||||
double min_separation;
|
double min_separation;
|
||||||
|
|
||||||
|
/* Expected precision of readings */
|
||||||
|
double precision;
|
||||||
|
|
||||||
/* Flag indicating the offset and frequency values are valid */
|
/* Flag indicating the offset and frequency values are valid */
|
||||||
int valid_coefs;
|
int valid_coefs;
|
||||||
|
|
||||||
/* Estimated offset and frequency of HW clock relative to local clock */
|
/* Estimated offset and frequency of HW clock relative to local clock */
|
||||||
double offset;
|
double offset;
|
||||||
double frequency;
|
double frequency;
|
||||||
|
|
||||||
|
/* Estimated quantiles of reading delay */
|
||||||
|
QNT_Instance delay_quants;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -92,7 +106,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
HCL_Instance
|
HCL_Instance
|
||||||
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
HCL_CreateInstance(int min_samples, int max_samples, double min_separation, double precision)
|
||||||
{
|
{
|
||||||
HCL_Instance clock;
|
HCL_Instance clock;
|
||||||
|
|
||||||
@@ -110,6 +124,10 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
|||||||
clock->n_samples = 0;
|
clock->n_samples = 0;
|
||||||
clock->valid_coefs = 0;
|
clock->valid_coefs = 0;
|
||||||
clock->min_separation = min_separation;
|
clock->min_separation = min_separation;
|
||||||
|
clock->precision = precision;
|
||||||
|
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
|
||||||
|
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
|
||||||
|
DELAY_QUANT_MIN_STEP);
|
||||||
|
|
||||||
LCL_AddParameterChangeHandler(handle_slew, clock);
|
LCL_AddParameterChangeHandler(handle_slew, clock);
|
||||||
|
|
||||||
@@ -121,6 +139,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
|||||||
void HCL_DestroyInstance(HCL_Instance clock)
|
void HCL_DestroyInstance(HCL_Instance clock)
|
||||||
{
|
{
|
||||||
LCL_RemoveParameterChangeHandler(handle_slew, clock);
|
LCL_RemoveParameterChangeHandler(handle_slew, clock);
|
||||||
|
QNT_DestroyInstance(clock->delay_quants);
|
||||||
Free(clock->y_data);
|
Free(clock->y_data);
|
||||||
Free(clock->x_data);
|
Free(clock->x_data);
|
||||||
Free(clock);
|
Free(clock);
|
||||||
@@ -140,6 +159,94 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||||
|
struct timespec *hw_ts, struct timespec *local_ts, double *err)
|
||||||
|
{
|
||||||
|
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
|
||||||
|
double delay_sum, hw_sum, local_sum, local_prec, freq;
|
||||||
|
int i, min_reading, combined;
|
||||||
|
struct timespec ts1, ts2;
|
||||||
|
|
||||||
|
if (n_readings < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Work out the current correction multiplier needed to get cooked delays */
|
||||||
|
LCL_CookTime(&tss[0][0], &ts1, NULL);
|
||||||
|
LCL_CookTime(&tss[n_readings - 1][2], &ts2, NULL);
|
||||||
|
if (UTI_CompareTimespecs(&tss[0][0], &tss[n_readings - 1][2]) < 0)
|
||||||
|
freq = UTI_DiffTimespecsToDouble(&ts1, &ts2) /
|
||||||
|
UTI_DiffTimespecsToDouble(&tss[0][0], &tss[n_readings - 1][2]);
|
||||||
|
else
|
||||||
|
freq = 1.0;
|
||||||
|
|
||||||
|
for (i = 0; i < n_readings; i++) {
|
||||||
|
delay = freq * UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
|
||||||
|
|
||||||
|
if (delay < 0.0) {
|
||||||
|
/* Step in the middle of a reading? */
|
||||||
|
DEBUG_LOG("Bad reading delay=%e", delay);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || min_delay > delay) {
|
||||||
|
min_delay = delay;
|
||||||
|
min_reading = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNT_Accumulate(clock->delay_quants, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_prec = LCL_GetSysPrecisionAsQuantum();
|
||||||
|
|
||||||
|
low_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MIN_K);
|
||||||
|
high_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MAX_K);
|
||||||
|
low_delay = MIN(low_delay, high_delay);
|
||||||
|
high_delay = MAX(high_delay, low_delay + local_prec);
|
||||||
|
|
||||||
|
/* Combine readings with delay in the expected interval */
|
||||||
|
for (i = combined = 0, delay_sum = hw_sum = local_sum = 0.0; i < n_readings; i++) {
|
||||||
|
raw_delay = UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
|
||||||
|
delay = freq * raw_delay;
|
||||||
|
|
||||||
|
if (delay < low_delay || delay > high_delay)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
delay_sum += delay;
|
||||||
|
hw_sum += UTI_DiffTimespecsToDouble(&tss[i][1], &tss[0][1]);
|
||||||
|
local_sum += UTI_DiffTimespecsToDouble(&tss[i][0], &tss[0][0]) + raw_delay / 2.0;
|
||||||
|
combined++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Combined %d readings lo=%e hi=%e", combined, low_delay, high_delay);
|
||||||
|
|
||||||
|
if (combined > 0) {
|
||||||
|
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
|
||||||
|
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
|
||||||
|
*err = MAX(delay_sum / combined / 2.0, clock->precision);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accept the reading with minimum delay if its interval does not contain
|
||||||
|
the current offset predicted from previous samples */
|
||||||
|
|
||||||
|
*hw_ts = tss[min_reading][1];
|
||||||
|
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
|
||||||
|
*err = MAX(min_delay / 2.0, clock->precision);
|
||||||
|
|
||||||
|
pred_err = 0.0;
|
||||||
|
LCL_CookTime(local_ts, &ts1, NULL);
|
||||||
|
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
|
||||||
|
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
|
||||||
|
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||||
struct timespec *local_ts, double err)
|
struct timespec *local_ts, double err)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ typedef struct HCL_Instance_Record *HCL_Instance;
|
|||||||
|
|
||||||
/* Create a new HW clock instance */
|
/* Create a new HW clock instance */
|
||||||
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
|
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
|
||||||
double min_separation);
|
double min_separation, double precision);
|
||||||
|
|
||||||
/* Destroy a HW clock instance */
|
/* Destroy a HW clock instance */
|
||||||
extern void HCL_DestroyInstance(HCL_Instance clock);
|
extern void HCL_DestroyInstance(HCL_Instance clock);
|
||||||
@@ -38,6 +38,11 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
|
|||||||
/* Check if a new sample should be accumulated at this time */
|
/* Check if a new sample should be accumulated at this time */
|
||||||
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
|
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
|
||||||
|
|
||||||
|
/* Process new readings of the HW clock in form of (sys, hw, sys) triplets and
|
||||||
|
produce a sample which can be accumulated */
|
||||||
|
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||||
|
struct timespec *hw_ts, struct timespec *local_ts, double *err);
|
||||||
|
|
||||||
/* Accumulate a new sample */
|
/* Accumulate a new sample */
|
||||||
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||||
struct timespec *local_ts, double err);
|
struct timespec *local_ts, double err);
|
||||||
|
|||||||
5
keys.c
5
keys.c
@@ -182,6 +182,9 @@ KEY_Reload(void)
|
|||||||
if (!key_file)
|
if (!key_file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!UTI_CheckFilePermissions(key_file, 0771))
|
||||||
|
;
|
||||||
|
|
||||||
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
|
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
|
||||||
if (!in) {
|
if (!in) {
|
||||||
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
|
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
|
||||||
@@ -255,6 +258,8 @@ KEY_Reload(void)
|
|||||||
more careful! */
|
more careful! */
|
||||||
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
|
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
|
||||||
|
|
||||||
|
LOG(LOGS_INFO, "Loaded %u symmetric keys", ARR_GetSize(keys));
|
||||||
|
|
||||||
/* Check for duplicates */
|
/* Check for duplicates */
|
||||||
for (i = 1; i < ARR_GetSize(keys); i++) {
|
for (i = 1; i < ARR_GetSize(keys); i++) {
|
||||||
if (get_key(i - 1)->id == get_key(i)->id)
|
if (get_key(i - 1)->id == get_key(i)->id)
|
||||||
|
|||||||
20
local.c
20
local.c
@@ -563,6 +563,8 @@ void
|
|||||||
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
|
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
|
||||||
double offset, double dispersion)
|
double offset, double dispersion)
|
||||||
{
|
{
|
||||||
|
LCL_CancelOffsetCorrection();
|
||||||
|
|
||||||
/* Dispatch to all handlers */
|
/* Dispatch to all handlers */
|
||||||
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
||||||
|
|
||||||
@@ -628,6 +630,24 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
|
||||||
|
{
|
||||||
|
ChangeListEntry *first_handler;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
first_handler = change_list.next;
|
||||||
|
change_list.next = &change_list;
|
||||||
|
|
||||||
|
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
|
||||||
|
|
||||||
|
change_list.next = first_handler;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
lcl_InvokeDispersionNotifyHandlers(double dispersion)
|
lcl_InvokeDispersionNotifyHandlers(double dispersion)
|
||||||
{
|
{
|
||||||
|
|||||||
5
local.h
5
local.h
@@ -173,6 +173,11 @@ extern void LCL_NotifyLeap(int leap);
|
|||||||
a slew, in one easy step */
|
a slew, in one easy step */
|
||||||
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
|
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
|
||||||
|
|
||||||
|
/* Same as the routine above, except it does not call the registered
|
||||||
|
parameter change handlers */
|
||||||
|
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
|
||||||
|
double corr_rate);
|
||||||
|
|
||||||
/* Routine to read the system precision as a log to base 2 value. */
|
/* Routine to read the system precision as a log to base 2 value. */
|
||||||
extern int LCL_GetSysPrecisionAsLog(void);
|
extern int LCL_GetSysPrecisionAsLog(void);
|
||||||
|
|
||||||
|
|||||||
29
logging.c
29
logging.c
@@ -39,6 +39,9 @@
|
|||||||
/* This is used by DEBUG_LOG macro */
|
/* This is used by DEBUG_LOG macro */
|
||||||
LOG_Severity log_min_severity = LOGS_INFO;
|
LOG_Severity log_min_severity = LOGS_INFO;
|
||||||
|
|
||||||
|
/* Current logging contexts */
|
||||||
|
static LOG_Context log_contexts;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Flag indicating we have initialised */
|
/* Flag indicating we have initialised */
|
||||||
static int initialised = 0;
|
static int initialised = 0;
|
||||||
@@ -72,6 +75,8 @@ void
|
|||||||
LOG_Initialise(void)
|
LOG_Initialise(void)
|
||||||
{
|
{
|
||||||
debug_prefix = Strdup("");
|
debug_prefix = Strdup("");
|
||||||
|
log_contexts = 0;
|
||||||
|
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
LOG_OpenFileLog(NULL);
|
LOG_OpenFileLog(NULL);
|
||||||
}
|
}
|
||||||
@@ -237,6 +242,30 @@ LOG_GetMinSeverity(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LOG_SetContext(LOG_Context context)
|
||||||
|
{
|
||||||
|
log_contexts |= context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LOG_UnsetContext(LOG_Context context)
|
||||||
|
{
|
||||||
|
log_contexts &= ~context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
LOG_Severity
|
||||||
|
LOG_GetContextSeverity(LOG_Context contexts)
|
||||||
|
{
|
||||||
|
return log_contexts & contexts ? LOGS_INFO : LOGS_DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
LOG_SetDebugPrefix(const char *prefix)
|
LOG_SetDebugPrefix(const char *prefix)
|
||||||
{
|
{
|
||||||
|
|||||||
14
logging.h
14
logging.h
@@ -100,6 +100,20 @@ extern void LOG_SetMinSeverity(LOG_Severity severity);
|
|||||||
/* Get the minimum severity */
|
/* Get the minimum severity */
|
||||||
extern LOG_Severity LOG_GetMinSeverity(void);
|
extern LOG_Severity LOG_GetMinSeverity(void);
|
||||||
|
|
||||||
|
/* Flags for info messages that should be logged only in specific contexts */
|
||||||
|
typedef enum {
|
||||||
|
LOGC_Command = 1,
|
||||||
|
LOGC_SourceFile = 2,
|
||||||
|
} LOG_Context;
|
||||||
|
|
||||||
|
/* Modify current contexts */
|
||||||
|
extern void LOG_SetContext(LOG_Context context);
|
||||||
|
extern void LOG_UnsetContext(LOG_Context context);
|
||||||
|
|
||||||
|
/* Get severity depending on the current active contexts: INFO if they contain
|
||||||
|
at least one of the specified contexts, DEBUG otherwise */
|
||||||
|
extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
|
||||||
|
|
||||||
/* Set a prefix for debug messages */
|
/* Set a prefix for debug messages */
|
||||||
extern void LOG_SetDebugPrefix(const char *prefix);
|
extern void LOG_SetDebugPrefix(const char *prefix);
|
||||||
|
|
||||||
|
|||||||
15
main.c
15
main.c
@@ -166,6 +166,8 @@ signal_cleanup(int x)
|
|||||||
static void
|
static void
|
||||||
quit_timeout(void *arg)
|
quit_timeout(void *arg)
|
||||||
{
|
{
|
||||||
|
LOG(LOGS_INFO, "Timeout reached");
|
||||||
|
|
||||||
/* Return with non-zero status if the clock is not synchronised */
|
/* Return with non-zero status if the clock is not synchronised */
|
||||||
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
|
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
|
||||||
SCH_QuitProgram();
|
SCH_QuitProgram();
|
||||||
@@ -329,6 +331,9 @@ go_daemon(void)
|
|||||||
char message[1024];
|
char message[1024];
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
/* Don't exit before the 'parent' */
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
close(pipefd[1]);
|
close(pipefd[1]);
|
||||||
r = read(pipefd[0], message, sizeof (message));
|
r = read(pipefd[0], message, sizeof (message));
|
||||||
if (r) {
|
if (r) {
|
||||||
@@ -351,7 +356,9 @@ go_daemon(void)
|
|||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
LOG_FATAL("fork() failed : %s", strerror(errno));
|
LOG_FATAL("fork() failed : %s", strerror(errno));
|
||||||
} else if (pid > 0) {
|
} else if (pid > 0) {
|
||||||
exit(0); /* In the 'parent' */
|
/* In the 'parent' */
|
||||||
|
close(pipefd[1]);
|
||||||
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
/* In the child we want to leave running as the daemon */
|
/* In the child we want to leave running as the daemon */
|
||||||
|
|
||||||
@@ -635,9 +642,13 @@ int main
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Drop root privileges if the specified user has a non-zero UID */
|
/* Drop root privileges if the specified user has a non-zero UID */
|
||||||
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
|
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
|
||||||
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
|
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
|
||||||
|
|
||||||
|
/* Warn if missing read access or having write access to keys */
|
||||||
|
CNF_CheckReadOnlyAccess();
|
||||||
|
}
|
||||||
|
|
||||||
if (!geteuid())
|
if (!geteuid())
|
||||||
LOG(LOGS_WARN, "Running with root privileges");
|
LOG(LOGS_WARN, "Running with root privileges");
|
||||||
|
|
||||||
|
|||||||
15
md5.c
15
md5.c
@@ -117,8 +117,7 @@ inline UINT4 ROTATE_LEFT(UINT4 x, int n)
|
|||||||
/* The routine MD5Init initializes the message-digest context
|
/* The routine MD5Init initializes the message-digest context
|
||||||
mdContext. All fields are set to zero.
|
mdContext. All fields are set to zero.
|
||||||
*/
|
*/
|
||||||
void MD5Init (mdContext)
|
void MD5Init (MD5_CTX *mdContext)
|
||||||
MD5_CTX *mdContext;
|
|
||||||
{
|
{
|
||||||
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
|
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
|
||||||
|
|
||||||
@@ -134,10 +133,7 @@ MD5_CTX *mdContext;
|
|||||||
account for the presence of each of the characters inBuf[0..inLen-1]
|
account for the presence of each of the characters inBuf[0..inLen-1]
|
||||||
in the message whose digest is being computed.
|
in the message whose digest is being computed.
|
||||||
*/
|
*/
|
||||||
void MD5Update (mdContext, inBuf, inLen)
|
void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
|
||||||
MD5_CTX *mdContext;
|
|
||||||
unsigned const char *inBuf;
|
|
||||||
unsigned int inLen;
|
|
||||||
{
|
{
|
||||||
UINT4 in[16];
|
UINT4 in[16];
|
||||||
int mdi;
|
int mdi;
|
||||||
@@ -173,8 +169,7 @@ unsigned int inLen;
|
|||||||
ends with the desired message digest in mdContext->digest[0...15].
|
ends with the desired message digest in mdContext->digest[0...15].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void MD5Final (mdContext)
|
void MD5Final (MD5_CTX *mdContext)
|
||||||
MD5_CTX *mdContext;
|
|
||||||
{
|
{
|
||||||
UINT4 in[16];
|
UINT4 in[16];
|
||||||
int mdi;
|
int mdi;
|
||||||
@@ -214,9 +209,7 @@ MD5_CTX *mdContext;
|
|||||||
|
|
||||||
/* Basic MD5 step. Transforms buf based on in.
|
/* Basic MD5 step. Transforms buf based on in.
|
||||||
*/
|
*/
|
||||||
static void Transform (buf, in)
|
static void Transform (UINT4 *buf, UINT4 *in)
|
||||||
UINT4 *buf;
|
|
||||||
UINT4 *in;
|
|
||||||
{
|
{
|
||||||
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
|
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
|
||||||
|
|
||||||
|
|||||||
7
memory.c
7
memory.c
@@ -47,8 +47,13 @@ Realloc(void *ptr, size_t size)
|
|||||||
{
|
{
|
||||||
void *r;
|
void *r;
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
Free(ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
r = realloc(ptr, size);
|
r = realloc(ptr, size);
|
||||||
if (!r && size)
|
if (!r)
|
||||||
LOG_FATAL("Could not allocate memory");
|
LOG_FATAL("Could not allocate memory");
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
7
ntp.h
7
ntp.h
@@ -179,4 +179,11 @@ typedef struct {
|
|||||||
double root_dispersion;
|
double root_dispersion;
|
||||||
} NTP_Sample;
|
} NTP_Sample;
|
||||||
|
|
||||||
|
/* Possible sources of timestamps */
|
||||||
|
typedef enum {
|
||||||
|
NTP_TS_DAEMON = 0,
|
||||||
|
NTP_TS_KERNEL,
|
||||||
|
NTP_TS_HARDWARE
|
||||||
|
} NTP_Timestamp_Source;
|
||||||
|
|
||||||
#endif /* GOT_NTP_H */
|
#endif /* GOT_NTP_H */
|
||||||
|
|||||||
354
ntp_core.c
354
ntp_core.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2020
|
* Copyright (C) Miroslav Lichvar 2009-2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "ntp_ext.h"
|
#include "ntp_ext.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "quantiles.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
#include "reference.h"
|
#include "reference.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
@@ -63,6 +64,17 @@ typedef enum {
|
|||||||
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
|
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
|
||||||
} OperatingMode;
|
} OperatingMode;
|
||||||
|
|
||||||
|
/* Structure holding a response and other data waiting to be processed when
|
||||||
|
a late HW transmit timestamp of the request is available, or a timeout is
|
||||||
|
reached */
|
||||||
|
struct SavedResponse {
|
||||||
|
NTP_Local_Address local_addr;
|
||||||
|
NTP_Local_Timestamp rx_ts;
|
||||||
|
NTP_Packet message;
|
||||||
|
NTP_PacketInfo info;
|
||||||
|
SCH_TimeoutID timeout_id;
|
||||||
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Structure used for holding a single peer/server's
|
/* Structure used for holding a single peer/server's
|
||||||
protocol machine */
|
protocol machine */
|
||||||
@@ -196,8 +208,18 @@ struct NCR_Instance_Record {
|
|||||||
|
|
||||||
SRC_Instance source;
|
SRC_Instance source;
|
||||||
|
|
||||||
|
/* Optional long-term quantile estimate of peer delay */
|
||||||
|
QNT_Instance delay_quant;
|
||||||
|
|
||||||
/* Optional median filter for NTP measurements */
|
/* Optional median filter for NTP measurements */
|
||||||
SPF_Instance filter;
|
SPF_Instance filter;
|
||||||
|
int filter_count;
|
||||||
|
|
||||||
|
/* Flag indicating HW transmit timestamps are expected */
|
||||||
|
int had_hw_tx_timestamp;
|
||||||
|
|
||||||
|
/* Response waiting for a HW transmit timestamp of the request */
|
||||||
|
struct SavedResponse *saved_response;
|
||||||
|
|
||||||
int burst_good_samples_to_go;
|
int burst_good_samples_to_go;
|
||||||
int burst_total_samples_to_go;
|
int burst_total_samples_to_go;
|
||||||
@@ -265,8 +287,12 @@ static ARR_Instance broadcasts;
|
|||||||
#define MAX_MAXDELAYRATIO 1.0e6
|
#define MAX_MAXDELAYRATIO 1.0e6
|
||||||
#define MAX_MAXDELAYDEVRATIO 1.0e6
|
#define MAX_MAXDELAYDEVRATIO 1.0e6
|
||||||
|
|
||||||
|
/* Parameters for the peer delay quantile */
|
||||||
|
#define DELAY_QUANT_Q 100
|
||||||
|
#define DELAY_QUANT_REPEAT 7
|
||||||
|
|
||||||
/* Minimum and maximum allowed poll interval */
|
/* Minimum and maximum allowed poll interval */
|
||||||
#define MIN_POLL -6
|
#define MIN_POLL -7
|
||||||
#define MAX_POLL 24
|
#define MAX_POLL 24
|
||||||
|
|
||||||
/* Enable sub-second polling intervals only when the peer delay is not
|
/* Enable sub-second polling intervals only when the peer delay is not
|
||||||
@@ -315,9 +341,15 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
|
|||||||
/* Forward prototypes */
|
/* Forward prototypes */
|
||||||
|
|
||||||
static void transmit_timeout(void *arg);
|
static void transmit_timeout(void *arg);
|
||||||
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
|
static double get_transmit_delay(NCR_Instance inst, int on_tx);
|
||||||
static double get_separation(int poll);
|
static double get_separation(int poll);
|
||||||
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
|
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
|
||||||
|
static void process_sample(NCR_Instance inst, NTP_Sample *sample);
|
||||||
|
static int has_saved_response(NCR_Instance inst);
|
||||||
|
static void process_saved_response(NCR_Instance inst);
|
||||||
|
static int process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
|
||||||
|
NTP_Local_Timestamp *rx_ts, NTP_Packet *message,
|
||||||
|
NTP_PacketInfo *info);
|
||||||
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
|
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -480,8 +512,7 @@ restart_timeout(NCR_Instance inst, double delay)
|
|||||||
static void
|
static void
|
||||||
start_initial_timeout(NCR_Instance inst)
|
start_initial_timeout(NCR_Instance inst)
|
||||||
{
|
{
|
||||||
double delay, last_tx;
|
double delay;
|
||||||
struct timespec now;
|
|
||||||
|
|
||||||
if (!inst->tx_timeout_id) {
|
if (!inst->tx_timeout_id) {
|
||||||
/* This will be the first transmission after mode change */
|
/* This will be the first transmission after mode change */
|
||||||
@@ -494,11 +525,7 @@ start_initial_timeout(NCR_Instance inst)
|
|||||||
the interval between packets at least as long as the current polling
|
the interval between packets at least as long as the current polling
|
||||||
interval */
|
interval */
|
||||||
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
|
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
|
||||||
SCH_GetLastEventTime(&now, NULL, NULL);
|
delay = get_transmit_delay(inst, 0);
|
||||||
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
|
|
||||||
if (last_tx < 0.0)
|
|
||||||
last_tx = 0.0;
|
|
||||||
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
|
|
||||||
} else {
|
} else {
|
||||||
delay = 0.0;
|
delay = 0.0;
|
||||||
}
|
}
|
||||||
@@ -521,6 +548,11 @@ close_client_socket(NCR_Instance inst)
|
|||||||
|
|
||||||
SCH_RemoveTimeout(inst->rx_timeout_id);
|
SCH_RemoveTimeout(inst->rx_timeout_id);
|
||||||
inst->rx_timeout_id = 0;
|
inst->rx_timeout_id = 0;
|
||||||
|
|
||||||
|
if (has_saved_response(inst)) {
|
||||||
|
SCH_RemoveTimeout(inst->saved_response->timeout_id);
|
||||||
|
inst->saved_response->timeout_id = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -546,6 +578,16 @@ take_offline(NCR_Instance inst)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_report(NCR_Instance inst)
|
||||||
|
{
|
||||||
|
memset(&inst->report, 0, sizeof (inst->report));
|
||||||
|
inst->report.remote_addr = inst->remote_addr.ip_addr;
|
||||||
|
inst->report.remote_port = inst->remote_addr.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
NCR_Instance
|
NCR_Instance
|
||||||
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||||
SourceParameters *params, const char *name)
|
SourceParameters *params, const char *name)
|
||||||
@@ -605,7 +647,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
|||||||
result->auto_burst = params->burst;
|
result->auto_burst = params->burst;
|
||||||
result->auto_offline = params->auto_offline;
|
result->auto_offline = params->auto_offline;
|
||||||
result->copy = params->copy && result->mode == MODE_CLIENT;
|
result->copy = params->copy && result->mode == MODE_CLIENT;
|
||||||
result->poll_target = params->poll_target;
|
result->poll_target = MAX(1, params->poll_target);
|
||||||
result->ext_field_flags = params->ext_fields;
|
result->ext_field_flags = params->ext_fields;
|
||||||
|
|
||||||
if (params->nts) {
|
if (params->nts) {
|
||||||
@@ -640,27 +682,37 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
|||||||
params->min_samples, params->max_samples,
|
params->min_samples, params->max_samples,
|
||||||
params->min_delay, params->asymmetry);
|
params->min_delay, params->asymmetry);
|
||||||
|
|
||||||
|
if (params->max_delay_quant > 0.0) {
|
||||||
|
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);
|
||||||
|
result->delay_quant = QNT_CreateInstance(k, k, DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
|
||||||
|
LCL_GetSysPrecisionAsQuantum() / 2.0);
|
||||||
|
} else {
|
||||||
|
result->delay_quant = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (params->filter_length >= 1)
|
if (params->filter_length >= 1)
|
||||||
result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
|
result->filter = SPF_CreateInstance(1, params->filter_length, NTP_MAX_DISPERSION, 0.0);
|
||||||
NTP_MAX_DISPERSION, 0.0);
|
|
||||||
else
|
else
|
||||||
result->filter = NULL;
|
result->filter = NULL;
|
||||||
|
|
||||||
|
result->saved_response = NULL;
|
||||||
|
|
||||||
result->rx_timeout_id = 0;
|
result->rx_timeout_id = 0;
|
||||||
result->tx_timeout_id = 0;
|
result->tx_timeout_id = 0;
|
||||||
result->tx_suspended = 1;
|
result->tx_suspended = 1;
|
||||||
result->opmode = MD_OFFLINE;
|
result->opmode = MD_OFFLINE;
|
||||||
result->local_poll = result->minpoll;
|
result->local_poll = MAX(result->minpoll, MIN_NONLAN_POLL);
|
||||||
result->poll_score = 0.0;
|
result->poll_score = 0.0;
|
||||||
zero_local_timestamp(&result->local_tx);
|
zero_local_timestamp(&result->local_tx);
|
||||||
result->burst_good_samples_to_go = 0;
|
result->burst_good_samples_to_go = 0;
|
||||||
result->burst_total_samples_to_go = 0;
|
result->burst_total_samples_to_go = 0;
|
||||||
memset(&result->report, 0, sizeof (result->report));
|
|
||||||
|
|
||||||
NCR_ResetInstance(result);
|
NCR_ResetInstance(result);
|
||||||
|
|
||||||
set_connectivity(result, params->connectivity);
|
set_connectivity(result, params->connectivity);
|
||||||
|
|
||||||
|
reset_report(result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,9 +728,14 @@ NCR_DestroyInstance(NCR_Instance instance)
|
|||||||
if (instance->mode == MODE_ACTIVE)
|
if (instance->mode == MODE_ACTIVE)
|
||||||
NIO_CloseServerSocket(instance->local_addr.sock_fd);
|
NIO_CloseServerSocket(instance->local_addr.sock_fd);
|
||||||
|
|
||||||
|
if (instance->delay_quant)
|
||||||
|
QNT_DestroyInstance(instance->delay_quant);
|
||||||
if (instance->filter)
|
if (instance->filter)
|
||||||
SPF_DestroyInstance(instance->filter);
|
SPF_DestroyInstance(instance->filter);
|
||||||
|
|
||||||
|
if (instance->saved_response)
|
||||||
|
Free(instance->saved_response);
|
||||||
|
|
||||||
NAU_DestroyInstance(instance->auth);
|
NAU_DestroyInstance(instance->auth);
|
||||||
|
|
||||||
/* This will destroy the source instance inside the
|
/* This will destroy the source instance inside the
|
||||||
@@ -732,8 +789,13 @@ NCR_ResetInstance(NCR_Instance instance)
|
|||||||
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
|
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
|
||||||
zero_local_timestamp(&instance->init_local_rx);
|
zero_local_timestamp(&instance->init_local_rx);
|
||||||
|
|
||||||
|
if (instance->delay_quant)
|
||||||
|
QNT_Reset(instance->delay_quant);
|
||||||
if (instance->filter)
|
if (instance->filter)
|
||||||
SPF_DropSamples(instance->filter);
|
SPF_DropSamples(instance->filter);
|
||||||
|
instance->filter_count = 0;
|
||||||
|
|
||||||
|
instance->had_hw_tx_timestamp = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -741,12 +803,14 @@ NCR_ResetInstance(NCR_Instance instance)
|
|||||||
void
|
void
|
||||||
NCR_ResetPoll(NCR_Instance instance)
|
NCR_ResetPoll(NCR_Instance instance)
|
||||||
{
|
{
|
||||||
|
instance->poll_score = 0.0;
|
||||||
|
|
||||||
if (instance->local_poll != instance->minpoll) {
|
if (instance->local_poll != instance->minpoll) {
|
||||||
instance->local_poll = instance->minpoll;
|
instance->local_poll = instance->minpoll;
|
||||||
|
|
||||||
/* The timer was set with a longer poll interval, restart it */
|
/* The timer was set with a longer poll interval, restart it */
|
||||||
if (instance->tx_timeout_id)
|
if (instance->tx_timeout_id)
|
||||||
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
|
restart_timeout(instance, get_transmit_delay(instance, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -755,7 +819,6 @@ NCR_ResetPoll(NCR_Instance instance)
|
|||||||
void
|
void
|
||||||
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
|
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
|
||||||
{
|
{
|
||||||
memset(&inst->report, 0, sizeof (inst->report));
|
|
||||||
NCR_ResetInstance(inst);
|
NCR_ResetInstance(inst);
|
||||||
|
|
||||||
if (!ntp_only)
|
if (!ntp_only)
|
||||||
@@ -772,10 +835,18 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
|
|||||||
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
|
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reset the polling interval only if the source wasn't unreachable to
|
||||||
|
avoid increasing server/network load in case that is what caused
|
||||||
|
the source to be unreachable */
|
||||||
|
if (SRC_IsReachable(inst->source))
|
||||||
|
NCR_ResetPoll(inst);
|
||||||
|
|
||||||
/* Update the reference ID and reset the source/sourcestats instances */
|
/* Update the reference ID and reset the source/sourcestats instances */
|
||||||
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
|
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
|
||||||
&inst->remote_addr.ip_addr);
|
&inst->remote_addr.ip_addr);
|
||||||
SRC_ResetInstance(inst->source);
|
SRC_ResetInstance(inst->source);
|
||||||
|
|
||||||
|
reset_report(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -783,6 +854,8 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
|
|||||||
static void
|
static void
|
||||||
adjust_poll(NCR_Instance inst, double adj)
|
adjust_poll(NCR_Instance inst, double adj)
|
||||||
{
|
{
|
||||||
|
NTP_Sample last_sample;
|
||||||
|
|
||||||
inst->poll_score += adj;
|
inst->poll_score += adj;
|
||||||
|
|
||||||
if (inst->poll_score >= 1.0) {
|
if (inst->poll_score >= 1.0) {
|
||||||
@@ -808,7 +881,9 @@ adjust_poll(NCR_Instance inst, double adj)
|
|||||||
or it is not in a local network according to the measured delay */
|
or it is not in a local network according to the measured delay */
|
||||||
if (inst->local_poll < MIN_NONLAN_POLL &&
|
if (inst->local_poll < MIN_NONLAN_POLL &&
|
||||||
(!SRC_IsReachable(inst->source) ||
|
(!SRC_IsReachable(inst->source) ||
|
||||||
SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY))
|
(SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY &&
|
||||||
|
(!inst->filter || !SPF_GetLastSample(inst->filter, &last_sample) ||
|
||||||
|
last_sample.peer_delay > MAX_LAN_PEER_DELAY))))
|
||||||
inst->local_poll = MIN_NONLAN_POLL;
|
inst->local_poll = MIN_NONLAN_POLL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -862,10 +937,19 @@ get_transmit_poll(NCR_Instance inst)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static double
|
static double
|
||||||
get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
|
get_transmit_delay(NCR_Instance inst, int on_tx)
|
||||||
{
|
{
|
||||||
int poll_to_use, stratum_diff;
|
int poll_to_use, stratum_diff;
|
||||||
double delay_time;
|
double delay_time, last_tx;
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
/* Calculate the interval since last transmission if known */
|
||||||
|
if (!on_tx && !UTI_IsZeroTimespec(&inst->local_tx.ts)) {
|
||||||
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
|
||||||
|
} else {
|
||||||
|
last_tx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* If we're in burst mode, queue for immediate dispatch.
|
/* If we're in burst mode, queue for immediate dispatch.
|
||||||
|
|
||||||
@@ -905,12 +989,6 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
|
|||||||
last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5))
|
last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5))
|
||||||
delay_time *= PEER_SAMPLING_ADJ;
|
delay_time *= PEER_SAMPLING_ADJ;
|
||||||
|
|
||||||
/* Substract the already spend time */
|
|
||||||
if (last_tx > 0.0)
|
|
||||||
delay_time -= last_tx;
|
|
||||||
if (delay_time < 0.0)
|
|
||||||
delay_time = 0.0;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
@@ -928,6 +1006,12 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Subtract elapsed time */
|
||||||
|
if (last_tx > 0.0)
|
||||||
|
delay_time -= last_tx;
|
||||||
|
if (delay_time < 0.0)
|
||||||
|
delay_time = 0.0;
|
||||||
|
|
||||||
return delay_time;
|
return delay_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1240,6 +1324,15 @@ transmit_timeout(void *arg)
|
|||||||
|
|
||||||
inst->tx_timeout_id = 0;
|
inst->tx_timeout_id = 0;
|
||||||
|
|
||||||
|
if (has_saved_response(inst)) {
|
||||||
|
process_saved_response(inst);
|
||||||
|
|
||||||
|
/* Wait for the new transmission timeout (if the response was still
|
||||||
|
valid and it did not cause switch to offline) */
|
||||||
|
if (inst->tx_timeout_id != 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (inst->opmode) {
|
switch (inst->opmode) {
|
||||||
case MD_BURST_WAS_ONLINE:
|
case MD_BURST_WAS_ONLINE:
|
||||||
/* With online burst switch to online before last packet */
|
/* With online burst switch to online before last packet */
|
||||||
@@ -1271,11 +1364,10 @@ transmit_timeout(void *arg)
|
|||||||
|
|
||||||
/* Prepare authentication */
|
/* Prepare authentication */
|
||||||
if (!NAU_PrepareRequestAuth(inst->auth)) {
|
if (!NAU_PrepareRequestAuth(inst->auth)) {
|
||||||
if (inst->burst_total_samples_to_go > 0)
|
|
||||||
inst->burst_total_samples_to_go--;
|
|
||||||
adjust_poll(inst, 0.25);
|
|
||||||
SRC_UpdateReachability(inst->source, 0);
|
SRC_UpdateReachability(inst->source, 0);
|
||||||
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
|
restart_timeout(inst, get_transmit_delay(inst, 1));
|
||||||
|
/* Count missing samples for the sample filter */
|
||||||
|
process_sample(inst, NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1353,6 +1445,9 @@ transmit_timeout(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SRC_UpdateReachability(inst->source, 0);
|
SRC_UpdateReachability(inst->source, 0);
|
||||||
|
|
||||||
|
/* Count missing samples for the sample filter */
|
||||||
|
process_sample(inst, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* With auto_offline take the source offline if sending failed */
|
/* With auto_offline take the source offline if sending failed */
|
||||||
@@ -1375,7 +1470,7 @@ transmit_timeout(void *arg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Restart timer for this message */
|
/* Restart timer for this message */
|
||||||
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
|
restart_timeout(inst, get_transmit_delay(inst, 1));
|
||||||
|
|
||||||
/* If a client packet was just sent, schedule a timeout to close the socket
|
/* If a client packet was just sent, schedule a timeout to close the socket
|
||||||
at the time when all server replies would fail the delay test, so the
|
at the time when all server replies would fail the delay test, so the
|
||||||
@@ -1541,6 +1636,22 @@ check_delay_ratio(NCR_Instance inst, SST_Stats stats,
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_delay_quant(NCR_Instance inst, double delay)
|
||||||
|
{
|
||||||
|
double quant;
|
||||||
|
|
||||||
|
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
|
||||||
|
|
||||||
|
if (delay <= quant)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
|
check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
|
||||||
struct timespec *sample_time, double offset, double delay)
|
struct timespec *sample_time, double offset, double delay)
|
||||||
@@ -1626,33 +1737,29 @@ check_sync_loop(NCR_Instance inst, NTP_Packet *message, NTP_Local_Address *local
|
|||||||
static void
|
static void
|
||||||
process_sample(NCR_Instance inst, NTP_Sample *sample)
|
process_sample(NCR_Instance inst, NTP_Sample *sample)
|
||||||
{
|
{
|
||||||
double estimated_offset, error_in_estimate, filtered_sample_ago;
|
double estimated_offset, error_in_estimate;
|
||||||
NTP_Sample filtered_sample;
|
NTP_Sample filtered_sample;
|
||||||
int filtered_samples;
|
|
||||||
|
|
||||||
/* Accumulate the sample to the median filter if it is enabled. When the
|
/* Accumulate the sample to the median filter if enabled and wait for
|
||||||
filter produces a result, check if it is not too old, i.e. the filter did
|
the configured number of samples before processing (NULL indicates
|
||||||
not miss too many samples due to missing responses or failing tests. */
|
a missing sample) */
|
||||||
if (inst->filter) {
|
if (inst->filter) {
|
||||||
|
if (sample)
|
||||||
SPF_AccumulateSample(inst->filter, sample);
|
SPF_AccumulateSample(inst->filter, sample);
|
||||||
|
|
||||||
filtered_samples = SPF_GetNumberOfSamples(inst->filter);
|
if (++inst->filter_count < SPF_GetMaxSamples(inst->filter))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
|
if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
filtered_sample_ago = UTI_DiffTimespecsToDouble(&sample->time, &filtered_sample.time);
|
|
||||||
|
|
||||||
if (filtered_sample_ago > SOURCE_REACH_BITS / 2 * filtered_samples *
|
|
||||||
UTI_Log2ToDouble(inst->local_poll)) {
|
|
||||||
DEBUG_LOG("filtered sample dropped ago=%f poll=%d", filtered_sample_ago,
|
|
||||||
inst->local_poll);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sample = &filtered_sample;
|
sample = &filtered_sample;
|
||||||
|
inst->filter_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!sample)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Get the estimated offset predicted from previous samples. The
|
/* Get the estimated offset predicted from previous samples. The
|
||||||
convention here is that positive means local clock FAST of
|
convention here is that positive means local clock FAST of
|
||||||
reference, i.e. backwards to the way that 'offset' is defined. */
|
reference, i.e. backwards to the way that 'offset' is defined. */
|
||||||
@@ -1676,7 +1783,69 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
has_saved_response(NCR_Instance inst)
|
||||||
|
{
|
||||||
|
return inst->saved_response && inst->saved_response->timeout_id > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_saved_response(NCR_Instance inst)
|
||||||
|
{
|
||||||
|
SCH_RemoveTimeout(inst->saved_response->timeout_id);
|
||||||
|
inst->saved_response->timeout_id = 0;
|
||||||
|
|
||||||
|
DEBUG_LOG("Processing saved response from %s", UTI_IPToString(&inst->remote_addr.ip_addr));
|
||||||
|
process_response(inst, 1, &inst->saved_response->local_addr, &inst->saved_response->rx_ts,
|
||||||
|
&inst->saved_response->message, &inst->saved_response->info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
saved_response_timeout(void *arg)
|
||||||
|
{
|
||||||
|
NCR_Instance inst = arg;
|
||||||
|
|
||||||
|
inst->saved_response->timeout_id = 0;
|
||||||
|
process_saved_response(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
save_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||||
|
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
|
||||||
|
{
|
||||||
|
double timeout = CNF_GetHwTsTimeout();
|
||||||
|
|
||||||
|
if (timeout <= 0.0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* If another message is already saved, process both immediately */
|
||||||
|
if (has_saved_response(inst)) {
|
||||||
|
process_saved_response(inst);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inst->saved_response)
|
||||||
|
inst->saved_response = MallocNew(struct SavedResponse);
|
||||||
|
inst->saved_response->local_addr = *local_addr;
|
||||||
|
inst->saved_response->rx_ts = *rx_ts;
|
||||||
|
inst->saved_response->message = *message;
|
||||||
|
inst->saved_response->info = *info;
|
||||||
|
inst->saved_response->timeout_id = SCH_AddTimeoutByDelay(timeout, saved_response_timeout,
|
||||||
|
inst);
|
||||||
|
DEBUG_LOG("Saved valid response for later processing");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
|
||||||
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
|
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
|
||||||
{
|
{
|
||||||
NTP_Sample sample;
|
NTP_Sample sample;
|
||||||
@@ -1770,8 +1939,10 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
/* Test 4 would check for denied access. It would always pass as this
|
/* Test 4 would check for denied access. It would always pass as this
|
||||||
function is called only for known sources. */
|
function is called only for known sources. */
|
||||||
|
|
||||||
/* Test 5 checks for authentication failure */
|
/* Test 5 checks for authentication failure. If it is a saved message,
|
||||||
test5 = NAU_CheckResponseAuth(inst->auth, message, info);
|
which had to pass all these tests before, avoid authenticating it for
|
||||||
|
the second time (that is not allowed in the NTS code). */
|
||||||
|
test5 = saved || NAU_CheckResponseAuth(inst->auth, message, info);
|
||||||
|
|
||||||
/* Test 6 checks for unsynchronised server */
|
/* Test 6 checks for unsynchronised server */
|
||||||
test6 = pkt_leap != LEAP_Unsynchronised &&
|
test6 = pkt_leap != LEAP_Unsynchronised &&
|
||||||
@@ -1787,6 +1958,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
valid_packet = test1 && test2 && test3 && test5;
|
valid_packet = test1 && test2 && test3 && test5;
|
||||||
synced_packet = valid_packet && test6 && test7;
|
synced_packet = valid_packet && test6 && test7;
|
||||||
|
|
||||||
|
/* If the server is very close and/or the NIC hardware/driver is slow, it
|
||||||
|
is possible that a response from the server is received before the HW
|
||||||
|
transmit timestamp of the request. To avoid getting a less accurate
|
||||||
|
offset or failing one of the later tests, save the response and wait for
|
||||||
|
the transmit timestamp or timeout. Allow this only for the first valid
|
||||||
|
response to the request, when at least one good response has already been
|
||||||
|
accepted to avoid incorrectly confirming a tentative source. */
|
||||||
|
if (valid_packet && synced_packet && !saved && !inst->valid_rx &&
|
||||||
|
inst->had_hw_tx_timestamp && inst->local_tx.source != NTP_TS_HARDWARE &&
|
||||||
|
inst->report.total_good_count > 0) {
|
||||||
|
if (save_response(inst, local_addr, rx_ts, message, info))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for Kiss-o'-Death codes */
|
/* Check for Kiss-o'-Death codes */
|
||||||
kod_rate = 0;
|
kod_rate = 0;
|
||||||
if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised &&
|
if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised &&
|
||||||
@@ -1907,12 +2092,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
|
|
||||||
/* Test A requires that the minimum estimate of the peer delay is not
|
/* Test A requires that the minimum estimate of the peer delay is not
|
||||||
larger than the configured maximum, in both client modes that the server
|
larger than the configured maximum, in both client modes that the server
|
||||||
processing time is sane, and in interleaved symmetric mode that the
|
processing time is sane, in interleaved client/server mode that the
|
||||||
|
previous response was not in basic mode (which prevents using timestamps
|
||||||
|
that minimise delay error), and in interleaved symmetric mode that the
|
||||||
measured delay and intervals between remote timestamps don't indicate
|
measured delay and intervals between remote timestamps don't indicate
|
||||||
a missed response */
|
a missed response */
|
||||||
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
|
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
|
||||||
precision <= inst->max_delay &&
|
precision <= inst->max_delay &&
|
||||||
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
|
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
|
||||||
|
!(inst->mode == MODE_CLIENT && interleaved_packet &&
|
||||||
|
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
|
||||||
|
UTI_CompareTimespecs(&local_transmit.ts, &inst->local_tx.ts) == 0) &&
|
||||||
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
|
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
|
||||||
(sample.peer_delay > 0.5 * prev_remote_poll_interval ||
|
(sample.peer_delay > 0.5 * prev_remote_poll_interval ||
|
||||||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
|
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
|
||||||
@@ -1925,12 +2115,18 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
administrator-defined value */
|
administrator-defined value */
|
||||||
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
|
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
|
||||||
|
|
||||||
/* Test C requires that the ratio of the increase in delay from the minimum
|
/* Test C either requires that the delay is less than an estimate of an
|
||||||
|
administrator-defined quantile, or (if the quantile is not specified)
|
||||||
|
it requires that the ratio of the increase in delay from the minimum
|
||||||
one in the stats data register to the standard deviation of the offsets
|
one in the stats data register to the standard deviation of the offsets
|
||||||
in the register is less than an administrator-defined value or the
|
in the register is less than an administrator-defined value or the
|
||||||
difference between measured offset and predicted offset is larger than
|
difference between measured offset and predicted offset is larger than
|
||||||
the increase in delay */
|
the increase in delay */
|
||||||
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
|
if (inst->delay_quant)
|
||||||
|
testC = check_delay_quant(inst, sample.peer_delay);
|
||||||
|
else
|
||||||
|
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset,
|
||||||
|
sample.peer_delay);
|
||||||
|
|
||||||
/* Test D requires that the source is not synchronised to us and is not us
|
/* Test D requires that the source is not synchronised to us and is not us
|
||||||
to prevent a synchronisation loop */
|
to prevent a synchronisation loop */
|
||||||
@@ -1972,7 +2168,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
updated_timestamps = 2;
|
updated_timestamps = 2;
|
||||||
|
|
||||||
/* If available, update the monotonic timestamp and accumulate the offset.
|
/* If available, update the monotonic timestamp and accumulate the offset.
|
||||||
This needs to be done here to no lose changes in remote_ntp_rx in
|
This needs to be done here to not lose changes in remote_ntp_rx in
|
||||||
symmetric mode when there are multiple responses per request. */
|
symmetric mode when there are multiple responses per request. */
|
||||||
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
|
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
|
||||||
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
|
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
|
||||||
@@ -2063,6 +2259,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
|
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
|
||||||
|
|
||||||
|
if (inst->delay_quant)
|
||||||
|
QNT_Accumulate(inst->delay_quant, sample.peer_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (good_packet) {
|
if (good_packet) {
|
||||||
@@ -2088,6 +2287,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
} else {
|
} else {
|
||||||
/* Slowly increase the polling interval if we can't get a good response */
|
/* Slowly increase the polling interval if we can't get a good response */
|
||||||
adjust_poll(inst, testD ? 0.02 : 0.1);
|
adjust_poll(inst, testD ? 0.02 : 0.1);
|
||||||
|
|
||||||
|
/* Count missing samples for the sample filter */
|
||||||
|
process_sample(inst, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If in client mode, no more packets are expected to be coming from the
|
/* If in client mode, no more packets are expected to be coming from the
|
||||||
@@ -2100,8 +2302,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
|
|
||||||
/* And now, requeue the timer */
|
/* And now, requeue the timer */
|
||||||
if (inst->opmode != MD_OFFLINE) {
|
if (inst->opmode != MD_OFFLINE) {
|
||||||
delay_time = get_transmit_delay(inst, 0,
|
delay_time = get_transmit_delay(inst, 0);
|
||||||
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
|
|
||||||
|
|
||||||
if (kod_rate) {
|
if (kod_rate) {
|
||||||
LOG(LOGS_WARN, "Received KoD RATE from %s",
|
LOG(LOGS_WARN, "Received KoD RATE from %s",
|
||||||
@@ -2116,14 +2317,13 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get rid of old timeout and start a new one */
|
/* Get rid of old timeout and start a new one */
|
||||||
|
if (!saved)
|
||||||
assert(inst->tx_timeout_id);
|
assert(inst->tx_timeout_id);
|
||||||
restart_timeout(inst, delay_time);
|
restart_timeout(inst, delay_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the NTP report */
|
/* Update the NTP report */
|
||||||
inst->report.remote_addr = inst->remote_addr.ip_addr;
|
|
||||||
inst->report.local_addr = inst->local_addr.ip_addr;
|
inst->report.local_addr = inst->local_addr.ip_addr;
|
||||||
inst->report.remote_port = inst->remote_addr.port;
|
|
||||||
inst->report.leap = pkt_leap;
|
inst->report.leap = pkt_leap;
|
||||||
inst->report.version = pkt_version;
|
inst->report.version = pkt_version;
|
||||||
inst->report.mode = NTP_LVM_TO_MODE(message->lvm);
|
inst->report.mode = NTP_LVM_TO_MODE(message->lvm);
|
||||||
@@ -2148,6 +2348,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
inst->report.rx_tss_char = tss_chars[local_receive.source];
|
inst->report.rx_tss_char = tss_chars[local_receive.source];
|
||||||
|
|
||||||
inst->report.total_valid_count++;
|
inst->report.total_valid_count++;
|
||||||
|
if (good_packet)
|
||||||
|
inst->report.total_good_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do measurement logging */
|
/* Do measurement logging */
|
||||||
@@ -2261,8 +2463,8 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
client mode operation.
|
client mode operation.
|
||||||
|
|
||||||
This copes with the case for an isolated network where one
|
This copes with the case for an isolated network where one
|
||||||
machine is set by eye and is used as the master, with the
|
machine is set by eye and is used as the primary server, with
|
||||||
other machines pointed at it. If the master goes down, we
|
the other machines pointed at it. If the server goes down, we
|
||||||
want to be able to reset its time at startup by relying on
|
want to be able to reset its time at startup by relying on
|
||||||
one of the secondaries to flywheel it. The behaviour coded here
|
one of the secondaries to flywheel it. The behaviour coded here
|
||||||
is required in the secondaries to make this possible. */
|
is required in the secondaries to make this possible. */
|
||||||
@@ -2305,7 +2507,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_response(inst, local_addr, rx_ts, message, &info);
|
return process_response(inst, 0, local_addr, rx_ts, message, &info);
|
||||||
} else if (proc_as_unknown) {
|
} else if (proc_as_unknown) {
|
||||||
NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
|
NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
|
||||||
/* It's not a reply to our request, don't return success */
|
/* It's not a reply to our request, don't return success */
|
||||||
@@ -2386,8 +2588,6 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||||||
/* Don't respond unless a non-zero KoD was returned */
|
/* Don't respond unless a non-zero KoD was returned */
|
||||||
if (kod == 0)
|
if (kod == 0)
|
||||||
return;
|
return;
|
||||||
} else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) {
|
|
||||||
CLG_LogAuthNtpRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local_ntp_rx = NULL;
|
local_ntp_rx = NULL;
|
||||||
@@ -2408,13 +2608,18 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||||||
ntp_rx = message->originate_ts;
|
ntp_rx = message->originate_ts;
|
||||||
local_ntp_rx = &ntp_rx;
|
local_ntp_rx = &ntp_rx;
|
||||||
UTI_ZeroTimespec(&local_tx.ts);
|
UTI_ZeroTimespec(&local_tx.ts);
|
||||||
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts);
|
local_tx.source = NTP_TS_DAEMON;
|
||||||
|
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts, &local_tx.source);
|
||||||
|
|
||||||
tx_ts = &local_tx;
|
tx_ts = &local_tx;
|
||||||
if (interleaved)
|
if (interleaved)
|
||||||
CLG_DisableNtpTimestamps(&ntp_rx);
|
CLG_DisableNtpTimestamps(&ntp_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLG_UpdateNtpStats(kod != 0 && info.auth.mode != NTP_AUTH_NONE &&
|
||||||
|
info.auth.mode != NTP_AUTH_MSSNTP,
|
||||||
|
rx_ts->source, interleaved ? tx_ts->source : NTP_TS_DAEMON);
|
||||||
|
|
||||||
/* Suggest the client to increase its polling interval if it indicates
|
/* Suggest the client to increase its polling interval if it indicates
|
||||||
the interval is shorter than the rate limiting interval */
|
the interval is shorter than the rate limiting interval */
|
||||||
poll = CLG_GetNtpMinPoll();
|
poll = CLG_GetNtpMinPoll();
|
||||||
@@ -2431,7 +2636,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (local_ntp_rx)
|
if (local_ntp_rx)
|
||||||
CLG_SaveNtpTimestamps(local_ntp_rx, tx_ts ? &tx_ts->ts : NULL);
|
CLG_SaveNtpTimestamps(local_ntp_rx, &tx_ts->ts, tx_ts->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -2485,6 +2690,13 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
|
|||||||
|
|
||||||
update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
|
update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
|
||||||
message);
|
message);
|
||||||
|
|
||||||
|
if (tx_ts->source == NTP_TS_HARDWARE) {
|
||||||
|
inst->had_hw_tx_timestamp = 1;
|
||||||
|
|
||||||
|
if (has_saved_response(inst))
|
||||||
|
process_saved_response(inst);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -2509,7 +2721,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||||||
local_ntp_rx = &message->receive_ts;
|
local_ntp_rx = &message->receive_ts;
|
||||||
new_tx = *tx_ts;
|
new_tx = *tx_ts;
|
||||||
|
|
||||||
if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts))
|
if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts, &old_tx.source))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Undo a clock adjustment between the RX and TX timestamps to minimise error
|
/* Undo a clock adjustment between the RX and TX timestamps to minimise error
|
||||||
@@ -2518,7 +2730,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
|
|||||||
|
|
||||||
update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
|
update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
|
||||||
|
|
||||||
CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts);
|
CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts, new_tx.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -2541,6 +2753,10 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
|
|||||||
|
|
||||||
if (inst->filter)
|
if (inst->filter)
|
||||||
SPF_SlewSamples(inst->filter, when, dfreq, doffset);
|
SPF_SlewSamples(inst->filter, when, dfreq, doffset);
|
||||||
|
|
||||||
|
if (has_saved_response(inst))
|
||||||
|
UTI_AdjustTimespec(&inst->saved_response->rx_ts.ts, when, &inst->saved_response->rx_ts.ts,
|
||||||
|
&delta, dfreq, doffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -2686,7 +2902,7 @@ NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
|
|||||||
void
|
void
|
||||||
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
|
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
|
||||||
{
|
{
|
||||||
inst->poll_target = new_poll_target;
|
inst->poll_target = MAX(1, new_poll_target);
|
||||||
LOG(LOGS_INFO, "Source %s new polltarget %d",
|
LOG(LOGS_INFO, "Source %s new polltarget %d",
|
||||||
UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target);
|
UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target);
|
||||||
}
|
}
|
||||||
@@ -2786,6 +3002,10 @@ NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
|
|||||||
if (status != ADF_SUCCESS)
|
if (status != ADF_SUCCESS)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
|
||||||
|
allow ? "Allowed" : "Denied", all ? " all" : "", "NTP",
|
||||||
|
UTI_IPSubnetToString(ip_addr, subnet_bits));
|
||||||
|
|
||||||
/* Keep server sockets open only when an address allowed */
|
/* Keep server sockets open only when an address allowed */
|
||||||
if (allow) {
|
if (allow) {
|
||||||
NTP_Remote_Address remote_addr;
|
NTP_Remote_Address remote_addr;
|
||||||
|
|||||||
@@ -38,12 +38,6 @@ typedef enum {
|
|||||||
NTP_SERVER, NTP_PEER
|
NTP_SERVER, NTP_PEER
|
||||||
} NTP_Source_Type;
|
} NTP_Source_Type;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NTP_TS_DAEMON = 0,
|
|
||||||
NTP_TS_KERNEL,
|
|
||||||
NTP_TS_HARDWARE
|
|
||||||
} NTP_Timestamp_Source;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
double err;
|
double err;
|
||||||
|
|||||||
21
ntp_io.c
21
ntp_io.c
@@ -4,7 +4,7 @@
|
|||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Timo Teras 2009
|
* Copyright (C) Timo Teras 2009
|
||||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020
|
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -126,8 +126,14 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
|
|||||||
dscp = CNF_GetNtpDscp();
|
dscp = CNF_GetNtpDscp();
|
||||||
if (dscp > 0 && dscp < 64) {
|
if (dscp > 0 && dscp < 64) {
|
||||||
#ifdef IP_TOS
|
#ifdef IP_TOS
|
||||||
|
if (family == IPADDR_INET4)
|
||||||
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
|
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
|
||||||
;
|
;
|
||||||
|
#endif
|
||||||
|
#if defined(FEAT_IPV6) && defined(IPV6_TCLASS)
|
||||||
|
if (family == IPADDR_INET6)
|
||||||
|
if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, dscp << 2))
|
||||||
|
;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,9 +169,6 @@ close_socket(int sock_fd)
|
|||||||
if (sock_fd == INVALID_SOCK_FD)
|
if (sock_fd == INVALID_SOCK_FD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
||||||
NIO_Linux_NotifySocketClosing(sock_fd);
|
|
||||||
#endif
|
|
||||||
SCH_RemoveFileHandler(sock_fd);
|
SCH_RemoveFileHandler(sock_fd);
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
}
|
}
|
||||||
@@ -461,11 +464,6 @@ read_from_socket(int sock_fd, int event, void *anything)
|
|||||||
SCK_Message *messages;
|
SCK_Message *messages;
|
||||||
int i, received, flags = 0;
|
int i, received, flags = 0;
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
||||||
if (NIO_Linux_ProcessEvent(sock_fd, event))
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (event == SCH_FILE_EXCEPTION) {
|
if (event == SCH_FILE_EXCEPTION) {
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||||
flags |= SCK_FLAG_MSG_ERRQUEUE;
|
flags |= SCK_FLAG_MSG_ERRQUEUE;
|
||||||
@@ -522,6 +520,8 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
|
|||||||
static int
|
static int
|
||||||
wrap_message(SCK_Message *message, int sock_fd)
|
wrap_message(SCK_Message *message, int sock_fd)
|
||||||
{
|
{
|
||||||
|
static uint16_t sequence_id = 0;
|
||||||
|
|
||||||
assert(PTP_NTP_PREFIX_LENGTH == 48);
|
assert(PTP_NTP_PREFIX_LENGTH == 48);
|
||||||
|
|
||||||
if (!is_ptp_socket(sock_fd))
|
if (!is_ptp_socket(sock_fd))
|
||||||
@@ -542,6 +542,7 @@ wrap_message(SCK_Message *message, int sock_fd)
|
|||||||
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
|
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
|
||||||
ptp_message->header.domain = PTP_DOMAIN_NTP;
|
ptp_message->header.domain = PTP_DOMAIN_NTP;
|
||||||
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
|
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
|
||||||
|
ptp_message->header.sequence_id = htons(sequence_id++);
|
||||||
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
|
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
|
||||||
ptp_message->tlv_header.length = htons(message->length);
|
ptp_message->tlv_header.length = htons(message->length);
|
||||||
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
|
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
|
||||||
@@ -594,7 +595,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
|||||||
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
||||||
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
||||||
if (message.local_addr.ip.family == IPADDR_INET4 &&
|
if (message.local_addr.ip.family == IPADDR_INET4 &&
|
||||||
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
|
(bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
|
||||||
message.local_addr.ip.family = IPADDR_UNSPEC;
|
message.local_addr.ip.family = IPADDR_UNSPEC;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
202
ntp_io_linux.c
202
ntp_io_linux.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2016-2019
|
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "hwclock.h"
|
#include "hwclock.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
#include "ntp_io_linux.h"
|
#include "ntp_io_linux.h"
|
||||||
@@ -59,19 +60,20 @@ struct Interface {
|
|||||||
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
|
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
|
||||||
int l2_udp4_ntp_start;
|
int l2_udp4_ntp_start;
|
||||||
int l2_udp6_ntp_start;
|
int l2_udp6_ntp_start;
|
||||||
/* Precision of PHC readings */
|
|
||||||
double precision;
|
|
||||||
/* Compensation of errors in TX and RX timestamping */
|
/* Compensation of errors in TX and RX timestamping */
|
||||||
double tx_comp;
|
double tx_comp;
|
||||||
double rx_comp;
|
double rx_comp;
|
||||||
HCL_Instance clock;
|
HCL_Instance clock;
|
||||||
|
int maxpoll;
|
||||||
|
SCH_TimeoutID poll_timeout_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Number of PHC readings per HW clock sample */
|
/* Number of PHC readings per HW clock sample */
|
||||||
#define PHC_READINGS 10
|
#define PHC_READINGS 25
|
||||||
|
|
||||||
/* Minimum interval between PHC readings */
|
/* Minimum and maximum interval between PHC readings */
|
||||||
#define MIN_PHC_POLL -6
|
#define MIN_PHC_POLL -6
|
||||||
|
#define MAX_PHC_POLL 20
|
||||||
|
|
||||||
/* Maximum acceptable offset between SW/HW and daemon timestamp */
|
/* Maximum acceptable offset between SW/HW and daemon timestamp */
|
||||||
#define MAX_TS_DELAY 1.0
|
#define MAX_TS_DELAY 1.0
|
||||||
@@ -86,19 +88,6 @@ static int ts_tx_flags;
|
|||||||
/* Flag indicating the socket options can't be changed in control messages */
|
/* Flag indicating the socket options can't be changed in control messages */
|
||||||
static int permanent_ts_options;
|
static int permanent_ts_options;
|
||||||
|
|
||||||
/* When sending client requests to a close and fast server, it is possible that
|
|
||||||
a response will be received before the HW transmit timestamp of the request
|
|
||||||
itself. To avoid processing of the response without the HW timestamp, we
|
|
||||||
monitor events returned by select() and suspend reading of packets from the
|
|
||||||
receive queue for up to 200 microseconds. As the requests are normally
|
|
||||||
separated by at least 200 milliseconds, it is sufficient to monitor and
|
|
||||||
suspend one socket at a time. */
|
|
||||||
static int monitored_socket;
|
|
||||||
static int suspended_socket;
|
|
||||||
static SCH_TimeoutID resume_timeout_id;
|
|
||||||
|
|
||||||
#define RESUME_TIMEOUT 200.0e-6
|
|
||||||
|
|
||||||
/* Unbound socket keeping the kernel RX timestamping permanently enabled
|
/* Unbound socket keeping the kernel RX timestamping permanently enabled
|
||||||
in order to avoid a race condition between receiving a server response
|
in order to avoid a race condition between receiving a server response
|
||||||
and the kernel actually starting to timestamp received packets after
|
and the kernel actually starting to timestamp received packets after
|
||||||
@@ -109,13 +98,17 @@ static int dummy_rxts_socket;
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void poll_phc(struct Interface *iface, struct timespec *now);
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_interface(CNF_HwTsInterface *conf_iface)
|
add_interface(CNF_HwTsInterface *conf_iface)
|
||||||
{
|
{
|
||||||
|
int sock_fd, if_index, minpoll, phc_fd, req_hwts_flags, rx_filter;
|
||||||
struct ethtool_ts_info ts_info;
|
struct ethtool_ts_info ts_info;
|
||||||
struct hwtstamp_config ts_config;
|
struct hwtstamp_config ts_config;
|
||||||
struct ifreq req;
|
struct ifreq req;
|
||||||
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
struct Interface *iface;
|
struct Interface *iface;
|
||||||
|
|
||||||
@@ -244,12 +237,18 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
|||||||
iface->l2_udp4_ntp_start = 42;
|
iface->l2_udp4_ntp_start = 42;
|
||||||
iface->l2_udp6_ntp_start = 62;
|
iface->l2_udp6_ntp_start = 62;
|
||||||
|
|
||||||
iface->precision = conf_iface->precision;
|
|
||||||
iface->tx_comp = conf_iface->tx_comp;
|
iface->tx_comp = conf_iface->tx_comp;
|
||||||
iface->rx_comp = conf_iface->rx_comp;
|
iface->rx_comp = conf_iface->rx_comp;
|
||||||
|
|
||||||
|
minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
|
||||||
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
|
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
|
||||||
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
|
UTI_Log2ToDouble(minpoll), conf_iface->precision);
|
||||||
|
|
||||||
|
iface->maxpoll = CLAMP(minpoll, conf_iface->maxpoll, MAX_PHC_POLL);
|
||||||
|
|
||||||
|
/* Do not schedule the first poll timeout here! The argument (interface) can
|
||||||
|
move until all interfaces are added. Wait for the first HW timestamp. */
|
||||||
|
iface->poll_timeout_id = 0;
|
||||||
|
|
||||||
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
|
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
|
||||||
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
|
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
|
||||||
@@ -414,8 +413,6 @@ NIO_Linux_Initialise(void)
|
|||||||
/* Kernels before 4.7 ignore timestamping flags set in control messages */
|
/* Kernels before 4.7 ignore timestamping flags set in control messages */
|
||||||
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
|
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
|
||||||
|
|
||||||
monitored_socket = INVALID_SOCK_FD;
|
|
||||||
suspended_socket = INVALID_SOCK_FD;
|
|
||||||
dummy_rxts_socket = INVALID_SOCK_FD;
|
dummy_rxts_socket = INVALID_SOCK_FD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,6 +429,7 @@ NIO_Linux_Finalise(void)
|
|||||||
|
|
||||||
for (i = 0; i < ARR_GetSize(interfaces); i++) {
|
for (i = 0; i < ARR_GetSize(interfaces); i++) {
|
||||||
iface = ARR_GetElement(interfaces, i);
|
iface = ARR_GetElement(interfaces, i);
|
||||||
|
SCH_RemoveTimeout(iface->poll_timeout_id);
|
||||||
HCL_DestroyInstance(iface->clock);
|
HCL_DestroyInstance(iface->clock);
|
||||||
close(iface->phc_fd);
|
close(iface->phc_fd);
|
||||||
}
|
}
|
||||||
@@ -474,73 +472,6 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
|
||||||
resume_socket(int sock_fd)
|
|
||||||
{
|
|
||||||
if (monitored_socket == sock_fd)
|
|
||||||
monitored_socket = INVALID_SOCK_FD;
|
|
||||||
|
|
||||||
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
|
|
||||||
return;
|
|
||||||
|
|
||||||
suspended_socket = INVALID_SOCK_FD;
|
|
||||||
|
|
||||||
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
|
|
||||||
|
|
||||||
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
|
|
||||||
resume_timeout_id ? "before" : "on", sock_fd);
|
|
||||||
|
|
||||||
if (resume_timeout_id) {
|
|
||||||
SCH_RemoveTimeout(resume_timeout_id);
|
|
||||||
resume_timeout_id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
resume_timeout(void *arg)
|
|
||||||
{
|
|
||||||
resume_timeout_id = 0;
|
|
||||||
resume_socket(suspended_socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
suspend_socket(int sock_fd)
|
|
||||||
{
|
|
||||||
resume_socket(suspended_socket);
|
|
||||||
|
|
||||||
suspended_socket = sock_fd;
|
|
||||||
|
|
||||||
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
|
|
||||||
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
|
|
||||||
|
|
||||||
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
int
|
|
||||||
NIO_Linux_ProcessEvent(int sock_fd, int event)
|
|
||||||
{
|
|
||||||
if (sock_fd != monitored_socket)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (event == SCH_FILE_INPUT) {
|
|
||||||
suspend_socket(monitored_socket);
|
|
||||||
monitored_socket = INVALID_SOCK_FD;
|
|
||||||
|
|
||||||
/* Don't process the message yet */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static struct Interface *
|
static struct Interface *
|
||||||
get_interface(int if_index)
|
get_interface(int if_index)
|
||||||
{
|
{
|
||||||
@@ -560,26 +491,70 @@ get_interface(int if_index)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
poll_timeout(void *arg)
|
||||||
|
{
|
||||||
|
struct Interface *iface = arg;
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
iface->poll_timeout_id = 0;
|
||||||
|
|
||||||
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
poll_phc(iface, &now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
poll_phc(struct Interface *iface, struct timespec *now)
|
||||||
|
{
|
||||||
|
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
|
||||||
|
struct timespec phc_readings[PHC_READINGS][3];
|
||||||
|
double phc_err, local_err, interval;
|
||||||
|
int n_readings;
|
||||||
|
|
||||||
|
if (!HCL_NeedsNewSample(iface->clock, now))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DEBUG_LOG("Polling PHC on %s%s",
|
||||||
|
iface->name, iface->poll_timeout_id != 0 ? " before timeout" : "");
|
||||||
|
|
||||||
|
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
|
||||||
|
&iface->phc_mode, PHC_READINGS, phc_readings);
|
||||||
|
|
||||||
|
/* Add timeout for the next poll in case no HW timestamp will be captured
|
||||||
|
between the minpoll and maxpoll. Separate reading of different PHCs to
|
||||||
|
avoid long intervals between handling I/O events. */
|
||||||
|
SCH_RemoveTimeout(iface->poll_timeout_id);
|
||||||
|
interval = UTI_Log2ToDouble(iface->maxpoll);
|
||||||
|
iface->poll_timeout_id = SCH_AddTimeoutInClass(interval, interval /
|
||||||
|
ARR_GetSize(interfaces) / 4, 0.1,
|
||||||
|
SCH_PhcPollClass, poll_timeout, iface);
|
||||||
|
|
||||||
|
if (n_readings <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
|
||||||
|
&sample_phc_ts, &sample_sys_ts, &phc_err))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
||||||
|
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, phc_err + local_err);
|
||||||
|
|
||||||
|
update_interface_speed(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
||||||
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
|
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
|
||||||
int l2_length)
|
int l2_length)
|
||||||
{
|
{
|
||||||
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
|
double rx_correction, ts_delay, local_err;
|
||||||
double rx_correction, ts_delay, phc_err, local_err;
|
struct timespec ts;
|
||||||
|
|
||||||
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
|
poll_phc(iface, &local_ts->ts);
|
||||||
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
|
|
||||||
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
|
|
||||||
&phc_err))
|
|
||||||
return;
|
|
||||||
|
|
||||||
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
|
||||||
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
|
|
||||||
phc_err + local_err);
|
|
||||||
|
|
||||||
update_interface_speed(iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to transpose RX timestamps as hardware timestamps are normally
|
/* We need to transpose RX timestamps as hardware timestamps are normally
|
||||||
preamble timestamps and RX timestamps in NTP are supposed to be trailer
|
preamble timestamps and RX timestamps in NTP are supposed to be trailer
|
||||||
@@ -757,11 +732,6 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
|||||||
} else {
|
} else {
|
||||||
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
|
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a HW transmit timestamp was received, resume processing
|
|
||||||
of non-error messages on this socket */
|
|
||||||
if (is_tx)
|
|
||||||
resume_socket(local_addr->sock_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
|
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
|
||||||
@@ -824,23 +794,9 @@ NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
|
|||||||
if (!ts_flags)
|
if (!ts_flags)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If a HW transmit timestamp is requested on a client socket, monitor
|
|
||||||
events on the socket in order to avoid processing of a fast response
|
|
||||||
without the HW timestamp of the request */
|
|
||||||
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
|
|
||||||
monitored_socket = sock_fd;
|
|
||||||
|
|
||||||
/* Check if TX timestamping is disabled on this socket */
|
/* Check if TX timestamping is disabled on this socket */
|
||||||
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
|
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
message->timestamp.tx_flags = ts_tx_flags;
|
message->timestamp.tx_flags = ts_tx_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
void
|
|
||||||
NIO_Linux_NotifySocketClosing(int sock_fd)
|
|
||||||
{
|
|
||||||
resume_socket(sock_fd);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,13 +35,9 @@ extern void NIO_Linux_Finalise(void);
|
|||||||
|
|
||||||
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
|
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
|
||||||
|
|
||||||
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
|
|
||||||
|
|
||||||
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
||||||
NTP_Local_Timestamp *local_ts, int event);
|
NTP_Local_Timestamp *local_ts, int event);
|
||||||
|
|
||||||
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
|
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
|
||||||
|
|
||||||
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
180
ntp_sources.c
180
ntp_sources.c
@@ -32,6 +32,7 @@
|
|||||||
#include "sysincl.h"
|
#include "sysincl.h"
|
||||||
|
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
|
#include "conf.h"
|
||||||
#include "ntp_sources.h"
|
#include "ntp_sources.h"
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
@@ -64,6 +65,7 @@ typedef struct {
|
|||||||
received from the source yet */
|
received from the source yet */
|
||||||
uint32_t conf_id; /* Configuration ID, which can be shared with
|
uint32_t conf_id; /* Configuration ID, which can be shared with
|
||||||
different sources in case of a pool */
|
different sources in case of a pool */
|
||||||
|
double last_resolving; /* Time of last name resolving (monotonic) */
|
||||||
} SourceRecord;
|
} SourceRecord;
|
||||||
|
|
||||||
/* Hash table of SourceRecord, its size is a power of two and it's never
|
/* Hash table of SourceRecord, its size is a power of two and it's never
|
||||||
@@ -96,6 +98,9 @@ struct UnresolvedSource {
|
|||||||
char *name;
|
char *name;
|
||||||
/* Flag indicating addresses should be used in a random order */
|
/* Flag indicating addresses should be used in a random order */
|
||||||
int random_order;
|
int random_order;
|
||||||
|
/* Flag indicating current address should be replaced only if it is
|
||||||
|
no longer returned by the resolver */
|
||||||
|
int refreshment;
|
||||||
/* Next unresolved source in the list */
|
/* Next unresolved source in the list */
|
||||||
struct UnresolvedSource *next;
|
struct UnresolvedSource *next;
|
||||||
};
|
};
|
||||||
@@ -103,7 +108,7 @@ struct UnresolvedSource {
|
|||||||
#define RESOLVE_INTERVAL_UNIT 7
|
#define RESOLVE_INTERVAL_UNIT 7
|
||||||
#define MIN_RESOLVE_INTERVAL 2
|
#define MIN_RESOLVE_INTERVAL 2
|
||||||
#define MAX_RESOLVE_INTERVAL 9
|
#define MAX_RESOLVE_INTERVAL 9
|
||||||
#define MIN_REPLACEMENT_INTERVAL 8
|
#define MAX_REPLACEMENT_INTERVAL 9
|
||||||
|
|
||||||
static struct UnresolvedSource *unresolved_sources = NULL;
|
static struct UnresolvedSource *unresolved_sources = NULL;
|
||||||
static int resolving_interval = 0;
|
static int resolving_interval = 0;
|
||||||
@@ -184,6 +189,7 @@ void
|
|||||||
NSR_Initialise(void)
|
NSR_Initialise(void)
|
||||||
{
|
{
|
||||||
n_sources = 0;
|
n_sources = 0;
|
||||||
|
resolving_id = 0;
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
|
|
||||||
records = ARR_CreateInstance(sizeof (SourceRecord));
|
records = ARR_CreateInstance(sizeof (SourceRecord));
|
||||||
@@ -206,6 +212,7 @@ NSR_Finalise(void)
|
|||||||
ARR_DestroyInstance(records);
|
ARR_DestroyInstance(records);
|
||||||
ARR_DestroyInstance(pools);
|
ARR_DestroyInstance(pools);
|
||||||
|
|
||||||
|
SCH_RemoveTimeout(resolving_id);
|
||||||
while (unresolved_sources)
|
while (unresolved_sources)
|
||||||
remove_unresolved_source(unresolved_sources);
|
remove_unresolved_source(unresolved_sources);
|
||||||
|
|
||||||
@@ -317,6 +324,31 @@ rehash_records(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_source(SourceRecord *record, int addition, int once_per_pool)
|
||||||
|
{
|
||||||
|
int pool, log_addr;
|
||||||
|
char *ip_str;
|
||||||
|
|
||||||
|
if (once_per_pool && record->pool_id != INVALID_POOL) {
|
||||||
|
if (get_pool(record->pool_id)->sources > 1)
|
||||||
|
return;
|
||||||
|
pool = 1;
|
||||||
|
log_addr = 0;
|
||||||
|
} else {
|
||||||
|
ip_str = UTI_IPToString(&record->remote_addr->ip_addr);
|
||||||
|
pool = 0;
|
||||||
|
log_addr = strcmp(record->name, ip_str) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(LOG_GetContextSeverity(LOGC_Command | LOGC_SourceFile), "%s %s %s%s%s%s",
|
||||||
|
addition ? "Added" : "Removed", pool ? "pool" : "source",
|
||||||
|
log_addr ? ip_str : record->name,
|
||||||
|
log_addr ? " (" : "", log_addr ? record->name : "", log_addr ? ")" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
/* Procedure to add a new source */
|
/* Procedure to add a new source */
|
||||||
static NSR_Status
|
static NSR_Status
|
||||||
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
||||||
@@ -353,13 +385,13 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
|||||||
record_lock = 1;
|
record_lock = 1;
|
||||||
|
|
||||||
record = get_record(slot);
|
record = get_record(slot);
|
||||||
assert(!name || !UTI_IsStringIP(name));
|
|
||||||
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
|
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
|
||||||
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
|
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
|
||||||
record->remote_addr = NCR_GetRemoteAddress(record->data);
|
record->remote_addr = NCR_GetRemoteAddress(record->data);
|
||||||
record->pool_id = pool_id;
|
record->pool_id = pool_id;
|
||||||
record->tentative = 1;
|
record->tentative = 1;
|
||||||
record->conf_id = conf_id;
|
record->conf_id = conf_id;
|
||||||
|
record->last_resolving = SCH_GetLastEventMonoTime();
|
||||||
|
|
||||||
record_lock = 0;
|
record_lock = 0;
|
||||||
|
|
||||||
@@ -372,6 +404,8 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
|||||||
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
|
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
|
||||||
NCR_StartInstance(record->data);
|
NCR_StartInstance(record->data);
|
||||||
|
|
||||||
|
log_source(record, 1, 1);
|
||||||
|
|
||||||
/* The new instance is allowed to change its address immediately */
|
/* The new instance is allowed to change its address immediately */
|
||||||
handle_saved_address_update();
|
handle_saved_address_update();
|
||||||
|
|
||||||
@@ -491,6 +525,19 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
|
|||||||
unsigned short first = 0;
|
unsigned short first = 0;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
/* Keep using the current address if it is being refreshed and it is
|
||||||
|
still included in the resolved addresses */
|
||||||
|
if (us->refreshment) {
|
||||||
|
assert(us->pool_id == INVALID_POOL);
|
||||||
|
|
||||||
|
for (i = 0; i < n_addrs; i++) {
|
||||||
|
if (UTI_CompareIPs(&us->address.ip_addr, &ip_addrs[i], NULL) == 0) {
|
||||||
|
DEBUG_LOG("%s still fresh", UTI_IPToString(&us->address.ip_addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (us->random_order)
|
if (us->random_order)
|
||||||
UTI_GetRandomBytes(&first, sizeof (first));
|
UTI_GetRandomBytes(&first, sizeof (first));
|
||||||
|
|
||||||
@@ -698,21 +745,25 @@ static int get_unused_pool_id(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
get_next_conf_id(uint32_t *conf_id)
|
||||||
|
{
|
||||||
|
last_conf_id++;
|
||||||
|
|
||||||
|
if (conf_id)
|
||||||
|
*conf_id = last_conf_id;
|
||||||
|
|
||||||
|
return last_conf_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
NSR_Status
|
NSR_Status
|
||||||
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||||
SourceParameters *params, uint32_t *conf_id)
|
SourceParameters *params, uint32_t *conf_id)
|
||||||
{
|
{
|
||||||
NSR_Status s;
|
return add_source(remote_addr, NULL, type, params, INVALID_POOL,
|
||||||
|
get_next_conf_id(conf_id));
|
||||||
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
|
|
||||||
if (s != NSR_Success)
|
|
||||||
return s;
|
|
||||||
|
|
||||||
last_conf_id++;
|
|
||||||
if (conf_id)
|
|
||||||
*conf_id = last_conf_id;
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -725,11 +776,13 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
|||||||
struct SourcePool *sp;
|
struct SourcePool *sp;
|
||||||
NTP_Remote_Address remote_addr;
|
NTP_Remote_Address remote_addr;
|
||||||
int i, new_sources, pool_id;
|
int i, new_sources, pool_id;
|
||||||
|
uint32_t cid;
|
||||||
|
|
||||||
/* If the name is an IP address, add the source with the address directly */
|
/* If the name is an IP address, add the source with the address directly */
|
||||||
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
|
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
|
||||||
remote_addr.port = port;
|
remote_addr.port = port;
|
||||||
return NSR_AddSource(&remote_addr, type, params, conf_id);
|
return add_source(&remote_addr, name, type, params, INVALID_POOL,
|
||||||
|
get_next_conf_id(conf_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the name is at least printable and has no spaces */
|
/* Make sure the name is at least printable and has no spaces */
|
||||||
@@ -741,6 +794,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
|||||||
us = MallocNew(struct UnresolvedSource);
|
us = MallocNew(struct UnresolvedSource);
|
||||||
us->name = Strdup(name);
|
us->name = Strdup(name);
|
||||||
us->random_order = 0;
|
us->random_order = 0;
|
||||||
|
us->refreshment = 0;
|
||||||
|
|
||||||
remote_addr.ip_addr.family = IPADDR_ID;
|
remote_addr.ip_addr.family = IPADDR_ID;
|
||||||
remote_addr.ip_addr.addr.id = ++last_address_id;
|
remote_addr.ip_addr.addr.id = ++last_address_id;
|
||||||
@@ -770,14 +824,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
|||||||
|
|
||||||
append_unresolved_source(us);
|
append_unresolved_source(us);
|
||||||
|
|
||||||
last_conf_id++;
|
cid = get_next_conf_id(conf_id);
|
||||||
if (conf_id)
|
|
||||||
*conf_id = last_conf_id;
|
|
||||||
|
|
||||||
for (i = 0; i < new_sources; i++) {
|
for (i = 0; i < new_sources; i++) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
remote_addr.ip_addr.addr.id = ++last_address_id;
|
remote_addr.ip_addr.addr.id = ++last_address_id;
|
||||||
if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
|
if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success)
|
||||||
return NSR_TooManySources;
|
return NSR_TooManySources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,6 +933,7 @@ NSR_RemoveSource(IPAddr *address)
|
|||||||
if (find_slot(address, &slot) == 0)
|
if (find_slot(address, &slot) == 0)
|
||||||
return NSR_NoSuchSource;
|
return NSR_NoSuchSource;
|
||||||
|
|
||||||
|
log_source(get_record(slot), 0, 0);
|
||||||
clean_source_record(get_record(slot));
|
clean_source_record(get_record(slot));
|
||||||
|
|
||||||
/* Rehash the table to make sure there are no broken probe sequences.
|
/* Rehash the table to make sure there are no broken probe sequences.
|
||||||
@@ -903,6 +956,7 @@ NSR_RemoveSourcesById(uint32_t conf_id)
|
|||||||
record = get_record(i);
|
record = get_record(i);
|
||||||
if (!record->remote_addr || record->conf_id != conf_id)
|
if (!record->remote_addr || record->conf_id != conf_id)
|
||||||
continue;
|
continue;
|
||||||
|
log_source(record, 0, 1);
|
||||||
clean_source_record(record);
|
clean_source_record(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,20 +984,23 @@ NSR_RemoveAllSources(void)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
resolve_source_replacement(SourceRecord *record)
|
resolve_source_replacement(SourceRecord *record, int refreshment)
|
||||||
{
|
{
|
||||||
struct UnresolvedSource *us;
|
struct UnresolvedSource *us;
|
||||||
|
|
||||||
DEBUG_LOG("trying to replace %s (%s)",
|
DEBUG_LOG("%s %s (%s)", refreshment ? "refreshing" : "trying to replace",
|
||||||
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
|
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
|
||||||
|
|
||||||
|
record->last_resolving = SCH_GetLastEventMonoTime();
|
||||||
|
|
||||||
us = MallocNew(struct UnresolvedSource);
|
us = MallocNew(struct UnresolvedSource);
|
||||||
us->name = Strdup(record->name);
|
us->name = Strdup(record->name);
|
||||||
/* If there never was a valid reply from this source (e.g. it was a bad
|
/* Ignore the order of addresses from the resolver to not get
|
||||||
replacement), ignore the order of addresses from the resolver to not get
|
stuck with a pair of unreachable or otherwise unusable servers
|
||||||
stuck to a pair of addresses if the order doesn't change, or a group of
|
(e.g. falsetickers) in case the order doesn't change, or a group
|
||||||
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
|
of servers if they are ordered by IP family */
|
||||||
us->random_order = record->tentative;
|
us->random_order = 1;
|
||||||
|
us->refreshment = refreshment;
|
||||||
us->pool_id = INVALID_POOL;
|
us->pool_id = INVALID_POOL;
|
||||||
us->address = *record->remote_addr;
|
us->address = *record->remote_addr;
|
||||||
|
|
||||||
@@ -956,11 +1013,11 @@ resolve_source_replacement(SourceRecord *record)
|
|||||||
void
|
void
|
||||||
NSR_HandleBadSource(IPAddr *address)
|
NSR_HandleBadSource(IPAddr *address)
|
||||||
{
|
{
|
||||||
static struct timespec last_replacement;
|
static double next_replacement = 0.0;
|
||||||
struct timespec now;
|
|
||||||
SourceRecord *record;
|
SourceRecord *record;
|
||||||
IPAddr ip_addr;
|
IPAddr ip_addr;
|
||||||
double diff;
|
uint32_t rnd;
|
||||||
|
double now;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
if (!find_slot(address, &slot))
|
if (!find_slot(address, &slot))
|
||||||
@@ -975,15 +1032,56 @@ NSR_HandleBadSource(IPAddr *address)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Don't resolve names too frequently */
|
/* Don't resolve names too frequently */
|
||||||
SCH_GetLastEventTime(NULL, NULL, &now);
|
now = SCH_GetLastEventMonoTime();
|
||||||
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
|
if (now < next_replacement) {
|
||||||
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
|
|
||||||
DEBUG_LOG("replacement postponed");
|
DEBUG_LOG("replacement postponed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
last_replacement = now;
|
|
||||||
|
|
||||||
resolve_source_replacement(record);
|
UTI_GetRandomBytes(&rnd, sizeof (rnd));
|
||||||
|
next_replacement = now + ((double)rnd / (uint32_t)-1) *
|
||||||
|
(RESOLVE_INTERVAL_UNIT * (1 << MAX_REPLACEMENT_INTERVAL));
|
||||||
|
|
||||||
|
resolve_source_replacement(record, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_refresh_source(void)
|
||||||
|
{
|
||||||
|
static double last_refreshment = 0.0;
|
||||||
|
SourceRecord *record, *oldest_record;
|
||||||
|
int i, min_interval;
|
||||||
|
double now;
|
||||||
|
|
||||||
|
min_interval = CNF_GetRefresh();
|
||||||
|
|
||||||
|
now = SCH_GetLastEventMonoTime();
|
||||||
|
if (min_interval <= 0 || now < last_refreshment + min_interval)
|
||||||
|
return;
|
||||||
|
|
||||||
|
last_refreshment = now;
|
||||||
|
|
||||||
|
for (i = 0, oldest_record = NULL; i < ARR_GetSize(records); i++) {
|
||||||
|
record = get_record(i);
|
||||||
|
if (!record->remote_addr || UTI_IsStringIP(record->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!oldest_record || oldest_record->last_resolving > record->last_resolving)
|
||||||
|
oldest_record = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!oldest_record)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Check if the name wasn't already resolved in the last interval */
|
||||||
|
if (now < oldest_record->last_resolving + min_interval) {
|
||||||
|
last_refreshment = oldest_record->last_resolving;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_source_replacement(oldest_record, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -999,7 +1097,7 @@ NSR_RefreshAddresses(void)
|
|||||||
if (!record->remote_addr)
|
if (!record->remote_addr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
resolve_source_replacement(record);
|
resolve_source_replacement(record, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1100,8 +1198,10 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
|||||||
|
|
||||||
assert(initialised);
|
assert(initialised);
|
||||||
|
|
||||||
/* Must match IP address AND port number */
|
/* Avoid unnecessary lookup if the packet cannot be a response from our
|
||||||
if (find_slot2(remote_addr, &slot) == 2) {
|
source. Otherwise, it must match both IP address and port number. */
|
||||||
|
if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
|
||||||
|
find_slot2(remote_addr, &slot) == 2) {
|
||||||
record = get_record(slot);
|
record = get_record(slot);
|
||||||
|
|
||||||
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
|
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
|
||||||
@@ -1123,6 +1223,8 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
|||||||
remove_pool_sources(record->pool_id, 1, 0);
|
remove_pool_sources(record->pool_id, 1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybe_refresh_source();
|
||||||
} else {
|
} else {
|
||||||
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
|
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
|
||||||
}
|
}
|
||||||
@@ -1137,8 +1239,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
|||||||
SourceRecord *record;
|
SourceRecord *record;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
/* Must match IP address AND port number */
|
/* Avoid unnecessary lookup if the packet cannot be a request to our
|
||||||
if (find_slot2(remote_addr, &slot) == 2) {
|
source. Otherwise, it must match both IP address and port number. */
|
||||||
|
if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
|
||||||
|
find_slot2(remote_addr, &slot) == 2) {
|
||||||
record = get_record(slot);
|
record = get_record(slot);
|
||||||
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
|
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -102,16 +102,22 @@ static int
|
|||||||
prepare_request(NKC_Instance inst)
|
prepare_request(NKC_Instance inst)
|
||||||
{
|
{
|
||||||
NKSN_Instance session = inst->session;
|
NKSN_Instance session = inst->session;
|
||||||
uint16_t datum;
|
uint16_t data[2];
|
||||||
|
int length;
|
||||||
|
|
||||||
NKSN_BeginMessage(session);
|
NKSN_BeginMessage(session);
|
||||||
|
|
||||||
datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
|
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
datum = htons(AEAD_AES_SIV_CMAC_256);
|
length = 0;
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
|
||||||
|
data[length++] = htons(AEAD_AES_128_GCM_SIV);
|
||||||
|
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
|
||||||
|
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
|
||||||
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
|
||||||
|
length * sizeof (data[0])))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!NKSN_EndMessage(session))
|
if (!NKSN_EndMessage(session))
|
||||||
@@ -159,12 +165,14 @@ process_response(NKC_Instance inst)
|
|||||||
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
|
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_AEAD_ALGORITHM:
|
case NKE_RECORD_AEAD_ALGORITHM:
|
||||||
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
|
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
|
||||||
|
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
|
||||||
|
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
|
||||||
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
|
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
|
||||||
error = 1;
|
error = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
aead_algorithm = AEAD_AES_SIV_CMAC_256;
|
aead_algorithm = ntohs(data[0]);
|
||||||
inst->context.algorithm = aead_algorithm;
|
inst->context.algorithm = aead_algorithm;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_ERROR:
|
case NKE_RECORD_ERROR:
|
||||||
@@ -236,7 +244,7 @@ process_response(NKC_Instance inst)
|
|||||||
|
|
||||||
if (error || inst->num_cookies == 0 ||
|
if (error || inst->num_cookies == 0 ||
|
||||||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
|
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
|
||||||
aead_algorithm != AEAD_AES_SIV_CMAC_256)
|
aead_algorithm < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -370,6 +378,13 @@ NKC_Start(NKC_Instance inst)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't try to connect if missing the algorithm which all servers
|
||||||
|
are required to support */
|
||||||
|
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) <= 0) {
|
||||||
|
LOG(LOGS_ERR, "Missing AES-SIV-CMAC-256");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Follow the bindacqaddress and bindacqdevice settings */
|
/* Follow the bindacqaddress and bindacqdevice settings */
|
||||||
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
|
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
|
||||||
local_addr.port = 0;
|
local_addr.port = 0;
|
||||||
|
|||||||
205
nts_ke_server.c
205
nts_ke_server.c
@@ -47,31 +47,33 @@
|
|||||||
|
|
||||||
#define SERVER_TIMEOUT 2.0
|
#define SERVER_TIMEOUT 2.0
|
||||||
|
|
||||||
#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
|
#define MAX_COOKIE_NONCE_LENGTH 16
|
||||||
#define SERVER_COOKIE_NONCE_LENGTH 16
|
|
||||||
|
|
||||||
#define KEY_ID_INDEX_BITS 2
|
#define KEY_ID_INDEX_BITS 2
|
||||||
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
|
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
|
||||||
#define FUTURE_KEYS 1
|
#define FUTURE_KEYS 1
|
||||||
|
|
||||||
#define DUMP_FILENAME "ntskeys"
|
#define DUMP_FILENAME "ntskeys"
|
||||||
#define DUMP_IDENTIFIER "NKS0\n"
|
#define DUMP_IDENTIFIER "NKS1\n"
|
||||||
|
#define OLD_DUMP_IDENTIFIER "NKS0\n"
|
||||||
|
|
||||||
#define INVALID_SOCK_FD (-7)
|
#define INVALID_SOCK_FD (-7)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t key_id;
|
uint32_t key_id;
|
||||||
unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
|
|
||||||
} ServerCookieHeader;
|
} ServerCookieHeader;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
unsigned char key[SIV_MAX_KEY_LENGTH];
|
unsigned char key[SIV_MAX_KEY_LENGTH];
|
||||||
|
SIV_Algorithm siv_algorithm;
|
||||||
SIV_Instance siv;
|
SIV_Instance siv;
|
||||||
|
int nonce_length;
|
||||||
} ServerKey;
|
} ServerKey;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t key_id;
|
uint32_t key_id;
|
||||||
|
uint32_t siv_algorithm;
|
||||||
unsigned char key[SIV_MAX_KEY_LENGTH];
|
unsigned char key[SIV_MAX_KEY_LENGTH];
|
||||||
IPAddr client_addr;
|
IPAddr client_addr;
|
||||||
uint16_t client_port;
|
uint16_t client_port;
|
||||||
@@ -148,12 +150,30 @@ handle_client(int sock_fd, IPSockAddr *addr)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_key_siv(ServerKey *key, SIV_Algorithm algorithm)
|
||||||
|
{
|
||||||
|
if (!key->siv || key->siv_algorithm != algorithm) {
|
||||||
|
if (key->siv)
|
||||||
|
SIV_DestroyInstance(key->siv);
|
||||||
|
key->siv_algorithm = algorithm;
|
||||||
|
key->siv = SIV_CreateInstance(algorithm);
|
||||||
|
key->nonce_length = MIN(SIV_GetMaxNonceLength(key->siv), MAX_COOKIE_NONCE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key->siv || !SIV_SetKey(key->siv, key->key, SIV_GetKeyLength(key->siv_algorithm)))
|
||||||
|
LOG_FATAL("Could not set SIV key");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_helper_request(int fd, int event, void *arg)
|
handle_helper_request(int fd, int event, void *arg)
|
||||||
{
|
{
|
||||||
SCK_Message *message;
|
SCK_Message *message;
|
||||||
HelperRequest *req;
|
HelperRequest *req;
|
||||||
IPSockAddr client_addr;
|
IPSockAddr client_addr;
|
||||||
|
ServerKey *key;
|
||||||
int sock_fd;
|
int sock_fd;
|
||||||
|
|
||||||
/* Receive the helper request with the NTS-KE session socket.
|
/* Receive the helper request with the NTS-KE session socket.
|
||||||
@@ -181,16 +201,14 @@ handle_helper_request(int fd, int event, void *arg)
|
|||||||
req = message->data;
|
req = message->data;
|
||||||
|
|
||||||
/* Extract the current server key and client address from the request */
|
/* Extract the current server key and client address from the request */
|
||||||
server_keys[current_server_key].id = ntohl(req->key_id);
|
key = &server_keys[current_server_key];
|
||||||
assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key));
|
key->id = ntohl(req->key_id);
|
||||||
memcpy(server_keys[current_server_key].key, req->key,
|
assert(sizeof (key->key) == sizeof (req->key));
|
||||||
sizeof (server_keys[current_server_key].key));
|
memcpy(key->key, req->key, sizeof (key->key));
|
||||||
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
|
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
|
||||||
client_addr.port = ntohs(req->client_port);
|
client_addr.port = ntohs(req->client_port);
|
||||||
|
|
||||||
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
|
update_key_siv(key, ntohl(req->siv_algorithm));
|
||||||
SIV_GetKeyLength(SERVER_COOKIE_SIV)))
|
|
||||||
LOG_FATAL("Could not set SIV key");
|
|
||||||
|
|
||||||
if (!handle_client(sock_fd, &client_addr)) {
|
if (!handle_client(sock_fd, &client_addr)) {
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
@@ -240,6 +258,7 @@ accept_connection(int listening_fd, int event, void *arg)
|
|||||||
|
|
||||||
/* Include the current server key and client address in the request */
|
/* Include the current server key and client address in the request */
|
||||||
req.key_id = htonl(server_keys[current_server_key].id);
|
req.key_id = htonl(server_keys[current_server_key].id);
|
||||||
|
req.siv_algorithm = htonl(server_keys[current_server_key].siv_algorithm);
|
||||||
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
|
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
|
||||||
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
|
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
|
||||||
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
|
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
|
||||||
@@ -427,8 +446,9 @@ process_request(NKSN_Instance session)
|
|||||||
|
|
||||||
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
|
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
|
||||||
aead_algorithm_values++;
|
aead_algorithm_values++;
|
||||||
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
|
/* Use the first supported algorithm */
|
||||||
aead_algorithm = AEAD_AES_SIV_CMAC_256;
|
if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
|
||||||
|
aead_algorithm = ntohs(data[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_ERROR:
|
case NKE_RECORD_ERROR:
|
||||||
@@ -470,28 +490,38 @@ handle_message(void *arg)
|
|||||||
static void
|
static void
|
||||||
generate_key(int index)
|
generate_key(int index)
|
||||||
{
|
{
|
||||||
|
SIV_Algorithm algorithm;
|
||||||
|
ServerKey *key;
|
||||||
int key_length;
|
int key_length;
|
||||||
|
|
||||||
if (index < 0 || index >= MAX_SERVER_KEYS)
|
if (index < 0 || index >= MAX_SERVER_KEYS)
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
|
/* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
|
||||||
if (key_length > sizeof (server_keys[index].key))
|
from ntsdumpdir use a different algorithm, responding to NTP requests
|
||||||
|
with cookies encrypted with those keys will not work if the new algorithm
|
||||||
|
produces longer cookies (i.e. response would be longer than request).
|
||||||
|
Switching from AES-SIV-CMAC-256 to AES-128-GCM-SIV is ok. */
|
||||||
|
algorithm = SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
|
||||||
|
AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256;
|
||||||
|
|
||||||
|
key = &server_keys[index];
|
||||||
|
|
||||||
|
key_length = SIV_GetKeyLength(algorithm);
|
||||||
|
if (key_length > sizeof (key->key))
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
|
UTI_GetRandomBytesUrandom(key->key, key_length);
|
||||||
|
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
|
||||||
if (!server_keys[index].siv ||
|
UTI_GetRandomBytes(&key->id, sizeof (key->id));
|
||||||
!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
|
|
||||||
LOG_FATAL("Could not set SIV key");
|
|
||||||
|
|
||||||
UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
|
|
||||||
|
|
||||||
/* Encode the index in the lowest bits of the ID */
|
/* Encode the index in the lowest bits of the ID */
|
||||||
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
|
key->id &= -1U << KEY_ID_INDEX_BITS;
|
||||||
server_keys[index].id |= index;
|
key->id |= index;
|
||||||
|
|
||||||
DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
|
update_key_siv(key, algorithm);
|
||||||
|
|
||||||
|
DEBUG_LOG("Generated key %08"PRIX32" (%d)", key->id, (int)key->siv_algorithm);
|
||||||
|
|
||||||
last_server_key_ts = SCH_GetLastEventMonoTime();
|
last_server_key_ts = SCH_GetLastEventMonoTime();
|
||||||
}
|
}
|
||||||
@@ -519,18 +549,19 @@ save_keys(void)
|
|||||||
if (!f)
|
if (!f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
|
|
||||||
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
|
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
|
||||||
|
|
||||||
if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0)
|
if (fprintf(f, "%s%.1f\n", DUMP_IDENTIFIER, last_key_age) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
||||||
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
|
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
|
||||||
|
key_length = SIV_GetKeyLength(server_keys[index].siv_algorithm);
|
||||||
|
|
||||||
if (key_length > sizeof (server_keys[index].key) ||
|
if (key_length > sizeof (server_keys[index].key) ||
|
||||||
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
|
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
|
||||||
fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0)
|
fprintf(f, "%08"PRIX32" %s %d\n", server_keys[index].id, buf,
|
||||||
|
(int)server_keys[index].siv_algorithm) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +576,7 @@ save_keys(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
DEBUG_LOG("Could not %s server keys", "save");
|
LOG(LOGS_ERR, "Could not %s %s", "save", "server NTS keys");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
|
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
|
||||||
@@ -554,17 +585,16 @@ error:
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
#define MAX_WORDS 2
|
#define MAX_WORDS 3
|
||||||
|
|
||||||
static int
|
static int
|
||||||
load_keys(void)
|
load_keys(void)
|
||||||
{
|
{
|
||||||
|
int i, index, key_length, algorithm = 0, old_ver;
|
||||||
char *dump_dir, line[1024], *words[MAX_WORDS];
|
char *dump_dir, line[1024], *words[MAX_WORDS];
|
||||||
unsigned char key[SIV_MAX_KEY_LENGTH];
|
ServerKey new_keys[MAX_SERVER_KEYS];
|
||||||
int i, index, key_length, algorithm;
|
|
||||||
double key_age;
|
double key_age;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
uint32_t id;
|
|
||||||
|
|
||||||
dump_dir = CNF_GetNtsDumpDir();
|
dump_dir = CNF_GetNtsDumpDir();
|
||||||
if (!dump_dir)
|
if (!dump_dir)
|
||||||
@@ -574,43 +604,58 @@ load_keys(void)
|
|||||||
if (!f)
|
if (!f)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
|
if (!fgets(line, sizeof (line), f) ||
|
||||||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
|
(strcmp(line, DUMP_IDENTIFIER) != 0 && strcmp(line, OLD_DUMP_IDENTIFIER) != 0))
|
||||||
sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
|
|
||||||
sscanf(words[1], "%lf", &key_age) != 1)
|
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
|
old_ver = strcmp(line, DUMP_IDENTIFIER) != 0;
|
||||||
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
|
|
||||||
|
if (!fgets(line, sizeof (line), f) ||
|
||||||
|
UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 1) ||
|
||||||
|
(old_ver && sscanf(words[0], "%d", &algorithm) != 1) ||
|
||||||
|
sscanf(words[old_ver ? 1 : 0], "%lf", &key_age) != 1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
|
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
|
||||||
if (UTI_SplitString(line, words, MAX_WORDS) != 2 ||
|
if (UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 3) ||
|
||||||
sscanf(words[0], "%"PRIX32, &id) != 1)
|
sscanf(words[0], "%"PRIX32, &new_keys[i].id) != 1 ||
|
||||||
|
(!old_ver && sscanf(words[2], "%d", &algorithm) != 1))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length)
|
new_keys[i].siv_algorithm = algorithm;
|
||||||
|
key_length = SIV_GetKeyLength(algorithm);
|
||||||
|
|
||||||
|
if ((i > 0 && (new_keys[i].id - new_keys[i - 1].id) % MAX_SERVER_KEYS != 1) ||
|
||||||
|
key_length <= 0 ||
|
||||||
|
UTI_HexToBytes(words[1], new_keys[i].key, sizeof (new_keys[i].key)) != key_length)
|
||||||
|
goto error;
|
||||||
|
memset(new_keys[i].key + key_length, 0, sizeof (new_keys[i].key) - key_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < MAX_SERVER_KEYS)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
index = id % MAX_SERVER_KEYS;
|
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
||||||
|
index = new_keys[i].id % MAX_SERVER_KEYS;
|
||||||
|
server_keys[index].id = new_keys[i].id;
|
||||||
|
memcpy(server_keys[index].key, new_keys[i].key, sizeof (server_keys[index].key));
|
||||||
|
|
||||||
server_keys[index].id = id;
|
update_key_siv(&server_keys[index], new_keys[i].siv_algorithm);
|
||||||
assert(sizeof (server_keys[index].key) == sizeof (key));
|
|
||||||
memcpy(server_keys[index].key, key, key_length);
|
|
||||||
|
|
||||||
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
|
DEBUG_LOG("Loaded key %08"PRIX32" (%d)",
|
||||||
LOG_FATAL("Could not set SIV key");
|
server_keys[index].id, (int)server_keys[index].siv_algorithm);
|
||||||
|
}
|
||||||
DEBUG_LOG("Loaded key %"PRIX32, id);
|
|
||||||
|
|
||||||
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
|
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
|
||||||
}
|
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
LOG(LOGS_ERR, "Loaded %s", "server NTS keys");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
DEBUG_LOG("Could not %s server keys", "load");
|
LOG(LOGS_ERR, "Could not %s %s", "load", "server NTS keys");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -759,7 +804,7 @@ NKS_Initialise(void)
|
|||||||
/* Generate random keys, even if they will be replaced by reloaded keys,
|
/* Generate random keys, even if they will be replaced by reloaded keys,
|
||||||
or unused (in the helper) */
|
or unused (in the helper) */
|
||||||
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
||||||
server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
|
server_keys[i].siv = NULL;
|
||||||
generate_key(i);
|
generate_key(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,6 +824,11 @@ NKS_Initialise(void)
|
|||||||
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
|
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
|
||||||
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
|
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Warn if keys are not saved, which can cause a flood of requests
|
||||||
|
after server restart */
|
||||||
|
if (!CNF_GetNtsDumpDir())
|
||||||
|
LOG(LOGS_WARN, "No ntsdumpdir to save server keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
@@ -852,7 +902,7 @@ NKS_ReloadKeys(void)
|
|||||||
int
|
int
|
||||||
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
||||||
{
|
{
|
||||||
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
||||||
int plaintext_length, tag_length;
|
int plaintext_length, tag_length;
|
||||||
ServerCookieHeader *header;
|
ServerCookieHeader *header;
|
||||||
ServerKey *key;
|
ServerKey *key;
|
||||||
@@ -862,14 +912,12 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The algorithm is hardcoded for now */
|
/* The AEAD ID is not encoded in the cookie. It is implied from the key
|
||||||
if (context->algorithm != AEAD_AES_SIV_CMAC_256) {
|
length (as long as only algorithms with different key lengths are
|
||||||
DEBUG_LOG("Unexpected SIV algorithm");
|
supported). */
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
|
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
|
||||||
context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) {
|
context->s2c.length != context->c2s.length) {
|
||||||
DEBUG_LOG("Invalid key length");
|
DEBUG_LOG("Invalid key length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -879,7 +927,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
header = (ServerCookieHeader *)cookie->cookie;
|
header = (ServerCookieHeader *)cookie->cookie;
|
||||||
|
|
||||||
header->key_id = htonl(key->id);
|
header->key_id = htonl(key->id);
|
||||||
UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
|
|
||||||
|
nonce = cookie->cookie + sizeof (*header);
|
||||||
|
if (key->nonce_length > sizeof (cookie->cookie) - sizeof (*header))
|
||||||
|
assert(0);
|
||||||
|
UTI_GetRandomBytes(nonce, key->nonce_length);
|
||||||
|
|
||||||
plaintext_length = context->c2s.length + context->s2c.length;
|
plaintext_length = context->c2s.length + context->s2c.length;
|
||||||
assert(plaintext_length <= sizeof (plaintext));
|
assert(plaintext_length <= sizeof (plaintext));
|
||||||
@@ -887,11 +939,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
|
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
|
||||||
|
|
||||||
tag_length = SIV_GetTagLength(key->siv);
|
tag_length = SIV_GetTagLength(key->siv);
|
||||||
cookie->length = sizeof (*header) + plaintext_length + tag_length;
|
cookie->length = sizeof (*header) + key->nonce_length + plaintext_length + tag_length;
|
||||||
assert(cookie->length <= sizeof (cookie->cookie));
|
assert(cookie->length <= sizeof (cookie->cookie));
|
||||||
ciphertext = cookie->cookie + sizeof (*header);
|
ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
|
||||||
|
|
||||||
if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce),
|
if (!SIV_Encrypt(key->siv, nonce, key->nonce_length,
|
||||||
"", 0,
|
"", 0,
|
||||||
plaintext, plaintext_length,
|
plaintext, plaintext_length,
|
||||||
ciphertext, plaintext_length + tag_length)) {
|
ciphertext, plaintext_length + tag_length)) {
|
||||||
@@ -907,7 +959,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
int
|
int
|
||||||
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
||||||
{
|
{
|
||||||
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
||||||
int ciphertext_length, plaintext_length, tag_length;
|
int ciphertext_length, plaintext_length, tag_length;
|
||||||
ServerCookieHeader *header;
|
ServerCookieHeader *header;
|
||||||
ServerKey *key;
|
ServerKey *key;
|
||||||
@@ -924,8 +976,6 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
header = (ServerCookieHeader *)cookie->cookie;
|
header = (ServerCookieHeader *)cookie->cookie;
|
||||||
ciphertext = cookie->cookie + sizeof (*header);
|
|
||||||
ciphertext_length = cookie->length - sizeof (*header);
|
|
||||||
|
|
||||||
key_id = ntohl(header->key_id);
|
key_id = ntohl(header->key_id);
|
||||||
key = &server_keys[key_id % MAX_SERVER_KEYS];
|
key = &server_keys[key_id % MAX_SERVER_KEYS];
|
||||||
@@ -935,18 +985,23 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tag_length = SIV_GetTagLength(key->siv);
|
tag_length = SIV_GetTagLength(key->siv);
|
||||||
if (tag_length >= ciphertext_length) {
|
|
||||||
|
if (cookie->length <= (int)sizeof (*header) + key->nonce_length + tag_length) {
|
||||||
DEBUG_LOG("Invalid cookie length");
|
DEBUG_LOG("Invalid cookie length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonce = cookie->cookie + sizeof (*header);
|
||||||
|
ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
|
||||||
|
ciphertext_length = cookie->length - sizeof (*header) - key->nonce_length;
|
||||||
plaintext_length = ciphertext_length - tag_length;
|
plaintext_length = ciphertext_length - tag_length;
|
||||||
|
|
||||||
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
|
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
|
||||||
DEBUG_LOG("Invalid cookie length");
|
DEBUG_LOG("Invalid cookie length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce),
|
if (!SIV_Decrypt(key->siv, nonce, key->nonce_length,
|
||||||
"", 0,
|
"", 0,
|
||||||
ciphertext, ciphertext_length,
|
ciphertext, ciphertext_length,
|
||||||
plaintext, plaintext_length)) {
|
plaintext, plaintext_length)) {
|
||||||
@@ -954,7 +1009,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Select a supported algorithm corresponding to the key length, avoiding
|
||||||
|
potentially slow SIV_GetKeyLength() */
|
||||||
|
switch (plaintext_length / 2) {
|
||||||
|
case 16:
|
||||||
|
context->algorithm = AEAD_AES_128_GCM_SIV;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
context->algorithm = AEAD_AES_SIV_CMAC_256;
|
context->algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DEBUG_LOG("Unknown key length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
context->c2s.length = plaintext_length / 2;
|
context->c2s.length = plaintext_length / 2;
|
||||||
context->s2c.length = plaintext_length / 2;
|
context->s2c.length = plaintext_length / 2;
|
||||||
|
|||||||
@@ -594,13 +594,13 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
|||||||
|
|
||||||
static int gnutls_initialised = 0;
|
static int gnutls_initialised = 0;
|
||||||
|
|
||||||
static void
|
static int
|
||||||
init_gnutls(void)
|
init_gnutls(void)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (gnutls_initialised)
|
if (gnutls_initialised)
|
||||||
return;
|
return 1;
|
||||||
|
|
||||||
r = gnutls_global_init();
|
r = gnutls_global_init();
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@@ -611,8 +611,12 @@ init_gnutls(void)
|
|||||||
r = gnutls_priority_init2(&priority_cache,
|
r = gnutls_priority_init2(&priority_cache,
|
||||||
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
|
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
|
||||||
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
|
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
|
LOG(LOGS_ERR, "Could not initialise %s : %s",
|
||||||
|
"priority cache for TLS", gnutls_strerror(r));
|
||||||
|
gnutls_global_deinit();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Use our clock instead of the system clock in certificate verification */
|
/* Use our clock instead of the system clock in certificate verification */
|
||||||
gnutls_global_set_time_function(get_time);
|
gnutls_global_set_time_function(get_time);
|
||||||
@@ -621,6 +625,8 @@ init_gnutls(void)
|
|||||||
DEBUG_LOG("Initialised");
|
DEBUG_LOG("Initialised");
|
||||||
|
|
||||||
LCL_AddParameterChangeHandler(handle_step, NULL);
|
LCL_AddParameterChangeHandler(handle_step, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -649,7 +655,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
|
|||||||
gnutls_certificate_credentials_t credentials = NULL;
|
gnutls_certificate_credentials_t credentials = NULL;
|
||||||
int i, r;
|
int i, r;
|
||||||
|
|
||||||
init_gnutls();
|
if (!init_gnutls())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
r = gnutls_certificate_allocate_credentials(&credentials);
|
r = gnutls_certificate_allocate_credentials(&credentials);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@@ -660,6 +667,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
|
|||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
for (i = 0; i < n_certs_keys; i++) {
|
for (i = 0; i < n_certs_keys; i++) {
|
||||||
|
if (!UTI_CheckFilePermissions(keys[i], 0771))
|
||||||
|
;
|
||||||
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
|
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
|
||||||
GNUTLS_X509_FMT_PEM);
|
GNUTLS_X509_FMT_PEM);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|||||||
@@ -61,23 +61,25 @@ get_padded_length(int length)
|
|||||||
|
|
||||||
int
|
int
|
||||||
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
const unsigned char *nonce, int nonce_length,
|
const unsigned char *nonce, int max_nonce_length,
|
||||||
const unsigned char *plaintext, int plaintext_length,
|
const unsigned char *plaintext, int plaintext_length,
|
||||||
int min_ef_length)
|
int min_ef_length)
|
||||||
{
|
{
|
||||||
int auth_length, ciphertext_length, assoc_length;
|
int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length;
|
||||||
int nonce_padding, ciphertext_padding, additional_padding;
|
int nonce_padding, ciphertext_padding, additional_padding;
|
||||||
unsigned char *ciphertext, *body;
|
unsigned char *ciphertext, *body;
|
||||||
struct AuthHeader *header;
|
struct AuthHeader *header;
|
||||||
|
|
||||||
assert(sizeof (*header) == 4);
|
assert(sizeof (*header) == 4);
|
||||||
|
|
||||||
if (nonce_length <= 0 || plaintext_length < 0) {
|
if (max_nonce_length <= 0 || plaintext_length < 0) {
|
||||||
DEBUG_LOG("Invalid nonce/plaintext length");
|
DEBUG_LOG("Invalid nonce/plaintext length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
assoc_length = info->length;
|
assoc_length = info->length;
|
||||||
|
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
|
||||||
|
nonce_length = MIN(max_nonce_length, max_siv_nonce_length);
|
||||||
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
|
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
|
||||||
nonce_padding = get_padding_length(nonce_length);
|
nonce_padding = get_padding_length(nonce_length);
|
||||||
ciphertext_padding = get_padding_length(ciphertext_length);
|
ciphertext_padding = get_padding_length(ciphertext_length);
|
||||||
@@ -86,8 +88,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
|||||||
auth_length = sizeof (*header) + nonce_length + nonce_padding +
|
auth_length = sizeof (*header) + nonce_length + nonce_padding +
|
||||||
ciphertext_length + ciphertext_padding;
|
ciphertext_length + ciphertext_padding;
|
||||||
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
|
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
|
||||||
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
|
additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) -
|
||||||
additional_padding);
|
nonce_length - nonce_padding, additional_padding);
|
||||||
auth_length += additional_padding;
|
auth_length += additional_padding;
|
||||||
|
|
||||||
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
|
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
|
||||||
@@ -113,6 +115,7 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
|||||||
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
|
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
|
||||||
DEBUG_LOG("SIV encrypt failed");
|
DEBUG_LOG("SIV encrypt failed");
|
||||||
info->length = assoc_length;
|
info->length = assoc_length;
|
||||||
|
info->ext_fields--;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +130,7 @@ int
|
|||||||
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
|
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
|
||||||
unsigned char *plaintext, int buffer_length, int *plaintext_length)
|
unsigned char *plaintext, int buffer_length, int *plaintext_length)
|
||||||
{
|
{
|
||||||
unsigned int siv_tag_length, nonce_length, ciphertext_length;
|
int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length;
|
||||||
unsigned char *nonce, *ciphertext;
|
unsigned char *nonce, *ciphertext;
|
||||||
int ef_type, ef_body_length;
|
int ef_type, ef_body_length;
|
||||||
void *ef_body;
|
void *ef_body;
|
||||||
@@ -155,6 +158,7 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
|
|||||||
nonce = (unsigned char *)(header + 1);
|
nonce = (unsigned char *)(header + 1);
|
||||||
ciphertext = nonce + get_padded_length(nonce_length);
|
ciphertext = nonce + get_padded_length(nonce_length);
|
||||||
|
|
||||||
|
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
|
||||||
siv_tag_length = SIV_GetTagLength(siv);
|
siv_tag_length = SIV_GetTagLength(siv);
|
||||||
|
|
||||||
if (nonce_length < 1 ||
|
if (nonce_length < 1 ||
|
||||||
@@ -164,8 +168,8 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ef_body_length < sizeof (*header) +
|
if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) +
|
||||||
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
|
get_padded_length(ciphertext_length) > ef_body_length) {
|
||||||
DEBUG_LOG("Missing padding");
|
DEBUG_LOG("Missing padding");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include "siv.h"
|
#include "siv.h"
|
||||||
|
|
||||||
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
const unsigned char *nonce, int nonce_length,
|
const unsigned char *nonce, int max_nonce_length,
|
||||||
const unsigned char *plaintext, int plaintext_length,
|
const unsigned char *plaintext, int plaintext_length,
|
||||||
int min_ef_length);
|
int min_ef_length);
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,9 @@
|
|||||||
/* Maximum length of all cookies to avoid IP fragmentation */
|
/* Maximum length of all cookies to avoid IP fragmentation */
|
||||||
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
|
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
|
||||||
|
|
||||||
|
/* Retry interval for NTS-KE start (which doesn't generate network traffic) */
|
||||||
|
#define RETRY_INTERVAL_KE_START 2.0
|
||||||
|
|
||||||
/* Magic string of files containing keys and cookies */
|
/* Magic string of files containing keys and cookies */
|
||||||
#define DUMP_IDENTIFIER "NNC0\n"
|
#define DUMP_IDENTIFIER "NNC0\n"
|
||||||
|
|
||||||
@@ -203,10 +206,15 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_next_nke_attempt(NNC_Instance inst, double now)
|
update_next_nke_attempt(NNC_Instance inst, int failed_start, double now)
|
||||||
{
|
{
|
||||||
int factor, interval;
|
int factor, interval;
|
||||||
|
|
||||||
|
if (failed_start) {
|
||||||
|
inst->next_nke_attempt = now + RETRY_INTERVAL_KE_START;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!inst->nke)
|
if (!inst->nke)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -221,8 +229,8 @@ static int
|
|||||||
get_cookies(NNC_Instance inst)
|
get_cookies(NNC_Instance inst)
|
||||||
{
|
{
|
||||||
NTP_Remote_Address ntp_address;
|
NTP_Remote_Address ntp_address;
|
||||||
|
int got_data, failed_start = 0;
|
||||||
double now;
|
double now;
|
||||||
int got_data;
|
|
||||||
|
|
||||||
assert(inst->num_cookies == 0);
|
assert(inst->num_cookies == 0);
|
||||||
|
|
||||||
@@ -239,13 +247,12 @@ get_cookies(NNC_Instance inst)
|
|||||||
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
|
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
|
||||||
|
|
||||||
inst->nke_attempts++;
|
inst->nke_attempts++;
|
||||||
update_next_nke_attempt(inst, now);
|
|
||||||
|
|
||||||
if (!NKC_Start(inst->nke))
|
if (!NKC_Start(inst->nke))
|
||||||
return 0;
|
failed_start = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_next_nke_attempt(inst, now);
|
update_next_nke_attempt(inst, failed_start, now);
|
||||||
|
|
||||||
/* Wait until the session stops */
|
/* Wait until the session stops */
|
||||||
if (NKC_IsActive(inst->nke))
|
if (NKC_IsActive(inst->nke))
|
||||||
@@ -643,6 +650,7 @@ load_cookies(NNC_Instance inst)
|
|||||||
sizeof (inst->context.c2s.key));
|
sizeof (inst->context.c2s.key));
|
||||||
|
|
||||||
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
|
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
|
||||||
|
inst->context.s2c.length <= 0 ||
|
||||||
inst->context.c2s.length != inst->context.s2c.length)
|
inst->context.c2s.length != inst->context.s2c.length)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,15 @@
|
|||||||
#include "siv.h"
|
#include "siv.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define SERVER_SIV AEAD_AES_SIV_CMAC_256
|
#define MAX_SERVER_SIVS 2
|
||||||
|
|
||||||
struct NtsServer {
|
struct NtsServer {
|
||||||
SIV_Instance siv;
|
SIV_Instance sivs[MAX_SERVER_SIVS];
|
||||||
|
SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS];
|
||||||
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
|
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
|
||||||
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
|
int siv_index;
|
||||||
NTP_int64 req_tx;
|
NTP_int64 req_tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ void
|
|||||||
NNS_Initialise(void)
|
NNS_Initialise(void)
|
||||||
{
|
{
|
||||||
const char **certs, **keys;
|
const char **certs, **keys;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
|
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
|
||||||
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
|
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
|
||||||
@@ -68,9 +71,17 @@ NNS_Initialise(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
server = Malloc(sizeof (struct NtsServer));
|
server = Malloc(sizeof (struct NtsServer));
|
||||||
server->siv = SIV_CreateInstance(SERVER_SIV);
|
|
||||||
if (!server->siv)
|
server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
|
||||||
LOG_FATAL("Could not initialise SIV cipher");
|
server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV;
|
||||||
|
assert(MAX_SERVER_SIVS == 2);
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]);
|
||||||
|
|
||||||
|
/* AES-SIV-CMAC-256 is required on servers */
|
||||||
|
if (!server->sivs[0])
|
||||||
|
LOG_FATAL("Missing AES-SIV-CMAC-256");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -78,10 +89,15 @@ NNS_Initialise(void)
|
|||||||
void
|
void
|
||||||
NNS_Finalise(void)
|
NNS_Finalise(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!server)
|
if (!server)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SIV_DestroyInstance(server->siv);
|
for (i = 0; i < MAX_SERVER_SIVS; i++) {
|
||||||
|
if (server->sivs[i])
|
||||||
|
SIV_DestroyInstance(server->sivs[i]);
|
||||||
|
}
|
||||||
Free(server);
|
Free(server);
|
||||||
server = NULL;
|
server = NULL;
|
||||||
}
|
}
|
||||||
@@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
NKE_Cookie cookie;
|
NKE_Cookie cookie;
|
||||||
|
SIV_Instance siv;
|
||||||
void *ef_body;
|
void *ef_body;
|
||||||
|
|
||||||
*kod = 0;
|
*kod = 0;
|
||||||
@@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
server->num_cookies = 0;
|
server->num_cookies = 0;
|
||||||
|
server->siv_index = -1;
|
||||||
server->req_tx = packet->transmit_ts;
|
server->req_tx = packet->transmit_ts;
|
||||||
|
|
||||||
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
|
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
|
||||||
@@ -163,17 +181,22 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.algorithm != SERVER_SIV) {
|
/* Find the SIV instance needed for authentication */
|
||||||
|
for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++)
|
||||||
|
;
|
||||||
|
if (i == MAX_SERVER_SIVS || !server->sivs[i]) {
|
||||||
DEBUG_LOG("Unexpected SIV");
|
DEBUG_LOG("Unexpected SIV");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
server->siv_index = i;
|
||||||
|
siv = server->sivs[i];
|
||||||
|
|
||||||
if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) {
|
if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) {
|
||||||
DEBUG_LOG("Could not set C2S key");
|
DEBUG_LOG("Could not set C2S key");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
|
if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
|
||||||
plaintext, sizeof (plaintext), &plaintext_length)) {
|
plaintext, sizeof (plaintext), &plaintext_length)) {
|
||||||
*kod = NTP_KOD_NTS_NAK;
|
*kod = NTP_KOD_NTS_NAK;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -199,7 +222,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
|
if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) {
|
||||||
DEBUG_LOG("Could not set S2C key");
|
DEBUG_LOG("Could not set S2C key");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -271,9 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
|||||||
|
|
||||||
server->num_cookies = 0;
|
server->num_cookies = 0;
|
||||||
|
|
||||||
|
if (server->siv_index < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Generate an authenticator field which will make the length
|
/* Generate an authenticator field which will make the length
|
||||||
of the response equal to the length of the request */
|
of the response equal to the length of the request */
|
||||||
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
|
if (!NNA_GenerateAuthEF(response, res_info, server->sivs[server->siv_index],
|
||||||
server->nonce, sizeof (server->nonce),
|
server->nonce, sizeof (server->nonce),
|
||||||
plaintext, plaintext_length,
|
plaintext, plaintext_length,
|
||||||
req_info->length - res_info->length))
|
req_info->length - res_info->length))
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ static const struct request_length request_lengths[] = {
|
|||||||
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
|
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
|
||||||
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
|
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
|
||||||
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
|
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
|
||||||
|
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t reply_lengths[] = {
|
static const uint16_t reply_lengths[] = {
|
||||||
@@ -156,7 +157,8 @@ static const uint16_t reply_lengths[] = {
|
|||||||
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
|
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
|
||||||
0, /* SERVER_STATS2 - not supported */
|
0, /* SERVER_STATS2 - not supported */
|
||||||
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
|
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
|
||||||
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */
|
0, /* SERVER_STATS3 - not supported */
|
||||||
|
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|||||||
7
ptp.h
7
ptp.h
@@ -44,7 +44,12 @@ typedef struct {
|
|||||||
uint8_t domain;
|
uint8_t domain;
|
||||||
uint8_t min_sdoid;
|
uint8_t min_sdoid;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint8_t rest[26];
|
uint8_t correction[8];
|
||||||
|
uint8_t msg_specific[4];
|
||||||
|
uint8_t port_id[10];
|
||||||
|
uint16_t sequence_id;
|
||||||
|
uint8_t control;
|
||||||
|
int8_t interval;
|
||||||
} PTP_Header;
|
} PTP_Header;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
209
quantiles.c
Normal file
209
quantiles.c
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2022
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Estimation of quantiles using the Frugal-2U streaming algorithm
|
||||||
|
(https://arxiv.org/pdf/1407.1121v1.pdf)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "quantiles.h"
|
||||||
|
#include "regress.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/* Maximum number of repeated estimates for stabilisation */
|
||||||
|
#define MAX_REPEAT 64
|
||||||
|
|
||||||
|
struct Quantile {
|
||||||
|
double est;
|
||||||
|
double step;
|
||||||
|
int sign;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct QNT_Instance_Record {
|
||||||
|
struct Quantile *quants;
|
||||||
|
int n_quants;
|
||||||
|
int repeat;
|
||||||
|
int q;
|
||||||
|
int min_k;
|
||||||
|
double min_step;
|
||||||
|
int n_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
QNT_Instance
|
||||||
|
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
|
||||||
|
{
|
||||||
|
QNT_Instance inst;
|
||||||
|
long seed;
|
||||||
|
|
||||||
|
if (q < 2 || min_k > max_k || min_k < 1 || max_k >= q ||
|
||||||
|
repeat < 1 || repeat > MAX_REPEAT || min_step <= 0.0)
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
inst = MallocNew(struct QNT_Instance_Record);
|
||||||
|
inst->n_quants = (max_k - min_k + 1) * repeat;
|
||||||
|
inst->quants = MallocArray(struct Quantile, inst->n_quants);
|
||||||
|
inst->repeat = repeat;
|
||||||
|
inst->q = q;
|
||||||
|
inst->min_k = min_k;
|
||||||
|
inst->min_step = min_step;
|
||||||
|
|
||||||
|
QNT_Reset(inst);
|
||||||
|
|
||||||
|
/* Seed the random number generator, which will not be isolated from
|
||||||
|
other instances and other random() users */
|
||||||
|
UTI_GetRandomBytes(&seed, sizeof (seed));
|
||||||
|
srandom(seed);
|
||||||
|
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
QNT_DestroyInstance(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
Free(inst->quants);
|
||||||
|
Free(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
QNT_Reset(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
inst->n_set = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < inst->n_quants; i++) {
|
||||||
|
inst->quants[i].est = 0.0;
|
||||||
|
inst->quants[i].step = inst->min_step;
|
||||||
|
inst->quants[i].sign = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
insert_initial_value(QNT_Instance inst, double value)
|
||||||
|
{
|
||||||
|
int i, j, r = inst->repeat;
|
||||||
|
|
||||||
|
if (inst->n_set * r >= inst->n_quants)
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
/* Keep the initial estimates repeated and ordered */
|
||||||
|
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
|
||||||
|
for (j = 0; j < r; j++)
|
||||||
|
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < r; j++)
|
||||||
|
inst->quants[i * r + j].est = value;
|
||||||
|
inst->n_set++;
|
||||||
|
|
||||||
|
/* Duplicate the largest value in unset quantiles */
|
||||||
|
for (i = inst->n_set * r; i < inst->n_quants; i++)
|
||||||
|
inst->quants[i].est = inst->quants[i - 1].est;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_estimate(struct Quantile *quantile, double value, double p, double rand,
|
||||||
|
double min_step)
|
||||||
|
{
|
||||||
|
if (value > quantile->est && rand > (1.0 - p)) {
|
||||||
|
quantile->step += quantile->sign > 0 ? min_step : -min_step;
|
||||||
|
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
|
||||||
|
if (quantile->est > value) {
|
||||||
|
quantile->step += value - quantile->est;
|
||||||
|
quantile->est = value;
|
||||||
|
}
|
||||||
|
if (quantile->sign < 0 && quantile->step > min_step)
|
||||||
|
quantile->step = min_step;
|
||||||
|
quantile->sign = 1;
|
||||||
|
} else if (value < quantile->est && rand > p) {
|
||||||
|
quantile->step += quantile->sign < 0 ? min_step : -min_step;
|
||||||
|
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
|
||||||
|
if (quantile->est < value) {
|
||||||
|
quantile->step += quantile->est - value;
|
||||||
|
quantile->est = value;
|
||||||
|
}
|
||||||
|
if (quantile->sign > 0 && quantile->step > min_step)
|
||||||
|
quantile->step = min_step;
|
||||||
|
quantile->sign = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
QNT_Accumulate(QNT_Instance inst, double value)
|
||||||
|
{
|
||||||
|
double p, rand;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Initialise the estimates with first received values */
|
||||||
|
if (inst->n_set * inst->repeat < inst->n_quants) {
|
||||||
|
insert_initial_value(inst, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < inst->n_quants; i++) {
|
||||||
|
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
|
||||||
|
rand = (double)random() / ((1U << 31) - 1);
|
||||||
|
|
||||||
|
update_estimate(&inst->quants[i], value, p, rand, inst->min_step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
QNT_GetMinK(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
return inst->min_k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
double
|
||||||
|
QNT_GetQuantile(QNT_Instance inst, int k)
|
||||||
|
{
|
||||||
|
double estimates[MAX_REPEAT];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (k < inst->min_k || k - inst->min_k >= inst->n_quants)
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
for (i = 0; i < inst->repeat; i++)
|
||||||
|
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
|
||||||
|
|
||||||
|
return RGR_FindMedian(estimates, inst->repeat);
|
||||||
|
}
|
||||||
41
quantiles.h
Normal file
41
quantiles.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2022
|
||||||
|
*
|
||||||
|
* 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 estimation of quantiles.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_QUANTILES_H
|
||||||
|
#define GOT_QUANTILES_H
|
||||||
|
|
||||||
|
typedef struct QNT_Instance_Record *QNT_Instance;
|
||||||
|
|
||||||
|
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step);
|
||||||
|
extern void QNT_DestroyInstance(QNT_Instance inst);
|
||||||
|
|
||||||
|
extern void QNT_Reset(QNT_Instance inst);
|
||||||
|
extern void QNT_Accumulate(QNT_Instance inst, double value);
|
||||||
|
extern int QNT_GetMinK(QNT_Instance inst);
|
||||||
|
extern double QNT_GetQuantile(QNT_Instance inst, int k);
|
||||||
|
|
||||||
|
#endif
|
||||||
127
refclock.c
127
refclock.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
|
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -55,21 +55,6 @@ struct FilterSample {
|
|||||||
struct timespec sample_time;
|
struct timespec sample_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MedianFilter {
|
|
||||||
int length;
|
|
||||||
int index;
|
|
||||||
int used;
|
|
||||||
int last;
|
|
||||||
int avg_var_n;
|
|
||||||
double avg_var;
|
|
||||||
double max_var;
|
|
||||||
struct FilterSample *samples;
|
|
||||||
int *selected;
|
|
||||||
double *x_data;
|
|
||||||
double *y_data;
|
|
||||||
double *w_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RCL_Instance_Record {
|
struct RCL_Instance_Record {
|
||||||
RefclockDriver *driver;
|
RefclockDriver *driver;
|
||||||
void *data;
|
void *data;
|
||||||
@@ -79,6 +64,7 @@ struct RCL_Instance_Record {
|
|||||||
int driver_polled;
|
int driver_polled;
|
||||||
int poll;
|
int poll;
|
||||||
int leap_status;
|
int leap_status;
|
||||||
|
int local;
|
||||||
int pps_forced;
|
int pps_forced;
|
||||||
int pps_rate;
|
int pps_rate;
|
||||||
int pps_active;
|
int pps_active;
|
||||||
@@ -190,6 +176,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||||||
inst->poll = params->poll;
|
inst->poll = params->poll;
|
||||||
inst->driver_polled = 0;
|
inst->driver_polled = 0;
|
||||||
inst->leap_status = LEAP_Normal;
|
inst->leap_status = LEAP_Normal;
|
||||||
|
inst->local = params->local;
|
||||||
inst->pps_forced = params->pps_forced;
|
inst->pps_forced = params->pps_forced;
|
||||||
inst->pps_rate = params->pps_rate;
|
inst->pps_rate = params->pps_rate;
|
||||||
inst->pps_active = 0;
|
inst->pps_active = 0;
|
||||||
@@ -231,6 +218,13 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||||||
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inst->local) {
|
||||||
|
inst->pps_forced = 1;
|
||||||
|
inst->lock_ref = inst->ref_id;
|
||||||
|
inst->leap_status = LEAP_Unsynchronised;
|
||||||
|
inst->max_lock_age = MAX(inst->max_lock_age, 3);
|
||||||
|
}
|
||||||
|
|
||||||
if (inst->driver->poll) {
|
if (inst->driver->poll) {
|
||||||
int max_samples;
|
int max_samples;
|
||||||
|
|
||||||
@@ -300,7 +294,7 @@ RCL_StartRefclocks(void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lock_index == -1 || lock_index == i)
|
if (lock_index == -1 || (lock_index == i && !inst->local))
|
||||||
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
|
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,20 +434,24 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
|
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||||
|
struct timespec *ref_time, int leap)
|
||||||
{
|
{
|
||||||
double correction, dispersion;
|
double correction, dispersion, raw_offset, offset;
|
||||||
struct timespec cooked_time;
|
struct timespec cooked_time;
|
||||||
|
|
||||||
if (instance->pps_forced)
|
if (instance->pps_forced)
|
||||||
return RCL_AddPulse(instance, sample_time, -offset);
|
return RCL_AddPulse(instance, sample_time,
|
||||||
|
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
|
||||||
|
|
||||||
|
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
|
||||||
|
|
||||||
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
||||||
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
|
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
|
||||||
dispersion += instance->precision;
|
dispersion += instance->precision;
|
||||||
|
|
||||||
/* Make sure the timestamp and offset provided by the driver are sane */
|
/* Make sure the timestamp and offset provided by the driver are sane */
|
||||||
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
|
if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
|
||||||
!valid_sample_time(instance, &cooked_time))
|
!valid_sample_time(instance, &cooked_time))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -468,18 +466,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate offset = raw_offset - correction + instance->offset
|
||||||
|
in parts to avoid loss of precision if there are large differences */
|
||||||
|
offset = ref_time->tv_sec - sample_time->tv_sec -
|
||||||
|
(time_t)correction + (time_t)instance->offset;
|
||||||
|
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
|
||||||
|
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
|
||||||
|
|
||||||
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
|
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
|
||||||
DEBUG_LOG("refclock sample ignored unknown TAI offset");
|
DEBUG_LOG("refclock sample ignored unknown TAI offset");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!accumulate_sample(instance, &cooked_time,
|
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
|
||||||
offset - correction + instance->offset, dispersion))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
instance->pps_active = 0;
|
instance->pps_active = 0;
|
||||||
|
|
||||||
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
|
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
|
||||||
|
|
||||||
/* for logging purposes */
|
/* for logging purposes */
|
||||||
if (!instance->driver->poll)
|
if (!instance->driver->poll)
|
||||||
@@ -558,16 +562,30 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
|||||||
lock_refclock = get_refclock(instance->lock_ref);
|
lock_refclock = get_refclock(instance->lock_ref);
|
||||||
|
|
||||||
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
|
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
|
||||||
|
if (instance->local) {
|
||||||
|
/* Make the first sample in order to lock to itself */
|
||||||
|
ref_sample.time = *cooked_time;
|
||||||
|
ref_sample.offset = offset;
|
||||||
|
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
|
||||||
|
ref_sample.root_delay = ref_sample.root_dispersion = 0;
|
||||||
|
} else {
|
||||||
DEBUG_LOG("refclock pulse ignored no ref sample");
|
DEBUG_LOG("refclock pulse ignored no ref sample");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
|
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
|
||||||
|
|
||||||
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
|
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
|
||||||
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
|
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
|
||||||
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
|
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
|
||||||
sample_diff);
|
|
||||||
|
/* Restart the local mode */
|
||||||
|
if (instance->local) {
|
||||||
|
LOG(LOGS_WARN, "Local refclock lost lock");
|
||||||
|
SPF_DropSamples(instance->filter);
|
||||||
|
SRC_ResetInstance(instance->source);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,6 +711,52 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
|
||||||
|
{
|
||||||
|
double offset_sd, freq_sd, skew, root_delay, root_disp;
|
||||||
|
SST_Stats stats = SRC_GetSourcestats(inst->source);
|
||||||
|
|
||||||
|
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
|
||||||
|
UTI_ZeroTimespec(ref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
|
||||||
|
&skew, &root_delay, &root_disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
|
||||||
|
double prev_offset)
|
||||||
|
{
|
||||||
|
SST_Stats stats = SRC_GetSourcestats(inst->source);
|
||||||
|
double freq, dfreq, offset, doffset, elapsed;
|
||||||
|
struct timespec now, ref_time;
|
||||||
|
|
||||||
|
get_local_stats(inst, &ref_time, &freq, &offset);
|
||||||
|
|
||||||
|
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
|
||||||
|
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
|
||||||
|
doffset = offset - elapsed * prev_freq - prev_offset;
|
||||||
|
|
||||||
|
if (!REF_AdjustReference(doffset, dfreq))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LCL_ReadCookedTime(&now, NULL);
|
||||||
|
SST_SlewSamples(stats, &now, dfreq, doffset);
|
||||||
|
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
|
||||||
|
|
||||||
|
/* Keep the offset close to zero to not lose precision */
|
||||||
|
if (fabs(offset) >= 1.0) {
|
||||||
|
SST_CorrectOffset(stats, -round(offset));
|
||||||
|
SPF_CorrectOffset(inst->filter, -round(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
poll_timeout(void *arg)
|
poll_timeout(void *arg)
|
||||||
{
|
{
|
||||||
@@ -713,17 +777,28 @@ poll_timeout(void *arg)
|
|||||||
inst->driver_polled = 0;
|
inst->driver_polled = 0;
|
||||||
|
|
||||||
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
||||||
|
double local_freq, local_offset;
|
||||||
|
struct timespec local_ref_time;
|
||||||
|
|
||||||
/* Handle special case when PPS is used with the local reference */
|
/* Handle special case when PPS is used with the local reference */
|
||||||
if (inst->pps_active && inst->lock_ref == -1)
|
if (inst->pps_active && inst->lock_ref == -1)
|
||||||
stratum = pps_stratum(inst, &sample.time);
|
stratum = pps_stratum(inst, &sample.time);
|
||||||
else
|
else
|
||||||
stratum = inst->stratum;
|
stratum = inst->stratum;
|
||||||
|
|
||||||
|
if (inst->local) {
|
||||||
|
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
|
||||||
|
inst->leap_status = LEAP_Unsynchronised;
|
||||||
|
}
|
||||||
|
|
||||||
SRC_UpdateReachability(inst->source, 1);
|
SRC_UpdateReachability(inst->source, 1);
|
||||||
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
|
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
|
||||||
SRC_AccumulateSample(inst->source, &sample);
|
SRC_AccumulateSample(inst->source, &sample);
|
||||||
SRC_SelectSource(inst->source);
|
SRC_SelectSource(inst->source);
|
||||||
|
|
||||||
|
if (inst->local)
|
||||||
|
follow_local(inst, &local_ref_time, local_freq, local_offset);
|
||||||
|
|
||||||
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
||||||
} else {
|
} else {
|
||||||
SRC_UpdateReachability(inst->source, 0);
|
SRC_UpdateReachability(inst->source, 0);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ typedef struct {
|
|||||||
int driver_poll;
|
int driver_poll;
|
||||||
int poll;
|
int poll;
|
||||||
int filter_length;
|
int filter_length;
|
||||||
|
int local;
|
||||||
int pps_forced;
|
int pps_forced;
|
||||||
int pps_rate;
|
int pps_rate;
|
||||||
int min_samples;
|
int min_samples;
|
||||||
@@ -74,7 +75,8 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
|
|||||||
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
||||||
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
||||||
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
||||||
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
|
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||||
|
struct timespec *ref_time, int leap);
|
||||||
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
|
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
|
||||||
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||||
double second, double dispersion, double raw_correction);
|
double second, double dispersion, double raw_correction);
|
||||||
|
|||||||
111
refclock_phc.c
111
refclock_phc.c
@@ -33,6 +33,9 @@
|
|||||||
|
|
||||||
#include "sysincl.h"
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
|
||||||
|
#include "array.h"
|
||||||
#include "refclock.h"
|
#include "refclock.h"
|
||||||
#include "hwclock.h"
|
#include "hwclock.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
@@ -44,14 +47,19 @@
|
|||||||
|
|
||||||
struct phc_instance {
|
struct phc_instance {
|
||||||
int fd;
|
int fd;
|
||||||
|
int dev_index;
|
||||||
int mode;
|
int mode;
|
||||||
int nocrossts;
|
int nocrossts;
|
||||||
int extpps;
|
int extpps;
|
||||||
int pin;
|
int pin;
|
||||||
int channel;
|
int channel;
|
||||||
|
struct timespec last_extts;
|
||||||
HCL_Instance clock;
|
HCL_Instance clock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Array of RCL_Instance with enabled extpps */
|
||||||
|
static ARR_Instance extts_phcs = NULL;
|
||||||
|
|
||||||
static void read_ext_pulse(int sockfd, int event, void *anything);
|
static void read_ext_pulse(int sockfd, int event, void *anything);
|
||||||
|
|
||||||
static int phc_initialise(RCL_Instance instance)
|
static int phc_initialise(RCL_Instance instance)
|
||||||
@@ -59,6 +67,7 @@ static int phc_initialise(RCL_Instance instance)
|
|||||||
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc;
|
||||||
int phc_fd, rising_edge;
|
int phc_fd, rising_edge;
|
||||||
|
struct stat st;
|
||||||
char *path, *s;
|
char *path, *s;
|
||||||
|
|
||||||
RCL_CheckDriverOptions(instance, options);
|
RCL_CheckDriverOptions(instance, options);
|
||||||
@@ -71,9 +80,15 @@ static int phc_initialise(RCL_Instance instance)
|
|||||||
|
|
||||||
phc = MallocNew(struct phc_instance);
|
phc = MallocNew(struct phc_instance);
|
||||||
phc->fd = phc_fd;
|
phc->fd = phc_fd;
|
||||||
|
if (fstat(phc_fd, &st) < 0 || !S_ISCHR(st.st_mode))
|
||||||
|
LOG_FATAL("Could not get PHC index");
|
||||||
|
phc->dev_index = minor(st.st_rdev);
|
||||||
phc->mode = 0;
|
phc->mode = 0;
|
||||||
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
||||||
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
||||||
|
UTI_ZeroTimespec(&phc->last_extts);
|
||||||
|
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
|
||||||
|
RCL_GetPrecision(instance));
|
||||||
|
|
||||||
if (phc->extpps) {
|
if (phc->extpps) {
|
||||||
s = RCL_GetDriverOption(instance, "pin");
|
s = RCL_GetDriverOption(instance, "pin");
|
||||||
@@ -81,16 +96,18 @@ static int phc_initialise(RCL_Instance instance)
|
|||||||
s = RCL_GetDriverOption(instance, "channel");
|
s = RCL_GetDriverOption(instance, "channel");
|
||||||
phc->channel = s ? atoi(s) : 0;
|
phc->channel = s ? atoi(s) : 0;
|
||||||
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
|
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
|
||||||
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
|
|
||||||
|
|
||||||
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
|
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
|
||||||
rising_edge, !rising_edge, 1))
|
rising_edge, !rising_edge, 1))
|
||||||
LOG_FATAL("Could not enable external PHC timestamping");
|
LOG_FATAL("Could not enable external PHC timestamping");
|
||||||
|
|
||||||
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
|
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
|
||||||
|
|
||||||
|
if (!extts_phcs)
|
||||||
|
extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
|
||||||
|
ARR_AppendElement(extts_phcs, &instance);
|
||||||
} else {
|
} else {
|
||||||
phc->pin = phc->channel = 0;
|
phc->pin = phc->channel = 0;
|
||||||
phc->clock = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCL_SetDriverData(instance, phc);
|
RCL_SetDriverData(instance, phc);
|
||||||
@@ -100,68 +117,106 @@ static int phc_initialise(RCL_Instance instance)
|
|||||||
static void phc_finalise(RCL_Instance instance)
|
static void phc_finalise(RCL_Instance instance)
|
||||||
{
|
{
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||||
|
|
||||||
if (phc->extpps) {
|
if (phc->extpps) {
|
||||||
SCH_RemoveFileHandler(phc->fd);
|
SCH_RemoveFileHandler(phc->fd);
|
||||||
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
|
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
|
||||||
HCL_DestroyInstance(phc->clock);
|
|
||||||
|
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
|
||||||
|
if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
|
||||||
|
ARR_RemoveElement(extts_phcs, i--);
|
||||||
|
}
|
||||||
|
if (ARR_GetSize(extts_phcs) == 0) {
|
||||||
|
ARR_DestroyInstance(extts_phcs);
|
||||||
|
extts_phcs = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HCL_DestroyInstance(phc->clock);
|
||||||
close(phc->fd);
|
close(phc->fd);
|
||||||
Free(phc);
|
Free(phc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
|
||||||
|
{
|
||||||
|
struct phc_instance *phc;
|
||||||
|
struct timespec local_ts;
|
||||||
|
double local_err;
|
||||||
|
|
||||||
|
phc = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
|
if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
|
||||||
|
DEBUG_LOG("Ignoring duplicated PHC timestamp");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
phc->last_extts = *phc_ts;
|
||||||
|
|
||||||
|
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
|
||||||
|
return;
|
||||||
|
|
||||||
|
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
||||||
|
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
|
||||||
|
}
|
||||||
|
|
||||||
static void read_ext_pulse(int fd, int event, void *anything)
|
static void read_ext_pulse(int fd, int event, void *anything)
|
||||||
{
|
{
|
||||||
RCL_Instance instance;
|
RCL_Instance instance;
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc1, *phc2;
|
||||||
struct timespec phc_ts, local_ts;
|
struct timespec phc_ts;
|
||||||
double local_err;
|
unsigned int i;
|
||||||
int channel;
|
int channel;
|
||||||
|
|
||||||
|
if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
|
||||||
|
return;
|
||||||
|
|
||||||
instance = anything;
|
instance = anything;
|
||||||
phc = RCL_GetDriverData(instance);
|
phc1 = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
|
/* The Linux kernel (as of 6.2) has one shared queue of timestamps for all
|
||||||
return;
|
descriptors of the same PHC. Search for all refclocks that expect
|
||||||
|
the timestamp. */
|
||||||
|
|
||||||
if (channel != phc->channel) {
|
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
|
||||||
DEBUG_LOG("Unexpected extts channel %d\n", channel);
|
instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
|
||||||
return;
|
phc2 = RCL_GetDriverData(instance);
|
||||||
|
if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
|
||||||
|
continue;
|
||||||
|
process_ext_pulse(instance, &phc_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
|
|
||||||
return;
|
|
||||||
|
|
||||||
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
|
||||||
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PHC_READINGS 25
|
||||||
|
|
||||||
static int phc_poll(RCL_Instance instance)
|
static int phc_poll(RCL_Instance instance)
|
||||||
{
|
{
|
||||||
|
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc;
|
||||||
struct timespec phc_ts, sys_ts, local_ts;
|
double phc_err, local_err;
|
||||||
double offset, phc_err, local_err;
|
int n_readings;
|
||||||
|
|
||||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||||
|
|
||||||
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
|
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
|
||||||
&phc->mode, &phc_ts, &sys_ts, &phc_err))
|
PHC_READINGS, readings);
|
||||||
|
if (n_readings < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (phc->extpps) {
|
|
||||||
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
||||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||||
|
|
||||||
|
if (phc->extpps)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
|
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
|
||||||
|
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
|
||||||
|
|
||||||
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
|
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
|
||||||
|
|
||||||
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefclockDriver RCL_PHC_driver = {
|
RefclockDriver RCL_PHC_driver = {
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ static int shm_poll(RCL_Instance instance)
|
|||||||
{
|
{
|
||||||
struct timespec receive_ts, clock_ts;
|
struct timespec receive_ts, clock_ts;
|
||||||
struct shmTime t, *shm;
|
struct shmTime t, *shm;
|
||||||
double offset;
|
|
||||||
|
|
||||||
shm = (struct shmTime *)RCL_GetDriverData(instance);
|
shm = (struct shmTime *)RCL_GetDriverData(instance);
|
||||||
|
|
||||||
@@ -124,9 +123,8 @@ static int shm_poll(RCL_Instance instance)
|
|||||||
|
|
||||||
UTI_NormaliseTimespec(&clock_ts);
|
UTI_NormaliseTimespec(&clock_ts);
|
||||||
UTI_NormaliseTimespec(&receive_ts);
|
UTI_NormaliseTimespec(&receive_ts);
|
||||||
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
|
|
||||||
|
|
||||||
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
|
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefclockDriver RCL_SHM_driver = {
|
RefclockDriver RCL_SHM_driver = {
|
||||||
|
|||||||
@@ -58,23 +58,63 @@ struct sock_sample {
|
|||||||
int magic;
|
int magic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* On 32-bit glibc-based systems enable conversion between timevals using
|
||||||
|
32-bit and 64-bit time_t to support SOCK clients compiled with different
|
||||||
|
time_t size than chrony */
|
||||||
|
#ifdef __GLIBC_PREREQ
|
||||||
|
#if __GLIBC_PREREQ(2, 34) && __TIMESIZE == 32
|
||||||
|
#define CONVERT_TIMEVAL 1
|
||||||
|
#if defined(_TIME_BITS) && _TIME_BITS == 64
|
||||||
|
typedef int32_t alt_time_t;
|
||||||
|
typedef int32_t alt_suseconds_t;
|
||||||
|
#else
|
||||||
|
typedef int64_t alt_time_t;
|
||||||
|
typedef int64_t alt_suseconds_t;
|
||||||
|
#endif
|
||||||
|
struct alt_timeval {
|
||||||
|
alt_time_t tv_sec;
|
||||||
|
alt_suseconds_t tv_usec;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static void read_sample(int sockfd, int event, void *anything)
|
static void read_sample(int sockfd, int event, void *anything)
|
||||||
{
|
{
|
||||||
|
char buf[sizeof (struct sock_sample) + 16];
|
||||||
|
struct timespec sys_ts, ref_ts;
|
||||||
struct sock_sample sample;
|
struct sock_sample sample;
|
||||||
struct timespec ts;
|
|
||||||
RCL_Instance instance;
|
RCL_Instance instance;
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
instance = (RCL_Instance)anything;
|
instance = (RCL_Instance)anything;
|
||||||
|
|
||||||
s = recv(sockfd, &sample, sizeof (sample), 0);
|
s = recv(sockfd, buf, sizeof (buf), 0);
|
||||||
|
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
|
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s != sizeof (sample)) {
|
if (s == sizeof (sample)) {
|
||||||
|
memcpy(&sample, buf, sizeof (sample));
|
||||||
|
#ifdef CONVERT_TIMEVAL
|
||||||
|
} else if (s == sizeof (sample) - sizeof (struct timeval) + sizeof (struct alt_timeval)) {
|
||||||
|
struct alt_timeval atv;
|
||||||
|
memcpy(&atv, buf, sizeof (atv));
|
||||||
|
#ifndef HAVE_LONG_TIME_T
|
||||||
|
if (atv.tv_sec > INT32_MAX || atv.tv_sec < INT32_MIN ||
|
||||||
|
atv.tv_usec > INT32_MAX || atv.tv_usec < INT32_MIN) {
|
||||||
|
DEBUG_LOG("Could not convert 64-bit timeval");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
sample.tv.tv_sec = atv.tv_sec;
|
||||||
|
sample.tv.tv_usec = atv.tv_usec;
|
||||||
|
DEBUG_LOG("Converted %d-bit timeval", 8 * (int)sizeof (alt_time_t));
|
||||||
|
memcpy((char *)&sample + sizeof (struct timeval), buf + sizeof (struct alt_timeval),
|
||||||
|
sizeof (sample) - sizeof (struct timeval));
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
|
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
|
||||||
s, (long)sizeof (sample));
|
s, (long)sizeof (sample));
|
||||||
return;
|
return;
|
||||||
@@ -86,13 +126,18 @@ static void read_sample(int sockfd, int event, void *anything)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UTI_TimevalToTimespec(&sample.tv, &ts);
|
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
|
||||||
UTI_NormaliseTimespec(&ts);
|
UTI_NormaliseTimespec(&sys_ts);
|
||||||
|
|
||||||
|
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
|
||||||
|
return;
|
||||||
|
|
||||||
|
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
|
||||||
|
|
||||||
if (sample.pulse) {
|
if (sample.pulse) {
|
||||||
RCL_AddPulse(instance, &ts, sample.offset);
|
RCL_AddPulse(instance, &sys_ts, sample.offset);
|
||||||
} else {
|
} else {
|
||||||
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
|
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
reference.c
73
reference.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2018, 2020
|
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -150,6 +150,9 @@ static SCH_TimeoutID fb_drift_timeout_id;
|
|||||||
static double last_ref_update;
|
static double last_ref_update;
|
||||||
static double last_ref_update_interval;
|
static double last_ref_update_interval;
|
||||||
|
|
||||||
|
static double last_ref_adjustment;
|
||||||
|
static int ref_adjustments;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
|
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
|
||||||
@@ -286,6 +289,8 @@ REF_Initialise(void)
|
|||||||
UTI_ZeroTimespec(&our_ref_time);
|
UTI_ZeroTimespec(&our_ref_time);
|
||||||
last_ref_update = 0.0;
|
last_ref_update = 0.0;
|
||||||
last_ref_update_interval = 0.0;
|
last_ref_update_interval = 0.0;
|
||||||
|
last_ref_adjustment = 0.0;
|
||||||
|
ref_adjustments = 0;
|
||||||
|
|
||||||
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
||||||
|
|
||||||
@@ -960,6 +965,27 @@ fuzz_ref_time(struct timespec *ts)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static double
|
||||||
|
get_correction_rate(double offset_sd, double update_interval)
|
||||||
|
{
|
||||||
|
/* We want to correct the offset quickly, but we also want to keep the
|
||||||
|
frequency error caused by the correction itself low.
|
||||||
|
|
||||||
|
Define correction rate as the area of the region bounded by the graph of
|
||||||
|
offset corrected in time. Set the rate so that the time needed to correct
|
||||||
|
an offset equal to the current sourcestats stddev will be equal to the
|
||||||
|
update interval multiplied by the correction time ratio (assuming linear
|
||||||
|
adjustment). The offset and the time needed to make the correction are
|
||||||
|
inversely proportional.
|
||||||
|
|
||||||
|
This is only a suggestion and it's up to the system driver how the
|
||||||
|
adjustment will be executed. */
|
||||||
|
|
||||||
|
return correction_time_ratio * 0.5 * offset_sd * update_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||||
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
|
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
|
||||||
@@ -969,7 +995,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
{
|
{
|
||||||
double uncorrected_offset, accumulate_offset, step_offset;
|
double uncorrected_offset, accumulate_offset, step_offset;
|
||||||
double residual_frequency, local_abs_frequency;
|
double residual_frequency, local_abs_frequency;
|
||||||
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
|
double elapsed, mono_now, update_interval, orig_root_distance;
|
||||||
struct timespec now, raw_now;
|
struct timespec now, raw_now;
|
||||||
int manual;
|
int manual;
|
||||||
|
|
||||||
@@ -1024,21 +1050,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
last_ref_update_interval = update_interval;
|
last_ref_update_interval = update_interval;
|
||||||
last_offset = offset;
|
last_offset = offset;
|
||||||
|
|
||||||
/* We want to correct the offset quickly, but we also want to keep the
|
|
||||||
frequency error caused by the correction itself low.
|
|
||||||
|
|
||||||
Define correction rate as the area of the region bounded by the graph of
|
|
||||||
offset corrected in time. Set the rate so that the time needed to correct
|
|
||||||
an offset equal to the current sourcestats stddev will be equal to the
|
|
||||||
update interval multiplied by the correction time ratio (assuming linear
|
|
||||||
adjustment). The offset and the time needed to make the correction are
|
|
||||||
inversely proportional.
|
|
||||||
|
|
||||||
This is only a suggestion and it's up to the system driver how the
|
|
||||||
adjustment will be executed. */
|
|
||||||
|
|
||||||
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
|
|
||||||
|
|
||||||
/* Check if the clock should be stepped */
|
/* Check if the clock should be stepped */
|
||||||
if (is_step_limit_reached(offset, uncorrected_offset)) {
|
if (is_step_limit_reached(offset, uncorrected_offset)) {
|
||||||
/* Cancel the uncorrected offset and correct the total offset by step */
|
/* Cancel the uncorrected offset and correct the total offset by step */
|
||||||
@@ -1050,7 +1061,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust the clock */
|
/* Adjust the clock */
|
||||||
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
|
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
|
||||||
|
get_correction_rate(offset_sd, update_interval));
|
||||||
|
|
||||||
maybe_log_offset(offset, raw_now.tv_sec);
|
maybe_log_offset(offset, raw_now.tv_sec);
|
||||||
|
|
||||||
@@ -1095,6 +1107,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
avg2_moving = 1;
|
avg2_moving = 1;
|
||||||
avg2_offset = SQUARE(offset);
|
avg2_offset = SQUARE(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref_adjustments = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
REF_AdjustReference(double offset, double frequency)
|
||||||
|
{
|
||||||
|
double adj_corr_rate, ref_corr_rate, mono_now;
|
||||||
|
|
||||||
|
mono_now = SCH_GetLastEventMonoTime();
|
||||||
|
ref_adjustments++;
|
||||||
|
|
||||||
|
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
|
||||||
|
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
|
||||||
|
ref_adjustments;
|
||||||
|
last_ref_adjustment = mono_now;
|
||||||
|
|
||||||
|
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
|
||||||
|
MAX(adj_corr_rate, ref_corr_rate));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1296,6 +1329,7 @@ void
|
|||||||
REF_ModifyMaxupdateskew(double new_max_update_skew)
|
REF_ModifyMaxupdateskew(double new_max_update_skew)
|
||||||
{
|
{
|
||||||
max_update_skew = new_max_update_skew * 1.0e-6;
|
max_update_skew = new_max_update_skew * 1.0e-6;
|
||||||
|
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1305,6 +1339,7 @@ REF_ModifyMakestep(int limit, double threshold)
|
|||||||
{
|
{
|
||||||
make_step_limit = limit;
|
make_step_limit = limit;
|
||||||
make_step_threshold = threshold;
|
make_step_threshold = threshold;
|
||||||
|
LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1316,6 +1351,7 @@ REF_EnableLocal(int stratum, double distance, int orphan)
|
|||||||
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
|
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
|
||||||
local_distance = distance;
|
local_distance = distance;
|
||||||
local_orphan = !!orphan;
|
local_orphan = !!orphan;
|
||||||
|
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1324,6 +1360,7 @@ void
|
|||||||
REF_DisableLocal(void)
|
REF_DisableLocal(void)
|
||||||
{
|
{
|
||||||
enable_local_stratum = 0;
|
enable_local_stratum = 0;
|
||||||
|
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ extern void REF_SetManualReference
|
|||||||
extern void
|
extern void
|
||||||
REF_SetUnsynchronised(void);
|
REF_SetUnsynchronised(void);
|
||||||
|
|
||||||
|
/* Make a small correction of the clock without updating the reference
|
||||||
|
parameters and calling the clock change handlers */
|
||||||
|
extern int REF_AdjustReference(double offset, double frequency);
|
||||||
|
|
||||||
/* Announce a leap second before the full reference update */
|
/* Announce a leap second before the full reference update */
|
||||||
extern void REF_UpdateLeapStatus(NTP_Leap leap);
|
extern void REF_UpdateLeapStatus(NTP_Leap leap);
|
||||||
|
|
||||||
|
|||||||
29
reports.h
29
reports.h
@@ -109,17 +109,23 @@ typedef struct {
|
|||||||
} RPT_ClientAccessByIndex_Report;
|
} RPT_ClientAccessByIndex_Report;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t ntp_hits;
|
uint64_t ntp_hits;
|
||||||
uint32_t nke_hits;
|
uint64_t nke_hits;
|
||||||
uint32_t cmd_hits;
|
uint64_t cmd_hits;
|
||||||
uint32_t ntp_drops;
|
uint64_t ntp_drops;
|
||||||
uint32_t nke_drops;
|
uint64_t nke_drops;
|
||||||
uint32_t cmd_drops;
|
uint64_t cmd_drops;
|
||||||
uint32_t log_drops;
|
uint64_t log_drops;
|
||||||
uint32_t ntp_auth_hits;
|
uint64_t ntp_auth_hits;
|
||||||
uint32_t ntp_interleaved_hits;
|
uint64_t ntp_interleaved_hits;
|
||||||
uint32_t ntp_timestamps;
|
uint64_t ntp_timestamps;
|
||||||
uint32_t ntp_span_seconds;
|
uint64_t ntp_span_seconds;
|
||||||
|
uint64_t ntp_daemon_rx_timestamps;
|
||||||
|
uint64_t ntp_daemon_tx_timestamps;
|
||||||
|
uint64_t ntp_kernel_rx_timestamps;
|
||||||
|
uint64_t ntp_kernel_tx_timestamps;
|
||||||
|
uint64_t ntp_hw_rx_timestamps;
|
||||||
|
uint64_t ntp_hw_tx_timestamps;
|
||||||
} RPT_ServerStatsReport;
|
} RPT_ServerStatsReport;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -174,6 +180,7 @@ typedef struct {
|
|||||||
uint32_t total_tx_count;
|
uint32_t total_tx_count;
|
||||||
uint32_t total_rx_count;
|
uint32_t total_rx_count;
|
||||||
uint32_t total_valid_count;
|
uint32_t total_valid_count;
|
||||||
|
uint32_t total_good_count;
|
||||||
} RPT_NTPReport;
|
} RPT_NTPReport;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
72
samplefilt.c
72
samplefilt.c
@@ -162,6 +162,14 @@ SPF_GetNumberOfSamples(SPF_Instance filter)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SPF_GetMaxSamples(SPF_Instance filter)
|
||||||
|
{
|
||||||
|
return filter->max_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
double
|
double
|
||||||
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
||||||
{
|
{
|
||||||
@@ -170,11 +178,21 @@ SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
static void
|
||||||
SPF_DropSamples(SPF_Instance filter)
|
drop_samples(SPF_Instance filter, int keep_last)
|
||||||
{
|
{
|
||||||
filter->index = -1;
|
filter->index = -1;
|
||||||
filter->used = 0;
|
filter->used = 0;
|
||||||
|
if (!keep_last)
|
||||||
|
filter->last = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
SPF_DropSamples(SPF_Instance filter)
|
||||||
|
{
|
||||||
|
drop_samples(filter, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -399,17 +417,40 @@ SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
|
|||||||
|
|
||||||
n = select_samples(filter);
|
n = select_samples(filter);
|
||||||
|
|
||||||
|
DEBUG_LOG("selected %d from %d samples", n, filter->used);
|
||||||
|
|
||||||
if (n < 1)
|
if (n < 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!combine_selected_samples(filter, n, sample))
|
if (!combine_selected_samples(filter, n, sample))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
SPF_DropSamples(filter);
|
drop_samples(filter, 1);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_first_last(SPF_Instance filter, int *first, int *last)
|
||||||
|
{
|
||||||
|
if (filter->last < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Always slew the last sample as it may be returned even if no new
|
||||||
|
samples were accumulated */
|
||||||
|
if (filter->used > 0) {
|
||||||
|
*first = 0;
|
||||||
|
*last = filter->used - 1;
|
||||||
|
} else {
|
||||||
|
*first = *last = filter->last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -418,18 +459,9 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
|
|||||||
int i, first, last;
|
int i, first, last;
|
||||||
double delta_time;
|
double delta_time;
|
||||||
|
|
||||||
if (filter->last < 0)
|
if (!get_first_last(filter, &first, &last))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Always slew the last sample as it may be returned even if no new
|
|
||||||
samples were accumulated */
|
|
||||||
if (filter->used > 0) {
|
|
||||||
first = 0;
|
|
||||||
last = filter->used - 1;
|
|
||||||
} else {
|
|
||||||
first = last = filter->last;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = first; i <= last; i++) {
|
for (i = first; i <= last; i++) {
|
||||||
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
||||||
&delta_time, dfreq, doffset);
|
&delta_time, dfreq, doffset);
|
||||||
@@ -439,6 +471,20 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
SPF_CorrectOffset(SPF_Instance filter, double doffset)
|
||||||
|
{
|
||||||
|
int i, first, last;
|
||||||
|
|
||||||
|
if (!get_first_last(filter, &first, &last))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = first; i <= last; i++)
|
||||||
|
filter->samples[i].offset -= doffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ extern void SPF_DestroyInstance(SPF_Instance filter);
|
|||||||
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
|
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
|
||||||
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
|
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
|
||||||
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
|
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
|
||||||
|
extern int SPF_GetMaxSamples(SPF_Instance filter);
|
||||||
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
|
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
|
||||||
extern void SPF_DropSamples(SPF_Instance filter);
|
extern void SPF_DropSamples(SPF_Instance filter);
|
||||||
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
|
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
|
||||||
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
|
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
|
||||||
double dfreq, double doffset);
|
double dfreq, double doffset);
|
||||||
|
extern void SPF_CorrectOffset(SPF_Instance filter, double doffset);
|
||||||
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
|
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
16
sched.c
16
sched.c
@@ -104,7 +104,10 @@ static unsigned long n_timer_queue_entries;
|
|||||||
static SCH_TimeoutID next_tqe_id;
|
static SCH_TimeoutID next_tqe_id;
|
||||||
|
|
||||||
/* Pointer to head of free list */
|
/* Pointer to head of free list */
|
||||||
static TimerQueueEntry *tqe_free_list = NULL;
|
static TimerQueueEntry *tqe_free_list;
|
||||||
|
|
||||||
|
/* Array of all allocated tqe blocks to be freed in finalisation */
|
||||||
|
static ARR_Instance tqe_blocks;
|
||||||
|
|
||||||
/* Timestamp when was last timeout dispatched for each class */
|
/* Timestamp when was last timeout dispatched for each class */
|
||||||
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
|
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
|
||||||
@@ -133,6 +136,8 @@ SCH_Initialise(void)
|
|||||||
|
|
||||||
n_timer_queue_entries = 0;
|
n_timer_queue_entries = 0;
|
||||||
next_tqe_id = 0;
|
next_tqe_id = 0;
|
||||||
|
tqe_free_list = NULL;
|
||||||
|
tqe_blocks = ARR_CreateInstance(sizeof (TimerQueueEntry *));
|
||||||
|
|
||||||
timer_queue.next = &timer_queue;
|
timer_queue.next = &timer_queue;
|
||||||
timer_queue.prev = &timer_queue;
|
timer_queue.prev = &timer_queue;
|
||||||
@@ -154,8 +159,16 @@ SCH_Initialise(void)
|
|||||||
|
|
||||||
void
|
void
|
||||||
SCH_Finalise(void) {
|
SCH_Finalise(void) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
ARR_DestroyInstance(file_handlers);
|
ARR_DestroyInstance(file_handlers);
|
||||||
|
|
||||||
|
timer_queue.next = &timer_queue;
|
||||||
|
timer_queue.prev = &timer_queue;
|
||||||
|
for (i = 0; i < ARR_GetSize(tqe_blocks); i++)
|
||||||
|
Free(*(TimerQueueEntry **)ARR_GetElement(tqe_blocks, i));
|
||||||
|
ARR_DestroyInstance(tqe_blocks);
|
||||||
|
|
||||||
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
|
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
|
||||||
|
|
||||||
initialised = 0;
|
initialised = 0;
|
||||||
@@ -281,6 +294,7 @@ allocate_tqe(void)
|
|||||||
}
|
}
|
||||||
new_block[0].next = NULL;
|
new_block[0].next = NULL;
|
||||||
tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
|
tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
|
||||||
|
ARR_AppendElement(tqe_blocks, &new_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = tqe_free_list;
|
result = tqe_free_list;
|
||||||
|
|||||||
1
sched.h
1
sched.h
@@ -37,6 +37,7 @@ typedef enum {
|
|||||||
SCH_NtpClientClass,
|
SCH_NtpClientClass,
|
||||||
SCH_NtpPeerClass,
|
SCH_NtpPeerClass,
|
||||||
SCH_NtpBroadcastClass,
|
SCH_NtpBroadcastClass,
|
||||||
|
SCH_PhcPollClass,
|
||||||
SCH_NumberOfClasses /* needs to be last */
|
SCH_NumberOfClasses /* needs to be last */
|
||||||
} SCH_TimeoutClass;
|
} SCH_TimeoutClass;
|
||||||
|
|
||||||
|
|||||||
4
siv.h
4
siv.h
@@ -53,6 +53,10 @@ extern int SIV_GetKeyLength(SIV_Algorithm algorithm);
|
|||||||
|
|
||||||
extern int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length);
|
extern int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length);
|
||||||
|
|
||||||
|
extern int SIV_GetMinNonceLength(SIV_Instance instance);
|
||||||
|
|
||||||
|
extern int SIV_GetMaxNonceLength(SIV_Instance instance);
|
||||||
|
|
||||||
extern int SIV_GetTagLength(SIV_Instance instance);
|
extern int SIV_GetTagLength(SIV_Instance instance);
|
||||||
|
|
||||||
extern int SIV_Encrypt(SIV_Instance instance,
|
extern int SIV_Encrypt(SIV_Instance instance,
|
||||||
|
|||||||
34
siv_gnutls.c
34
siv_gnutls.c
@@ -165,17 +165,29 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
|
|||||||
datum.data = (unsigned char *)key;
|
datum.data = (unsigned char *)key;
|
||||||
datum.size = length;
|
datum.size = length;
|
||||||
|
|
||||||
/* Initialise a new cipher with the provided key (gnutls does not seem to
|
#ifdef HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
|
||||||
have a function to change the key directly) */
|
if (instance->cipher) {
|
||||||
|
r = gnutls_aead_cipher_set_key(instance->cipher, &datum);
|
||||||
|
if (r < 0) {
|
||||||
|
DEBUG_LOG("Could not set cipher key : %s", gnutls_strerror(r));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialise a new cipher with the provided key */
|
||||||
r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
|
r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
|
DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Replace the previous cipher */
|
/* Destroy the previous cipher (if its key could not be changed directly) */
|
||||||
if (instance->cipher)
|
if (instance->cipher)
|
||||||
gnutls_aead_cipher_deinit(instance->cipher);
|
gnutls_aead_cipher_deinit(instance->cipher);
|
||||||
|
|
||||||
instance->cipher = cipher;
|
instance->cipher = cipher;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -183,6 +195,22 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SIV_GetMinNonceLength(SIV_Instance instance)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SIV_GetMaxNonceLength(SIV_Instance instance)
|
||||||
|
{
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
SIV_GetTagLength(SIV_Instance instance)
|
SIV_GetTagLength(SIV_Instance instance)
|
||||||
{
|
{
|
||||||
|
|||||||
131
siv_nettle.c
131
siv_nettle.c
@@ -34,12 +34,25 @@
|
|||||||
#include "siv_nettle_int.c"
|
#include "siv_nettle_int.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
#include <nettle/siv-gcm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "siv.h"
|
#include "siv.h"
|
||||||
|
|
||||||
struct SIV_Instance_Record {
|
struct SIV_Instance_Record {
|
||||||
struct siv_cmac_aes128_ctx siv;
|
SIV_Algorithm algorithm;
|
||||||
int key_set;
|
int key_set;
|
||||||
|
int min_nonce_length;
|
||||||
|
int max_nonce_length;
|
||||||
|
int tag_length;
|
||||||
|
union {
|
||||||
|
struct siv_cmac_aes128_ctx cmac_aes128;
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
struct aes128_ctx aes128;
|
||||||
|
#endif
|
||||||
|
} ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -49,12 +62,30 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
|
|||||||
{
|
{
|
||||||
SIV_Instance instance;
|
SIV_Instance instance;
|
||||||
|
|
||||||
if (algorithm != AEAD_AES_SIV_CMAC_256)
|
if (SIV_GetKeyLength(algorithm) <= 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
instance = MallocNew(struct SIV_Instance_Record);
|
instance = MallocNew(struct SIV_Instance_Record);
|
||||||
|
instance->algorithm = algorithm;
|
||||||
instance->key_set = 0;
|
instance->key_set = 0;
|
||||||
|
|
||||||
|
switch (algorithm) {
|
||||||
|
case AEAD_AES_SIV_CMAC_256:
|
||||||
|
instance->min_nonce_length = SIV_MIN_NONCE_SIZE;
|
||||||
|
instance->max_nonce_length = INT_MAX;
|
||||||
|
instance->tag_length = SIV_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
case AEAD_AES_128_GCM_SIV:
|
||||||
|
instance->min_nonce_length = SIV_GCM_NONCE_SIZE;
|
||||||
|
instance->max_nonce_length = SIV_GCM_NONCE_SIZE;
|
||||||
|
instance->tag_length = SIV_GCM_DIGEST_SIZE;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +102,18 @@ SIV_DestroyInstance(SIV_Instance instance)
|
|||||||
int
|
int
|
||||||
SIV_GetKeyLength(SIV_Algorithm algorithm)
|
SIV_GetKeyLength(SIV_Algorithm algorithm)
|
||||||
{
|
{
|
||||||
assert(32 <= SIV_MAX_KEY_LENGTH);
|
assert(2 * AES128_KEY_SIZE <= SIV_MAX_KEY_LENGTH);
|
||||||
|
|
||||||
if (algorithm == AEAD_AES_SIV_CMAC_256)
|
switch (algorithm) {
|
||||||
return 32;
|
case AEAD_AES_SIV_CMAC_256:
|
||||||
|
return 2 * AES128_KEY_SIZE;
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
case AEAD_AES_128_GCM_SIV:
|
||||||
|
return AES128_KEY_SIZE;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -83,10 +121,21 @@ SIV_GetKeyLength(SIV_Algorithm algorithm)
|
|||||||
int
|
int
|
||||||
SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
|
SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
|
||||||
{
|
{
|
||||||
if (length != 32)
|
if (length != SIV_GetKeyLength(instance->algorithm))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
siv_cmac_aes128_set_key(&instance->siv, key);
|
switch (instance->algorithm) {
|
||||||
|
case AEAD_AES_SIV_CMAC_256:
|
||||||
|
siv_cmac_aes128_set_key(&instance->ctx.cmac_aes128, key);
|
||||||
|
break;
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
case AEAD_AES_128_GCM_SIV:
|
||||||
|
aes128_set_encrypt_key(&instance->ctx.aes128, key);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
instance->key_set = 1;
|
instance->key_set = 1;
|
||||||
|
|
||||||
@@ -95,12 +144,28 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SIV_GetMinNonceLength(SIV_Instance instance)
|
||||||
|
{
|
||||||
|
return instance->min_nonce_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SIV_GetMaxNonceLength(SIV_Instance instance)
|
||||||
|
{
|
||||||
|
return instance->max_nonce_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
SIV_GetTagLength(SIV_Instance instance)
|
SIV_GetTagLength(SIV_Instance instance)
|
||||||
{
|
{
|
||||||
assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH);
|
if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH)
|
||||||
|
assert(0);
|
||||||
return SIV_DIGEST_SIZE;
|
return instance->tag_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -115,16 +180,31 @@ SIV_Encrypt(SIV_Instance instance,
|
|||||||
if (!instance->key_set)
|
if (!instance->key_set)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
|
if (nonce_length < instance->min_nonce_length ||
|
||||||
|
nonce_length > instance->max_nonce_length || assoc_length < 0 ||
|
||||||
plaintext_length < 0 || plaintext_length > ciphertext_length ||
|
plaintext_length < 0 || plaintext_length > ciphertext_length ||
|
||||||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
|
plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
assert(assoc && plaintext);
|
assert(assoc && plaintext);
|
||||||
|
|
||||||
siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce,
|
switch (instance->algorithm) {
|
||||||
assoc_length, assoc,
|
case AEAD_AES_SIV_CMAC_256:
|
||||||
|
siv_cmac_aes128_encrypt_message(&instance->ctx.cmac_aes128,
|
||||||
|
nonce_length, nonce, assoc_length, assoc,
|
||||||
ciphertext_length, ciphertext, plaintext);
|
ciphertext_length, ciphertext, plaintext);
|
||||||
|
break;
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
case AEAD_AES_128_GCM_SIV:
|
||||||
|
siv_gcm_aes128_encrypt_message(&instance->ctx.aes128,
|
||||||
|
nonce_length, nonce, assoc_length, assoc,
|
||||||
|
ciphertext_length, ciphertext, plaintext);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,17 +220,32 @@ SIV_Decrypt(SIV_Instance instance,
|
|||||||
if (!instance->key_set)
|
if (!instance->key_set)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
|
if (nonce_length < instance->min_nonce_length ||
|
||||||
|
nonce_length > instance->max_nonce_length || assoc_length < 0 ||
|
||||||
plaintext_length < 0 || plaintext_length > ciphertext_length ||
|
plaintext_length < 0 || plaintext_length > ciphertext_length ||
|
||||||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
|
plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
assert(assoc && plaintext);
|
assert(assoc && plaintext);
|
||||||
|
|
||||||
if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce,
|
switch (instance->algorithm) {
|
||||||
assoc_length, assoc,
|
case AEAD_AES_SIV_CMAC_256:
|
||||||
|
if (!siv_cmac_aes128_decrypt_message(&instance->ctx.cmac_aes128,
|
||||||
|
nonce_length, nonce, assoc_length, assoc,
|
||||||
plaintext_length, plaintext, ciphertext))
|
plaintext_length, plaintext, ciphertext))
|
||||||
return 0;
|
return 0;
|
||||||
|
break;
|
||||||
|
#ifdef HAVE_NETTLE_SIV_GCM
|
||||||
|
case AEAD_AES_128_GCM_SIV:
|
||||||
|
if (!siv_gcm_aes128_decrypt_message(&instance->ctx.aes128,
|
||||||
|
nonce_length, nonce, assoc_length, assoc,
|
||||||
|
plaintext_length, plaintext, ciphertext))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
4
smooth.c
4
smooth.c
@@ -302,7 +302,7 @@ SMT_Activate(struct timespec *now)
|
|||||||
if (!enabled || !locked)
|
if (!enabled || !locked)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG(LOGS_INFO, "Time smoothing activated%s", leap_only_mode ?
|
LOG(LOGS_INFO, "Activated %s%s", "time smoothing", leap_only_mode ?
|
||||||
" (leap seconds only)" : "");
|
" (leap seconds only)" : "");
|
||||||
locked = 0;
|
locked = 0;
|
||||||
last_update = *now;
|
last_update = *now;
|
||||||
@@ -322,6 +322,8 @@ SMT_Reset(struct timespec *now)
|
|||||||
|
|
||||||
for (i = 0; i < NUM_STAGES; i++)
|
for (i = 0; i < NUM_STAGES; i++)
|
||||||
stages[i].wander = stages[i].length = 0.0;
|
stages[i].wander = stages[i].length = 0.0;
|
||||||
|
|
||||||
|
LOG(LOGS_INFO, "Reset %s", "time smoothing");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
4
socket.c
4
socket.c
@@ -505,6 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
|
|||||||
{
|
{
|
||||||
union sockaddr_all saddr;
|
union sockaddr_all saddr;
|
||||||
|
|
||||||
|
memset(&saddr, 0, sizeof (saddr));
|
||||||
|
|
||||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
||||||
sizeof (saddr.un.sun_path)) {
|
sizeof (saddr.un.sun_path)) {
|
||||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||||
@@ -537,6 +539,8 @@ connect_unix_address(int sock_fd, const char *addr)
|
|||||||
{
|
{
|
||||||
union sockaddr_all saddr;
|
union sockaddr_all saddr;
|
||||||
|
|
||||||
|
memset(&saddr, 0, sizeof (saddr));
|
||||||
|
|
||||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
||||||
sizeof (saddr.un.sun_path)) {
|
sizeof (saddr.un.sun_path)) {
|
||||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||||
|
|||||||
185
sources.c
185
sources.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020
|
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -68,6 +68,7 @@ struct SelectInfo {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
SRC_OK, /* OK so far, not a final status! */
|
SRC_OK, /* OK so far, not a final status! */
|
||||||
SRC_UNSELECTABLE, /* Has noselect option set */
|
SRC_UNSELECTABLE, /* Has noselect option set */
|
||||||
|
SRC_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
|
||||||
SRC_BAD_STATS, /* Doesn't have valid stats data */
|
SRC_BAD_STATS, /* Doesn't have valid stats data */
|
||||||
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
|
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
|
||||||
SRC_JITTERY, /* Had std dev larger than allowed maximum */
|
SRC_JITTERY, /* Had std dev larger than allowed maximum */
|
||||||
@@ -111,6 +112,9 @@ struct SRC_Instance_Record {
|
|||||||
/* Updates left before allowing combining */
|
/* Updates left before allowing combining */
|
||||||
int distant;
|
int distant;
|
||||||
|
|
||||||
|
/* Updates with a status requiring source replacement */
|
||||||
|
int bad;
|
||||||
|
|
||||||
/* Flag indicating the status of the source */
|
/* Flag indicating the status of the source */
|
||||||
SRC_Status status;
|
SRC_Status status;
|
||||||
|
|
||||||
@@ -139,6 +143,10 @@ struct SRC_Instance_Record {
|
|||||||
|
|
||||||
/* Flag indicating the source has a leap second vote */
|
/* Flag indicating the source has a leap second vote */
|
||||||
int leap_vote;
|
int leap_vote;
|
||||||
|
|
||||||
|
/* Flag indicating the source was already reported as
|
||||||
|
a falseticker since the last selection change */
|
||||||
|
int reported_falseticker;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -164,6 +172,8 @@ static int max_n_sources; /* Capacity of the table */
|
|||||||
static int selected_source_index; /* Which source index is currently
|
static int selected_source_index; /* Which source index is currently
|
||||||
selected (set to INVALID_SOURCE
|
selected (set to INVALID_SOURCE
|
||||||
if no current valid reference) */
|
if no current valid reference) */
|
||||||
|
static int reported_no_majority; /* Flag to avoid repeated log message
|
||||||
|
about no majority */
|
||||||
|
|
||||||
/* Score needed to replace the currently selected source */
|
/* Score needed to replace the currently selected source */
|
||||||
#define SCORE_LIMIT 10.0
|
#define SCORE_LIMIT 10.0
|
||||||
@@ -171,12 +181,19 @@ static int selected_source_index; /* Which source index is currently
|
|||||||
/* Number of updates needed to reset the distant status */
|
/* Number of updates needed to reset the distant status */
|
||||||
#define DISTANT_PENALTY 32
|
#define DISTANT_PENALTY 32
|
||||||
|
|
||||||
|
/* Number of updates needed to trigger handling of bad sources */
|
||||||
|
#define BAD_HANDLE_THRESHOLD 4
|
||||||
|
|
||||||
static double max_distance;
|
static double max_distance;
|
||||||
static double max_jitter;
|
static double max_jitter;
|
||||||
static double reselect_distance;
|
static double reselect_distance;
|
||||||
static double stratum_weight;
|
static double stratum_weight;
|
||||||
static double combine_limit;
|
static double combine_limit;
|
||||||
|
|
||||||
|
static SRC_Instance last_updated_inst;
|
||||||
|
|
||||||
|
static LOG_FileID logfileid;
|
||||||
|
|
||||||
/* Identifier of the dump file */
|
/* Identifier of the dump file */
|
||||||
#define DUMP_IDENTIFIER "SRC0\n"
|
#define DUMP_IDENTIFIER "SRC0\n"
|
||||||
|
|
||||||
@@ -188,6 +205,7 @@ static void slew_sources(struct timespec *raw, struct timespec *cooked, double d
|
|||||||
double doffset, LCL_ChangeType change_type, void *anything);
|
double doffset, LCL_ChangeType change_type, void *anything);
|
||||||
static void add_dispersion(double dispersion, void *anything);
|
static void add_dispersion(double dispersion, void *anything);
|
||||||
static char *source_to_string(SRC_Instance inst);
|
static char *source_to_string(SRC_Instance inst);
|
||||||
|
static char get_status_char(SRC_Status status);
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Initialisation function */
|
/* Initialisation function */
|
||||||
@@ -207,6 +225,12 @@ void SRC_Initialise(void) {
|
|||||||
|
|
||||||
LCL_AddParameterChangeHandler(slew_sources, NULL);
|
LCL_AddParameterChangeHandler(slew_sources, NULL);
|
||||||
LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
|
LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
|
||||||
|
|
||||||
|
last_updated_inst = NULL;
|
||||||
|
|
||||||
|
logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection",
|
||||||
|
" Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end")
|
||||||
|
: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -287,7 +311,13 @@ void SRC_DestroyInstance(SRC_Instance instance)
|
|||||||
{
|
{
|
||||||
int dead_index, i;
|
int dead_index, i;
|
||||||
|
|
||||||
|
if (last_updated_inst == instance)
|
||||||
|
last_updated_inst = NULL;
|
||||||
|
|
||||||
assert(initialised);
|
assert(initialised);
|
||||||
|
if (instance->index < 0 || instance->index >= n_sources ||
|
||||||
|
instance != sources[instance->index])
|
||||||
|
assert(0);
|
||||||
|
|
||||||
SST_DeleteInstance(instance->stats);
|
SST_DeleteInstance(instance->stats);
|
||||||
dead_index = instance->index;
|
dead_index = instance->index;
|
||||||
@@ -316,11 +346,13 @@ SRC_ResetInstance(SRC_Instance instance)
|
|||||||
instance->reachability = 0;
|
instance->reachability = 0;
|
||||||
instance->reachability_size = 0;
|
instance->reachability_size = 0;
|
||||||
instance->distant = 0;
|
instance->distant = 0;
|
||||||
|
instance->bad = 0;
|
||||||
instance->status = SRC_BAD_STATS;
|
instance->status = SRC_BAD_STATS;
|
||||||
instance->sel_score = 1.0;
|
instance->sel_score = 1.0;
|
||||||
instance->stratum = 0;
|
instance->stratum = 0;
|
||||||
instance->leap = LEAP_Unsynchronised;
|
instance->leap = LEAP_Unsynchronised;
|
||||||
instance->leap_vote = 0;
|
instance->leap_vote = 0;
|
||||||
|
instance->reported_falseticker = 0;
|
||||||
|
|
||||||
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
|
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
|
||||||
|
|
||||||
@@ -460,6 +492,19 @@ special_mode_end(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_bad_source(SRC_Instance inst)
|
||||||
|
{
|
||||||
|
if (inst->type == SRC_NTP) {
|
||||||
|
DEBUG_LOG("Bad source status=%c", get_status_char(inst->status));
|
||||||
|
NSR_HandleBadSource(inst->ip_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
SRC_UpdateReachability(SRC_Instance inst, int reachable)
|
SRC_UpdateReachability(SRC_Instance inst, int reachable)
|
||||||
{
|
{
|
||||||
@@ -480,14 +525,9 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
|
|||||||
REF_SetUnsynchronised();
|
REF_SetUnsynchronised();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to replace NTP sources that are unreachable, falsetickers, or
|
/* Try to replace unreachable NTP sources */
|
||||||
have root distance or jitter larger than the allowed maximums */
|
if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
|
||||||
if (inst->type == SRC_NTP &&
|
handle_bad_source(inst);
|
||||||
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
|
|
||||||
inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY ||
|
|
||||||
inst->status == SRC_FALSETICKER)) {
|
|
||||||
NSR_HandleBadSource(inst->ip_addr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -572,17 +612,17 @@ update_sel_options(void)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
log_selection_message(const char *format, const char *arg)
|
log_selection_message(LOG_Severity severity, const char *format, const char *arg)
|
||||||
{
|
{
|
||||||
if (REF_GetMode() != REF_ModeNormal)
|
if (REF_GetMode() != REF_ModeNormal)
|
||||||
return;
|
return;
|
||||||
LOG(LOGS_INFO, format, arg);
|
LOG(severity, format, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
log_selection_source(const char *format, SRC_Instance inst)
|
log_selection_source(LOG_Severity severity, const char *format, SRC_Instance inst)
|
||||||
{
|
{
|
||||||
char buf[320], *name, *ntp_name;
|
char buf[320], *name, *ntp_name;
|
||||||
|
|
||||||
@@ -594,7 +634,7 @@ log_selection_source(const char *format, SRC_Instance inst)
|
|||||||
else
|
else
|
||||||
snprintf(buf, sizeof (buf), "%s", name);
|
snprintf(buf, sizeof (buf), "%s", name);
|
||||||
|
|
||||||
log_selection_message(format, buf);
|
log_selection_message(severity, format, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -639,12 +679,44 @@ source_to_string(SRC_Instance inst)
|
|||||||
static void
|
static void
|
||||||
mark_source(SRC_Instance inst, SRC_Status status)
|
mark_source(SRC_Instance inst, SRC_Status status)
|
||||||
{
|
{
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
inst->status = status;
|
inst->status = status;
|
||||||
|
|
||||||
DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
|
/* Try to replace NTP sources that are falsetickers, or have a root
|
||||||
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
|
distance or jitter larger than the allowed maximums */
|
||||||
(unsigned int)inst->reachability, inst->reachability_size, inst->updates,
|
if (inst == last_updated_inst) {
|
||||||
inst->distant, (int)inst->leap, inst->leap_vote,
|
if (inst->bad < INT_MAX &&
|
||||||
|
(status == SRC_FALSETICKER || status == SRC_BAD_DISTANCE || status == SRC_JITTERY))
|
||||||
|
inst->bad++;
|
||||||
|
else
|
||||||
|
inst->bad = 0;
|
||||||
|
if (inst->bad >= BAD_HANDLE_THRESHOLD)
|
||||||
|
handle_bad_source(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d bad=%d leap=%d vote=%d lo=%f hi=%f",
|
||||||
|
source_to_string(inst), get_status_char(inst->status),
|
||||||
|
(unsigned int)inst->sel_options, (unsigned int)inst->reachability,
|
||||||
|
inst->reachability_size, inst->updates,
|
||||||
|
inst->distant, inst->bad, (int)inst->leap, inst->leap_vote,
|
||||||
|
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
|
||||||
|
|
||||||
|
if (logfileid == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
|
||||||
|
LOG_FileWrite(logfileid,
|
||||||
|
"%s %-15s %c -%c%c%c%c %4o %5.2f %10.3e %10.3e %10.3e",
|
||||||
|
UTI_TimeToLogForm(now.tv_sec), source_to_string(inst),
|
||||||
|
get_status_char(inst->status),
|
||||||
|
inst->sel_options & SRC_SELECT_NOSELECT ? 'N' : '-',
|
||||||
|
inst->sel_options & SRC_SELECT_PREFER ? 'P' : '-',
|
||||||
|
inst->sel_options & SRC_SELECT_TRUST ? 'T' : '-',
|
||||||
|
inst->sel_options & SRC_SELECT_REQUIRE ? 'R' : '-',
|
||||||
|
(unsigned int)inst->reachability, inst->sel_score,
|
||||||
|
inst->sel_info.last_sample_ago,
|
||||||
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
|
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -722,8 +794,8 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
|
|||||||
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
|
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
|
||||||
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
|
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
|
||||||
|
|
||||||
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
|
DEBUG_LOG("combining %s oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
|
||||||
index, offset_weight, src_offset, src_offset_sd,
|
source_to_string(sources[index]), offset_weight, src_offset, src_offset_sd,
|
||||||
frequency_weight, src_frequency, src_frequency_sd, src_skew);
|
frequency_weight, src_frequency, src_frequency_sd, src_skew);
|
||||||
|
|
||||||
sum_offset_weight += offset_weight;
|
sum_offset_weight += offset_weight;
|
||||||
@@ -773,13 +845,15 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
|||||||
double first_sample_ago, max_reach_sample_ago;
|
double first_sample_ago, max_reach_sample_ago;
|
||||||
NTP_Leap leap_status;
|
NTP_Leap leap_status;
|
||||||
|
|
||||||
if (updated_inst)
|
if (updated_inst) {
|
||||||
updated_inst->updates++;
|
updated_inst->updates++;
|
||||||
|
last_updated_inst = updated_inst;
|
||||||
|
}
|
||||||
|
|
||||||
if (n_sources == 0) {
|
if (n_sources == 0) {
|
||||||
/* In this case, we clearly cannot synchronise to anything */
|
/* In this case, we clearly cannot synchronise to anything */
|
||||||
if (selected_source_index != INVALID_SOURCE) {
|
if (selected_source_index != INVALID_SOURCE) {
|
||||||
log_selection_message("Can't synchronise: no sources", NULL);
|
log_selection_message(LOGS_INFO, "Can't synchronise: no sources", NULL);
|
||||||
selected_source_index = INVALID_SOURCE;
|
selected_source_index = INVALID_SOURCE;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -815,6 +889,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ignore sources which are not synchronised */
|
||||||
|
if (sources[i]->leap == LEAP_Unsynchronised) {
|
||||||
|
mark_source(sources[i], SRC_UNSYNCHRONISED);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
si = &sources[i]->sel_info;
|
si = &sources[i]->sel_info;
|
||||||
SST_GetSelectionData(sources[i]->stats, &now,
|
SST_GetSelectionData(sources[i]->stats, &now,
|
||||||
&si->lo_limit, &si->hi_limit, &si->root_distance,
|
&si->lo_limit, &si->hi_limit, &si->root_distance,
|
||||||
@@ -966,7 +1046,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
|||||||
if (n_endpoints == 0) {
|
if (n_endpoints == 0) {
|
||||||
/* No sources provided valid endpoints */
|
/* No sources provided valid endpoints */
|
||||||
if (selected_source_index != INVALID_SOURCE) {
|
if (selected_source_index != INVALID_SOURCE) {
|
||||||
log_selection_message("Can't synchronise: no selectable sources", NULL);
|
log_selection_message(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
|
||||||
selected_source_index = INVALID_SOURCE;
|
selected_source_index = INVALID_SOURCE;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1046,8 +1126,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
|||||||
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
|
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
|
||||||
/* Could not even get half the reachable (trusted) sources to agree */
|
/* Could not even get half the reachable (trusted) sources to agree */
|
||||||
|
|
||||||
|
if (!reported_no_majority) {
|
||||||
|
log_selection_message(LOGS_WARN, "Can't synchronise: no majority", NULL);
|
||||||
|
reported_no_majority = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (selected_source_index != INVALID_SOURCE) {
|
if (selected_source_index != INVALID_SOURCE) {
|
||||||
log_selection_message("Can't synchronise: no majority", NULL);
|
|
||||||
REF_SetUnsynchronised();
|
REF_SetUnsynchronised();
|
||||||
selected_source_index = INVALID_SOURCE;
|
selected_source_index = INVALID_SOURCE;
|
||||||
}
|
}
|
||||||
@@ -1093,12 +1177,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
|||||||
sel_req_source = 0;
|
sel_req_source = 0;
|
||||||
} else {
|
} else {
|
||||||
mark_source(sources[i], SRC_FALSETICKER);
|
mark_source(sources[i], SRC_FALSETICKER);
|
||||||
|
if (!sources[i]->reported_falseticker) {
|
||||||
|
log_selection_source(LOGS_WARN, "Detected falseticker %s", sources[i]);
|
||||||
|
sources[i]->reported_falseticker = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) {
|
if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) {
|
||||||
if (selected_source_index != INVALID_SOURCE) {
|
if (selected_source_index != INVALID_SOURCE) {
|
||||||
log_selection_message("Can't synchronise: %s selectable sources",
|
log_selection_message(LOGS_INFO, "Can't synchronise: %s selectable sources",
|
||||||
!n_sel_sources ? "no" :
|
!n_sel_sources ? "no" :
|
||||||
sel_req_source ? "no required source in" : "not enough");
|
sel_req_source ? "no required source in" : "not enough");
|
||||||
selected_source_index = INVALID_SOURCE;
|
selected_source_index = INVALID_SOURCE;
|
||||||
@@ -1215,13 +1303,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
selected_source_index = max_score_index;
|
selected_source_index = max_score_index;
|
||||||
log_selection_source("Selected source %s", sources[selected_source_index]);
|
log_selection_source(LOGS_INFO, "Selected source %s", sources[selected_source_index]);
|
||||||
|
|
||||||
/* New source has been selected, reset all scores */
|
/* New source has been selected, reset all scores */
|
||||||
for (i = 0; i < n_sources; i++) {
|
for (i = 0; i < n_sources; i++) {
|
||||||
sources[i]->sel_score = 1.0;
|
sources[i]->sel_score = 1.0;
|
||||||
sources[i]->distant = 0;
|
sources[i]->distant = 0;
|
||||||
|
sources[i]->reported_falseticker = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reported_no_majority = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_source(sources[selected_source_index], SRC_SELECTED);
|
mark_source(sources[selected_source_index], SRC_SELECTED);
|
||||||
@@ -1512,6 +1603,8 @@ SRC_ResetSources(void)
|
|||||||
|
|
||||||
for (i = 0; i < n_sources; i++)
|
for (i = 0; i < n_sources; i++)
|
||||||
SRC_ResetInstance(sources[i]);
|
SRC_ResetInstance(sources[i]);
|
||||||
|
|
||||||
|
LOG(LOGS_INFO, "Reset all sources");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1559,6 +1652,46 @@ SRC_ActiveSources(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static SRC_Instance
|
||||||
|
find_source(IPAddr *ip, uint32_t ref_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < n_sources; i++) {
|
||||||
|
if ((ip->family != IPADDR_UNSPEC && sources[i]->type == SRC_NTP &&
|
||||||
|
UTI_CompareIPs(ip, sources[i]->ip_addr, NULL) == 0) ||
|
||||||
|
(ip->family == IPADDR_UNSPEC && sources[i]->type == SRC_REFCLOCK &&
|
||||||
|
ref_id == sources[i]->ref_id))
|
||||||
|
return sources[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SRC_ModifySelectOptions(IPAddr *ip, uint32_t ref_id, int options, int mask)
|
||||||
|
{
|
||||||
|
SRC_Instance inst;
|
||||||
|
|
||||||
|
inst = find_source(ip, ref_id);
|
||||||
|
if (!inst)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((inst->conf_sel_options & mask) == options)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
inst->conf_sel_options = (inst->conf_sel_options & ~mask) | options;
|
||||||
|
LOG(LOGS_INFO, "Source %s selection options modified", source_to_string(inst));
|
||||||
|
|
||||||
|
update_sel_options();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
|
SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
|
||||||
{
|
{
|
||||||
@@ -1642,6 +1775,8 @@ get_status_char(SRC_Status status)
|
|||||||
switch (status) {
|
switch (status) {
|
||||||
case SRC_UNSELECTABLE:
|
case SRC_UNSELECTABLE:
|
||||||
return 'N';
|
return 'N';
|
||||||
|
case SRC_UNSYNCHRONISED:
|
||||||
|
return 's';
|
||||||
case SRC_BAD_STATS:
|
case SRC_BAD_STATS:
|
||||||
return 'M';
|
return 'M';
|
||||||
case SRC_BAD_DISTANCE:
|
case SRC_BAD_DISTANCE:
|
||||||
|
|||||||
@@ -131,6 +131,10 @@ extern int SRC_IsReachable(SRC_Instance inst);
|
|||||||
extern int SRC_ReadNumberOfSources(void);
|
extern int SRC_ReadNumberOfSources(void);
|
||||||
extern int SRC_ActiveSources(void);
|
extern int SRC_ActiveSources(void);
|
||||||
|
|
||||||
|
/* Modify selection options of an NTP source specified by address, or
|
||||||
|
refclock specified by its reference ID */
|
||||||
|
extern int SRC_ModifySelectOptions(IPAddr *ip, uint32_t ref_id, int options, int mask);
|
||||||
|
|
||||||
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
|
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
|
||||||
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
|
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
|
||||||
extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);
|
extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ static LOG_FileID logfileid;
|
|||||||
|
|
||||||
struct SST_Stats_Record {
|
struct SST_Stats_Record {
|
||||||
|
|
||||||
/* Reference ID and IP address of source, used for logging to statistics log */
|
/* Reference ID and IP address (NULL if not an NTP source) */
|
||||||
uint32_t refid;
|
uint32_t refid;
|
||||||
IPAddr *ip_addr;
|
IPAddr *ip_addr;
|
||||||
|
|
||||||
@@ -211,8 +211,8 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_sample
|
|||||||
SST_Stats inst;
|
SST_Stats inst;
|
||||||
inst = MallocNew(struct SST_Stats_Record);
|
inst = MallocNew(struct SST_Stats_Record);
|
||||||
|
|
||||||
inst->min_samples = min_samples;
|
inst->max_samples = max_samples > 0 ? CLAMP(1, max_samples, MAX_SAMPLES) : MAX_SAMPLES;
|
||||||
inst->max_samples = max_samples;
|
inst->min_samples = CLAMP(1, min_samples, inst->max_samples);
|
||||||
inst->fixed_min_delay = min_delay;
|
inst->fixed_min_delay = min_delay;
|
||||||
inst->fixed_asymmetry = asymmetry;
|
inst->fixed_asymmetry = asymmetry;
|
||||||
|
|
||||||
@@ -698,7 +698,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
|||||||
|
|
||||||
/* If maxsamples is too small to have a successful regression, enable the
|
/* If maxsamples is too small to have a successful regression, enable the
|
||||||
selection as a special case for a fast update/print-once reference mode */
|
selection as a special case for a fast update/print-once reference mode */
|
||||||
if (!*select_ok && inst->n_samples < 3 && inst->n_samples == inst->max_samples) {
|
if (!*select_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
|
||||||
|
inst->n_samples == inst->max_samples) {
|
||||||
*std_dev = CNF_GetMaxJitter();
|
*std_dev = CNF_GetMaxJitter();
|
||||||
*select_ok = 1;
|
*select_ok = 1;
|
||||||
}
|
}
|
||||||
@@ -814,7 +815,7 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
|
|||||||
{
|
{
|
||||||
double elapsed;
|
double elapsed;
|
||||||
|
|
||||||
if (inst->n_samples < 3) {
|
if (inst->n_samples < MIN_SAMPLES_FOR_REGRESS) {
|
||||||
/* We don't have any useful statistics, and presumably the poll
|
/* We don't have any useful statistics, and presumably the poll
|
||||||
interval is minimal. We can't do any useful prediction other
|
interval is minimal. We can't do any useful prediction other
|
||||||
than use the latest sample or zero if we don't have any samples */
|
than use the latest sample or zero if we don't have any samples */
|
||||||
@@ -930,6 +931,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
|
|||||||
|
|
||||||
/* Make sure the samples are sane and they are in order */
|
/* Make sure the samples are sane and they are in order */
|
||||||
if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
|
if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
|
||||||
|
UTI_CompareTimespecs(&now, &inst->sample_times[i]) < 0 ||
|
||||||
!(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
|
!(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
|
||||||
fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
|
fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
|
||||||
(i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
|
(i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
|
||||||
@@ -962,8 +964,9 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
|
|||||||
report->latest_meas = inst->offsets[i];
|
report->latest_meas = inst->offsets[i];
|
||||||
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
|
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
|
||||||
|
|
||||||
/* Align the sample time to reduce the leak of the receive timestamp */
|
/* Align the sample time to reduce the leak of the NTP receive timestamp */
|
||||||
last_sample_time = inst->sample_times[i];
|
last_sample_time = inst->sample_times[i];
|
||||||
|
if (inst->ip_addr)
|
||||||
last_sample_time.tv_nsec = 0;
|
last_sample_time.tv_nsec = 0;
|
||||||
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
|
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
|
||||||
} else {
|
} else {
|
||||||
@@ -985,6 +988,14 @@ SST_Samples(SST_Stats inst)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SST_GetMinSamples(SST_Stats inst)
|
||||||
|
{
|
||||||
|
return inst->min_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
|
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -133,6 +133,8 @@ extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *repor
|
|||||||
|
|
||||||
extern int SST_Samples(SST_Stats inst);
|
extern int SST_Samples(SST_Stats inst);
|
||||||
|
|
||||||
|
extern int SST_GetMinSamples(SST_Stats inst);
|
||||||
|
|
||||||
extern double SST_GetJitterAsymmetry(SST_Stats inst);
|
extern double SST_GetJitterAsymmetry(SST_Stats inst);
|
||||||
|
|
||||||
#endif /* GOT_SOURCESTATS_H */
|
#endif /* GOT_SOURCESTATS_H */
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ typedef struct {
|
|||||||
double max_delay;
|
double max_delay;
|
||||||
double max_delay_ratio;
|
double max_delay_ratio;
|
||||||
double max_delay_dev_ratio;
|
double max_delay_dev_ratio;
|
||||||
|
double max_delay_quant;
|
||||||
double min_delay;
|
double min_delay;
|
||||||
double asymmetry;
|
double asymmetry;
|
||||||
double offset;
|
double offset;
|
||||||
|
|||||||
@@ -73,13 +73,28 @@ static double slew_freq;
|
|||||||
/* Time (raw) of last update of slewing frequency and offset */
|
/* Time (raw) of last update of slewing frequency and offset */
|
||||||
static struct timespec slew_start;
|
static struct timespec slew_start;
|
||||||
|
|
||||||
/* Limits for the slew timeout */
|
/* Limits for the slew length */
|
||||||
#define MIN_SLEW_TIMEOUT 1.0
|
#define MIN_SLEW_DURATION 1.0
|
||||||
#define MAX_SLEW_TIMEOUT 1.0e4
|
#define MAX_SLEW_DURATION 1.0e4
|
||||||
|
|
||||||
/* Scheduler timeout ID for ending of the currently running slew */
|
/* Scheduler timeout ID for ending of the currently running slew */
|
||||||
static SCH_TimeoutID slew_timeout_id;
|
static SCH_TimeoutID slew_timeout_id;
|
||||||
|
|
||||||
|
/* Scheduled duration of the currently running slew */
|
||||||
|
static double slew_duration;
|
||||||
|
|
||||||
|
/* Expected delay in ending of the slew due to process scheduling and
|
||||||
|
execution time, tracked as a decaying maximum value */
|
||||||
|
static double slew_excess_duration;
|
||||||
|
|
||||||
|
/* Maximum accepted excess duration to ignore large jumps after resuming
|
||||||
|
suspended system and other reasons (which should be handled in the
|
||||||
|
scheduler), a constant to determine the minimum slew duration to avoid
|
||||||
|
oscillations due to the excess, and the decay constant */
|
||||||
|
#define MAX_SLEW_EXCESS_DURATION 100.0
|
||||||
|
#define MIN_SLEW_DURATION_EXCESS_RATIO 5.0
|
||||||
|
#define SLEW_EXCESS_DURATION_DECAY 0.9
|
||||||
|
|
||||||
/* Suggested offset correction rate (correction time * offset) */
|
/* Suggested offset correction rate (correction time * offset) */
|
||||||
static double correction_rate;
|
static double correction_rate;
|
||||||
|
|
||||||
@@ -109,12 +124,7 @@ static void
|
|||||||
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||||
double doffset, LCL_ChangeType change_type, void *anything)
|
double doffset, LCL_ChangeType change_type, void *anything)
|
||||||
{
|
{
|
||||||
if (change_type == LCL_ChangeUnknownStep) {
|
if (change_type == LCL_ChangeStep) {
|
||||||
/* Reset offset and slewing */
|
|
||||||
slew_start = *raw;
|
|
||||||
offset_register = 0.0;
|
|
||||||
update_slew();
|
|
||||||
} else if (change_type == LCL_ChangeStep) {
|
|
||||||
UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
|
UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,8 +179,8 @@ clamp_freq(double freq)
|
|||||||
static void
|
static void
|
||||||
update_slew(void)
|
update_slew(void)
|
||||||
{
|
{
|
||||||
|
double old_slew_freq, total_freq, corr_freq, duration, excess_duration;
|
||||||
struct timespec now, end_of_slew;
|
struct timespec now, end_of_slew;
|
||||||
double old_slew_freq, total_freq, corr_freq, duration;
|
|
||||||
|
|
||||||
/* Remove currently running timeout */
|
/* Remove currently running timeout */
|
||||||
SCH_RemoveTimeout(slew_timeout_id);
|
SCH_RemoveTimeout(slew_timeout_id);
|
||||||
@@ -183,13 +193,25 @@ update_slew(void)
|
|||||||
|
|
||||||
stop_fastslew(&now);
|
stop_fastslew(&now);
|
||||||
|
|
||||||
/* Estimate how long should the next slew take */
|
/* Update the maximum excess duration, decaying even when the slew did
|
||||||
|
not time out (i.e. frequency was set or offset accrued), but add a small
|
||||||
|
value to avoid denormals */
|
||||||
|
slew_excess_duration = (slew_excess_duration + 1.0e-9) * SLEW_EXCESS_DURATION_DECAY;
|
||||||
|
excess_duration = duration - slew_duration;
|
||||||
|
if (slew_excess_duration < excess_duration &&
|
||||||
|
excess_duration <= MAX_SLEW_EXCESS_DURATION)
|
||||||
|
slew_excess_duration = excess_duration;
|
||||||
|
|
||||||
|
/* Calculate the duration of the new slew, considering the current correction
|
||||||
|
rate and previous delays in stopping of the slew */
|
||||||
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
|
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
|
||||||
duration = MAX_SLEW_TIMEOUT;
|
duration = MAX_SLEW_DURATION;
|
||||||
} else {
|
} else {
|
||||||
duration = correction_rate / fabs(offset_register);
|
duration = correction_rate / fabs(offset_register);
|
||||||
if (duration < MIN_SLEW_TIMEOUT)
|
if (duration < MIN_SLEW_DURATION)
|
||||||
duration = MIN_SLEW_TIMEOUT;
|
duration = MIN_SLEW_DURATION;
|
||||||
|
if (duration < MIN_SLEW_DURATION_EXCESS_RATIO * slew_excess_duration)
|
||||||
|
duration = MIN_SLEW_DURATION_EXCESS_RATIO * slew_excess_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get frequency offset needed to slew the offset in the duration
|
/* Get frequency offset needed to slew the offset in the duration
|
||||||
@@ -232,23 +254,25 @@ update_slew(void)
|
|||||||
maximum timeout and try again on the next update. */
|
maximum timeout and try again on the next update. */
|
||||||
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
|
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
|
||||||
offset_register * slew_freq <= 0.0) {
|
offset_register * slew_freq <= 0.0) {
|
||||||
duration = MAX_SLEW_TIMEOUT;
|
duration = MAX_SLEW_DURATION;
|
||||||
} else {
|
} else {
|
||||||
duration = offset_register / slew_freq;
|
duration = offset_register / slew_freq;
|
||||||
if (duration < MIN_SLEW_TIMEOUT)
|
if (duration < MIN_SLEW_DURATION)
|
||||||
duration = MIN_SLEW_TIMEOUT;
|
duration = MIN_SLEW_DURATION;
|
||||||
else if (duration > MAX_SLEW_TIMEOUT)
|
else if (duration > MAX_SLEW_DURATION)
|
||||||
duration = MAX_SLEW_TIMEOUT;
|
duration = MAX_SLEW_DURATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restart timer for the next update */
|
/* Restart timer for the next update */
|
||||||
UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
|
UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
|
||||||
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
|
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
|
||||||
slew_start = now;
|
slew_start = now;
|
||||||
|
slew_duration = duration;
|
||||||
|
|
||||||
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
|
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e"
|
||||||
|
" duration=%f excess=%f slew_error=%e",
|
||||||
offset_register, correction_rate, base_freq, total_freq, slew_freq,
|
offset_register, correction_rate, base_freq, total_freq, slew_freq,
|
||||||
duration, slew_error);
|
slew_duration, slew_excess_duration, slew_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -385,6 +409,7 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
|
|||||||
base_freq = (*drv_read_freq)();
|
base_freq = (*drv_read_freq)();
|
||||||
slew_freq = 0.0;
|
slew_freq = 0.0;
|
||||||
offset_register = 0.0;
|
offset_register = 0.0;
|
||||||
|
slew_excess_duration = 0.0;
|
||||||
|
|
||||||
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
|
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
|
||||||
|
|
||||||
|
|||||||
137
sys_linux.c
137
sys_linux.c
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||||
#include <linux/ptp_clock.h>
|
#include <linux/ptp_clock.h>
|
||||||
|
#include <poll.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEAT_SCFILTER
|
#ifdef FEAT_SCFILTER
|
||||||
@@ -497,6 +498,12 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
|
|||||||
SCMP_SYS(getrlimit),
|
SCMP_SYS(getrlimit),
|
||||||
SCMP_SYS(getuid),
|
SCMP_SYS(getuid),
|
||||||
SCMP_SYS(getuid32),
|
SCMP_SYS(getuid32),
|
||||||
|
#ifdef __NR_membarrier
|
||||||
|
SCMP_SYS(membarrier),
|
||||||
|
#endif
|
||||||
|
#ifdef __NR_rseq
|
||||||
|
SCMP_SYS(rseq),
|
||||||
|
#endif
|
||||||
SCMP_SYS(rt_sigaction),
|
SCMP_SYS(rt_sigaction),
|
||||||
SCMP_SYS(rt_sigreturn),
|
SCMP_SYS(rt_sigreturn),
|
||||||
SCMP_SYS(rt_sigprocmask),
|
SCMP_SYS(rt_sigprocmask),
|
||||||
@@ -596,6 +603,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
|
|||||||
SCMP_SYS(select),
|
SCMP_SYS(select),
|
||||||
SCMP_SYS(set_robust_list),
|
SCMP_SYS(set_robust_list),
|
||||||
SCMP_SYS(write),
|
SCMP_SYS(write),
|
||||||
|
SCMP_SYS(writev),
|
||||||
|
|
||||||
/* Miscellaneous */
|
/* Miscellaneous */
|
||||||
SCMP_SYS(getrandom),
|
SCMP_SYS(getrandom),
|
||||||
@@ -630,6 +638,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
|
|||||||
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
|
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
|
||||||
#ifdef FEAT_IPV6
|
#ifdef FEAT_IPV6
|
||||||
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
|
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
|
||||||
|
#ifdef IPV6_TCLASS
|
||||||
|
{ SOL_IPV6, IPV6_TCLASS },
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef SO_BINDTODEVICE
|
#ifdef SO_BINDTODEVICE
|
||||||
{ SOL_SOCKET, SO_BINDTODEVICE },
|
{ SOL_SOCKET, SO_BINDTODEVICE },
|
||||||
@@ -647,7 +658,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
|
|||||||
const static int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL };
|
const static int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL };
|
||||||
|
|
||||||
const static unsigned long ioctls[] = {
|
const static unsigned long ioctls[] = {
|
||||||
FIONREAD, TCGETS,
|
FIONREAD, TCGETS, TIOCGWINSZ,
|
||||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||||
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
|
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
|
||||||
#ifdef PTP_PIN_SETFUNC
|
#ifdef PTP_PIN_SETFUNC
|
||||||
@@ -791,73 +802,25 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
|
|||||||
|
|
||||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||||
|
|
||||||
#define PHC_READINGS 10
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
process_phc_readings(struct timespec ts[][3], int n, double precision,
|
get_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
|
||||||
{
|
{
|
||||||
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
|
|
||||||
int i, combined;
|
|
||||||
|
|
||||||
if (n > PTP_MAX_SAMPLES)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
|
|
||||||
|
|
||||||
if (delays[i] < 0.0) {
|
|
||||||
/* Step in the middle of a PHC reading? */
|
|
||||||
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!i || delays[i] < min_delay)
|
|
||||||
min_delay = delays[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_prec = LCL_GetSysPrecisionAsQuantum();
|
|
||||||
|
|
||||||
/* Combine best readings */
|
|
||||||
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
|
|
||||||
if (delays[i] > min_delay + MAX(sys_prec, precision))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
|
|
||||||
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
|
|
||||||
combined++;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(combined);
|
|
||||||
|
|
||||||
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
|
|
||||||
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
|
|
||||||
*err = MAX(min_delay / 2.0, precision);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static int
|
|
||||||
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
|
||||||
struct timespec *sys_ts, double *err)
|
|
||||||
{
|
|
||||||
struct timespec ts[PHC_READINGS][3];
|
|
||||||
struct ptp_sys_offset sys_off;
|
struct ptp_sys_offset sys_off;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
|
||||||
|
|
||||||
/* Silence valgrind */
|
/* Silence valgrind */
|
||||||
memset(&sys_off, 0, sizeof (sys_off));
|
memset(&sys_off, 0, sizeof (sys_off));
|
||||||
|
|
||||||
sys_off.n_samples = PHC_READINGS;
|
sys_off.n_samples = max_samples;
|
||||||
|
|
||||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < PHC_READINGS; i++) {
|
for (i = 0; i < max_samples; i++) {
|
||||||
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
|
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
|
||||||
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
|
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
|
||||||
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
|
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||||
@@ -866,31 +829,31 @@ get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
|||||||
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
return max_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
get_extended_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||||
struct timespec *sys_ts, double *err)
|
|
||||||
{
|
{
|
||||||
#ifdef PTP_SYS_OFFSET_EXTENDED
|
#ifdef PTP_SYS_OFFSET_EXTENDED
|
||||||
struct timespec ts[PHC_READINGS][3];
|
|
||||||
struct ptp_sys_offset_extended sys_off;
|
struct ptp_sys_offset_extended sys_off;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
|
||||||
|
|
||||||
/* Silence valgrind */
|
/* Silence valgrind */
|
||||||
memset(&sys_off, 0, sizeof (sys_off));
|
memset(&sys_off, 0, sizeof (sys_off));
|
||||||
|
|
||||||
sys_off.n_samples = PHC_READINGS;
|
sys_off.n_samples = max_samples;
|
||||||
|
|
||||||
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
|
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
|
||||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
|
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < PHC_READINGS; i++) {
|
for (i = 0; i < max_samples; i++) {
|
||||||
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
|
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
|
||||||
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
|
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
|
||||||
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
|
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
|
||||||
@@ -899,7 +862,7 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
|||||||
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
|
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
return max_samples;
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -908,12 +871,14 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||||
struct timespec *sys_ts, double *err)
|
|
||||||
{
|
{
|
||||||
#ifdef PTP_SYS_OFFSET_PRECISE
|
#ifdef PTP_SYS_OFFSET_PRECISE
|
||||||
struct ptp_sys_offset_precise sys_off;
|
struct ptp_sys_offset_precise sys_off;
|
||||||
|
|
||||||
|
if (max_samples < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Silence valgrind */
|
/* Silence valgrind */
|
||||||
memset(&sys_off, 0, sizeof (sys_off));
|
memset(&sys_off, 0, sizeof (sys_off));
|
||||||
|
|
||||||
@@ -923,11 +888,11 @@ get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
phc_ts->tv_sec = sys_off.device.sec;
|
ts[0][0].tv_sec = sys_off.sys_realtime.sec;
|
||||||
phc_ts->tv_nsec = sys_off.device.nsec;
|
ts[0][0].tv_nsec = sys_off.sys_realtime.nsec;
|
||||||
sys_ts->tv_sec = sys_off.sys_realtime.sec;
|
ts[0][1].tv_sec = sys_off.device.sec;
|
||||||
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
|
ts[0][1].tv_nsec = sys_off.device.nsec;
|
||||||
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
|
ts[0][2] = ts[0][0];
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
#else
|
#else
|
||||||
@@ -971,23 +936,23 @@ SYS_Linux_OpenPHC(const char *path, int phc_index)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
|
SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
|
||||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
struct timespec tss[][3])
|
||||||
{
|
{
|
||||||
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
|
int r = 0;
|
||||||
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
|
||||||
|
if ((*reading_mode == 2 || *reading_mode == 0) && !nocrossts &&
|
||||||
|
(r = get_precise_phc_readings(fd, max_readings, tss)) > 0) {
|
||||||
*reading_mode = 2;
|
*reading_mode = 2;
|
||||||
return 1;
|
} else if ((*reading_mode == 3 || *reading_mode == 0) &&
|
||||||
} else if ((*reading_mode == 3 || !*reading_mode) &&
|
(r = get_extended_phc_readings(fd, max_readings, tss)) > 0) {
|
||||||
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
|
||||||
*reading_mode = 3;
|
*reading_mode = 3;
|
||||||
return 1;
|
} else if ((*reading_mode == 1 || *reading_mode == 0) &&
|
||||||
} else if ((*reading_mode == 1 || !*reading_mode) &&
|
(r = get_phc_readings(fd, max_readings, tss)) > 0) {
|
||||||
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
|
||||||
*reading_mode = 1;
|
*reading_mode = 1;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1005,7 +970,7 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
|||||||
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
|
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
|
||||||
pin_desc.chan = channel;
|
pin_desc.chan = channel;
|
||||||
|
|
||||||
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
|
if (pin >= 0 && ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
|
||||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
|
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1034,6 +999,16 @@ int
|
|||||||
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
|
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
|
||||||
{
|
{
|
||||||
struct ptp_extts_event extts_event;
|
struct ptp_extts_event extts_event;
|
||||||
|
struct pollfd pfd;
|
||||||
|
|
||||||
|
/* Make sure the read will not block in case we have multiple
|
||||||
|
descriptors of the same PHC (O_NONBLOCK does not work) */
|
||||||
|
pfd.fd = fd;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
if (poll(&pfd, 1, 0) != 1 || pfd.revents != POLLIN) {
|
||||||
|
DEBUG_LOG("Missing PHC extts event");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
|
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
|
||||||
DEBUG_LOG("Could not read PHC extts event");
|
DEBUG_LOG("Could not read PHC extts event");
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
|
|||||||
|
|
||||||
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
|
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
|
||||||
|
|
||||||
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
|
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
|
||||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
|
struct timespec tss[][3]);
|
||||||
|
|
||||||
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
||||||
int rising, int falling, int enable);
|
int rising, int falling, int enable);
|
||||||
|
|||||||
@@ -21,23 +21,54 @@
|
|||||||
|
|
||||||
=======================================================================
|
=======================================================================
|
||||||
|
|
||||||
Driver file for Solaris operating system
|
Driver file for illumos operating system (previously Solaris)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "sysincl.h"
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
#include "privops.h"
|
#include "privops.h"
|
||||||
#include "sys_solaris.h"
|
#include "sys_solaris.h"
|
||||||
#include "sys_timex.h"
|
#include "sys_timex.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <kvm.h>
|
||||||
|
#include <nlist.h>
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_dosynctodr(int on_off)
|
||||||
|
{
|
||||||
|
struct nlist nl[] = { {"dosynctodr"}, {NULL} };
|
||||||
|
kvm_t *kt;
|
||||||
|
|
||||||
|
kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
|
||||||
|
if (!kt)
|
||||||
|
LOG_FATAL("Could not open kvm");
|
||||||
|
|
||||||
|
if (kvm_nlist(kt, nl) < 0 || !nl[0].n_value)
|
||||||
|
LOG_FATAL("Could not get dosynctodr address");
|
||||||
|
|
||||||
|
if (kvm_kwrite(kt, nl[0].n_value, &on_off, sizeof (on_off)) < 0)
|
||||||
|
LOG_FATAL("Could not write to dosynctodr");
|
||||||
|
|
||||||
|
kvm_close(kt);
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
SYS_Solaris_Initialise(void)
|
SYS_Solaris_Initialise(void)
|
||||||
{
|
{
|
||||||
|
/* The kernel keeps the system clock and hardware clock synchronised to each
|
||||||
|
other. The dosynctodr variable needs to be set to zero to prevent the
|
||||||
|
the system clock from following the hardware clock when the system clock
|
||||||
|
is not adjusted by adjtime() or ntp_adjtime(modes=MOD_OFFSET). */
|
||||||
|
set_dosynctodr(0);
|
||||||
|
|
||||||
/* The kernel allows the frequency to be set in the full range off int32_t */
|
/* The kernel allows the frequency to be set in the full range off int32_t */
|
||||||
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
|
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
|
||||||
0.0, 0.0, NULL, NULL);
|
0.0, 0.0, NULL, NULL);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
# Run the unit and simulation tests with different compiler sanitizers
|
# Run the unit and simulation tests with different compiler sanitizers
|
||||||
# and under valgrind
|
# and under valgrind
|
||||||
|
|
||||||
|
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
|
||||||
|
|
||||||
cd ../..
|
cd ../..
|
||||||
|
|
||||||
if [ "$(uname -sm)" != "Linux x86_64" ]; then
|
if [ "$(uname -sm)" != "Linux x86_64" ]; then
|
||||||
@@ -24,6 +26,7 @@ for extra_config_opts in \
|
|||||||
"--all-privops" \
|
"--all-privops" \
|
||||||
"--disable-ipv6" \
|
"--disable-ipv6" \
|
||||||
"--disable-scfilter" \
|
"--disable-scfilter" \
|
||||||
|
"--without-aes-gcm-siv" \
|
||||||
"--without-gnutls" \
|
"--without-gnutls" \
|
||||||
"--without-nettle" \
|
"--without-nettle" \
|
||||||
"--without-nettle --without-nss" \
|
"--without-nettle --without-nss" \
|
||||||
@@ -42,7 +45,7 @@ do
|
|||||||
export CC
|
export CC
|
||||||
|
|
||||||
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
|
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
|
||||||
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
|
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize-recover=all $san_options $arch_opts"
|
||||||
|
|
||||||
# clang msan doesn't work on i686 and otherwise requires patches
|
# clang msan doesn't work on i686 and otherwise requires patches
|
||||||
echo $CFLAGS | grep -q 'sanitize=memory' && continue
|
echo $CFLAGS | grep -q 'sanitize=memory' && continue
|
||||||
@@ -75,7 +78,7 @@ do
|
|||||||
pushd test/unit || exit 1
|
pushd test/unit || exit 1
|
||||||
make "$@" || exit 1
|
make "$@" || exit 1
|
||||||
if [ "$san_options" = "" ]; then
|
if [ "$san_options" = "" ]; then
|
||||||
make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1
|
make check TEST_WRAPPER="valgrind $valgrind_opts --error-exitcode=1" || exit 1
|
||||||
else
|
else
|
||||||
make check || exit 1
|
make check || exit 1
|
||||||
fi
|
fi
|
||||||
@@ -87,7 +90,7 @@ do
|
|||||||
pushd test/simulation || exit 1
|
pushd test/simulation || exit 1
|
||||||
export CLKNETSIM_RANDOM_SEED=101
|
export CLKNETSIM_RANDOM_SEED=101
|
||||||
if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
|
if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
|
||||||
CLKNETSIM_CLIENT_WRAPPER=valgrind ./run -i 1 || exit 1
|
CLKNETSIM_CLIENT_WRAPPER="valgrind $valgrind_opts" ./run -i 1 || exit 1
|
||||||
elif [ "$CC" = "gcc" ] && ! echo $CFLAGS | grep -q "-static-libasan"; then
|
elif [ "$CC" = "gcc" ] && ! echo $CFLAGS | grep -q "-static-libasan"; then
|
||||||
libasan=$(ldd ../../chronyd | grep -o '/.*lib.*/libasan.so.[0-9]')
|
libasan=$(ldd ../../chronyd | grep -o '/.*lib.*/libasan.so.[0-9]')
|
||||||
CLKNETSIM_PRELOAD=$libasan ./run -i 1 || exit 1
|
CLKNETSIM_PRELOAD=$libasan ./run -i 1 || exit 1
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ echo "$ntp_start" | grep -q '-' && test_skip
|
|||||||
|
|
||||||
for time_offset in -1e-1 1e-1; do
|
for time_offset in -1e-1 1e-1; do
|
||||||
for start_offset in 0 "2^32 - $limit"; do
|
for start_offset in 0 "2^32 - $limit"; do
|
||||||
export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
|
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
|
||||||
run_test || test_fail
|
run_test || test_fail
|
||||||
check_chronyd_exit || test_fail
|
check_chronyd_exit || test_fail
|
||||||
check_source_selection || test_fail
|
check_source_selection || test_fail
|
||||||
@@ -38,7 +38,7 @@ for time_offset in -1e-1 1e-1; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
for start_offset in -$limit "2^32"; do
|
for start_offset in -$limit "2^32"; do
|
||||||
export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
|
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
|
||||||
run_test || test_fail
|
run_test || test_fail
|
||||||
check_chronyd_exit || test_fail
|
check_chronyd_exit || test_fail
|
||||||
check_source_selection || test_fail
|
check_source_selection || test_fail
|
||||||
|
|||||||
@@ -27,4 +27,30 @@ for poll in $(seq 1 14); do
|
|||||||
check_sync || test_fail
|
check_sync || test_fail
|
||||||
done
|
done
|
||||||
|
|
||||||
|
min_sync_time=$default_min_sync_time
|
||||||
|
max_sync_time=$default_max_sync_time
|
||||||
|
client_max_min_out_interval=$default_client_max_min_out_interval
|
||||||
|
client_min_mean_out_interval=$default_client_min_mean_out_interval
|
||||||
|
|
||||||
|
limit=10
|
||||||
|
|
||||||
|
for poll in $(seq -7 2 -1); do
|
||||||
|
client_server_options="minpoll $poll maxpoll $poll"
|
||||||
|
|
||||||
|
base_delay=1e-4
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_file_messages " 2 1 " \
|
||||||
|
$[2**-poll * limit * 9 / 10] $[2**-poll * limit] log.packets || test_fail
|
||||||
|
|
||||||
|
base_delay=2e-2
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_file_messages " 2 1 " $[limit * 9 / 10] $limit log.packets || test_fail
|
||||||
|
done
|
||||||
|
|
||||||
test_pass
|
test_pass
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
. ./test.common
|
. ./test.common
|
||||||
test_start "SHM refclock"
|
test_start "reference clocks"
|
||||||
|
|
||||||
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||||
check_config_h 'FEAT_PHC 1' || test_skip
|
check_config_h 'FEAT_PHC 1' || test_skip
|
||||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||||
|
|
||||||
|
export CLKNETSIM_PHC_DELAY=1e-6
|
||||||
|
export CLKNETSIM_PHC_JITTER=1e-7
|
||||||
|
|
||||||
servers=0
|
servers=0
|
||||||
limit=1000
|
limit=1000
|
||||||
refclock_jitter=$jitter
|
refclock_jitter=$jitter
|
||||||
@@ -15,7 +18,7 @@ max_sync_time=70
|
|||||||
chronyc_start=70
|
chronyc_start=70
|
||||||
chronyc_conf="tracking"
|
chronyc_conf="tracking"
|
||||||
|
|
||||||
for refclock in "SHM 0" "PHC /dev/ptp0"; do
|
for refclock in "SHM 0" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
|
||||||
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
|
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
|
||||||
logdir tmp
|
logdir tmp
|
||||||
log refclocks"
|
log refclocks"
|
||||||
@@ -32,7 +35,11 @@ Root delay : 0.001000000 seconds
|
|||||||
Update interval : 16\.. seconds
|
Update interval : 16\.. seconds
|
||||||
.*$" || test_fail
|
.*$" || test_fail
|
||||||
|
|
||||||
|
if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
|
||||||
|
check_file_messages "20.* GPS.*[0-9] N " 650 750 refclocks.log || test_fail
|
||||||
|
else
|
||||||
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
|
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
|
||||||
|
fi
|
||||||
check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
|
check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
|
||||||
rm -f tmp/refclocks.log
|
rm -f tmp/refclocks.log
|
||||||
done
|
done
|
||||||
@@ -106,4 +113,31 @@ Root delay : 0\.000000001 seconds
|
|||||||
rm -f tmp/refclocks.log
|
rm -f tmp/refclocks.log
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
refclock_offset="(+ 0.399 (sum 1e-3))"
|
||||||
|
refclock_jitter=1e-6
|
||||||
|
servers=1
|
||||||
|
freq_offset="(* 1e-4 (sine 1000))"
|
||||||
|
base_delay="(* -1.0 (equal 0.1 (min time 5000) 5000))"
|
||||||
|
client_server_options="minpoll 4 maxpoll 4 filter 5 minsamples 64"
|
||||||
|
client_conf="
|
||||||
|
refclock PHC /dev/ptp0 local poll 2
|
||||||
|
logdir tmp
|
||||||
|
log refclocks tracking"
|
||||||
|
chronyc_conf=""
|
||||||
|
limit=10000
|
||||||
|
max_sync_time=5000
|
||||||
|
time_max_limit=1e-3
|
||||||
|
time_rms_limit=5e-4
|
||||||
|
freq_max_limit=2e-5
|
||||||
|
freq_rms_limit=5e-6
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_file_messages "20.* PHC0 .* [0-9] ? " 9999 10001 refclocks.log || test_fail
|
||||||
|
check_file_messages "20.* PHC0 .* - ? " 2499 2501 refclocks.log || test_fail
|
||||||
|
check_file_messages "20.* PHC0 " 0 0 tracking.log || test_fail
|
||||||
|
rm -f tmp/refclocks.log tmp/tracking.log
|
||||||
|
|
||||||
test_pass
|
test_pass
|
||||||
|
|||||||
@@ -91,6 +91,18 @@ check_chronyd_exit || test_fail
|
|||||||
check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
|
check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
|
||||||
|| test_fail
|
|| test_fail
|
||||||
|
|
||||||
|
chronyc_options="-c -e"
|
||||||
|
chronyc_conf="sources"
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
|
||||||
|
check_chronyc_output "^#,.,SHM0.*
|
||||||
|
\^,.,192\.168\.123\.1.*
|
||||||
|
\^,.,192\.168\.123\.2.*
|
||||||
|
\.$" \
|
||||||
|
|| test_fail
|
||||||
|
|
||||||
chronyc_options=""
|
chronyc_options=""
|
||||||
server_strata=0
|
server_strata=0
|
||||||
chronyc_start=0.5
|
chronyc_start=0.5
|
||||||
@@ -102,7 +114,7 @@ limit=1
|
|||||||
for chronyc_conf in \
|
for chronyc_conf in \
|
||||||
"accheck 1.2.3.4" \
|
"accheck 1.2.3.4" \
|
||||||
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
|
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
|
||||||
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
|
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
|
||||||
"add server node1.net1.clk" \
|
"add server node1.net1.clk" \
|
||||||
"allow 1.2.3.4" \
|
"allow 1.2.3.4" \
|
||||||
"allow 1.2" \
|
"allow 1.2" \
|
||||||
@@ -164,6 +176,9 @@ for chronyc_conf in \
|
|||||||
"reselectdist 1e-3" \
|
"reselectdist 1e-3" \
|
||||||
"reset sources" \
|
"reset sources" \
|
||||||
"selectdata" \
|
"selectdata" \
|
||||||
|
"selectopts 1.2.3.4 -noselect +trust +require +prefer" \
|
||||||
|
"selectopts ID#0000000001 +prefer" \
|
||||||
|
"selectopts PPS0 +prefer" \
|
||||||
"settime 16:30" \
|
"settime 16:30" \
|
||||||
"settime 16:30:05" \
|
"settime 16:30:05" \
|
||||||
"settime Nov 21, 2015 16:30:05" \
|
"settime Nov 21, 2015 16:30:05" \
|
||||||
@@ -231,6 +246,7 @@ RX timestamping : Kernel
|
|||||||
Total TX : 1
|
Total TX : 1
|
||||||
Total RX : 1
|
Total RX : 1
|
||||||
Total valid RX : 1
|
Total valid RX : 1
|
||||||
|
Total good RX : 0
|
||||||
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||||
=======================================================================
|
=======================================================================
|
||||||
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
|
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
|
||||||
@@ -244,7 +260,13 @@ NTS-KE connections dropped : 0
|
|||||||
Authenticated NTP packets : 0
|
Authenticated NTP packets : 0
|
||||||
Interleaved NTP packets : 0
|
Interleaved NTP packets : 0
|
||||||
NTP timestamps held : 0
|
NTP timestamps held : 0
|
||||||
NTP timestamp span : 0$" || test_fail
|
NTP timestamp span : 0
|
||||||
|
NTP daemon RX timestamps : 0
|
||||||
|
NTP daemon TX timestamps : 1
|
||||||
|
NTP kernel RX timestamps : 1
|
||||||
|
NTP kernel TX timestamps : 0
|
||||||
|
NTP hardware RX timestamps : 0
|
||||||
|
NTP hardware TX timestamps : 0$" || test_fail
|
||||||
|
|
||||||
chronyc_conf="
|
chronyc_conf="
|
||||||
deny all
|
deny all
|
||||||
@@ -326,6 +348,8 @@ maxupdateskew 192.168.123.1 10.0
|
|||||||
minpoll 192.168.123.1 3
|
minpoll 192.168.123.1 3
|
||||||
minstratum 192.168.123.1 1
|
minstratum 192.168.123.1 1
|
||||||
polltarget 192.168.123.1 10
|
polltarget 192.168.123.1 10
|
||||||
|
selectopts 192.168.123.1 +trust +prefer -require
|
||||||
|
selectdata
|
||||||
delete 192.168.123.1"
|
delete 192.168.123.1"
|
||||||
|
|
||||||
run_test || test_fail
|
run_test || test_fail
|
||||||
@@ -344,6 +368,10 @@ check_chronyc_output "^200 OK
|
|||||||
200 OK
|
200 OK
|
||||||
200 OK
|
200 OK
|
||||||
200 OK
|
200 OK
|
||||||
|
200 OK
|
||||||
|
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||||
|
=======================================================================
|
||||||
|
M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
|
||||||
200 OK$" || test_fail
|
200 OK$" || test_fail
|
||||||
|
|
||||||
chronyc_conf="
|
chronyc_conf="
|
||||||
|
|||||||
@@ -25,4 +25,18 @@ for client_server_options in "maxpoll 6 maxdelay 2e-5"; do
|
|||||||
check_sync && test_fail
|
check_sync && test_fail
|
||||||
done
|
done
|
||||||
|
|
||||||
|
min_sync_time=10
|
||||||
|
client_conf="
|
||||||
|
logdir tmp
|
||||||
|
log rawmeasurements"
|
||||||
|
client_server_options="minpoll 2 maxpoll 2 maxdelayquant 0.1"
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_file_messages "20.*123\.1.* 111 111 1111" 200 500 measurements.log || test_fail
|
||||||
|
check_file_messages "20.*123\.1.* 111 111 1101" 2000 2300 measurements.log || test_fail
|
||||||
|
|
||||||
test_pass
|
test_pass
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ check_chronyd_exit || test_fail
|
|||||||
check_source_selection || test_fail
|
check_source_selection || test_fail
|
||||||
check_sync || test_fail
|
check_sync || test_fail
|
||||||
|
|
||||||
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
|
check_file_messages "111 111 .111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
|
||||||
rm -f tmp/measurements.log
|
rm -f tmp/measurements.log
|
||||||
|
|
||||||
server_conf=""
|
server_conf=""
|
||||||
|
max_sync_time=270
|
||||||
|
|
||||||
run_test || test_fail
|
run_test || test_fail
|
||||||
check_chronyd_exit || test_fail
|
check_chronyd_exit || test_fail
|
||||||
@@ -28,6 +29,7 @@ check_sync || test_fail
|
|||||||
|
|
||||||
check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
|
check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
|
||||||
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
|
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
|
||||||
|
check_file_messages "111 111 0111.* 4I [DKH] [DKH]\$" 1 1 measurements.log || test_fail
|
||||||
rm -f tmp/measurements.log
|
rm -f tmp/measurements.log
|
||||||
|
|
||||||
clients=2
|
clients=2
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
test_start "filter option"
|
test_start "filter option"
|
||||||
|
|
||||||
client_server_options="minpoll 4 maxpoll 4 filter 15"
|
client_server_options="minpoll 4 maxpoll 4 filter 15 maxdelay 3.5e-4"
|
||||||
min_sync_time=710
|
min_sync_time=710
|
||||||
max_sync_time=720
|
max_sync_time=720
|
||||||
client_max_min_out_interval=16.1
|
client_max_min_out_interval=16.1
|
||||||
@@ -16,4 +16,28 @@ check_source_selection || test_fail
|
|||||||
check_packet_interval || test_fail
|
check_packet_interval || test_fail
|
||||||
check_sync || test_fail
|
check_sync || test_fail
|
||||||
|
|
||||||
|
base_delay="(+ 1e-4 (* -1 (equal 0.3 (uniform) 0.0)))"
|
||||||
|
client_server_options="minpoll 4 maxpoll 4 filter 3"
|
||||||
|
min_sync_time=130
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
limit=10
|
||||||
|
client_server_options="minpoll -6 maxpoll -6 filter 1"
|
||||||
|
|
||||||
|
base_delay=1e-4
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_file_messages " 2 1 " 590 640 log.packets || test_fail
|
||||||
|
|
||||||
|
base_delay=2e-2
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_file_messages " 2 1 " 9 10 log.packets || test_fail
|
||||||
|
|
||||||
test_pass
|
test_pass
|
||||||
|
|||||||
@@ -7,33 +7,83 @@ test_start "hwtimestamp directive"
|
|||||||
check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
|
check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
|
||||||
|
|
||||||
export CLKNETSIM_TIMESTAMPING=2
|
export CLKNETSIM_TIMESTAMPING=2
|
||||||
|
export CLKNETSIM_PHC_DELAY=1e-6
|
||||||
|
export CLKNETSIM_PHC_JITTER=1e-7
|
||||||
|
export CLKNETSIM_PHC_JITTER_ASYM=0.4
|
||||||
|
|
||||||
refclock_jitter=1e-8
|
refclock_jitter=1e-8
|
||||||
refclock_offset=10.0
|
refclock_offset=10.0
|
||||||
min_sync_time=4
|
min_sync_time=4
|
||||||
max_sync_time=20
|
max_sync_time=20
|
||||||
|
time_rms_limit=1e-7
|
||||||
|
freq_rms_limit=3e-8
|
||||||
|
jitter=1e-8
|
||||||
|
freq_offset=1e-5
|
||||||
limit=200
|
limit=200
|
||||||
server_conf="hwtimestamp eth0"
|
server_conf="
|
||||||
client_server_options="minpoll 0 maxpoll 0 minsamples 32 xleave"
|
clockprecision 1e-9
|
||||||
|
hwtimestamp eth0"
|
||||||
|
client_server_options="minpoll 0 maxpoll 0 xleave"
|
||||||
client_chronyd_options="-d"
|
client_chronyd_options="-d"
|
||||||
|
|
||||||
for client_conf in "hwtimestamp eth0" "hwtimestamp eth0
|
for client_conf in \
|
||||||
acquisitionport 123"; do
|
"hwtimestamp eth0 nocrossts
|
||||||
|
clockprecision 1e-9" \
|
||||||
|
"hwtimestamp eth0
|
||||||
|
clockprecision 1e-9
|
||||||
|
acquisitionport 123"; do
|
||||||
run_test || test_fail
|
run_test || test_fail
|
||||||
check_chronyd_exit || test_fail
|
check_chronyd_exit || test_fail
|
||||||
check_source_selection || test_fail
|
check_source_selection || test_fail
|
||||||
check_sync || test_fail
|
check_sync || test_fail
|
||||||
|
|
||||||
if check_config_h 'FEAT_DEBUG 1'; then
|
if check_config_h 'FEAT_DEBUG 1'; then
|
||||||
|
check_log_messages "Accepted reading" 0 2 || test_fail
|
||||||
|
check_log_messages "Combined .* readings" 190 220 || test_fail
|
||||||
check_log_messages "HW clock samples" 190 200 || test_fail
|
check_log_messages "HW clock samples" 190 200 || test_fail
|
||||||
check_log_messages "HW clock reset" 0 0 || test_fail
|
check_log_messages "HW clock reset" 0 0 || test_fail
|
||||||
|
check_log_messages "Missing TX timestamp" 1 1 || test_fail
|
||||||
check_log_messages "Received message.*tss=KH" 195 200 || test_fail
|
check_log_messages "Received message.*tss=KH" 195 200 || test_fail
|
||||||
check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
|
check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
|
||||||
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
|
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
|
||||||
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
|
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
|
||||||
|
check_log_messages "Polling PHC" 195 220 || test_fail
|
||||||
|
if echo "$client_conf" | grep -q nocrossts; then
|
||||||
|
check_log_messages "update_tx_timestamp.*Updated" 180 200 || test_fail
|
||||||
|
check_log_messages "update_tx_timestamp.*Unacceptable" 0 13 || test_fail
|
||||||
|
else
|
||||||
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
|
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
|
||||||
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
|
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
server_conf+="
|
||||||
|
server 192.168.123.2 minpoll 1 maxpoll 1 noselect"
|
||||||
|
|
||||||
|
for maxpoll in -1 0 1; do
|
||||||
|
client_conf="hwtimestamp eth0 minpoll -1 maxpoll $maxpoll nocrossts"
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
if check_config_h 'FEAT_DEBUG 1'; then
|
||||||
|
case $maxpoll in
|
||||||
|
-1)
|
||||||
|
check_log_messages "Polling PHC on eth0$" 360 380 || test_fail
|
||||||
|
check_log_messages "Polling PHC.*before" 3 25 || test_fail
|
||||||
|
;;
|
||||||
|
0)
|
||||||
|
check_log_messages "Polling PHC on eth0$" 8 45 || test_fail
|
||||||
|
check_log_messages "Polling PHC.*before" 150 190 || test_fail
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
check_log_messages "Polling PHC on eth0$" 1 1 || test_fail
|
||||||
|
check_log_messages "Polling PHC.*before" 194 199 || test_fail
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
test_pass
|
test_pass
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ client_server_options="maxpoll 6"
|
|||||||
client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
|
client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
|
||||||
logbanner 10
|
logbanner 10
|
||||||
logdir tmp
|
logdir tmp
|
||||||
log tracking rawmeasurements measurements statistics rtc refclocks tempcomp
|
log tracking rawmeasurements measurements selection statistics rtc refclocks tempcomp
|
||||||
tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
|
tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
|
||||||
|
|
||||||
echo 0.0 > tmp/tempcomp
|
echo 0.0 > tmp/tempcomp
|
||||||
@@ -26,6 +26,8 @@ check_file_messages "=============" 31 33 \
|
|||||||
tracking.log measurements.log tempcomp.log || test_fail
|
tracking.log measurements.log tempcomp.log || test_fail
|
||||||
check_file_messages "20.*192\.168\.123\.1" 150 160 \
|
check_file_messages "20.*192\.168\.123\.1" 150 160 \
|
||||||
tracking.log measurements.log statistics.log || test_fail
|
tracking.log measurements.log statistics.log || test_fail
|
||||||
|
check_file_messages "20.*PHC0 * N " 300 320 selection.log || test_fail
|
||||||
|
check_file_messages "20.*192\.168\.123\.1 *[M*]" 300 320 selection.log || test_fail
|
||||||
check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
|
check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
|
||||||
check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
|
check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
|
||||||
check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail
|
check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ for dns in 1 0; do
|
|||||||
|
|
||||||
check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail
|
check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail
|
||||||
check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail
|
check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail
|
||||||
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail
|
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 4 10 || test_fail
|
||||||
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail
|
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 4 10 || test_fail
|
||||||
|
|
||||||
servers=2
|
servers=2
|
||||||
|
|
||||||
|
|||||||
75
test/simulation/145-rtc
Executable file
75
test/simulation/145-rtc
Executable file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
. ./test.common
|
||||||
|
test_start "RTC tracking"
|
||||||
|
|
||||||
|
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||||
|
check_config_h 'FEAT_RTC 1' || test_skip
|
||||||
|
|
||||||
|
export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s')
|
||||||
|
export CLKNETSIM_RTC_OFFSET=-10.0
|
||||||
|
|
||||||
|
time_offset=$(awk "BEGIN {print -($freq_offset * $limit)}")
|
||||||
|
wander=0.0
|
||||||
|
chronyc_start=9900
|
||||||
|
chronyc_conf="rtcdata"
|
||||||
|
client_chronyd_options="-x"
|
||||||
|
|
||||||
|
client_conf="
|
||||||
|
hwclockfile /dev/null
|
||||||
|
driftfile tmp/drift
|
||||||
|
rtcfile tmp/rtc
|
||||||
|
rtconutc"
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_chronyc_output "^RTC ref time \(UTC\) : Fri Jan 01 02:4[34]:.. 2010
|
||||||
|
Number of samples : [0-9]+
|
||||||
|
Number of runs : [0-9]+
|
||||||
|
Sample span period : [ 0-9]+
|
||||||
|
RTC is fast by : -9\.01.... seconds
|
||||||
|
RTC gains time at : 99\.9[98]. ppm$" \
|
||||||
|
|| test_fail
|
||||||
|
|
||||||
|
export CLKNETSIM_START_DATE=$(date -d 'Jan 5 00:00:00 UTC 2010' +'%s')
|
||||||
|
export CLKNETSIM_RTC_OFFSET=$(awk "BEGIN {print -(10.0 - 4 * 86400 * $freq_offset)}")
|
||||||
|
touch -d 'Jan 1 00:00:00 UTC 2010' tmp/drift
|
||||||
|
|
||||||
|
time_offset=10
|
||||||
|
min_sync_time=2
|
||||||
|
max_sync_time=12
|
||||||
|
time_max_limit=1e-2
|
||||||
|
freq_max_limit=1e-1
|
||||||
|
time_rms_limit=1e-3
|
||||||
|
freq_rms_limit=1e-3
|
||||||
|
client_chronyd_options="-s"
|
||||||
|
client_conf+="
|
||||||
|
rtcautotrim 1"
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
check_chronyc_output "^RTC ref time \(UTC\) : Tue Jan 05 02:4[34]:.. 2010
|
||||||
|
Number of samples : [0-9]+
|
||||||
|
Number of runs : [0-9]+
|
||||||
|
Sample span period : [ 0-9]+
|
||||||
|
RTC is fast by : 0\.1..... seconds
|
||||||
|
RTC gains time at : [- ]0\.0.. ppm$" \
|
||||||
|
|| test_fail
|
||||||
|
|
||||||
|
export CLKNETSIM_START_DATE=$(date -d 'Jan 10 00:00:00 UTC 2010' +'%s')
|
||||||
|
export CLKNETSIM_RTC_OFFSET=-10.0
|
||||||
|
touch -d 'Jan 10 00:00:00 UTC 2010' tmp/drift
|
||||||
|
|
||||||
|
time_offset=-10000
|
||||||
|
min_sync_time=1
|
||||||
|
max_sync_time=1
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
test_pass
|
||||||
73
test/simulation/146-offline
Executable file
73
test/simulation/146-offline
Executable file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
. ./test.common
|
||||||
|
|
||||||
|
test_start "online/offline switching"
|
||||||
|
|
||||||
|
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||||
|
|
||||||
|
servers=2
|
||||||
|
limit=$[10 * 1800]
|
||||||
|
client_server_conf="
|
||||||
|
server 192.168.123.1 offline iburst
|
||||||
|
server 192.168.123.2 polltarget 64"
|
||||||
|
chronyc_conf="timeout 4000000
|
||||||
|
activity
|
||||||
|
offline
|
||||||
|
activity
|
||||||
|
onoffline 192.168.123.1
|
||||||
|
online 192.168.123.2
|
||||||
|
activity
|
||||||
|
offline
|
||||||
|
activity
|
||||||
|
"
|
||||||
|
chronyc_start=1
|
||||||
|
base_delay="(+ 1e-4 (* 1800 (equal 0.1 from 4)))"
|
||||||
|
jitter=1e-6
|
||||||
|
|
||||||
|
time_max_limit=2e-2
|
||||||
|
freq_max_limit=1e-3
|
||||||
|
time_rms_limit=2e-2
|
||||||
|
freq_rms_limit=1e-5
|
||||||
|
min_sync_time=120
|
||||||
|
max_sync_time=140
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_file_messages " 3 1 .* 123 " 30 90 log.packets || test_fail
|
||||||
|
check_file_messages " 3 2 .* 123 " 130 150 log.packets || test_fail
|
||||||
|
|
||||||
|
check_chronyc_output "^200 OK
|
||||||
|
1 sources online
|
||||||
|
1 sources offline
|
||||||
|
0 sources doing burst \(return to online\)
|
||||||
|
0 sources doing burst \(return to offline\)
|
||||||
|
0 sources with unknown address
|
||||||
|
200 OK
|
||||||
|
200 OK
|
||||||
|
0 sources online
|
||||||
|
2 sources offline
|
||||||
|
0 sources doing burst \(return to online\)
|
||||||
|
0 sources doing burst \(return to offline\)
|
||||||
|
0 sources with unknown address
|
||||||
|
200 OK
|
||||||
|
200 OK
|
||||||
|
200 OK
|
||||||
|
2 sources online
|
||||||
|
0 sources offline
|
||||||
|
0 sources doing burst \(return to online\)
|
||||||
|
0 sources doing burst \(return to offline\)
|
||||||
|
0 sources with unknown address
|
||||||
|
200 OK
|
||||||
|
200 OK
|
||||||
|
0 sources online
|
||||||
|
2 sources offline
|
||||||
|
0 sources doing burst \(return to online\)
|
||||||
|
0 sources doing burst \(return to offline\)
|
||||||
|
0 sources with unknown address" \
|
||||||
|
|| test_fail
|
||||||
|
|
||||||
|
test_pass
|
||||||
59
test/simulation/147-refresh
Executable file
59
test/simulation/147-refresh
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
. ./test.common
|
||||||
|
|
||||||
|
test_start "address refreshment"
|
||||||
|
|
||||||
|
limit=1000
|
||||||
|
servers=5
|
||||||
|
client_conf="logdir tmp
|
||||||
|
log measurements"
|
||||||
|
client_server_conf="server nodes-1-2.net1.clk maxpoll 6
|
||||||
|
pool nodes-3-4-5.net1.clk maxpoll 6 maxsources 2"
|
||||||
|
client_chronyd_options="-d"
|
||||||
|
chronyc_conf="refresh"
|
||||||
|
chronyc_start=500
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_file_messages "20.*192.168.123.1" 0 0 measurements.log || test_fail
|
||||||
|
check_file_messages "20.*192.168.123.2" 15 17 measurements.log || test_fail
|
||||||
|
check_file_messages "20.*192.168.123.[345]" 31 33 measurements.log || test_fail
|
||||||
|
rm -f tmp/measurements.log
|
||||||
|
if check_config_h 'FEAT_DEBUG 1'; then
|
||||||
|
check_log_messages "refreshing 192.168.123" 3 3 || test_fail
|
||||||
|
check_log_messages "resolved_name.*still fresh" 3 3 || test_fail
|
||||||
|
fi
|
||||||
|
|
||||||
|
limit=1100
|
||||||
|
client_server_conf="
|
||||||
|
server nodes-1-2.net1.clk maxpoll 6
|
||||||
|
pool nodes-3-4-5.net1.clk maxpoll 6 maxsources 3"
|
||||||
|
client_conf+="
|
||||||
|
refresh 128"
|
||||||
|
chronyc_conf=""
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_file_messages "20.*192.168.123.1" 0 0 measurements.log || test_fail
|
||||||
|
check_file_messages "20.*192.168.123.2" 16 18 measurements.log || test_fail
|
||||||
|
check_file_messages "20.*192.168.123.[345]" 50 55 measurements.log || test_fail
|
||||||
|
rm -f tmp/measurements.log
|
||||||
|
if check_config_h 'FEAT_DEBUG 1'; then
|
||||||
|
check_log_messages "refreshing 192.168.123" 8 8 || test_fail
|
||||||
|
check_log_messages "resolved_name.*still fresh" 8 8 || test_fail
|
||||||
|
check_log_messages "refreshing 192.168.123.2" 2 2 || test_fail
|
||||||
|
check_log_messages "refreshing 192.168.123.3" 2 2 || test_fail
|
||||||
|
check_log_messages "refreshing 192.168.123.4" 2 2 || test_fail
|
||||||
|
check_log_messages "refreshing 192.168.123.5" 2 2 || test_fail
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_pass
|
||||||
56
test/simulation/148-replacement
Executable file
56
test/simulation/148-replacement
Executable file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
. ./test.common
|
||||||
|
|
||||||
|
test_start "source replacement"
|
||||||
|
|
||||||
|
limit=5000
|
||||||
|
client_conf="logdir tmp
|
||||||
|
log measurements"
|
||||||
|
|
||||||
|
servers=6
|
||||||
|
falsetickers=2
|
||||||
|
client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk maxsources 5 polltarget 1 iburst"
|
||||||
|
wander=1e-12
|
||||||
|
jitter=1e-6
|
||||||
|
min_sync_time=7
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection || test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_log_messages "Detected falseticker" 2 10 || test_fail
|
||||||
|
check_log_messages "Source 192.168.123.. replaced with" 1 3 || test_fail
|
||||||
|
check_file_messages "20.*192.168.123.* 11.1 6 6 " 15 17 measurements.log || test_fail
|
||||||
|
check_file_messages "20.*00:[1-5].:.. 192.168.123.* 11.1 6 6 " 1 4 measurements.log || test_fail
|
||||||
|
rm -f tmp/measurements.log
|
||||||
|
|
||||||
|
# 1 unreplaceable falseticker against 2 replaceable unreachable servers
|
||||||
|
servers=5
|
||||||
|
falsetickers=1
|
||||||
|
limit=200000
|
||||||
|
base_delay="(+ 1e-4 (* -1 (equal 0.6 to 4.5)))"
|
||||||
|
client_conf+="
|
||||||
|
minsources 2"
|
||||||
|
client_server_conf="
|
||||||
|
server 192.168.123.1
|
||||||
|
server nodes-2-4.net1.clk
|
||||||
|
server nodes-3-5.net1.clk"
|
||||||
|
max_sync_time=150000
|
||||||
|
|
||||||
|
run_test || test_fail
|
||||||
|
check_chronyd_exit || test_fail
|
||||||
|
check_source_selection && test_fail
|
||||||
|
check_packet_interval || test_fail
|
||||||
|
check_sync || test_fail
|
||||||
|
|
||||||
|
check_log_messages "Detected falseticker" 2 10 || test_fail
|
||||||
|
check_log_messages "Source 192.168.123.. replaced with" 2 70 || test_fail
|
||||||
|
check_log_messages "2010-01-01T0[0-4]:.*Source 192.168.123.. replaced with" 2 15 || test_fail
|
||||||
|
check_log_messages "2010-01-01T0[5-9]:.*Source 192.168.123.. replaced with" 0 15 || test_fail
|
||||||
|
check_file_messages "20.*192.168.123.* 11.1 6 6 " 20 500 measurements.log || test_fail
|
||||||
|
rm -f tmp/measurements.log
|
||||||
|
|
||||||
|
test_pass
|
||||||
@@ -78,6 +78,7 @@ default_client_min_mean_out_interval=0.0
|
|||||||
default_client_max_min_out_interval=inf
|
default_client_max_min_out_interval=inf
|
||||||
|
|
||||||
default_cmdmon_unix=1
|
default_cmdmon_unix=1
|
||||||
|
default_pcap_dumps=0
|
||||||
default_dns=0
|
default_dns=0
|
||||||
|
|
||||||
# Initialize test settings from their defaults
|
# Initialize test settings from their defaults
|
||||||
@@ -469,6 +470,9 @@ run_test() {
|
|||||||
|
|
||||||
for i in $(seq 1 $n); do
|
for i in $(seq 1 $n); do
|
||||||
test_message 2 0 "starting node $node:"
|
test_message 2 0 "starting node $node:"
|
||||||
|
|
||||||
|
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
|
||||||
|
|
||||||
if [ $stratum -eq 1 ]; then
|
if [ $stratum -eq 1 ]; then
|
||||||
step=$server_step
|
step=$server_step
|
||||||
start=$server_start
|
start=$server_start
|
||||||
@@ -509,6 +513,8 @@ run_test() {
|
|||||||
for i in $(seq 1 $[$nodes - $node + 1]); do
|
for i in $(seq 1 $[$nodes - $node + 1]); do
|
||||||
test_message 2 0 "starting node $node:"
|
test_message 2 0 "starting node $node:"
|
||||||
|
|
||||||
|
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
|
||||||
|
|
||||||
options=$([ $dns -eq 0 ] && printf "%s" "-n")
|
options=$([ $dns -eq 0 ] && printf "%s" "-n")
|
||||||
if [ $cmdmon_unix -ne 0 ]; then
|
if [ $cmdmon_unix -ne 0 ]; then
|
||||||
options+=" -h /clknetsim/unix/$[$node - $clients]:1"
|
options+=" -h /clknetsim/unix/$[$node - $clients]:1"
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ for command in \
|
|||||||
"reselect" \
|
"reselect" \
|
||||||
"reselectdist 1e-3" \
|
"reselectdist 1e-3" \
|
||||||
"reset sources" \
|
"reset sources" \
|
||||||
|
"selectopts $server -noselect +trust +prefer +require" \
|
||||||
"smoothtime reset" \
|
"smoothtime reset" \
|
||||||
"smoothtime activate" \
|
"smoothtime activate" \
|
||||||
; do
|
; do
|
||||||
@@ -95,12 +96,13 @@ TX timestamping : (Daemon|Kernel)
|
|||||||
RX timestamping : (Daemon|Kernel)
|
RX timestamping : (Daemon|Kernel)
|
||||||
Total TX : [0-9]+
|
Total TX : [0-9]+
|
||||||
Total RX : [0-9]+
|
Total RX : [0-9]+
|
||||||
Total valid RX : [0-9]+$" || test_fail
|
Total valid RX : [0-9]+
|
||||||
|
Total good RX : [0-9]+$" || test_fail
|
||||||
|
|
||||||
run_chronyc "selectdata" || test_fail
|
run_chronyc "selectdata" || test_fail
|
||||||
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||||
=======================================================================
|
=======================================================================
|
||||||
M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
|
s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
|
||||||
|
|
||||||
run_chronyc "serverstats" || test_fail
|
run_chronyc "serverstats" || test_fail
|
||||||
check_chronyc_output "^NTP packets received : [0-9]+
|
check_chronyc_output "^NTP packets received : [0-9]+
|
||||||
@@ -113,7 +115,13 @@ NTS-KE connections dropped : 0
|
|||||||
Authenticated NTP packets : 0
|
Authenticated NTP packets : 0
|
||||||
Interleaved NTP packets : 0
|
Interleaved NTP packets : 0
|
||||||
NTP timestamps held : 0
|
NTP timestamps held : 0
|
||||||
NTP timestamp span : 0$"|| test_fail
|
NTP timestamp span : 0
|
||||||
|
NTP daemon RX timestamps : 0
|
||||||
|
NTP daemon TX timestamps : [0-9]+
|
||||||
|
NTP kernel RX timestamps : [0-9]+
|
||||||
|
NTP kernel TX timestamps : 0
|
||||||
|
NTP hardware RX timestamps : 0
|
||||||
|
NTP hardware TX timestamps : 0$"|| test_fail
|
||||||
|
|
||||||
run_chronyc "manual on" || test_fail
|
run_chronyc "manual on" || test_fail
|
||||||
check_chronyc_output "^200 OK$" || test_fail
|
check_chronyc_output "^200 OK$" || test_fail
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ wait_for_sync || test_fail
|
|||||||
run_chronyc "authdata" || test_fail
|
run_chronyc "authdata" || test_fail
|
||||||
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
||||||
=========================================================================
|
=========================================================================
|
||||||
127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail
|
127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
|
||||||
|
|
||||||
stop_chronyd || test_fail
|
stop_chronyd || test_fail
|
||||||
check_chronyd_messages || test_fail
|
check_chronyd_messages || test_fail
|
||||||
@@ -57,7 +57,7 @@ wait_for_sync || test_fail
|
|||||||
run_chronyc "authdata" || test_fail
|
run_chronyc "authdata" || test_fail
|
||||||
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
||||||
=========================================================================
|
=========================================================================
|
||||||
127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail
|
127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
|
||||||
|
|
||||||
stop_chronyd || test_fail
|
stop_chronyd || test_fail
|
||||||
check_chronyd_messages || test_fail
|
check_chronyd_messages || test_fail
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
|
|||||||
|
|
||||||
test_start "system call filter in non-destructive tests"
|
test_start "system call filter in non-destructive tests"
|
||||||
|
|
||||||
for level in "-1" "1" "-2" "2"; do
|
for level in 1 2 -1 -2; do
|
||||||
test_message 1 1 "level $level:"
|
test_message 1 1 "level $level:"
|
||||||
for test in 0[0-8][0-9]-*[^_]; do
|
for test in 0[0-8][0-9]-*[^_]; do
|
||||||
test_message 2 0 "$test"
|
test_message 2 0 "$test"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user