mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 17:35:06 -05:00
Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fe4a60a1d | ||
|
|
2da8fbc4c3 | ||
|
|
c252e52ee2 | ||
|
|
c8a9ca4cf0 | ||
|
|
4659b574bf | ||
|
|
5436618c05 | ||
|
|
a0f460569e | ||
|
|
3c39afa13c | ||
|
|
03875f1ea5 | ||
|
|
2e29935c54 | ||
|
|
8084961011 | ||
|
|
120bf44989 | ||
|
|
9e8541e3c4 | ||
|
|
e95d5a161d | ||
|
|
2c63dfee34 | ||
|
|
42e6b5577a | ||
|
|
830c8bb18a | ||
|
|
0289442998 | ||
|
|
e9848c0176 | ||
|
|
2cfe969940 | ||
|
|
487cf3840f | ||
|
|
4886c776d5 | ||
|
|
d3f3638b3d | ||
|
|
6c5973741b | ||
|
|
51d161a028 | ||
|
|
5535384878 | ||
|
|
f78e4681ef | ||
|
|
b365edb48e | ||
|
|
93a78c73ad | ||
|
|
abc267a556 | ||
|
|
9b183fe98f | ||
|
|
be7f5e8916 | ||
|
|
5e2cd47ad1 | ||
|
|
9eaf8bc521 | ||
|
|
54010586aa | ||
|
|
90d808ed28 | ||
|
|
1d9e080749 | ||
|
|
d30913e78c | ||
|
|
c5d3be8cc4 | ||
|
|
3e32e7e694 | ||
|
|
52cce3dea8 | ||
|
|
1bcbea9bd2 | ||
|
|
2ac581e04a | ||
|
|
4a8da7e02d | ||
|
|
e463fcab49 | ||
|
|
df98fb4fc7 | ||
|
|
551bc266e4 | ||
|
|
c4234dd1f7 | ||
|
|
fd50f3c80c | ||
|
|
9b9823b377 | ||
|
|
c900dff314 | ||
|
|
2c6cbde058 | ||
|
|
cacd64bf4a | ||
|
|
6c2ee89970 | ||
|
|
68b2ffa97c | ||
|
|
f591133f4c | ||
|
|
48cc072c06 | ||
|
|
8211978570 | ||
|
|
4b2b6bbf97 | ||
|
|
551ad40a04 | ||
|
|
09cd057c23 | ||
|
|
363aa86861 | ||
|
|
bda2ff77e8 | ||
|
|
545bd59563 | ||
|
|
082af24114 | ||
|
|
577221295f | ||
|
|
eb08c3b013 | ||
|
|
391882dc41 | ||
|
|
b32d85bc54 | ||
|
|
0bf7e49148 | ||
|
|
f5fdfee150 | ||
|
|
c17f481a20 | ||
|
|
8140feb4fd | ||
|
|
6e541afbe9 | ||
|
|
81362fa201 | ||
|
|
ec57de02c7 | ||
|
|
f7da309b83 | ||
|
|
9b28b2f493 | ||
|
|
ebf19ca760 | ||
|
|
2d96077b9f | ||
|
|
2c2dd2d126 | ||
|
|
f7daec0693 | ||
|
|
d115449e85 | ||
|
|
dab98fa8da | ||
|
|
dd8738119b | ||
|
|
75bbccf518 | ||
|
|
1b24a66b3c | ||
|
|
8fd386f287 | ||
|
|
e694ae769a | ||
|
|
2b127b2e93 | ||
|
|
2d3c89e1a3 | ||
|
|
b5d1ae147d | ||
|
|
bf964673cb | ||
|
|
c66c774e26 | ||
|
|
274c1767f2 | ||
|
|
c27a298d9c | ||
|
|
072ec20e2c | ||
|
|
711c7c0c8a | ||
|
|
454ce62672 | ||
|
|
ab850d41f4 | ||
|
|
744768306b | ||
|
|
26d4f0c401 | ||
|
|
717db2cfdd | ||
|
|
55898e9b07 | ||
|
|
f7bb283536 | ||
|
|
1967fbf1f2 | ||
|
|
51da7a0694 | ||
|
|
9ba6e7655c | ||
|
|
3dea7dd723 | ||
|
|
cb1118456b | ||
|
|
8ee725ff18 | ||
|
|
a90a7cf51c | ||
|
|
4f22883f4e | ||
|
|
65be9d9a02 | ||
|
|
a1191f8392 | ||
|
|
924199ef69 | ||
|
|
8028984f34 | ||
|
|
68301238a0 | ||
|
|
13db3d6902 | ||
|
|
8c24086c6d | ||
|
|
e30d5a6e6f | ||
|
|
a0d34eb372 | ||
|
|
7196943f11 | ||
|
|
26fef1751a | ||
|
|
d71e22594e | ||
|
|
d7a578fed2 | ||
|
|
014a67b29e | ||
|
|
d22c8fbcb2 | ||
|
|
2da4e3ce53 | ||
|
|
c92358e041 | ||
|
|
77962bb527 | ||
|
|
65b5f111d2 | ||
|
|
9856bf2d5f | ||
|
|
2205eae2a1 | ||
|
|
6f381fa78b | ||
|
|
bb8050d884 | ||
|
|
c85ec4ff0f | ||
|
|
d9d97f76d7 | ||
|
|
837323d687 | ||
|
|
12237bf283 | ||
|
|
b9b338a8df | ||
|
|
a5d73f692f |
@@ -37,7 +37,9 @@ GETDATE_CFLAGS = @GETDATE_CFLAGS@
|
||||
|
||||
EXTRA_OBJS = @EXTRA_OBJS@
|
||||
|
||||
OBJS = array.o cmdparse.o conf.o leapdb.o local.o logging.o main.o memory.o quantiles.o \
|
||||
OBJS = addrfilt.o array.o clientlog.o cmdparse.o conf.o keys.o leapdb.o \
|
||||
local.o logging.o main.o memory.o nameserv.o nameserv_async.o \
|
||||
ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o quantiles.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)
|
||||
|
||||
|
||||
45
NEWS
45
NEWS
@@ -1,3 +1,48 @@
|
||||
New in version 4.8
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add maxunreach option to limit selection of unreachable sources
|
||||
* Add -u option to chronyc to drop root privileges (default chronyc
|
||||
user is set by configure script)
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Hide chronyc socket to mitigate unsafe permissions change
|
||||
* Fix refclock extpps option to work on Linux >= 6.15
|
||||
* Validate refclock samples for reachability updates
|
||||
|
||||
New in version 4.7
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add opencommands directive to select remote monitoring commands
|
||||
* Add interval option to driftfile directive
|
||||
* Add waitsynced and waitunsynced options to local directive
|
||||
* Add sanity checks for integer values in configuration
|
||||
* Add support for systemd Type=notify service
|
||||
* Add RTC refclock driver
|
||||
* Allow PHC refclock to be specified with network interface name
|
||||
* Don't require multiple refclock samples per poll to simplify
|
||||
filter configuration
|
||||
* Keep refclock reachable when dropping samples with large delay
|
||||
* Improve quantile-based filtering to adapt faster to larger delay
|
||||
* Improve logging of selection failures
|
||||
* Detect clock interference from other processes
|
||||
* Try to reopen message log (-l option) on cyclelogs command
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Fix sourcedir reloading to not multiply sources
|
||||
* Fix tracking offset after failed clock step
|
||||
|
||||
Removed features
|
||||
----------------
|
||||
* Drop support for NTS with Nettle < 3.6 and GnuTLS < 3.6.14
|
||||
* Drop support for building without POSIX threads
|
||||
|
||||
New in version 4.6.1
|
||||
====================
|
||||
|
||||
|
||||
5
README
5
README
@@ -78,17 +78,21 @@ Vincent Blut <vincent.debian@free.fr>
|
||||
Luca Boccassi <bluca@debian.org>
|
||||
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
|
||||
David Bohman <debohman@gmail.com>
|
||||
Anthony Brandon <anthony@amarulasolutions.com>
|
||||
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
|
||||
Leigh Brown <leigh@solinno.co.uk>
|
||||
Erik Bryer <ebryer@spots.ab.ca>
|
||||
Jonathan Cameron <jic23@cam.ac.uk>
|
||||
Bryan Christianson <bryan@whatroute.net>
|
||||
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
||||
Paul Donald <newtwen+gitlab@gmail.com>
|
||||
Dan Drown <dan-ntp@drown.org>
|
||||
Kamil Dudka <kdudka@redhat.com>
|
||||
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
||||
Paul Elliott <pelliott@io.com>
|
||||
Robert Fairley <rfairley@redhat.com>
|
||||
Ahmad Fatoum <a.fatoum@pengutronix.de>
|
||||
Andreas Fenkart <extern-afe@mission-embedded.com>
|
||||
Stefan R. Filipek <srfilipek@gmail.com>
|
||||
Andy Fiddaman <illumos@fiddaman.net>
|
||||
Mike Fleetwood <mike@rockover.demon.co.uk>
|
||||
@@ -121,6 +125,7 @@ Rupesh Patel <rupatel@redhat.com>
|
||||
Chris Perl <cperl@janestreet.com>
|
||||
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
||||
Andreas Piesk <apiesk@virbus.de>
|
||||
Shachar Raindel <shacharr@google.com>
|
||||
Mike Ryan <msr@hsilop.net>
|
||||
Baruch Siach <baruch@tkos.co.il>
|
||||
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
|
||||
|
||||
19
candm.h
19
candm.h
@@ -37,7 +37,6 @@
|
||||
#define DEFAULT_CANDM_PORT 323
|
||||
|
||||
/* Request codes */
|
||||
#define REQ_NULL 0
|
||||
#define REQ_ONLINE 1
|
||||
#define REQ_OFFLINE 2
|
||||
#define REQ_BURST 3
|
||||
@@ -223,11 +222,6 @@ typedef struct {
|
||||
int32_t EOR;
|
||||
} REQ_Modify_Makestep;
|
||||
|
||||
typedef struct {
|
||||
Timespec ts;
|
||||
int32_t EOR;
|
||||
} REQ_Logon;
|
||||
|
||||
typedef struct {
|
||||
Timespec ts;
|
||||
int32_t EOR;
|
||||
@@ -239,7 +233,8 @@ typedef struct {
|
||||
Float distance;
|
||||
int32_t orphan;
|
||||
Float activate;
|
||||
uint32_t reserved[2];
|
||||
Float wait_synced;
|
||||
Float wait_unsynced;
|
||||
int32_t EOR;
|
||||
} REQ_Local;
|
||||
|
||||
@@ -311,7 +306,7 @@ typedef struct {
|
||||
int32_t filter_length;
|
||||
uint32_t cert_set;
|
||||
Float max_delay_quant;
|
||||
uint32_t reserved[1];
|
||||
int32_t max_unreach;
|
||||
int32_t EOR;
|
||||
} REQ_NTP_Source;
|
||||
|
||||
@@ -487,7 +482,6 @@ typedef struct {
|
||||
REQ_Modify_Polltarget modify_polltarget;
|
||||
REQ_Modify_Maxupdateskew modify_maxupdateskew;
|
||||
REQ_Modify_Makestep modify_makestep;
|
||||
REQ_Logon logon;
|
||||
REQ_Settime settime;
|
||||
REQ_Local local;
|
||||
REQ_Manual manual;
|
||||
@@ -517,13 +511,6 @@ typedef struct {
|
||||
|
||||
} CMD_Request;
|
||||
|
||||
/* ================================================== */
|
||||
/* Authority codes for command types */
|
||||
|
||||
#define PERMIT_OPEN 0
|
||||
#define PERMIT_LOCAL 1
|
||||
#define PERMIT_AUTH 2
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Reply codes */
|
||||
|
||||
215
client.c
215
client.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Lonnie Abelbeck 2016, 2018
|
||||
* Copyright (C) Miroslav Lichvar 2009-2024
|
||||
* Copyright (C) Miroslav Lichvar 2009-2025
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -47,6 +47,8 @@
|
||||
#include <editline/readline.h>
|
||||
#endif
|
||||
|
||||
#define MAX_UNIX_SOCKET_LENGTH (sizeof ((struct sockaddr_un *)NULL)->sun_path)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
struct Address {
|
||||
@@ -61,6 +63,9 @@ static ARR_Instance server_addresses;
|
||||
|
||||
static int sock_fd = -1;
|
||||
|
||||
static char sock_dir1[MAX_UNIX_SOCKET_LENGTH];
|
||||
static char sock_dir2[MAX_UNIX_SOCKET_LENGTH];
|
||||
|
||||
static volatile int quit = 0;
|
||||
|
||||
static int on_terminal = 0;
|
||||
@@ -125,12 +130,11 @@ read_line(void)
|
||||
strncpy(line, cmd, sizeof(line) - 1);
|
||||
line[sizeof(line) - 1] = '\0';
|
||||
add_history(cmd);
|
||||
/* free the buffer allocated by readline */
|
||||
Free(cmd);
|
||||
} else {
|
||||
/* simulate the user has entered an empty line */
|
||||
*line = '\0';
|
||||
}
|
||||
Free(cmd);
|
||||
return( line );
|
||||
#else
|
||||
printf("%s", prompt);
|
||||
@@ -208,35 +212,139 @@ free_addresses(ARR_Instance addresses)
|
||||
ARR_DestroyInstance(addresses);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Bind a Unix domain socket and connect it to the server chronyd socket.
|
||||
|
||||
Access to the sockets is restricted by the permissions and ownership
|
||||
of the directory in which they are bound (by default /var/run/chrony).
|
||||
The location of the chronyc socket follows the location of the chronyd
|
||||
socket. Due to the possibility of chronyc running under a different
|
||||
user, the permissions of the chronyc socket need to be loosened on
|
||||
most systems (illumos is an exception) to allow chronyd to send a
|
||||
response to the chronyc socket.
|
||||
|
||||
Unfortunately, there does not seem to be a safe and portable way to
|
||||
do that directly (e.g. a compromised chronyd process could replace it
|
||||
with a symlink and cause a privileged chronyc process to change the
|
||||
permissions of something else).
|
||||
|
||||
The workaround is to bind the socket in a randomly named subdirectory
|
||||
hidden in another subdirectory to avoid matching an existing path and
|
||||
force the bind()/chmod() call to fail if the visible upper directory
|
||||
is replaced.
|
||||
|
||||
Note that the socket cannot be moved once bound, because the source
|
||||
address that chronyd will see would not match the actual path and the
|
||||
responses would not reach chronyc. */
|
||||
|
||||
static int
|
||||
open_unix_socket(char *server_path)
|
||||
{
|
||||
char *sock_dir0, sock_path[MAX_UNIX_SOCKET_LENGTH], rand_dir[16 + 1];
|
||||
int i, s, dir_fd1 = -1;
|
||||
struct stat st;
|
||||
|
||||
/* Check if the server socket is accessible */
|
||||
s = SCK_OpenUnixDatagramSocket(server_path, NULL, 0);
|
||||
if (s < 0)
|
||||
return -1;
|
||||
|
||||
SCK_CloseSocket(s);
|
||||
|
||||
/* Generate the random hidden component of the socket path */
|
||||
for (i = 0; i + 1 < sizeof (rand_dir); i++) {
|
||||
do
|
||||
UTI_GetRandomBytesUrandom(&rand_dir[i], sizeof (rand_dir[i]));
|
||||
while (!isalnum((unsigned char)rand_dir[i]));
|
||||
}
|
||||
rand_dir[i] = '\0';
|
||||
|
||||
/* Format the paths of the subdirectories and socket:
|
||||
sock_dir0 = dirname(server_path)
|
||||
sock_dir1 = sock_dir0/chronyc.$PID
|
||||
sock_dir2 = sock_dir0/chronyc.$PID/$RANDOM
|
||||
sock_path = sock_dir0/chronyc.$PID/$RANDOM/sock */
|
||||
|
||||
sock_dir0 = UTI_PathToDir(server_path);
|
||||
if (snprintf(sock_dir1, sizeof (sock_dir1),
|
||||
"%s/chronyc.%d", sock_dir0, (int)getpid()) >= sizeof (sock_dir1) ||
|
||||
snprintf(sock_dir2, sizeof (sock_dir2),
|
||||
"%s/%s", sock_dir1, rand_dir) >= sizeof (sock_dir2) ||
|
||||
snprintf(sock_path, sizeof (sock_path),
|
||||
"%s/sock", sock_dir2) >= sizeof (sock_path)) {
|
||||
LOG(LOGS_ERR, "Server socket path %s is too long", server_path);
|
||||
Free(sock_dir0);
|
||||
goto error1;
|
||||
}
|
||||
|
||||
Free(sock_dir0);
|
||||
|
||||
if (mkdir(sock_dir1, 0711) < 0 && errno != EEXIST) {
|
||||
LOG(LOGS_ERR, "Could not create %s : %s", sock_dir1, strerror(errno));
|
||||
goto error1;
|
||||
}
|
||||
|
||||
dir_fd1 = open(sock_dir1, O_RDONLY | O_NOFOLLOW);
|
||||
if (dir_fd1 < 0 || fstat(dir_fd1, &st) < 0) {
|
||||
LOG(LOGS_ERR, "Could not open/stat %s : %s", sock_dir1, strerror(errno));
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode) || (st.st_mode & 0777 & ~0711) != 0 || st.st_uid != geteuid()) {
|
||||
LOG(LOGS_ERR, "Unexpected mode/owner of %s", sock_dir1);
|
||||
goto error2;
|
||||
}
|
||||
|
||||
if (mkdirat(dir_fd1, rand_dir, 0711) < 0) {
|
||||
LOG(LOGS_ERR, "Could not create %s : %s", sock_dir2, strerror(errno));
|
||||
goto error2;
|
||||
}
|
||||
|
||||
s = SCK_OpenUnixDatagramSocket(server_path, sock_path, 0);
|
||||
if (s < 0) {
|
||||
LOG(LOGS_ERR, "Could not bind/connect client Unix socket");
|
||||
goto error3;
|
||||
}
|
||||
|
||||
if (chmod(sock_path, 0666) < 0 ||
|
||||
chmod(sock_dir2, 0711) < 0 ||
|
||||
fchmod(dir_fd1, 0711) < 0) {
|
||||
LOG(LOGS_ERR, "Could not change socket or directory permissions : %s", strerror(errno));
|
||||
SCK_RemoveSocket(s);
|
||||
SCK_CloseSocket(s);
|
||||
goto error3;
|
||||
}
|
||||
|
||||
close(dir_fd1);
|
||||
|
||||
return s;
|
||||
|
||||
error3:
|
||||
rmdir(sock_dir2);
|
||||
error2:
|
||||
rmdir(sock_dir1);
|
||||
error1:
|
||||
if (dir_fd1 >= 0)
|
||||
close(dir_fd1);
|
||||
sock_dir1[0] = '\0';
|
||||
sock_dir2[0] = '\0';
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Initialise the socket used to talk to the daemon */
|
||||
|
||||
static int
|
||||
open_socket(struct Address *addr)
|
||||
{
|
||||
char *dir, *local_addr;
|
||||
size_t local_addr_len;
|
||||
|
||||
switch (addr->type) {
|
||||
case SCK_ADDR_IP:
|
||||
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
|
||||
break;
|
||||
case SCK_ADDR_UNIX:
|
||||
/* Construct path of our socket. Use the same directory as the server
|
||||
socket and include our process ID to allow multiple chronyc instances
|
||||
running at the same time. */
|
||||
|
||||
dir = UTI_PathToDir(addr->addr.path);
|
||||
local_addr_len = strlen(dir) + 50;
|
||||
local_addr = Malloc(local_addr_len);
|
||||
|
||||
snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid());
|
||||
|
||||
sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr,
|
||||
SCK_FLAG_ALL_PERMISSIONS);
|
||||
Free(dir);
|
||||
Free(local_addr);
|
||||
|
||||
sock_fd = open_unix_socket(addr->addr.path);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
@@ -258,6 +366,14 @@ close_io(void)
|
||||
|
||||
SCK_RemoveSocket(sock_fd);
|
||||
SCK_CloseSocket(sock_fd);
|
||||
|
||||
if (sock_dir2[0] != '\0')
|
||||
rmdir(sock_dir2);
|
||||
if (sock_dir1[0] != '\0')
|
||||
rmdir(sock_dir1);
|
||||
sock_dir2[0] = '\0';
|
||||
sock_dir1[0] = '\0';
|
||||
|
||||
sock_fd = -1;
|
||||
}
|
||||
|
||||
@@ -754,12 +870,13 @@ process_cmd_burst(CMD_Request *msg, char *line)
|
||||
static int
|
||||
process_cmd_local(CMD_Request *msg, char *line)
|
||||
{
|
||||
double distance = 0.0, activate = 0.0, wait_synced = 0.0, wait_unsynced = 0.0;
|
||||
int on_off, stratum = 0, orphan = 0;
|
||||
double distance = 0.0, activate = 0.0;
|
||||
|
||||
if (!strcmp(line, "off")) {
|
||||
on_off = 0;
|
||||
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate)) {
|
||||
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate,
|
||||
&wait_synced, &wait_unsynced) == CPS_Success) {
|
||||
on_off = 1;
|
||||
} else {
|
||||
LOG(LOGS_ERR, "Invalid syntax for local command");
|
||||
@@ -772,7 +889,8 @@ process_cmd_local(CMD_Request *msg, char *line)
|
||||
msg->data.local.distance = UTI_FloatHostToNetwork(distance);
|
||||
msg->data.local.orphan = htonl(orphan);
|
||||
msg->data.local.activate = UTI_FloatHostToNetwork(activate);
|
||||
memset(msg->data.local.reserved, 0, sizeof (msg->data.local.reserved));
|
||||
msg->data.local.wait_synced = UTI_FloatHostToNetwork(wait_synced);
|
||||
msg->data.local.wait_unsynced = UTI_FloatHostToNetwork(wait_unsynced);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -906,8 +1024,9 @@ static int
|
||||
process_cmd_add_source(CMD_Request *msg, char *line)
|
||||
{
|
||||
CPS_NTP_Source data;
|
||||
CPS_Status status;
|
||||
IPAddr ip_addr;
|
||||
int result = 0, status, type;
|
||||
int result = 0, type;
|
||||
const char *opt_name, *word;
|
||||
|
||||
msg->command = htons(REQ_ADD_SOURCE);
|
||||
@@ -928,10 +1047,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
|
||||
|
||||
status = CPS_ParseNTPSourceAdd(line, &data);
|
||||
switch (status) {
|
||||
case 0:
|
||||
LOG(LOGS_ERR, "Invalid syntax for add command");
|
||||
break;
|
||||
default:
|
||||
case CPS_Success:
|
||||
/* Verify that the address is resolvable (chronyc and chronyd are
|
||||
assumed to be running on the same host) */
|
||||
if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) ||
|
||||
@@ -947,8 +1063,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
|
||||
}
|
||||
|
||||
msg->data.ntp_source.type = htonl(type);
|
||||
if (strlen(data.name) >= sizeof (msg->data.ntp_source.name))
|
||||
assert(0);
|
||||
BRIEF_ASSERT(strlen(data.name) < sizeof (msg->data.ntp_source.name));
|
||||
strncpy((char *)msg->data.ntp_source.name, data.name,
|
||||
sizeof (msg->data.ntp_source.name));
|
||||
msg->data.ntp_source.port = htonl(data.port);
|
||||
@@ -989,10 +1104,19 @@ process_cmd_add_source(CMD_Request *msg, char *line)
|
||||
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));
|
||||
msg->data.ntp_source.max_unreach = htonl(data.params.max_unreach);
|
||||
|
||||
result = 1;
|
||||
|
||||
break;
|
||||
case CPS_InvalidOption:
|
||||
LOG(LOGS_ERR, "Invalid %s add command", "option in");
|
||||
break;
|
||||
case CPS_InvalidValue:
|
||||
LOG(LOGS_ERR, "Invalid %s add command", "value in");
|
||||
break;
|
||||
default:
|
||||
LOG(LOGS_ERR, "Invalid %s add command", "syntax for");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2452,10 +2576,10 @@ process_cmd_selectdata(char *line)
|
||||
n_sources = ntohl(reply.data.n_sources.n_sources);
|
||||
|
||||
if (verbose) {
|
||||
printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
|
||||
printf( " . State: N - noselect, s - unsynchronised, M - missing samples, r - stratum\n");
|
||||
printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
|
||||
printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
|
||||
printf( "| U - waits for update,, x - falseticker, + - combined, * - best.\n");
|
||||
printf( "| U - waits for update, x - falseticker, + - combined, * - best.\n");
|
||||
printf( "| Effective options ---------. (N - noselect, P - prefer\n");
|
||||
printf( "| Configured options ----. \\ T - trust, R - require)\n");
|
||||
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
|
||||
@@ -3440,7 +3564,7 @@ static void
|
||||
display_gpl(void)
|
||||
{
|
||||
printf("chrony version %s\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2024 Richard P. Curnow and others\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2025 Richard P. Curnow and others\n"
|
||||
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
|
||||
"you are welcome to redistribute it under certain conditions. See the\n"
|
||||
"GNU General Public License version 2 for details.\n\n",
|
||||
@@ -3466,9 +3590,11 @@ print_help(const char *progname)
|
||||
" -m\t\tAccept multiple commands\n"
|
||||
" -h HOST\tSpecify server (%s)\n"
|
||||
" -p PORT\tSpecify UDP port (%d)\n"
|
||||
" -u USER\tSpecify user (%s)\n"
|
||||
" -v, --version\tPrint version and exit\n"
|
||||
" --help\tPrint usage and exit\n",
|
||||
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1", DEFAULT_CANDM_PORT);
|
||||
progname, DEFAULT_COMMAND_SOCKET",127.0.0.1,::1",
|
||||
DEFAULT_CANDM_PORT, DEFAULT_CHRONYC_USER);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -3485,10 +3611,11 @@ int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char *line;
|
||||
const char *hostnames = NULL, *user = DEFAULT_CHRONYC_USER;
|
||||
const char *progname = argv[0];
|
||||
const char *hostnames = NULL;
|
||||
int opt, ret = 1, multi = 0, family = IPADDR_UNSPEC;
|
||||
int port = DEFAULT_CANDM_PORT;
|
||||
struct passwd *pw;
|
||||
|
||||
/* Parse long command-line options */
|
||||
for (optind = 1; optind < argc; optind++) {
|
||||
@@ -3504,7 +3631,7 @@ main(int argc, char **argv)
|
||||
optind = 1;
|
||||
|
||||
/* Parse short command-line options */
|
||||
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:v")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:u:v")) != -1) {
|
||||
switch (opt) {
|
||||
case '4':
|
||||
case '6':
|
||||
@@ -3540,6 +3667,9 @@ main(int argc, char **argv)
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
user = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
print_version();
|
||||
return 0;
|
||||
@@ -3549,6 +3679,14 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop root privileges if configured to do so */
|
||||
if (strcmp(user, "root") != 0 && geteuid() == 0) {
|
||||
pw = getpwnam(user);
|
||||
if (!pw)
|
||||
LOG_FATAL("Could not get user/group ID of %s", user);
|
||||
UTI_DropRoot(pw->pw_uid, pw->pw_gid);
|
||||
}
|
||||
|
||||
if (isatty(0) && isatty(1) && isatty(2)) {
|
||||
on_terminal = 1;
|
||||
}
|
||||
@@ -3588,6 +3726,7 @@ main(int argc, char **argv)
|
||||
close_io();
|
||||
free_addresses(server_addresses);
|
||||
SCK_Finalise();
|
||||
UTI_ResetGetRandomFunctions();
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
@@ -265,16 +265,13 @@ get_record(IPAddr *ip)
|
||||
}
|
||||
|
||||
record->ip_addr = *ip;
|
||||
for (i = 0; i < MAX_SERVICES; i++)
|
||||
for (i = 0; i < MAX_SERVICES; i++) {
|
||||
record->last_hit[i] = INVALID_TS;
|
||||
for (i = 0; i < MAX_SERVICES; i++)
|
||||
record->hits[i] = 0;
|
||||
for (i = 0; i < MAX_SERVICES; i++)
|
||||
record->drops[i] = 0;
|
||||
for (i = 0; i < MAX_SERVICES; i++)
|
||||
record->tokens[i] = max_tokens[i];
|
||||
for (i = 0; i < MAX_SERVICES; i++)
|
||||
record->rate[i] = INVALID_RATE;
|
||||
}
|
||||
record->ntp_timeout_rate = INVALID_RATE;
|
||||
record->drop_flags = 0;
|
||||
|
||||
|
||||
669
cmdmon.c
669
cmdmon.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2024
|
||||
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2025
|
||||
*
|
||||
* 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
|
||||
@@ -68,87 +68,6 @@ static int bound_sock_fd4;
|
||||
/* Flag indicating whether this module has been initialised or not */
|
||||
static int initialised = 0;
|
||||
|
||||
/* ================================================== */
|
||||
/* Array of permission levels for command types */
|
||||
|
||||
static const char permissions[] = {
|
||||
PERMIT_OPEN, /* NULL */
|
||||
PERMIT_AUTH, /* ONLINE */
|
||||
PERMIT_AUTH, /* OFFLINE */
|
||||
PERMIT_AUTH, /* BURST */
|
||||
PERMIT_AUTH, /* MODIFY_MINPOLL */
|
||||
PERMIT_AUTH, /* MODIFY_MAXPOLL */
|
||||
PERMIT_AUTH, /* DUMP */
|
||||
PERMIT_AUTH, /* MODIFY_MAXDELAY */
|
||||
PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */
|
||||
PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */
|
||||
PERMIT_OPEN, /* LOGON */
|
||||
PERMIT_AUTH, /* SETTIME */
|
||||
PERMIT_AUTH, /* LOCAL */
|
||||
PERMIT_AUTH, /* MANUAL */
|
||||
PERMIT_OPEN, /* N_SOURCES */
|
||||
PERMIT_OPEN, /* SOURCE_DATA */
|
||||
PERMIT_AUTH, /* REKEY */
|
||||
PERMIT_AUTH, /* ALLOW */
|
||||
PERMIT_AUTH, /* ALLOWALL */
|
||||
PERMIT_AUTH, /* DENY */
|
||||
PERMIT_AUTH, /* DENYALL */
|
||||
PERMIT_AUTH, /* CMDALLOW */
|
||||
PERMIT_AUTH, /* CMDALLOWALL */
|
||||
PERMIT_AUTH, /* CMDDENY */
|
||||
PERMIT_AUTH, /* CMDDENYALL */
|
||||
PERMIT_AUTH, /* ACCHECK */
|
||||
PERMIT_AUTH, /* CMDACCHECK */
|
||||
PERMIT_AUTH, /* ADD_SERVER */
|
||||
PERMIT_AUTH, /* ADD_PEER */
|
||||
PERMIT_AUTH, /* DEL_SOURCE */
|
||||
PERMIT_AUTH, /* WRITERTC */
|
||||
PERMIT_AUTH, /* DFREQ */
|
||||
PERMIT_AUTH, /* DOFFSET */
|
||||
PERMIT_OPEN, /* TRACKING */
|
||||
PERMIT_OPEN, /* SOURCESTATS */
|
||||
PERMIT_OPEN, /* RTCREPORT */
|
||||
PERMIT_AUTH, /* TRIMRTC */
|
||||
PERMIT_AUTH, /* CYCLELOGS */
|
||||
PERMIT_AUTH, /* SUBNETS_ACCESSED */
|
||||
PERMIT_AUTH, /* CLIENT_ACCESSES (by subnet) */
|
||||
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX */
|
||||
PERMIT_OPEN, /* MANUAL_LIST */
|
||||
PERMIT_AUTH, /* MANUAL_DELETE */
|
||||
PERMIT_AUTH, /* MAKESTEP */
|
||||
PERMIT_OPEN, /* ACTIVITY */
|
||||
PERMIT_AUTH, /* MODIFY_MINSTRATUM */
|
||||
PERMIT_AUTH, /* MODIFY_POLLTARGET */
|
||||
PERMIT_AUTH, /* MODIFY_MAXDELAYDEVRATIO */
|
||||
PERMIT_AUTH, /* RESELECT */
|
||||
PERMIT_AUTH, /* RESELECTDISTANCE */
|
||||
PERMIT_AUTH, /* MODIFY_MAKESTEP */
|
||||
PERMIT_OPEN, /* SMOOTHING */
|
||||
PERMIT_AUTH, /* SMOOTHTIME */
|
||||
PERMIT_AUTH, /* REFRESH */
|
||||
PERMIT_AUTH, /* SERVER_STATS */
|
||||
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
|
||||
PERMIT_AUTH, /* LOCAL2 */
|
||||
PERMIT_AUTH, /* NTP_DATA */
|
||||
PERMIT_AUTH, /* ADD_SERVER2 */
|
||||
PERMIT_AUTH, /* ADD_PEER2 */
|
||||
PERMIT_AUTH, /* ADD_SERVER3 */
|
||||
PERMIT_AUTH, /* ADD_PEER3 */
|
||||
PERMIT_AUTH, /* SHUTDOWN */
|
||||
PERMIT_AUTH, /* ONOFFLINE */
|
||||
PERMIT_AUTH, /* ADD_SOURCE */
|
||||
PERMIT_OPEN, /* NTP_SOURCE_NAME */
|
||||
PERMIT_AUTH, /* RESET_SOURCES */
|
||||
PERMIT_AUTH, /* AUTH_DATA */
|
||||
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
|
||||
PERMIT_AUTH, /* SELECT_DATA */
|
||||
PERMIT_AUTH, /* RELOAD_SOURCES */
|
||||
PERMIT_AUTH, /* DOFFSET2 */
|
||||
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
|
||||
PERMIT_AUTH, /* MODIFY_OFFSET */
|
||||
PERMIT_AUTH, /* LOCAL3 */
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* This authorisation table is used for checking whether particular
|
||||
@@ -227,19 +146,17 @@ do_size_checks(void)
|
||||
request.command = htons(i);
|
||||
request_length = PKL_CommandLength(&request);
|
||||
padding_length = PKL_CommandPaddingLength(&request);
|
||||
if (padding_length > MAX_PADDING_LENGTH || padding_length > request_length ||
|
||||
request_length > sizeof (CMD_Request) ||
|
||||
(request_length && request_length < offsetof(CMD_Request, data)))
|
||||
assert(0);
|
||||
BRIEF_ASSERT(padding_length <= MAX_PADDING_LENGTH && padding_length <= request_length &&
|
||||
request_length <= sizeof (CMD_Request) &&
|
||||
(request_length == 0 || request_length >= offsetof(CMD_Request, data)));
|
||||
}
|
||||
|
||||
for (i = 1; i < N_REPLY_TYPES; i++) {
|
||||
reply.reply = htons(i);
|
||||
reply.status = STT_SUCCESS;
|
||||
reply_length = PKL_ReplyLength(&reply);
|
||||
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
|
||||
reply_length > sizeof (CMD_Reply))
|
||||
assert(0);
|
||||
BRIEF_ASSERT((reply_length == 0 || reply_length >= offsetof(CMD_Reply, data)) &&
|
||||
reply_length <= sizeof (CMD_Reply));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +166,6 @@ void
|
||||
CAM_Initialise(void)
|
||||
{
|
||||
assert(!initialised);
|
||||
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
|
||||
do_size_checks();
|
||||
|
||||
initialised = 1;
|
||||
@@ -533,7 +449,9 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
|
||||
UTI_FloatNetworkToHost(rx_message->data.local.distance),
|
||||
ntohl(rx_message->data.local.orphan),
|
||||
UTI_FloatNetworkToHost(rx_message->data.local.activate));
|
||||
UTI_FloatNetworkToHost(rx_message->data.local.activate),
|
||||
UTI_FloatNetworkToHost(rx_message->data.local.wait_synced),
|
||||
UTI_FloatNetworkToHost(rx_message->data.local.wait_unsynced));
|
||||
} else {
|
||||
REF_DisableLocal();
|
||||
}
|
||||
@@ -768,6 +686,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
|
||||
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
|
||||
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
|
||||
params.max_unreach = ntohl(rx_message->data.ntp_source.max_unreach);
|
||||
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
|
||||
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
|
||||
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
|
||||
@@ -1439,18 +1358,242 @@ handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
tx_message->status = htons(STT_NOSUCHSOURCE);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
handle_readwrite_commands(int command, CMD_Request *request, CMD_Reply *reply)
|
||||
{
|
||||
switch (command) {
|
||||
case REQ_ADD_SOURCE:
|
||||
handle_add_source(request, reply);
|
||||
break;
|
||||
case REQ_ALLOW:
|
||||
handle_allowdeny(request, reply, 1, 0);
|
||||
break;
|
||||
case REQ_ALLOWALL:
|
||||
handle_allowdeny(request, reply, 1, 1);
|
||||
break;
|
||||
case REQ_BURST:
|
||||
handle_burst(request, reply);
|
||||
break;
|
||||
case REQ_CMDALLOW:
|
||||
handle_cmdallowdeny(request, reply, 1, 0);
|
||||
break;
|
||||
case REQ_CMDALLOWALL:
|
||||
handle_cmdallowdeny(request, reply, 1, 1);
|
||||
break;
|
||||
case REQ_CMDDENY:
|
||||
handle_cmdallowdeny(request, reply, 0, 0);
|
||||
break;
|
||||
case REQ_CMDDENYALL:
|
||||
handle_cmdallowdeny(request, reply, 0, 1);
|
||||
break;
|
||||
case REQ_CYCLELOGS:
|
||||
handle_cyclelogs(request, reply);
|
||||
break;
|
||||
case REQ_DEL_SOURCE:
|
||||
handle_del_source(request, reply);
|
||||
break;
|
||||
case REQ_DENY:
|
||||
handle_allowdeny(request, reply, 0, 0);
|
||||
break;
|
||||
case REQ_DENYALL:
|
||||
handle_allowdeny(request, reply, 0, 1);
|
||||
break;
|
||||
case REQ_DFREQ:
|
||||
handle_dfreq(request, reply);
|
||||
break;
|
||||
case REQ_DOFFSET2:
|
||||
handle_doffset(request, reply);
|
||||
break;
|
||||
case REQ_DUMP:
|
||||
handle_dump(request, reply);
|
||||
break;
|
||||
case REQ_LOCAL3:
|
||||
handle_local(request, reply);
|
||||
break;
|
||||
case REQ_MAKESTEP:
|
||||
handle_make_step(request, reply);
|
||||
break;
|
||||
case REQ_MANUAL:
|
||||
handle_manual(request, reply);
|
||||
break;
|
||||
case REQ_MANUAL_DELETE:
|
||||
handle_manual_delete(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MAKESTEP:
|
||||
handle_modify_makestep(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MAXDELAY:
|
||||
handle_modify_maxdelay(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MAXDELAYDEVRATIO:
|
||||
handle_modify_maxdelaydevratio(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MAXDELAYRATIO:
|
||||
handle_modify_maxdelayratio(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MAXPOLL:
|
||||
handle_modify_maxpoll(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MAXUPDATESKEW:
|
||||
handle_modify_maxupdateskew(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MINPOLL:
|
||||
handle_modify_minpoll(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_MINSTRATUM:
|
||||
handle_modify_minstratum(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_OFFSET:
|
||||
handle_modify_offset(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_POLLTARGET:
|
||||
handle_modify_polltarget(request, reply);
|
||||
break;
|
||||
case REQ_MODIFY_SELECTOPTS:
|
||||
handle_modify_selectopts(request, reply);
|
||||
break;
|
||||
case REQ_OFFLINE:
|
||||
handle_offline(request, reply);
|
||||
break;
|
||||
case REQ_ONLINE:
|
||||
handle_online(request, reply);
|
||||
break;
|
||||
case REQ_ONOFFLINE:
|
||||
handle_onoffline(request, reply);
|
||||
break;
|
||||
case REQ_REFRESH:
|
||||
handle_refresh(request, reply);
|
||||
break;
|
||||
case REQ_REKEY:
|
||||
handle_rekey(request, reply);
|
||||
break;
|
||||
case REQ_RELOAD_SOURCES:
|
||||
handle_reload_sources(request, reply);
|
||||
break;
|
||||
case REQ_RESELECT:
|
||||
handle_reselect(request, reply);
|
||||
break;
|
||||
case REQ_RESELECTDISTANCE:
|
||||
handle_reselect_distance(request, reply);
|
||||
break;
|
||||
case REQ_RESET_SOURCES:
|
||||
handle_reset_sources(request, reply);
|
||||
break;
|
||||
case REQ_SETTIME:
|
||||
handle_settime(request, reply);
|
||||
break;
|
||||
case REQ_SHUTDOWN:
|
||||
handle_shutdown(request, reply);
|
||||
break;
|
||||
case REQ_SMOOTHTIME:
|
||||
handle_smoothtime(request, reply);
|
||||
break;
|
||||
case REQ_TRIMRTC:
|
||||
handle_trimrtc(request, reply);
|
||||
break;
|
||||
case REQ_WRITERTC:
|
||||
handle_writertc(request, reply);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
handle_readonly_commands(int command, int full_access, CMD_Request *request, CMD_Reply *reply)
|
||||
{
|
||||
ARR_Instance open_commands;
|
||||
int i, allowed = 0;
|
||||
|
||||
if (full_access) {
|
||||
allowed = 1;
|
||||
} else {
|
||||
open_commands = CNF_GetOpenCommands();
|
||||
|
||||
for (i = 0; i < ARR_GetSize(open_commands); i++) {
|
||||
if (*(int *)ARR_GetElement(open_commands, i) == command) {
|
||||
allowed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowed)
|
||||
return 0;
|
||||
|
||||
switch (command) {
|
||||
case REQ_ACCHECK:
|
||||
handle_accheck(request, reply);
|
||||
break;
|
||||
case REQ_ACTIVITY:
|
||||
handle_activity(request, reply);
|
||||
break;
|
||||
case REQ_AUTH_DATA:
|
||||
handle_auth_data(request, reply);
|
||||
break;
|
||||
case REQ_CLIENT_ACCESSES_BY_INDEX3:
|
||||
handle_client_accesses_by_index(request, reply);
|
||||
break;
|
||||
case REQ_CMDACCHECK:
|
||||
handle_cmdaccheck(request, reply);
|
||||
break;
|
||||
case REQ_MANUAL_LIST:
|
||||
handle_manual_list(request, reply);
|
||||
break;
|
||||
case REQ_NTP_DATA:
|
||||
handle_ntp_data(request, reply);
|
||||
break;
|
||||
case REQ_NTP_SOURCE_NAME:
|
||||
handle_ntp_source_name(request, reply);
|
||||
break;
|
||||
case REQ_N_SOURCES:
|
||||
handle_n_sources(request, reply);
|
||||
break;
|
||||
case REQ_RTCREPORT:
|
||||
handle_rtcreport(request, reply);
|
||||
break;
|
||||
case REQ_SELECT_DATA:
|
||||
handle_select_data(request, reply);
|
||||
break;
|
||||
case REQ_SERVER_STATS:
|
||||
handle_server_stats(request, reply);
|
||||
break;
|
||||
case REQ_SMOOTHING:
|
||||
handle_smoothing(request, reply);
|
||||
break;
|
||||
case REQ_SOURCESTATS:
|
||||
handle_sourcestats(request, reply);
|
||||
break;
|
||||
case REQ_SOURCE_DATA:
|
||||
handle_source_data(request, reply);
|
||||
break;
|
||||
case REQ_TRACKING:
|
||||
handle_tracking(request, reply);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Read a packet and process it */
|
||||
|
||||
static void
|
||||
read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
{
|
||||
int read_length, expected_length, localhost, log_index, full_access, handled;
|
||||
SCK_Message *sck_message;
|
||||
CMD_Request rx_message;
|
||||
CMD_Reply tx_message;
|
||||
IPAddr loopback_addr, remote_ip;
|
||||
int read_length, expected_length;
|
||||
int localhost, allowed, log_index;
|
||||
uint16_t rx_command;
|
||||
struct timespec now, cooked_now;
|
||||
|
||||
@@ -1463,32 +1606,27 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
/* Get current time cheaply */
|
||||
SCH_GetLastEventTime(&cooked_now, NULL, &now);
|
||||
|
||||
/* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
|
||||
or an authorised address */
|
||||
switch (sck_message->addr_type) {
|
||||
case SCK_ADDR_IP:
|
||||
assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
|
||||
remote_ip = sck_message->remote_addr.ip.ip_addr;
|
||||
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
|
||||
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
|
||||
/* Check if the request came from the Unix domain socket, or network and
|
||||
whether the address is allowed (127.0.0.1 and ::1 is always allowed) */
|
||||
if ((sock_fd == sock_fd4 || sock_fd == sock_fd6) && sck_message->addr_type == SCK_ADDR_IP) {
|
||||
remote_ip = sck_message->remote_addr.ip.ip_addr;
|
||||
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
|
||||
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
|
||||
|
||||
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
|
||||
DEBUG_LOG("Unauthorised host %s",
|
||||
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
|
||||
return;
|
||||
}
|
||||
|
||||
assert(remote_ip.family != IPADDR_UNSPEC);
|
||||
|
||||
break;
|
||||
case SCK_ADDR_UNIX:
|
||||
assert(sock_fd == sock_fdu);
|
||||
remote_ip.family = IPADDR_UNSPEC;
|
||||
localhost = 1;
|
||||
break;
|
||||
default:
|
||||
DEBUG_LOG("Unexpected address type");
|
||||
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
|
||||
DEBUG_LOG("Unauthorised host %s",
|
||||
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
|
||||
return;
|
||||
}
|
||||
|
||||
full_access = 0;
|
||||
} else if (sock_fd == sock_fdu && sck_message->addr_type == SCK_ADDR_UNIX) {
|
||||
remote_ip.family = IPADDR_UNSPEC;
|
||||
localhost = 1;
|
||||
full_access = 1;
|
||||
} else {
|
||||
DEBUG_LOG("Unexpected socket/address");
|
||||
return;
|
||||
}
|
||||
|
||||
if (read_length < offsetof(CMD_Request, data) ||
|
||||
@@ -1564,297 +1702,20 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||
|
||||
/* OK, we have a valid message. Now dispatch on message type and process it. */
|
||||
|
||||
if (rx_command >= N_REQUEST_TYPES) {
|
||||
/* This should be already handled */
|
||||
assert(0);
|
||||
} else {
|
||||
/* Check level of authority required to issue the command. All commands
|
||||
from the Unix domain socket (which is accessible only by the root and
|
||||
chrony user/group) are allowed. */
|
||||
if (remote_ip.family == IPADDR_UNSPEC) {
|
||||
assert(sock_fd == sock_fdu);
|
||||
allowed = 1;
|
||||
} else {
|
||||
switch (permissions[rx_command]) {
|
||||
case PERMIT_AUTH:
|
||||
allowed = 0;
|
||||
break;
|
||||
case PERMIT_LOCAL:
|
||||
allowed = localhost;
|
||||
break;
|
||||
case PERMIT_OPEN:
|
||||
allowed = 1;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
allowed = 0;
|
||||
}
|
||||
}
|
||||
LOG_SetContext(LOGC_Command);
|
||||
|
||||
if (allowed) {
|
||||
LOG_SetContext(LOGC_Command);
|
||||
if (full_access)
|
||||
handled = handle_readwrite_commands(rx_command, &rx_message, &tx_message);
|
||||
else
|
||||
handled = 0;
|
||||
|
||||
switch(rx_command) {
|
||||
case REQ_NULL:
|
||||
/* Do nothing */
|
||||
break;
|
||||
if (!handled)
|
||||
handled = handle_readonly_commands(rx_command, full_access, &rx_message, &tx_message);
|
||||
|
||||
case REQ_DUMP:
|
||||
handle_dump(&rx_message, &tx_message);
|
||||
break;
|
||||
if (!handled)
|
||||
tx_message.status = htons(STT_UNAUTH);
|
||||
|
||||
case REQ_ONLINE:
|
||||
handle_online(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_OFFLINE:
|
||||
handle_offline(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_BURST:
|
||||
handle_burst(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MINPOLL:
|
||||
handle_modify_minpoll(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MAXPOLL:
|
||||
handle_modify_maxpoll(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MAXDELAY:
|
||||
handle_modify_maxdelay(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MAXDELAYRATIO:
|
||||
handle_modify_maxdelayratio(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MAXDELAYDEVRATIO:
|
||||
handle_modify_maxdelaydevratio(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MAXUPDATESKEW:
|
||||
handle_modify_maxupdateskew(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MAKESTEP:
|
||||
handle_modify_makestep(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_LOGON:
|
||||
/* Authentication is no longer supported, log-on always fails */
|
||||
tx_message.status = htons(STT_FAILED);
|
||||
break;
|
||||
|
||||
case REQ_SETTIME:
|
||||
handle_settime(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_LOCAL3:
|
||||
handle_local(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MANUAL:
|
||||
handle_manual(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_N_SOURCES:
|
||||
handle_n_sources(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SOURCE_DATA:
|
||||
handle_source_data(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_REKEY:
|
||||
handle_rekey(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ALLOW:
|
||||
handle_allowdeny(&rx_message, &tx_message, 1, 0);
|
||||
break;
|
||||
|
||||
case REQ_ALLOWALL:
|
||||
handle_allowdeny(&rx_message, &tx_message, 1, 1);
|
||||
break;
|
||||
|
||||
case REQ_DENY:
|
||||
handle_allowdeny(&rx_message, &tx_message, 0, 0);
|
||||
break;
|
||||
|
||||
case REQ_DENYALL:
|
||||
handle_allowdeny(&rx_message, &tx_message, 0, 1);
|
||||
break;
|
||||
|
||||
case REQ_CMDALLOW:
|
||||
handle_cmdallowdeny(&rx_message, &tx_message, 1, 0);
|
||||
break;
|
||||
|
||||
case REQ_CMDALLOWALL:
|
||||
handle_cmdallowdeny(&rx_message, &tx_message, 1, 1);
|
||||
break;
|
||||
|
||||
case REQ_CMDDENY:
|
||||
handle_cmdallowdeny(&rx_message, &tx_message, 0, 0);
|
||||
break;
|
||||
|
||||
case REQ_CMDDENYALL:
|
||||
handle_cmdallowdeny(&rx_message, &tx_message, 0, 1);
|
||||
break;
|
||||
|
||||
case REQ_ACCHECK:
|
||||
handle_accheck(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_CMDACCHECK:
|
||||
handle_cmdaccheck(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ADD_SOURCE:
|
||||
handle_add_source(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_DEL_SOURCE:
|
||||
handle_del_source(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_WRITERTC:
|
||||
handle_writertc(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_DFREQ:
|
||||
handle_dfreq(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_DOFFSET2:
|
||||
handle_doffset(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_TRACKING:
|
||||
handle_tracking(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SMOOTHING:
|
||||
handle_smoothing(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SMOOTHTIME:
|
||||
handle_smoothtime(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SOURCESTATS:
|
||||
handle_sourcestats(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_RTCREPORT:
|
||||
handle_rtcreport(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_TRIMRTC:
|
||||
handle_trimrtc(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_CYCLELOGS:
|
||||
handle_cyclelogs(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_CLIENT_ACCESSES_BY_INDEX3:
|
||||
handle_client_accesses_by_index(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MANUAL_LIST:
|
||||
handle_manual_list(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MANUAL_DELETE:
|
||||
handle_manual_delete(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MAKESTEP:
|
||||
handle_make_step(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ACTIVITY:
|
||||
handle_activity(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_RESELECTDISTANCE:
|
||||
handle_reselect_distance(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_RESELECT:
|
||||
handle_reselect(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_MINSTRATUM:
|
||||
handle_modify_minstratum(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_POLLTARGET:
|
||||
handle_modify_polltarget(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_REFRESH:
|
||||
handle_refresh(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SERVER_STATS:
|
||||
handle_server_stats(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_NTP_DATA:
|
||||
handle_ntp_data(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SHUTDOWN:
|
||||
handle_shutdown(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_ONOFFLINE:
|
||||
handle_onoffline(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_NTP_SOURCE_NAME:
|
||||
handle_ntp_source_name(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_RESET_SOURCES:
|
||||
handle_reset_sources(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_AUTH_DATA:
|
||||
handle_auth_data(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_SELECT_DATA:
|
||||
handle_select_data(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_RELOAD_SOURCES:
|
||||
handle_reload_sources(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_SELECTOPTS:
|
||||
handle_modify_selectopts(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
case REQ_MODIFY_OFFSET:
|
||||
handle_modify_offset(&rx_message, &tx_message);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_LOG("Unhandled command %d", rx_command);
|
||||
tx_message.status = htons(STT_FAILED);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_UnsetContext(LOGC_Command);
|
||||
} else {
|
||||
tx_message.status = htons(STT_UNAUTH);
|
||||
}
|
||||
}
|
||||
LOG_UnsetContext(LOGC_Command);
|
||||
|
||||
/* Transmit the response */
|
||||
transmit_reply(sock_fd, read_length, sck_message);
|
||||
|
||||
116
cmdparse.c
116
cmdparse.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
|
||||
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021, 2025
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -39,7 +39,12 @@
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
|
||||
(sscanf((s), (f), (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
CPS_Status
|
||||
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
{
|
||||
char *hostname, *cmd;
|
||||
@@ -61,6 +66,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
|
||||
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
|
||||
src->params.max_unreach = SRC_DEFAULT_MAXUNREACH;
|
||||
src->params.filter_length = 0;
|
||||
src->params.interleaved = 0;
|
||||
src->params.sel_options = 0;
|
||||
@@ -82,7 +88,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (!*hostname)
|
||||
return 0;
|
||||
return CPS_MissingArgument;
|
||||
|
||||
src->name = hostname;
|
||||
|
||||
@@ -104,17 +110,17 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
src->params.connectivity = SRC_OFFLINE;
|
||||
} else if (!strcasecmp(cmd, "certset")) {
|
||||
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "key")) {
|
||||
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
|
||||
src->params.authkey == INACTIVE_AUTHKEY)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "asymmetry")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "extfield")) {
|
||||
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
switch (ef_type) {
|
||||
case NTP_EF_EXP_MONO_ROOT:
|
||||
src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
|
||||
@@ -123,78 +129,81 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "filter")) {
|
||||
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.filter_length, &n, 0, INT_MAX))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "ipv4")) {
|
||||
src->family = IPADDR_INET4;
|
||||
} else if (!strcasecmp(cmd, "ipv6")) {
|
||||
src->family = IPADDR_INET6;
|
||||
} else if (!strcasecmp(cmd, "maxdelay")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxdelayratio")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxdelayquant")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxpoll")) {
|
||||
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.maxpoll, &n, -32, 32))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxsamples")) {
|
||||
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_samples, &n, 0, INT_MAX))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxsources")) {
|
||||
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_sources, &n, 1, INT_MAX))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "maxunreach")) {
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_unreach, &n, 0, INT_MAX))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "mindelay")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "minpoll")) {
|
||||
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "minsamples")) {
|
||||
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_samples, &n, 0, INT_MAX))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "minstratum")) {
|
||||
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_stratum, &n, 0, NTP_MAX_STRATUM))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "nts")) {
|
||||
src->params.nts = 1;
|
||||
} else if (!strcasecmp(cmd, "ntsport")) {
|
||||
if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.nts_port, &n, 0, 65535))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "offset")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "port")) {
|
||||
if (sscanf(line, "%d%n", &src->port, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->port, &n, 0, 65535))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "polltarget")) {
|
||||
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.poll_target, &n, 1, INT_MAX))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "presend")) {
|
||||
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.presend_minpoll, &n, -32, 32))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "version")) {
|
||||
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.version, &n, 1, NTP_VERSION))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "xleave")) {
|
||||
src->params.interleaved = 1;
|
||||
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
|
||||
src->params.sel_options |= sel_option;
|
||||
} else {
|
||||
return 0;
|
||||
return CPS_InvalidOption;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return CPS_Success;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -295,8 +304,9 @@ CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate)
|
||||
CPS_Status
|
||||
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate,
|
||||
double *wait_synced, double *wait_unsynced)
|
||||
{
|
||||
int n;
|
||||
char *cmd;
|
||||
@@ -305,32 +315,42 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *
|
||||
*distance = 1.0;
|
||||
*activate = 0.0;
|
||||
*orphan = 0;
|
||||
*wait_synced = 0;
|
||||
*wait_unsynced = -1.0;
|
||||
|
||||
while (*line) {
|
||||
cmd = line;
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (!strcasecmp(cmd, "stratum")) {
|
||||
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
|
||||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
|
||||
return 0;
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", stratum, &n, 1, NTP_MAX_STRATUM - 1))
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "orphan")) {
|
||||
*orphan = 1;
|
||||
n = 0;
|
||||
} else if (!strcasecmp(cmd, "distance")) {
|
||||
if (sscanf(line, "%lf%n", distance, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "activate")) {
|
||||
if (sscanf(line, "%lf%n", activate, &n) != 1)
|
||||
return 0;
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "waitsynced")) {
|
||||
if (sscanf(line, "%lf%n", wait_synced, &n) != 1)
|
||||
return CPS_InvalidValue;
|
||||
} else if (!strcasecmp(cmd, "waitunsynced")) {
|
||||
if (sscanf(line, "%lf%n", wait_unsynced, &n) != 1)
|
||||
return CPS_InvalidValue;
|
||||
} else {
|
||||
return 0;
|
||||
return CPS_InvalidOption;
|
||||
}
|
||||
|
||||
line += n;
|
||||
}
|
||||
|
||||
return 1;
|
||||
if (*wait_unsynced < 0.0)
|
||||
*wait_unsynced = *orphan ? 300 : 0.0;
|
||||
|
||||
return CPS_Success;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
12
cmdparse.h
12
cmdparse.h
@@ -30,6 +30,13 @@
|
||||
#include "srcparams.h"
|
||||
#include "addressing.h"
|
||||
|
||||
typedef enum {
|
||||
CPS_Success,
|
||||
CPS_InvalidValue,
|
||||
CPS_InvalidOption,
|
||||
CPS_MissingArgument,
|
||||
} CPS_Status;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int family;
|
||||
@@ -38,7 +45,7 @@ typedef struct {
|
||||
} CPS_NTP_Source;
|
||||
|
||||
/* Parse a command to add an NTP server or peer */
|
||||
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
|
||||
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
|
||||
|
||||
/* Get an NTP/refclock select option */
|
||||
extern int CPS_GetSelectOption(char *option);
|
||||
@@ -47,7 +54,8 @@ extern int CPS_GetSelectOption(char *option);
|
||||
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
|
||||
|
||||
/* Parse a command to enable local reference */
|
||||
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate);
|
||||
extern CPS_Status CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance,
|
||||
double *activate, double *wait_synced, double *wait_unsynced);
|
||||
|
||||
/* Remove extra white-space and comments */
|
||||
extern void CPS_NormalizeLine(char *line);
|
||||
|
||||
349
conf.c
349
conf.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2017, 2020, 2024
|
||||
* Copyright (C) Miroslav Lichvar 2009-2017, 2020, 2024-2025
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -50,14 +50,17 @@
|
||||
#define MAX_CONF_DIRS 10
|
||||
#define MAX_INCLUDE_LEVEL 10
|
||||
|
||||
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
|
||||
(sscanf((s), (f), (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
|
||||
|
||||
/* ================================================== */
|
||||
/* Forward prototypes */
|
||||
|
||||
static int parse_string(char *line, char **result);
|
||||
static int parse_int(char *line, int *result);
|
||||
static int parse_double(char *line, double *result);
|
||||
static int parse_null(char *line);
|
||||
static int parse_ints(char *line, ARR_Instance array);
|
||||
static void parse_string(char *line, char **result);
|
||||
static void parse_int(char *line, int *result, int min, int max);
|
||||
static void parse_double(char *line, double *result);
|
||||
static void parse_null(char *line, int *result);
|
||||
static void parse_ints(char *line, ARR_Instance array, int min, int max);
|
||||
|
||||
static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow);
|
||||
static void parse_authselectmode(char *);
|
||||
@@ -67,6 +70,7 @@ static void parse_bindcmdaddress(char *);
|
||||
static void parse_broadcast(char *);
|
||||
static void parse_clientloglimit(char *);
|
||||
static void parse_confdir(char *);
|
||||
static void parse_driftfile(char *);
|
||||
static void parse_fallbackdrift(char *);
|
||||
static void parse_hwtimestamp(char *);
|
||||
static void parse_include(char *);
|
||||
@@ -79,6 +83,7 @@ static void parse_makestep(char *);
|
||||
static void parse_maxchange(char *);
|
||||
static void parse_ntsserver(char *, ARR_Instance files);
|
||||
static void parse_ntstrustedcerts(char *);
|
||||
static void parse_open_commands(char *line);
|
||||
static void parse_pidfile(char *line);
|
||||
static void parse_ratelimit(char *line, int *enabled, int *interval,
|
||||
int *burst, int *leak, int *kod);
|
||||
@@ -98,6 +103,7 @@ static int acquisition_port = -1;
|
||||
static int ntp_port = NTP_PORT;
|
||||
static char *keys_file = NULL;
|
||||
static char *drift_file = NULL;
|
||||
static int drift_file_interval = 3600;
|
||||
static char *rtc_file = NULL;
|
||||
static double max_update_skew = 1000.0;
|
||||
static double correction_time_ratio = 3.0;
|
||||
@@ -109,6 +115,8 @@ static double clock_precision = 0.0; /* in seconds */
|
||||
static SRC_AuthSelectMode authselect_mode = SRC_AUTHSELECT_MIX;
|
||||
static double max_distance = 3.0;
|
||||
static double max_jitter = 1.0;
|
||||
static int max_stratum = NTP_MAX_STRATUM - 1;
|
||||
static int min_stratum = 0;
|
||||
static double reselect_distance = 1e-4;
|
||||
static double stratum_weight = 1e-3;
|
||||
static double combine_limit = 3.0;
|
||||
@@ -132,6 +140,8 @@ static int local_stratum;
|
||||
static int local_orphan;
|
||||
static double local_distance;
|
||||
static double local_activate;
|
||||
static double local_wait_synced;
|
||||
static double local_wait_unsynced;
|
||||
|
||||
/* Threshold (in seconds) - if absolute value of initial error is less
|
||||
than this, slew instead of stepping */
|
||||
@@ -324,6 +334,11 @@ typedef struct _AllowDeny {
|
||||
static ARR_Instance ntp_restrictions;
|
||||
static ARR_Instance cmd_restrictions;
|
||||
|
||||
#define DEFAULT_OPEN_COMMANDS "activity manual rtcdata smoothing sourcename sources sourcestats tracking"
|
||||
|
||||
/* Array of int specifying commands allowed from network */
|
||||
static ARR_Instance open_commands;
|
||||
|
||||
typedef struct {
|
||||
NTP_Remote_Address addr;
|
||||
int interval;
|
||||
@@ -353,12 +368,20 @@ command_parse_error(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
FORMAT_ATTRIBUTE_PRINTF(1, 2)
|
||||
static void
|
||||
other_parse_error(const char *message)
|
||||
other_parse_error(const char *format, ...)
|
||||
{
|
||||
LOG_FATAL("%s at line %d%s%s",
|
||||
message, line_number, processed_file ? " in file " : "",
|
||||
processed_file ? processed_file : "");
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(buf, sizeof (buf), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
LOG_FATAL("%s at line %d%s%s",
|
||||
buf, line_number, processed_file ? " in file " : "",
|
||||
processed_file ? processed_file : "");
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -401,7 +424,7 @@ check_number_of_args(char *line, int num)
|
||||
void
|
||||
CNF_Initialise(int r, int client_only)
|
||||
{
|
||||
char buf[10];
|
||||
char buf[128];
|
||||
|
||||
restarted = r;
|
||||
|
||||
@@ -416,9 +439,13 @@ CNF_Initialise(int r, int client_only)
|
||||
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
|
||||
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
|
||||
|
||||
open_commands = ARR_CreateInstance(sizeof (int));
|
||||
snprintf(buf, sizeof (buf), DEFAULT_OPEN_COMMANDS);
|
||||
parse_open_commands(buf);
|
||||
|
||||
nts_aeads = ARR_CreateInstance(sizeof (int));
|
||||
snprintf(buf, sizeof (buf), DEFAULT_NTS_AEADS);
|
||||
parse_ints(buf, nts_aeads);
|
||||
parse_ints(buf, nts_aeads, 0, INT_MAX);
|
||||
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
|
||||
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
|
||||
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
|
||||
@@ -475,6 +502,8 @@ CNF_Finalise(void)
|
||||
ARR_DestroyInstance(refclock_sources);
|
||||
ARR_DestroyInstance(broadcasts);
|
||||
|
||||
ARR_DestroyInstance(open_commands);
|
||||
|
||||
ARR_DestroyInstance(ntp_restrictions);
|
||||
ARR_DestroyInstance(cmd_restrictions);
|
||||
|
||||
@@ -573,7 +602,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
printf("%s%s%s\n", command, p[0] != '\0' ? " " : "", p);
|
||||
|
||||
if (!strcasecmp(command, "acquisitionport")) {
|
||||
parse_int(p, &acquisition_port);
|
||||
parse_int(p, &acquisition_port, 0, 65535);
|
||||
} else if (!strcasecmp(command, "allow")) {
|
||||
parse_allow_deny(p, ntp_restrictions, 1);
|
||||
} else if (!strcasecmp(command, "authselectmode")) {
|
||||
@@ -601,7 +630,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "cmddeny")) {
|
||||
parse_allow_deny(p, cmd_restrictions, 0);
|
||||
} else if (!strcasecmp(command, "cmdport")) {
|
||||
parse_int(p, &cmd_port);
|
||||
parse_int(p, &cmd_port, 0, 65535);
|
||||
} else if (!strcasecmp(command, "cmdratelimit")) {
|
||||
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
|
||||
&cmd_ratelimit_burst, &cmd_ratelimit_leak, NULL);
|
||||
@@ -614,9 +643,9 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "deny")) {
|
||||
parse_allow_deny(p, ntp_restrictions, 0);
|
||||
} else if (!strcasecmp(command, "driftfile")) {
|
||||
parse_string(p, &drift_file);
|
||||
parse_driftfile(p);
|
||||
} else if (!strcasecmp(command, "dscp")) {
|
||||
parse_int(p, &ntp_dscp);
|
||||
parse_int(p, &ntp_dscp, 0, 63);
|
||||
} else if (!strcasecmp(command, "dumpdir")) {
|
||||
parse_string(p, &dumpdir);
|
||||
} else if (!strcasecmp(command, "dumponexit")) {
|
||||
@@ -644,11 +673,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "local")) {
|
||||
parse_local(p);
|
||||
} else if (!strcasecmp(command, "lock_all")) {
|
||||
lock_memory = parse_null(p);
|
||||
parse_null(p, &lock_memory);
|
||||
} else if (!strcasecmp(command, "log")) {
|
||||
parse_log(p);
|
||||
} else if (!strcasecmp(command, "logbanner")) {
|
||||
parse_int(p, &log_banner);
|
||||
parse_int(p, &log_banner, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "logchange")) {
|
||||
parse_double(p, &log_change_threshold);
|
||||
} else if (!strcasecmp(command, "logdir")) {
|
||||
@@ -658,7 +687,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "makestep")) {
|
||||
parse_makestep(p);
|
||||
} else if (!strcasecmp(command, "manual")) {
|
||||
enable_manual = parse_null(p);
|
||||
parse_null(p, &enable_manual);
|
||||
} else if (!strcasecmp(command, "maxchange")) {
|
||||
parse_maxchange(p);
|
||||
} else if (!strcasecmp(command, "maxclockerror")) {
|
||||
@@ -670,27 +699,31 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "maxjitter")) {
|
||||
parse_double(p, &max_jitter);
|
||||
} else if (!strcasecmp(command, "maxntsconnections")) {
|
||||
parse_int(p, &nts_server_connections);
|
||||
parse_int(p, &nts_server_connections, 1, INT_MAX);
|
||||
} else if (!strcasecmp(command, "maxsamples")) {
|
||||
parse_int(p, &max_samples);
|
||||
parse_int(p, &max_samples, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "maxslewrate")) {
|
||||
parse_double(p, &max_slew_rate);
|
||||
} else if (!strcasecmp(command, "maxstratum")) {
|
||||
parse_int(p, &max_stratum, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "maxupdateskew")) {
|
||||
parse_double(p, &max_update_skew);
|
||||
} else if (!strcasecmp(command, "minsamples")) {
|
||||
parse_int(p, &min_samples);
|
||||
parse_int(p, &min_samples, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "minsources")) {
|
||||
parse_int(p, &min_sources);
|
||||
parse_int(p, &min_sources, 1, INT_MAX);
|
||||
} else if (!strcasecmp(command, "minstratum")) {
|
||||
parse_int(p, &min_stratum, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "nocerttimecheck")) {
|
||||
parse_int(p, &no_cert_time_check);
|
||||
parse_int(p, &no_cert_time_check, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "noclientlog")) {
|
||||
no_client_log = parse_null(p);
|
||||
parse_null(p, &no_client_log);
|
||||
} else if (!strcasecmp(command, "nosystemcert")) {
|
||||
no_system_cert = parse_null(p);
|
||||
parse_null(p, &no_system_cert);
|
||||
} else if (!strcasecmp(command, "ntpsigndsocket")) {
|
||||
parse_string(p, &ntp_signd_socket);
|
||||
} else if (!strcasecmp(command, "ntsaeads")) {
|
||||
parse_ints(p, nts_aeads);
|
||||
parse_ints(p, nts_aeads, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "ntsratelimit")) {
|
||||
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
|
||||
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
|
||||
@@ -700,19 +733,21 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "ntsntpserver")) {
|
||||
parse_string(p, &nts_ntp_server);
|
||||
} else if (!strcasecmp(command, "ntsport")) {
|
||||
parse_int(p, &nts_server_port);
|
||||
parse_int(p, &nts_server_port, 0, 65535);
|
||||
} else if (!strcasecmp(command, "ntsprocesses")) {
|
||||
parse_int(p, &nts_server_processes);
|
||||
parse_int(p, &nts_server_processes, 0, 1000);
|
||||
} else if (!strcasecmp(command, "ntsrefresh")) {
|
||||
parse_int(p, &nts_refresh);
|
||||
parse_int(p, &nts_refresh, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "ntsrotate")) {
|
||||
parse_int(p, &nts_rotate);
|
||||
parse_int(p, &nts_rotate, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "ntsservercert")) {
|
||||
parse_ntsserver(p, nts_server_cert_files);
|
||||
} else if (!strcasecmp(command, "ntsserverkey")) {
|
||||
parse_ntsserver(p, nts_server_key_files);
|
||||
} else if (!strcasecmp(command, "ntstrustedcerts")) {
|
||||
parse_ntstrustedcerts(p);
|
||||
} else if (!strcasecmp(command, "opencommands")) {
|
||||
parse_open_commands(p);
|
||||
} else if (!strcasecmp(command, "peer")) {
|
||||
parse_source(p, command, 1);
|
||||
} else if (!strcasecmp(command, "pidfile")) {
|
||||
@@ -720,18 +755,18 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "pool")) {
|
||||
parse_source(p, command, 1);
|
||||
} else if (!strcasecmp(command, "port")) {
|
||||
parse_int(p, &ntp_port);
|
||||
parse_int(p, &ntp_port, 0, 65535);
|
||||
} else if (!strcasecmp(command, "ptpdomain")) {
|
||||
parse_int(p, &ptp_domain);
|
||||
parse_int(p, &ptp_domain, 0, 255);
|
||||
} else if (!strcasecmp(command, "ptpport")) {
|
||||
parse_int(p, &ptp_port);
|
||||
parse_int(p, &ptp_port, 0, 65535);
|
||||
} else if (!strcasecmp(command, "ratelimit")) {
|
||||
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
|
||||
&ntp_ratelimit_burst, &ntp_ratelimit_leak, &ntp_ratelimit_kod);
|
||||
} else if (!strcasecmp(command, "refclock")) {
|
||||
parse_refclock(p);
|
||||
} else if (!strcasecmp(command, "refresh")) {
|
||||
parse_int(p, &refresh);
|
||||
parse_int(p, &refresh, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "reselectdist")) {
|
||||
parse_double(p, &reselect_distance);
|
||||
} else if (!strcasecmp(command, "rtcautotrim")) {
|
||||
@@ -741,11 +776,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
} else if (!strcasecmp(command, "rtcfile")) {
|
||||
parse_string(p, &rtc_file);
|
||||
} else if (!strcasecmp(command, "rtconutc")) {
|
||||
rtc_on_utc = parse_null(p);
|
||||
parse_null(p, &rtc_on_utc);
|
||||
} else if (!strcasecmp(command, "rtcsync")) {
|
||||
rtc_sync = parse_null(p);
|
||||
parse_null(p, &rtc_sync);
|
||||
} else if (!strcasecmp(command, "sched_priority")) {
|
||||
parse_int(p, &sched_priority);
|
||||
parse_int(p, &sched_priority, 0, 100);
|
||||
} else if (!strcasecmp(command, "server")) {
|
||||
parse_source(p, command, 1);
|
||||
} else if (!strcasecmp(command, "smoothtime")) {
|
||||
@@ -764,7 +799,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
!strcasecmp(command, "linux_hz")) {
|
||||
LOG(LOGS_WARN, "%s directive is no longer supported", command);
|
||||
} else {
|
||||
other_parse_error("Invalid directive");
|
||||
other_parse_error("Invalid directive %s", command);
|
||||
}
|
||||
|
||||
processed_file = processed_command = NULL;
|
||||
@@ -772,54 +807,58 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
static void
|
||||
parse_string(char *line, char **result)
|
||||
{
|
||||
check_number_of_args(line, 1);
|
||||
Free(*result);
|
||||
*result = Strdup(line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
parse_int(char *line, int *result)
|
||||
static void
|
||||
parse_int(char *line, int *result, int min, int max)
|
||||
{
|
||||
char *end;
|
||||
long r;
|
||||
|
||||
check_number_of_args(line, 1);
|
||||
if (sscanf(line, "%d", result) != 1) {
|
||||
|
||||
errno = 0;
|
||||
r = strtol(line, &end, 10);
|
||||
if (errno != 0 || *end != '\0')
|
||||
command_parse_error();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
if (r < min || r > max)
|
||||
other_parse_error("Invalid value %ld in %s directive (min %d, max %d)",
|
||||
r, processed_command, min, max);
|
||||
*result = r;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
static void
|
||||
parse_double(char *line, double *result)
|
||||
{
|
||||
check_number_of_args(line, 1);
|
||||
if (sscanf(line, "%lf", result) != 1) {
|
||||
command_parse_error();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
parse_null(char *line)
|
||||
static void
|
||||
parse_null(char *line, int *result)
|
||||
{
|
||||
check_number_of_args(line, 0);
|
||||
return 1;
|
||||
*result = 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
parse_ints(char *line, ARR_Instance array)
|
||||
static void
|
||||
parse_ints(char *line, ARR_Instance array, int min, int max)
|
||||
{
|
||||
char *s;
|
||||
int v;
|
||||
@@ -829,10 +868,9 @@ parse_ints(char *line, ARR_Instance array)
|
||||
while (*line) {
|
||||
s = line;
|
||||
line = CPS_SplitWord(line);
|
||||
parse_int(s, &v);
|
||||
parse_int(s, &v, min, max);
|
||||
ARR_AppendElement(array, &v);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -840,6 +878,7 @@ parse_ints(char *line, ARR_Instance array)
|
||||
static void
|
||||
parse_source(char *line, char *type, int fatal)
|
||||
{
|
||||
CPS_Status status;
|
||||
NTP_Source source;
|
||||
|
||||
if (strcasecmp(type, "peer") == 0) {
|
||||
@@ -860,9 +899,13 @@ parse_source(char *line, char *type, int fatal)
|
||||
/* Avoid comparing uninitialized data in compare_sources() */
|
||||
memset(&source.params, 0, sizeof (source.params));
|
||||
|
||||
if (!CPS_ParseNTPSourceAdd(line, &source.params)) {
|
||||
if (fatal)
|
||||
command_parse_error();
|
||||
status = CPS_ParseNTPSourceAdd(line, &source.params);
|
||||
if (status != CPS_Success) {
|
||||
if (fatal) {
|
||||
other_parse_error("Invalid %s %s directive",
|
||||
status == CPS_InvalidOption ? "option in" :
|
||||
status == CPS_InvalidValue ? "value in" : "syntax for", type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -897,7 +940,7 @@ parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak,
|
||||
while (*line) {
|
||||
opt = line;
|
||||
line = CPS_SplitWord(line);
|
||||
if (sscanf(line, "%d%n", &val, &n) != 1) {
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &val, &n, -32, 32)) {
|
||||
command_parse_error();
|
||||
return;
|
||||
}
|
||||
@@ -921,7 +964,7 @@ static void
|
||||
parse_refclock(char *line)
|
||||
{
|
||||
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
|
||||
int local, max_lock_age, pps_forced, sel_option, stratum, tai;
|
||||
int local, max_lock_age, max_unreach, pps_forced, sel_option, stratum, tai;
|
||||
uint32_t ref_id, lock_ref_id;
|
||||
double offset, delay, precision, max_dispersion, pulse_width;
|
||||
char *p, *cmd, *name, *param;
|
||||
@@ -935,6 +978,7 @@ parse_refclock(char *line)
|
||||
pps_rate = 0;
|
||||
min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||
max_samples = SRC_DEFAULT_MAXSAMPLES;
|
||||
max_unreach = SRC_DEFAULT_MAXUNREACH;
|
||||
sel_options = 0;
|
||||
offset = 0.0;
|
||||
delay = 1e-9;
|
||||
@@ -976,31 +1020,31 @@ parse_refclock(char *line)
|
||||
if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "poll")) {
|
||||
if (sscanf(line, "%d%n", &poll, &n) != 1) {
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &poll, &n, -32, 32))
|
||||
break;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "dpoll")) {
|
||||
if (sscanf(line, "%d%n", &dpoll, &n) != 1) {
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &dpoll, &n, -32, 32))
|
||||
break;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "filter")) {
|
||||
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &filter_length, &n, 0, INT_MAX))
|
||||
break;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "local")) {
|
||||
n = 0;
|
||||
local = 1;
|
||||
} else if (!strcasecmp(cmd, "rate")) {
|
||||
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &pps_rate, &n, 1, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "minsamples")) {
|
||||
if (sscanf(line, "%d%n", &min_samples, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &min_samples, &n, 0, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "maxlockage")) {
|
||||
if (sscanf(line, "%d%n", &max_lock_age, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &max_lock_age, &n, 0, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "maxsamples")) {
|
||||
if (sscanf(line, "%d%n", &max_samples, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &max_samples, &n, 0, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "maxunreach")) {
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &max_unreach, &n, 0, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "offset")) {
|
||||
if (sscanf(line, "%lf%n", &offset, &n) != 1)
|
||||
@@ -1018,8 +1062,7 @@ parse_refclock(char *line)
|
||||
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "stratum")) {
|
||||
if (sscanf(line, "%d%n", &stratum, &n) != 1 ||
|
||||
stratum >= NTP_MAX_STRATUM || stratum < 0)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &stratum, &n, 0, NTP_MAX_STRATUM - 1))
|
||||
break;
|
||||
} else if (!strcasecmp(cmd, "tai")) {
|
||||
n = 0;
|
||||
@@ -1031,13 +1074,13 @@ parse_refclock(char *line)
|
||||
n = 0;
|
||||
sel_options |= sel_option;
|
||||
} else {
|
||||
other_parse_error("Invalid refclock option");
|
||||
other_parse_error("Invalid %s %s directive", "option in", processed_command);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (*cmd) {
|
||||
command_parse_error();
|
||||
other_parse_error("Invalid %s %s directive", "value in", processed_command);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1052,6 +1095,7 @@ parse_refclock(char *line)
|
||||
refclock->pps_rate = pps_rate;
|
||||
refclock->min_samples = min_samples;
|
||||
refclock->max_samples = max_samples;
|
||||
refclock->max_unreach = max_unreach;
|
||||
refclock->sel_options = sel_options;
|
||||
refclock->stratum = stratum;
|
||||
refclock->tai = tai;
|
||||
@@ -1107,8 +1151,16 @@ parse_log(char *line)
|
||||
static void
|
||||
parse_local(char *line)
|
||||
{
|
||||
if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance, &local_activate))
|
||||
command_parse_error();
|
||||
CPS_Status status;
|
||||
|
||||
status = CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance,
|
||||
&local_activate, &local_wait_synced, &local_wait_unsynced);
|
||||
if (status != CPS_Success) {
|
||||
other_parse_error("Invalid %s %s directive",
|
||||
status == CPS_InvalidOption ? "option in" : "value in",
|
||||
processed_command);
|
||||
}
|
||||
|
||||
enable_local = 1;
|
||||
}
|
||||
|
||||
@@ -1269,6 +1321,72 @@ parse_ntstrustedcerts(char *line)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
add_open_command(int command)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Avoid duplicates */
|
||||
for (i = 0; i < ARR_GetSize(open_commands); i++) {
|
||||
if (*(int *)ARR_GetElement(open_commands, i) == command)
|
||||
return;
|
||||
}
|
||||
|
||||
ARR_AppendElement(open_commands, &command);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_open_commands(char *line)
|
||||
{
|
||||
char *s;
|
||||
|
||||
ARR_SetSize(open_commands, 0);
|
||||
|
||||
while (*line) {
|
||||
s = line;
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (strcasecmp(s, "activity") == 0) {
|
||||
add_open_command(REQ_ACTIVITY);
|
||||
} else if (strcasecmp(s, "authdata") == 0) {
|
||||
add_open_command(REQ_N_SOURCES);
|
||||
add_open_command(REQ_AUTH_DATA);
|
||||
} else if (strcasecmp(s, "clients") == 0) {
|
||||
add_open_command(REQ_CLIENT_ACCESSES_BY_INDEX3);
|
||||
} else if (strcasecmp(s, "manual") == 0) {
|
||||
add_open_command(REQ_MANUAL_LIST);
|
||||
} else if (strcasecmp(s, "ntpdata") == 0) {
|
||||
add_open_command(REQ_N_SOURCES);
|
||||
add_open_command(REQ_NTP_DATA);
|
||||
} else if (strcasecmp(s, "rtcdata") == 0) {
|
||||
add_open_command(REQ_RTCREPORT);
|
||||
} else if (strcasecmp(s, "selectdata") == 0) {
|
||||
add_open_command(REQ_N_SOURCES);
|
||||
add_open_command(REQ_SELECT_DATA);
|
||||
} else if (strcasecmp(s, "serverstats") == 0) {
|
||||
add_open_command(REQ_SERVER_STATS);
|
||||
} else if (strcasecmp(s, "smoothing") == 0) {
|
||||
add_open_command(REQ_SMOOTHING);
|
||||
} else if (strcasecmp(s, "sourcename") == 0) {
|
||||
add_open_command(REQ_NTP_SOURCE_NAME);
|
||||
} else if (strcasecmp(s, "sources") == 0) {
|
||||
add_open_command(REQ_N_SOURCES);
|
||||
add_open_command(REQ_SOURCE_DATA);
|
||||
} else if (strcasecmp(s, "sourcestats") == 0) {
|
||||
add_open_command(REQ_N_SOURCES);
|
||||
add_open_command(REQ_SOURCESTATS);
|
||||
} else if (strcasecmp(s, "tracking") == 0) {
|
||||
add_open_command(REQ_TRACKING);
|
||||
} else {
|
||||
command_parse_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
|
||||
{
|
||||
@@ -1510,17 +1628,17 @@ parse_hwtimestamp(char *line)
|
||||
line = CPS_SplitWord(line);
|
||||
|
||||
if (!strcasecmp(p, "maxsamples")) {
|
||||
if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->max_samples, &n, 0, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(p, "minpoll")) {
|
||||
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->minpoll, &n, -32, 32))
|
||||
break;
|
||||
} else if (!strcasecmp(p, "maxpoll")) {
|
||||
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->maxpoll, &n, -32, 32))
|
||||
break;
|
||||
maxpoll_set = 1;
|
||||
} else if (!strcasecmp(p, "minsamples")) {
|
||||
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
|
||||
if (!SSCANF_IN_RANGE(line, "%d%n", &iface->min_samples, &n, 0, INT_MAX))
|
||||
break;
|
||||
} else if (!strcasecmp(p, "precision")) {
|
||||
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
|
||||
@@ -1655,6 +1773,29 @@ parse_confdir(char *line)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_driftfile(char *line)
|
||||
{
|
||||
char *path, *opt, *val;
|
||||
|
||||
path = line;
|
||||
opt = CPS_SplitWord(path);
|
||||
val = CPS_SplitWord(opt);
|
||||
|
||||
if (*path == '\0' ||
|
||||
(*opt != '\0' && (strcasecmp(opt, "interval") != 0 ||
|
||||
sscanf(val, "%d", &drift_file_interval) != 1 ||
|
||||
*CPS_SplitWord(val) != '\0'))) {
|
||||
command_parse_error();
|
||||
return;
|
||||
}
|
||||
|
||||
Free(drift_file);
|
||||
drift_file = Strdup(path);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
parse_include(char *line)
|
||||
{
|
||||
@@ -1742,8 +1883,8 @@ reload_source_dirs(void)
|
||||
NTP_Source *prev_sources, *new_sources, *source;
|
||||
unsigned int i, j, prev_size, new_size, unresolved;
|
||||
char buf[MAX_LINE_LENGTH];
|
||||
int d, pass, was_added;
|
||||
NSR_Status s;
|
||||
int d, pass;
|
||||
|
||||
/* Ignore reload command before adding configured sources */
|
||||
if (!conf_ntp_sources_added)
|
||||
@@ -1782,13 +1923,16 @@ reload_source_dirs(void)
|
||||
else
|
||||
d = i < prev_size ? -1 : 1;
|
||||
|
||||
was_added = d <= 0 && (prev_sources[i].status == NSR_Success ||
|
||||
prev_sources[i].status == NSR_UnresolvedName);
|
||||
|
||||
/* Remove missing sources before adding others to avoid conflicts */
|
||||
if (pass == 0 && d < 0 && prev_sources[i].status == NSR_Success) {
|
||||
if (pass == 0 && d < 0 && was_added) {
|
||||
NSR_RemoveSourcesById(prev_sources[i].conf_id);
|
||||
}
|
||||
|
||||
/* Add new sources and sources that could not be added before */
|
||||
if (pass == 1 && (d > 0 || (d == 0 && prev_sources[i].status != NSR_Success))) {
|
||||
if (pass == 1 && (d > 0 || (d == 0 && !was_added))) {
|
||||
source = &new_sources[j];
|
||||
s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
|
||||
source->pool, source->type, &source->params.params,
|
||||
@@ -1980,8 +2124,9 @@ CNF_GetAcquisitionPort(void)
|
||||
/* ================================================== */
|
||||
|
||||
char *
|
||||
CNF_GetDriftFile(void)
|
||||
CNF_GetDriftFile(int *interval)
|
||||
{
|
||||
*interval = drift_file_interval;
|
||||
return drift_file;
|
||||
}
|
||||
|
||||
@@ -2172,6 +2317,22 @@ CNF_GetMaxJitter(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetMaxStratum(void)
|
||||
{
|
||||
return max_stratum;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetMinStratum(void)
|
||||
{
|
||||
return min_stratum;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
CNF_GetReselectDistance(void)
|
||||
{
|
||||
@@ -2204,6 +2365,14 @@ CNF_GetManualEnabled(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
ARR_Instance
|
||||
CNF_GetOpenCommands(void)
|
||||
{
|
||||
return open_commands;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetCommandPort(void) {
|
||||
return cmd_port;
|
||||
@@ -2212,13 +2381,16 @@ CNF_GetCommandPort(void) {
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate)
|
||||
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
|
||||
double *wait_synced, double *wait_unsynced)
|
||||
{
|
||||
if (enable_local) {
|
||||
*stratum = local_stratum;
|
||||
*orphan = local_orphan;
|
||||
*distance = local_distance;
|
||||
*activate = local_activate;
|
||||
*wait_synced = local_wait_synced;
|
||||
*wait_unsynced = local_wait_unsynced;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
@@ -2714,8 +2886,7 @@ CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids)
|
||||
*paths = ARR_GetElements(nts_trusted_certs_paths);
|
||||
*ids = ARR_GetElements(nts_trusted_certs_ids);
|
||||
|
||||
if (ARR_GetSize(nts_trusted_certs_paths) != ARR_GetSize(nts_trusted_certs_ids))
|
||||
assert(0);
|
||||
BRIEF_ASSERT(ARR_GetSize(nts_trusted_certs_paths) == ARR_GetSize(nts_trusted_certs_ids));
|
||||
|
||||
return ARR_GetSize(nts_trusted_certs_paths);
|
||||
}
|
||||
|
||||
8
conf.h
8
conf.h
@@ -56,7 +56,7 @@ extern void CNF_ReloadSources(void);
|
||||
|
||||
extern int CNF_GetAcquisitionPort(void);
|
||||
extern int CNF_GetNTPPort(void);
|
||||
extern char *CNF_GetDriftFile(void);
|
||||
extern char *CNF_GetDriftFile(int *interval);
|
||||
extern char *CNF_GetLogDir(void);
|
||||
extern char *CNF_GetDumpDir(void);
|
||||
extern int CNF_GetLogBanner(void);
|
||||
@@ -70,6 +70,7 @@ extern int CNF_GetLogTempComp(void);
|
||||
extern char *CNF_GetKeysFile(void);
|
||||
extern char *CNF_GetRtcFile(void);
|
||||
extern int CNF_GetManualEnabled(void);
|
||||
extern ARR_Instance CNF_GetOpenCommands(void);
|
||||
extern int CNF_GetCommandPort(void);
|
||||
extern int CNF_GetRtcOnUtc(void);
|
||||
extern int CNF_GetRtcSync(void);
|
||||
@@ -105,11 +106,14 @@ extern double CNF_GetClockPrecision(void);
|
||||
extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
|
||||
extern double CNF_GetMaxDistance(void);
|
||||
extern double CNF_GetMaxJitter(void);
|
||||
extern int CNF_GetMaxStratum(void);
|
||||
extern int CNF_GetMinStratum(void);
|
||||
extern double CNF_GetReselectDistance(void);
|
||||
extern double CNF_GetStratumWeight(void);
|
||||
extern double CNF_GetCombineLimit(void);
|
||||
|
||||
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate);
|
||||
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
|
||||
double *wait_synced, double *wait_unsynced);
|
||||
|
||||
extern void CNF_SetupAccessRestrictions(void);
|
||||
|
||||
|
||||
114
configure
vendored
114
configure
vendored
@@ -116,7 +116,6 @@ For better control, use the options below.
|
||||
--without-tomcrypt Don't use libtomcrypt even if it is available
|
||||
--disable-nts Disable NTS support
|
||||
--disable-cmdmon Disable command and monitoring support
|
||||
--disable-ntp Disable NTP support
|
||||
--disable-refclock Disable reference clock support
|
||||
--disable-phc Disable PHC refclock driver
|
||||
--disable-pps Disable PPS refclock driver
|
||||
@@ -126,7 +125,6 @@ For better control, use the options below.
|
||||
--without-libcap Don't use libcap even if it is available
|
||||
--enable-scfilter Enable support for system call filtering
|
||||
--without-seccomp Don't use seccomp even if it is available
|
||||
--disable-asyncdns Disable asynchronous name resolving
|
||||
--disable-forcednsretry Don't retry on permanent DNS error
|
||||
--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
|
||||
@@ -135,6 +133,7 @@ For better control, use the options below.
|
||||
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
|
||||
since 1970-01-01 [50*365 days ago]
|
||||
--with-user=USER Specify default chronyd user [root]
|
||||
--with-chronyc-user=USER Specify default chronyc user [root]
|
||||
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
|
||||
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
|
||||
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
|
||||
@@ -219,7 +218,6 @@ EXTRA_CLI_OBJECTS=""
|
||||
|
||||
feat_debug=0
|
||||
feat_cmdmon=1
|
||||
feat_ntp=1
|
||||
feat_refclock=1
|
||||
feat_readline=1
|
||||
try_editline=1
|
||||
@@ -243,7 +241,6 @@ try_phc=0
|
||||
feat_pps=1
|
||||
try_setsched=0
|
||||
try_lockmem=0
|
||||
feat_asyncdns=1
|
||||
feat_forcednsretry=1
|
||||
try_aes_gcm_siv=1
|
||||
try_clock_gettime=1
|
||||
@@ -253,8 +250,8 @@ feat_timestamping=1
|
||||
try_timestamping=0
|
||||
feat_ntp_signd=0
|
||||
ntp_era_split=""
|
||||
use_pthread=0
|
||||
default_user="root"
|
||||
default_chronyc_user="root"
|
||||
default_hwclockfile=""
|
||||
default_pidfile="/var/run/chrony/chronyd.pid"
|
||||
default_rtcdevice="/dev/rtc"
|
||||
@@ -308,9 +305,6 @@ do
|
||||
--disable-cmdmon)
|
||||
feat_cmdmon=0
|
||||
;;
|
||||
--disable-ntp)
|
||||
feat_ntp=0
|
||||
;;
|
||||
--disable-refclock)
|
||||
feat_refclock=0
|
||||
;;
|
||||
@@ -341,9 +335,6 @@ do
|
||||
--without-seccomp)
|
||||
try_seccomp=0
|
||||
;;
|
||||
--disable-asyncdns)
|
||||
feat_asyncdns=0
|
||||
;;
|
||||
--disable-forcednsretry)
|
||||
feat_forcednsretry=0
|
||||
;;
|
||||
@@ -365,6 +356,9 @@ do
|
||||
--with-user=* )
|
||||
default_user=`echo $option | sed -e 's/^.*=//;'`
|
||||
;;
|
||||
--with-chronyc-user=* )
|
||||
default_chronyc_user=`echo $option | sed -e 's/^.*=//;'`
|
||||
;;
|
||||
--with-hwclockfile=* )
|
||||
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
|
||||
;;
|
||||
@@ -504,27 +498,15 @@ if [ $feat_cmdmon = "1" ]; then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o"
|
||||
fi
|
||||
|
||||
if [ $feat_ntp = "1" ]; then
|
||||
add_def FEAT_NTP
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o"
|
||||
if [ $feat_ntp_signd = "1" ]; then
|
||||
add_def FEAT_SIGND
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
|
||||
fi
|
||||
else
|
||||
feat_asyncdns=0
|
||||
feat_timestamping=0
|
||||
fi
|
||||
|
||||
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS addrfilt.o clientlog.o keys.o nameserv.o"
|
||||
else
|
||||
feat_ipv6=0
|
||||
if [ $feat_ntp_signd = "1" ]; then
|
||||
add_def FEAT_SIGND
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
|
||||
fi
|
||||
|
||||
if [ $feat_refclock = "1" ]; then
|
||||
add_def FEAT_REFCLOCK
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o"
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o \
|
||||
refclock_rtc.o refclock_shm.o refclock_sock.o"
|
||||
fi
|
||||
|
||||
MYCC="$CC"
|
||||
@@ -684,13 +666,15 @@ then
|
||||
fi
|
||||
|
||||
if [ $try_clock_gettime = "1" ]; then
|
||||
if test_code 'clock_gettime()' 'time.h' '' '' \
|
||||
'clock_gettime(CLOCK_REALTIME, (void *)1);'
|
||||
if test_code 'clock_gettime()' 'time.h' '' '' '
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);'
|
||||
then
|
||||
add_def HAVE_CLOCK_GETTIME
|
||||
else
|
||||
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
|
||||
'clock_gettime(CLOCK_REALTIME, (void *)1);'
|
||||
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' '
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);'
|
||||
then
|
||||
add_def HAVE_CLOCK_GETTIME
|
||||
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
||||
@@ -705,25 +689,28 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $feat_asyncdns = "1" ] && \
|
||||
test_code 'pthread' 'pthread.h' '-pthread' '' '
|
||||
pthread_t thread;
|
||||
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
|
||||
if test_code 'pthread' 'pthread.h' '-pthread' '' '
|
||||
pthread_t thread;
|
||||
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
|
||||
then
|
||||
add_def FEAT_ASYNCDNS
|
||||
add_def USE_PTHREAD_ASYNCDNS
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
|
||||
use_pthread=1
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
else
|
||||
echo "error: pthread_create() not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $try_arc4random = "1" ] && \
|
||||
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
|
||||
'arc4random_buf((void *)1, 1);'
|
||||
test_code 'arc4random_buf()' 'stdlib.h' '' '' '
|
||||
char c;
|
||||
arc4random_buf(&c, 1);'
|
||||
then
|
||||
add_def HAVE_ARC4RANDOM
|
||||
else
|
||||
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
|
||||
'return getrandom((void *)1, 1, 0);'; then
|
||||
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' '
|
||||
char c;
|
||||
return getrandom(&c, 1, 0);'
|
||||
then
|
||||
add_def HAVE_GETRANDOM
|
||||
fi
|
||||
fi
|
||||
@@ -816,13 +803,12 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
|
||||
'seccomp_init(SCMP_ACT_KILL);'
|
||||
then
|
||||
add_def FEAT_SCFILTER
|
||||
if [ $feat_ntp = "1" ]; then
|
||||
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
|
||||
# used by the main thread as the helper process works on one request at
|
||||
# a time and the async resolver would block the main thread
|
||||
priv_ops="NAME2IPADDRESS RELOADDNS"
|
||||
fi
|
||||
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
|
||||
# used by the main thread as the helper process works on one request at
|
||||
# a time and the async resolver would block the main thread
|
||||
priv_ops="NAME2IPADDRESS RELOADDNS"
|
||||
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS sys_linux_scmp.o"
|
||||
fi
|
||||
|
||||
if [ "x$priv_ops" != "x" ]; then
|
||||
@@ -860,7 +846,6 @@ if [ $try_setsched = "1" ] && \
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
|
||||
then
|
||||
add_def HAVE_PTHREAD_SETSCHEDPARAM
|
||||
use_pthread=1
|
||||
fi
|
||||
|
||||
if [ $try_lockmem = "1" ] && \
|
||||
@@ -914,6 +899,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
|
||||
HASH_OBJ="hash_nettle.o"
|
||||
HASH_LINK="$test_link"
|
||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||
add_def HAVE_NETTLE
|
||||
add_def FEAT_SECHASH
|
||||
|
||||
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
|
||||
@@ -923,6 +909,13 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
|
||||
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o"
|
||||
fi
|
||||
|
||||
if test_code 'nettle_memeql_sec()' 'nettle/memops.h' \
|
||||
"$test_cflags" "$test_link" \
|
||||
'return nettle_memeql_sec("", "", 0);'
|
||||
then
|
||||
add_def HAVE_NETTLE_MEMEQL
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -936,6 +929,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
|
||||
HASH_OBJ="hash_gnutls.o"
|
||||
HASH_LINK="$test_link"
|
||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||
add_def HAVE_GNUTLS
|
||||
add_def FEAT_SECHASH
|
||||
|
||||
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
|
||||
@@ -977,7 +971,7 @@ EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
|
||||
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
|
||||
LIBS="$LIBS $HASH_LINK"
|
||||
|
||||
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
if [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
|
||||
test_cflags=""
|
||||
test_link=""
|
||||
@@ -997,7 +991,6 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||
add_def HAVE_SIV
|
||||
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,
|
||||
@@ -1025,18 +1018,11 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
then
|
||||
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
|
||||
fi
|
||||
else
|
||||
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
|
||||
'aes128_set_encrypt_key((void *)1, (void *)2);'
|
||||
then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||
add_def HAVE_SIV
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if grep '#define HAVE_SIV' config.h > /dev/null; then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o tls_gnutls.o"
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
|
||||
LIBS="$LIBS $test_link"
|
||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||
@@ -1045,10 +1031,6 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $use_pthread = "1" ]; then
|
||||
MYCFLAGS="$MYCFLAGS -pthread"
|
||||
fi
|
||||
|
||||
SYSCONFDIR=/etc
|
||||
if [ "x$SETSYSCONFDIR" != "x" ]; then
|
||||
SYSCONFDIR=$SETSYSCONFDIR
|
||||
@@ -1109,12 +1091,13 @@ add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
|
||||
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
|
||||
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
|
||||
add_def DEFAULT_USER "\"$default_user\""
|
||||
add_def DEFAULT_CHRONYC_USER "\"$default_chronyc_user\""
|
||||
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
|
||||
add_def MAIL_PROGRAM "\"$mail_program\""
|
||||
|
||||
common_features="`get_features SECHASH IPV6 DEBUG`"
|
||||
chronyc_features="`get_features READLINE`"
|
||||
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`"
|
||||
chronyd_features="`get_features CMDMON REFCLOCK RTC PRIVDROP SCFILTER SIGND NTS`"
|
||||
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
|
||||
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
|
||||
echo "Features : $chronyd_features $chronyc_features $common_features"
|
||||
@@ -1152,6 +1135,7 @@ do
|
||||
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
|
||||
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
|
||||
s%@DEFAULT_USER@%${default_user}%;\
|
||||
s%@DEFAULT_CHRONYC_USER@%${default_chronyc_user}%;\
|
||||
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
|
||||
< ${f}.in > $f
|
||||
done
|
||||
|
||||
@@ -17,6 +17,7 @@ CHRONYRUNDIR = @CHRONYRUNDIR@
|
||||
CHRONYVARDIR = @CHRONYVARDIR@
|
||||
CHRONY_VERSION = @CHRONY_VERSION@
|
||||
DEFAULT_USER = @DEFAULT_USER@
|
||||
DEFAULT_CHRONYC_USER = @DEFAULT_CHRONYC_USER@
|
||||
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
|
||||
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
|
||||
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
|
||||
@@ -29,6 +30,7 @@ SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
|
||||
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
|
||||
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
|
||||
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
|
||||
s%\@DEFAULT_CHRONYC_USER\@%$(DEFAULT_CHRONYC_USER)%g;\
|
||||
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
|
||||
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Stephen Wadeley 2016
|
||||
// Copyright (C) Bryan Christianson 2017
|
||||
// Copyright (C) Miroslav Lichvar 2009-2024
|
||||
// Copyright (C) Paul Donald 2025
|
||||
// Copyright (C) Miroslav Lichvar 2009-2025
|
||||
//
|
||||
// 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
|
||||
@@ -69,12 +70,21 @@ 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.
|
||||
response to the last 8 requests), unsynchronised, a falseticker (i.e. does not
|
||||
agree with a majority of other sources), the root distance is too large (the
|
||||
limit can be configured by the <<maxdistance,*maxdistance*>> directive), or
|
||||
the stratum is too small or too large (the limits can be configured by the
|
||||
<<minstratum,*minstratum*>> and <<maxstratum,*maxstratum*>> directives). The
|
||||
automatic replacement happens at most once per 30 minutes and only one
|
||||
server can be replaced at a time. The address is also refreshed periodically
|
||||
when the server is working normally (the interval can be configured by the
|
||||
<<refresh,*refresh*>> directive).
|
||||
+
|
||||
This directive can be used multiple times to specify multiple servers.
|
||||
This directive can be used multiple times to specify multiple servers. Each
|
||||
server must have a different IP address to be usable by *chronyd*. If a server
|
||||
is specified by a hostname that resolves only to addresses already used by
|
||||
other servers, the server will be treated as having an unknown address (e.g.
|
||||
reported by the *chronyc* <<chronyc.adoc#activity,*activity*>> command).
|
||||
+
|
||||
The directive supports the following options:
|
||||
+
|
||||
@@ -108,13 +118,13 @@ the current interval needs to be at least two times longer than the minimum
|
||||
interval in order to allow a burst with two requests.
|
||||
*key* _ID_:::
|
||||
The NTP protocol supports a message authentication code (MAC) to prevent
|
||||
computers having their system time upset by rogue packets being sent to them.
|
||||
computers from having their system time upset by rogue packets being sent to them.
|
||||
The MAC is generated as a function of a key specified in the key file,
|
||||
which is specified by the <<keyfile,*keyfile*>> directive.
|
||||
+
|
||||
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
|
||||
should *chronyd* use to authenticate requests sent to the server and verify its
|
||||
responses. The server must have the same key for this number configured,
|
||||
*chronyd* should use to authenticate requests sent to the server and verify its
|
||||
responses. The server must have the same key for this number configured;
|
||||
otherwise no relationship between the computers will be possible.
|
||||
+
|
||||
If the server is running *ntpd* and the output size of the hash function used
|
||||
@@ -128,7 +138,7 @@ the Transport Layer Security (TLS) protocol to get the keys and cookies
|
||||
required by NTS for authentication of NTP packets.
|
||||
+
|
||||
With this option, the hostname specified in the server or pool directive is the
|
||||
NTS-KE server or pool of NTS-KE servers respectively. The NTP server usually
|
||||
NTS-KE server or pool of NTS-KE servers, respectively. The NTP server usually
|
||||
runs on the same host, but it can be separated from the NTS-KE server (the
|
||||
hostname or address of the NTP server is provided to the client by the NTS-KE
|
||||
server).
|
||||
@@ -145,10 +155,10 @@ default trusted certificate authorities.
|
||||
*chronyd* uses the network round-trip delay to the server to determine how
|
||||
accurate a particular measurement is likely to be. Long round-trip delays
|
||||
indicate that the request, or the response, or both were delayed. If only one
|
||||
of the messages was delayed the measurement error is likely to be substantial.
|
||||
of the messages was delayed, the measurement error is likely to be substantial.
|
||||
+
|
||||
For small variations in the round-trip delay, *chronyd* uses a weighting scheme
|
||||
when processing the measurements. However, beyond a certain level of delay the
|
||||
when processing the measurements. Beyond a certain level of delay, however, the
|
||||
measurements are likely to be so corrupted as to be useless. (This is
|
||||
particularly so on wireless networks and other slow links, where a long delay
|
||||
probably indicates a highly asymmetric delay caused by the response waiting
|
||||
@@ -158,7 +168,7 @@ 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*
|
||||
option. For example, *maxdelay 0.3* would indicate that measurements with a
|
||||
round-trip delay greater than 0.3 seconds should be ignored. The default value
|
||||
is 3 seconds and the maximum value is 1000 seconds.
|
||||
is 3 seconds, and the maximum value is 1000 seconds.
|
||||
*maxdelayratio* _ratio_:::
|
||||
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
|
||||
@@ -198,7 +208,7 @@ The asymmetry can be between -0.5 and +0.5. A negative value means the delay of
|
||||
packets sent to the source is more variable than the delay of packets sent from
|
||||
the source back. By default, *chronyd* estimates the asymmetry automatically.
|
||||
*offset* _offset_:::
|
||||
This option specifies a correction (in seconds) which will be applied to
|
||||
This option specifies a correction (in seconds) that will be applied to
|
||||
offsets measured with this source. It's particularly useful to compensate for a
|
||||
known asymmetry in network delay or timestamping errors. For example, if
|
||||
packets sent to the source were on average delayed by 100 microseconds more
|
||||
@@ -210,6 +220,12 @@ Set the minimum number of samples kept for this source. This overrides the
|
||||
*maxsamples* _samples_:::
|
||||
Set the maximum number of samples kept for this source. This overrides the
|
||||
<<maxsamples,*maxsamples*>> directive.
|
||||
*maxunreach* _polls_:::
|
||||
This option specifies the maximum number of polls that this source can stay
|
||||
selected for synchronisation when it is unreachable (i.e. no valid response was
|
||||
received to the last 8 requests). Only sources with at least one sample more
|
||||
recent than the oldest sample of all reachable sources can be selected. The
|
||||
default is 100000.
|
||||
*filter* _polls_:::
|
||||
This option enables a median filter to reduce noise in NTP measurements. The
|
||||
filter will process samples collected in the specified number of polls
|
||||
@@ -244,11 +260,11 @@ authenticated source to be safely combined with unauthenticated sources in
|
||||
order to improve the accuracy of the clock. They can be selected and used for
|
||||
synchronisation only if they agree with the trusted and required source.
|
||||
*xleave*:::
|
||||
This option enables the interleaved mode of NTP. It enables the server to
|
||||
respond with more accurate transmit timestamps (e.g. kernel or hardware
|
||||
timestamps), which cannot be contained in the transmitted packet itself and
|
||||
need to refer to a previous packet instead. This can significantly improve the
|
||||
accuracy and stability of the measurements.
|
||||
This option enables the interleaved mode of NTP (RFC 9769). It enables the
|
||||
server to respond with more accurate transmit timestamps (e.g. kernel or
|
||||
hardware timestamps), which cannot be contained in the transmitted packet
|
||||
itself and need to refer to a previous packet instead. This can significantly
|
||||
improve the accuracy and stability of the measurements.
|
||||
+
|
||||
The interleaved mode is compatible with servers that support only the basic
|
||||
mode. Note that even
|
||||
@@ -292,7 +308,7 @@ included in a *server* directive:
|
||||
presend 9
|
||||
----
|
||||
+
|
||||
when the polling interval is 512 seconds or more, an extra NTP client packet
|
||||
When the polling interval is 512 seconds or more, an extra NTP client packet
|
||||
will be sent to the server a short time (2 seconds) before making the actual
|
||||
measurement.
|
||||
+
|
||||
@@ -303,15 +319,16 @@ When the synchronisation source is selected from available sources, sources
|
||||
with lower stratum are normally slightly preferred. This option can be used to
|
||||
increase stratum of the source to the specified minimum, so *chronyd* will
|
||||
avoid selecting that source. This is useful with low-stratum sources that are
|
||||
known to be unreliable or inaccurate and which should be used only when other
|
||||
sources are unreachable.
|
||||
known to be unreliable or inaccurate and that should be used only when other
|
||||
sources are unreachable. Note that the <<minstratum,*minstratum*>> directive
|
||||
is distinct from this option.
|
||||
*version* _version_:::
|
||||
This option sets the NTP version of packets sent to the server. This can be
|
||||
useful when the server runs an old NTP implementation that does not respond to
|
||||
requests using a newer version. The default version depends on other options.
|
||||
If the *extfield* or *xleave* option is used, the default version is 4. If
|
||||
those options are not used and the *key* option specifies a key using a hash
|
||||
function with output size longer than 160 bits (e.g. SHA256), the default
|
||||
function with an output size longer than 160 bits (e.g. SHA256), the default
|
||||
version is 3 for compatibility with older *chronyd* servers. In other cases,
|
||||
the default version is 4.
|
||||
*copy*:::
|
||||
@@ -374,7 +391,7 @@ directive too. There is one option specific to the *pool* directive:
|
||||
*maxsources* _sources_:::
|
||||
This option sets the desired number of sources to be used from the pool.
|
||||
*chronyd* will repeatedly try to resolve the name until it gets this number of
|
||||
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.
|
||||
+
|
||||
An example of the *pool* directive is
|
||||
@@ -398,7 +415,7 @@ The following options of the *server* directive do not work in the *peer*
|
||||
directive: *iburst*, *burst*, *nts*, *presend*, *copy*.
|
||||
+
|
||||
When using the *xleave* option, both peers must support and have enabled the
|
||||
interleaved mode, otherwise the synchronisation will work in one direction
|
||||
interleaved mode; otherwise the synchronisation will work in one direction
|
||||
only.
|
||||
When a key is specified by the *key* option to enable authentication, both
|
||||
peers must use the same key and the same key number.
|
||||
@@ -426,7 +443,7 @@ recommended to use two separate client/server associations (specified by the
|
||||
directive.)
|
||||
+
|
||||
The purpose of the *initstepslew* directive is to allow *chronyd* to make a
|
||||
rapid measurement of the system clock error at boot time, and to correct the
|
||||
rapid measurement of the system clock error at boot time and to correct the
|
||||
system clock by stepping before normal operation begins. Since this would
|
||||
normally be performed only at an appropriate point in the system boot sequence,
|
||||
no other software should be adversely affected by the step.
|
||||
@@ -455,7 +472,7 @@ The *initstepslew* directive can also be used in an isolated LAN environment,
|
||||
where the clocks are set manually. The most stable computer is chosen as the
|
||||
primary server and the other computers are its clients. If each of the clients
|
||||
is configured with the <<local,*local*>> directive, the server can be set up
|
||||
with an *initstepslew* directive which references some or all of the clients.
|
||||
with an *initstepslew* directive that references some or all of the clients.
|
||||
Then, if the server machine has to be rebooted, the clients can be relied on to
|
||||
act analogously to a flywheel and preserve the time for a short period while
|
||||
the server completes its reboot.
|
||||
@@ -478,7 +495,7 @@ the driver-specific parameter using the *:* character.
|
||||
+
|
||||
This directive can be used multiple times to specify multiple reference clocks.
|
||||
+
|
||||
There are four drivers included in *chronyd*:
|
||||
There are five drivers included in *chronyd*:
|
||||
+
|
||||
*PPS*:::
|
||||
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
|
||||
@@ -508,7 +525,7 @@ 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
|
||||
An application that 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
|
||||
@@ -557,8 +574,9 @@ refclock SHM 1:perm=0644 refid GPS2
|
||||
----
|
||||
+
|
||||
*PHC*:::
|
||||
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
|
||||
PTP hardware clock (PHC) driver. The parameter is the absolute path to the
|
||||
device of the PTP clock which should be used as a time source, or the name of
|
||||
the network interface whose PTP clock should be used. If the clock is kept in
|
||||
TAI instead of UTC (e.g. it is synchronised by a PTP daemon), the current
|
||||
UTC-TAI offset needs to be specified by the *offset* option. Alternatively, the
|
||||
*pps* refclock option can be enabled to treat the PHC as a PPS refclock, using
|
||||
@@ -596,6 +614,23 @@ refclock PHC /dev/ptp1:nocrossts poll 3 pps
|
||||
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
|
||||
----
|
||||
+
|
||||
*RTC*:::
|
||||
Driver for using the Real Time Clock (RTC) as a reference clock. The parameter
|
||||
is the path to the RTC character device which should be used as a time source.
|
||||
This driver cannot be used together with the <<rtcfile,*rtcfile*>> or
|
||||
<<rtcsync,*rtcsync*>> directive. The driver supports the following option:
|
||||
+
|
||||
*utc*::::
|
||||
Assume that RTC keeps Universal Coordinated Time (UTC) instead of local
|
||||
time.
|
||||
{blank}:::
|
||||
+
|
||||
Examples:
|
||||
+
|
||||
----
|
||||
refclock RTC /dev/rtc0:utc
|
||||
----
|
||||
+
|
||||
{blank}::
|
||||
The *refclock* directive supports the following options:
|
||||
+
|
||||
@@ -622,18 +657,18 @@ specified by its reference ID. In this mode received PPS samples are paired
|
||||
directly with raw samples from the specified refclock.
|
||||
*rate* _rate_:::
|
||||
This option sets the rate of the pulses in the PPS signal (in Hz). This option
|
||||
controls how the pulses will be completed with real time. To actually receive
|
||||
controls how the pulses will be completed with real time. In order to receive
|
||||
more than one pulse per second, a negative *dpoll* has to be specified (-3 for
|
||||
a 5Hz signal). The default is 1.
|
||||
*maxlockage* _pulses_:::
|
||||
This option specifies in number of pulses how old can be samples from the
|
||||
This option specifies in number of pulses how old samples can be from the
|
||||
refclock specified by the *lock* option to be paired with the pulses.
|
||||
Increasing this value is useful when the samples are produced at a lower rate
|
||||
than the pulses. The default is 2.
|
||||
*width* _width_:::
|
||||
This option specifies the width of the pulses (in seconds). It is used to
|
||||
filter PPS samples when the driver provides samples for both rising and falling
|
||||
edges. Note that it reduces the maximum allowed error of the time source which
|
||||
edges. Note that it reduces the maximum allowed error of the time source that
|
||||
completes the PPS samples. If the duty cycle is configurable, 50% should be
|
||||
preferred in order to maximise the allowed error.
|
||||
*pps*:::
|
||||
@@ -647,7 +682,7 @@ offset (in seconds) is applied to all samples produced by the reference clock.
|
||||
The default is 0.0.
|
||||
*delay* _delay_:::
|
||||
This option sets the NTP delay of the source (in seconds). Half of this value
|
||||
is included in the maximum assumed error which is used in the source selection
|
||||
is included in the maximum assumed error, which is used in the source selection
|
||||
algorithm. Increasing the delay is useful to avoid having no majority in the
|
||||
source selection or to make it prefer other sources. The default is 1e-9 (1
|
||||
nanosecond).
|
||||
@@ -661,14 +696,13 @@ value is the estimated precision of the system clock.
|
||||
Maximum allowed dispersion for filtered samples (in seconds). Samples with
|
||||
larger estimated dispersion are ignored. By default, this limit is disabled.
|
||||
*filter* _samples_:::
|
||||
This option sets the length of the median filter which is used to reduce the
|
||||
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
|
||||
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
|
||||
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_).
|
||||
This option sets the maximum number of samples that can be stored in the median
|
||||
filter between polls of the source. The filter combines about 60 percent of the
|
||||
samples closest to the median offset into one sample. If more samples are
|
||||
received by the driver between polls, the oldest samples will be dropped. One
|
||||
sample per poll is sufficient for the source to be selectable. The default
|
||||
is 64. Note that the PHC driver has additional filtering based on the reading
|
||||
delay.
|
||||
*prefer*:::
|
||||
Prefer this source over other selectable sources without the *prefer* option.
|
||||
*noselect*:::
|
||||
@@ -706,6 +740,12 @@ Set the minimum number of samples kept for this source. This overrides the
|
||||
*maxsamples* _samples_:::
|
||||
Set the maximum number of samples kept for this source. This overrides the
|
||||
<<maxsamples,*maxsamples*>> directive.
|
||||
*maxunreach* _polls_:::
|
||||
This option specifies the maximum number of polls that this source can stay
|
||||
selected for synchronisation when it is unreachable (i.e. no valid sample was
|
||||
received in the last 8 polls). Only sources with at least one sample more
|
||||
recent than the oldest sample of all reachable sources can be selected.
|
||||
The default is 100000.
|
||||
|
||||
[[manual]]*manual*::
|
||||
The *manual* directive enables support at run-time for the
|
||||
@@ -846,7 +886,7 @@ The following IDs are supported:
|
||||
* 30: AES-128-GCM-SIV
|
||||
{blank}::
|
||||
+
|
||||
The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
|
||||
The default list of IDs is _30 15_. AES-128-GCM-SIV is preferred over
|
||||
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
|
||||
reliability of NTS in networks that block or limit rate of longer NTP messages.
|
||||
+
|
||||
@@ -889,7 +929,7 @@ refresh the keys on each poll and no NTP packets will be exchanged.
|
||||
|
||||
[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
|
||||
This directive specifies a file or directory containing trusted certificates
|
||||
(in the PEM format) which are needed to verify certificates of NTS-KE servers,
|
||||
(in the PEM format) that are needed to verify certificates of NTS-KE servers,
|
||||
e.g. certificates of trusted certificate authorities (CA) or self-signed
|
||||
certificates of the servers.
|
||||
+
|
||||
@@ -924,7 +964,7 @@ specified by the *ntstrustedcerts* directive will be trusted.
|
||||
[[nocerttimecheck]]*nocerttimecheck* _limit_::
|
||||
This directive disables the checks of the activation and expiration times of
|
||||
certificates for the specified number of clock updates. It allows the NTS
|
||||
authentication mechanism to be used on computers which start with wrong time
|
||||
authentication mechanism to be used on computers which start with the wrong time
|
||||
(e.g. due to not having an RTC or backup battery). Disabling the time checks
|
||||
has important security implications and should be used only as a last resort,
|
||||
preferably with a minimal number of trusted certificates. The default value is
|
||||
@@ -938,17 +978,18 @@ nocerttimecheck 1
|
||||
+
|
||||
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
|
||||
with correct time.
|
||||
with the 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
|
||||
This directive specifies the minimum 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.
|
||||
Only one source is refreshed at a time and only when a valid response is
|
||||
received (unreachable sources are replaced independently). 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.
|
||||
@@ -961,11 +1002,11 @@ authentication to limit the impact of man-in-the-middle attacks. The
|
||||
attackers can drop or delay NTP packets (up to the *maxdelay* and
|
||||
<<maxdistance,*maxdistance*>> limits), but they cannot modify the timestamps
|
||||
contained in the packets. The attack can cause only a limited slew or step, and
|
||||
also cause the clock to run faster or slower than real time (up to double of
|
||||
also cause the clock to run faster or slower than real time (up to double
|
||||
the <<maxdrift,*maxdrift*>> limit).
|
||||
+
|
||||
When authentication is enabled for an NTP source, it is important to disable
|
||||
unauthenticated NTP sources which could be exploited in the attack, e.g. if
|
||||
unauthenticated NTP sources that could be exploited in the attack, e.g. if
|
||||
they are not reachable only over a trusted network. Alternatively, the source
|
||||
selection can be configured with the *require* and *trust* options to
|
||||
synchronise to the unauthenticated sources only if they agree with the
|
||||
@@ -1041,7 +1082,7 @@ If the selected source was specified with the *prefer* option, it can be
|
||||
combined only with other sources specified with this option.
|
||||
+
|
||||
By default, the limit is 3. Setting the limit to 0 effectively disables the
|
||||
source combining algorithm and only the selected source will be used to control
|
||||
source-combining algorithm and only the selected source will be used to control
|
||||
the system clock.
|
||||
|
||||
[[maxdistance]]*maxdistance* _distance_::
|
||||
@@ -1064,20 +1105,30 @@ with sources that have a small root distance, but their time is too variable.
|
||||
+
|
||||
By default, the maximum jitter is 1 second.
|
||||
|
||||
[[maxstratum]]*maxstratum* _stratum_::
|
||||
The *maxstratum* directive sets the maximum allowed stratum of the sources to
|
||||
be selected for synchronisation. The default value is 15. The useful range is 0
|
||||
to 15.
|
||||
|
||||
[[minstratum]]*minstratum* _stratum_::
|
||||
The *minstratum* directive sets the minimum allowed stratum of the sources to
|
||||
be selected for synchronisation. The default value is 0. The useful range is 0
|
||||
to 15.
|
||||
|
||||
[[minsources]]*minsources* _sources_::
|
||||
The *minsources* directive sets the minimum number of sources that need to be
|
||||
considered as selectable in the source selection algorithm before the local
|
||||
clock is updated. The default value is 1.
|
||||
+
|
||||
Setting this option to a larger number can be used to improve the reliability.
|
||||
More sources will have to agree with each other and the clock will not be
|
||||
More sources will have to agree with each other, and the clock will not be
|
||||
updated when only one source (which could be serving incorrect time) is
|
||||
reachable.
|
||||
|
||||
[[reselectdist]]*reselectdist* _distance_::
|
||||
When *chronyd* selects a synchronisation source from available sources, it
|
||||
will prefer the one with the shortest synchronisation distance. However, to
|
||||
avoid frequent reselecting when there are sources with similar distance, a
|
||||
will prefer the one with the shortest synchronisation distance. To avoid
|
||||
frequent reselecting when there are sources with similar distance, however, a
|
||||
fixed distance is added to the distance for sources that are currently not
|
||||
selected. This can be set with the *reselectdist* directive. By default, the
|
||||
distance is 100 microseconds.
|
||||
@@ -1095,23 +1146,29 @@ distances are in milliseconds.
|
||||
|
||||
[[clockprecision]]*clockprecision* _precision_::
|
||||
The *clockprecision* directive specifies the precision of the system clock (in
|
||||
seconds). It is used by *chronyd* to estimate the minimum noise in NTP
|
||||
measurements and randomise low-order bits of timestamps in NTP responses. By
|
||||
default, the precision is measured on start as the minimum time to read the
|
||||
clock.
|
||||
seconds). This value is used by *chronyd* as the minimum expected error and
|
||||
amount of noise in NTP and refclock measurements, and to randomise low-order
|
||||
bits of timestamps in NTP responses to make them less predictable. The minimum
|
||||
value is 1 nanosecond and the maximum value is 1 second.
|
||||
+
|
||||
The measured value works well in most cases. However, it generally
|
||||
overestimates the precision and it can be sensitive to the CPU speed, which can
|
||||
change over time to save power. In some cases with a high-precision clocksource
|
||||
(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the
|
||||
precision on the server to a smaller value can improve stability of clients'
|
||||
NTP measurements. The server's precision is reported on clients by the
|
||||
By default, *chronyd* tries to determine the precision on start-up as the
|
||||
resolution of the clock. On Linux, it tries to measure the resolution by
|
||||
observing the minimum change in differences between consecutive readings of the
|
||||
clock. On other systems it relies on the *clock_getres(2)* system function.
|
||||
+
|
||||
If the measurement fails, or the value provided by the system is too large, the
|
||||
minimum measured time needed to read the clock will be used instead. This value
|
||||
is typically larger than the resolution, and it is sensitive to the CPU speed,
|
||||
however, which can change over time to save power.
|
||||
+
|
||||
The server's precision is reported on clients by the
|
||||
<<chronyc.adoc#ntpdata,*ntpdata*>> command.
|
||||
+
|
||||
An example setting the precision to 8 nanoseconds is:
|
||||
An example setting the precision to 1 nanosecond (e.g. when the system clock is
|
||||
using a Time Stamp Counter (TSC) updated at a rate of at least 1 GHz) is:
|
||||
+
|
||||
----
|
||||
clockprecision 8e-9
|
||||
clockprecision 1e-9
|
||||
----
|
||||
|
||||
[[corrtimeratio]]*corrtimeratio* _ratio_::
|
||||
@@ -1124,7 +1181,7 @@ The *corrtimeratio* directive sets the ratio between the duration in which the
|
||||
clock is slewed for an average correction according to the source history and
|
||||
the interval in which the corrections are done (usually the NTP polling
|
||||
interval). Corrections larger than the average take less time and smaller
|
||||
corrections take more time, the amount of the correction and the correction
|
||||
corrections take more time; the amount of the correction and the correction
|
||||
time are inversely proportional.
|
||||
+
|
||||
Increasing *corrtimeratio* improves the overall frequency error of the system
|
||||
@@ -1137,7 +1194,7 @@ The maximum allowed slew rate can be set by the <<maxslewrate,*maxslewrate*>>
|
||||
directive. The current remaining correction is shown in the
|
||||
<<chronyc.adoc#tracking,*tracking*>> report as the *System time* value.
|
||||
|
||||
[[driftfile]]*driftfile* _file_::
|
||||
[[driftfile]]*driftfile* _file_ [*interval* _interval_]::
|
||||
One of the main activities of the *chronyd* program is to work out the rate at
|
||||
which the system clock gains or loses time relative to real time.
|
||||
+
|
||||
@@ -1156,6 +1213,10 @@ microseconds in reality (so the true time has only advanced by 999900
|
||||
microseconds). The second is an estimate of the error bound around the first
|
||||
value in which the true rate actually lies.
|
||||
+
|
||||
The *interval* option specifies the minimum interval between updates of the
|
||||
file in seconds. The file is written only on an update of the local clock.
|
||||
The default interval is 3600 seconds.
|
||||
+
|
||||
An example of the driftfile directive is:
|
||||
+
|
||||
----
|
||||
@@ -1179,7 +1240,7 @@ fallbackdrift 16 19
|
||||
+
|
||||
In this example, the minimum interval is 16 (18 hours) and the maximum interval is
|
||||
19 (6 days). The system clock frequency will be set to the first fallback 18
|
||||
hours after last clock update, to the second after 36 hours, and so on. This
|
||||
hours after the last clock update, to the second after 36 hours, and so on. This
|
||||
might be a good setting to cover frequency changes due to daily and weekly
|
||||
temperature fluctuations. When the frequency is set to a fallback, the state of
|
||||
the clock will change to '`Not synchronised`'.
|
||||
@@ -1238,7 +1299,7 @@ When smearing a leap second, the leap status is suppressed on the server and
|
||||
the served time is corrected slowly by slewing instead of stepping. The clients
|
||||
do not need any special configuration as they do not know there is any leap
|
||||
second and they follow the server time which eventually brings them back to
|
||||
UTC. Care must be taken to ensure they use only NTP servers which smear the
|
||||
UTC. Care must be taken to ensure they use only NTP servers that smear the
|
||||
leap second in exactly the same way for synchronisation.
|
||||
+
|
||||
This feature must be used carefully, because the server is intentionally not
|
||||
@@ -1271,15 +1332,15 @@ duration = sqrt(4 / wander)
|
||||
|
||||
[[leapsectz]]*leapsectz* _timezone_::
|
||||
This directive specifies a timezone in the system timezone database which
|
||||
*chronyd* can use to determine when will the next leap second occur and what is
|
||||
the current offset between TAI and UTC. It will periodically check if 23:59:59
|
||||
*chronyd* can use to determine when the next leap second occurs and what
|
||||
the current offset between TAI and UTC is. It will periodically check if 23:59:59
|
||||
and 23:59:60 are valid times in the timezone. This normally works with the
|
||||
_right/UTC_ timezone.
|
||||
+
|
||||
When a leap second is announced, the timezone needs to be updated at least 12
|
||||
hours before the leap second. It is not necessary to restart *chronyd*.
|
||||
+
|
||||
This directive is useful with reference clocks and other time sources which do
|
||||
This directive is useful with reference clocks and other time sources that do
|
||||
not announce leap seconds, or announce them too late for an NTP server to
|
||||
forward them to its own clients. Clients of leap smearing servers must not
|
||||
use this directive.
|
||||
@@ -1321,7 +1382,7 @@ leapseclist /usr/share/zoneinfo/leap-seconds.list
|
||||
----
|
||||
|
||||
[[makestep]]*makestep* _threshold_ _limit_::
|
||||
Normally *chronyd* will cause the system to gradually correct any time offset,
|
||||
Normally *chronyd* will cause the system to gradually correct any time offset
|
||||
by slowing down or speeding up the clock as required. In certain situations,
|
||||
e.g. when *chronyd* is initially started, the system clock might be so far
|
||||
adrift that this slewing process would take a very long time to correct the
|
||||
@@ -1343,6 +1404,9 @@ makestep 0.1 3
|
||||
+
|
||||
This would step the system clock if the adjustment is larger than 0.1 seconds, but
|
||||
only in the first three clock updates.
|
||||
+
|
||||
Note that this directive does not work and should not be used when the system
|
||||
clock control is disabled by the *-x* option of *chronyd*.
|
||||
|
||||
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
|
||||
This directive sets the maximum offset to be accepted on a clock update. The
|
||||
@@ -1550,13 +1614,13 @@ allow all 1.2.0.0/16
|
||||
+
|
||||
In the first example, the effect is the same regardless of what order the three
|
||||
directives are given in. So the _1.2.0.0/16_ subnet is allowed access, except
|
||||
for the _1.2.3.0/24_ subnet, which is denied access, however the host _1.2.3.4_
|
||||
for the _1.2.3.0/24_ subnet, which is denied access, while the host _1.2.3.4_
|
||||
is allowed access.
|
||||
+
|
||||
In the second example, the *allow all 1.2.0.0/16* directive overrides the
|
||||
effect of _any_ previous directive relating to a subnet within the specified
|
||||
subnet. Within a configuration file this capability is probably rather moot;
|
||||
however, it is of greater use for reconfiguration at run-time via *chronyc*
|
||||
yet, it is of greater use for reconfiguration at run-time via *chronyc*
|
||||
with the <<chronyc.adoc#allow,*allow all*>> command.
|
||||
+
|
||||
The rules are internally represented as a tree of tables with one level per
|
||||
@@ -1583,7 +1647,7 @@ This is similar to the <<allow,*allow*>> directive, except that it denies NTP
|
||||
and NTS-KE client access to a particular subnet or host, rather than allowing
|
||||
it.
|
||||
+
|
||||
The syntax is identical and the directive can be used multiple times too.
|
||||
The syntax is identical, and the directive can be used multiple times too.
|
||||
+
|
||||
There is also a *deny all* directive with similar behaviour to the *allow all*
|
||||
directive.
|
||||
@@ -1601,7 +1665,7 @@ bindaddress 192.168.1.1
|
||||
----
|
||||
+
|
||||
Currently, for each of the IPv4 and IPv6 protocols, only one *bindaddress*
|
||||
directive can be specified. Therefore, it is not useful on computers which
|
||||
directive can be specified. Therefore, it is not useful on computers that
|
||||
should serve NTP on multiple network interfaces.
|
||||
|
||||
[[binddevice]]*binddevice* _interface_::
|
||||
@@ -1695,16 +1759,19 @@ expected stratum in the network when external NTP servers are accessible.
|
||||
+
|
||||
Stratum 1 indicates a computer that has a true real-time reference directly
|
||||
connected to it (e.g. GPS, atomic clock, etc.), such computers are expected to
|
||||
be very close to real time. Stratum 2 computers are those which have a stratum
|
||||
be very close to real time. Stratum 2 computers are those that have a stratum
|
||||
1 server; stratum 3 computers have a stratum 2 server and so on. A value
|
||||
of 10 indicates that the clock is so many hops away from a reference clock that
|
||||
its time is fairly unreliable.
|
||||
*distance* _distance_:::
|
||||
This option sets the threshold for the root distance which will activate the local
|
||||
reference. If *chronyd* was synchronised to some source, the local reference
|
||||
will not be activated until its root distance reaches the specified value (the
|
||||
rate at which the distance is increasing depends on how well the clock was
|
||||
tracking the source). The default value is 1 second.
|
||||
reference. If *chronyd* was synchronised to a configured time source, the local
|
||||
reference will not be activated until its root distance reaches the specified
|
||||
value (the rate at which the distance is increasing depends on how well the
|
||||
clock was tracking the source). When the clock is not synchronised, it is
|
||||
considered to have an infinite root distance, i.e. the local reference
|
||||
activates as soon as allowed by the *waitunsynced* option. The default
|
||||
threshold is 1 second.
|
||||
+
|
||||
The current root distance can be calculated from root delay and root dispersion
|
||||
(reported by the <<chronyc.adoc#tracking,*tracking*>> command in *chronyc*) as:
|
||||
@@ -1727,7 +1794,7 @@ unless no other source is selectable and their reference IDs are smaller than
|
||||
the local reference ID.
|
||||
+
|
||||
This allows multiple servers in the network to use the same *local*
|
||||
configuration and to be synchronised to one another, without confusing clients
|
||||
configuration and to be synchronised to one another without confusing clients
|
||||
that poll more than one server. Each server needs to be configured to poll all
|
||||
other servers with the *local* directive. This ensures only the server with the
|
||||
smallest reference ID has the local reference active and others are
|
||||
@@ -1737,12 +1804,30 @@ smallest reference ID will take over when its local reference mode activates
|
||||
+
|
||||
The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
|
||||
*tos orphan* command).
|
||||
*waitsynced* _interval_:::
|
||||
This option specifies the minimum interval (in seconds) between the last update
|
||||
of the clock and activation of the local reference as configured by the
|
||||
*distance* and *activate* options. The *distance* option can be set to 0 to
|
||||
ignore the root distance and control the activation only by the interval. In
|
||||
such case it should be at least as long as the maximum expected polling
|
||||
interval to prevent frequent activation in normal polling of the source.
|
||||
The default minimum interval is 0.
|
||||
*waitunsynced* _interval_:::
|
||||
This option specifies how long (in seconds) *chronyd* needs to wait before
|
||||
activating the local reference when the clock is not considered to be
|
||||
synchronised (e.g. after start or the source selection failing due to no
|
||||
majority). This delay prevents *chronyd* from serving incorrect time to clients
|
||||
before the configured time sources are given a chance to synchronise the local
|
||||
clock. The default interval is 300 seconds if the *orphan* option is set,
|
||||
otherwise it is 0 (i.e. local reference activates immediately).
|
||||
{blank}::
|
||||
+
|
||||
An example of the directive is:
|
||||
Examples of the directive are:
|
||||
+
|
||||
----
|
||||
local stratum 5
|
||||
local stratum 10 orphan distance 0.1 activate 0.5
|
||||
local stratum 10 orphan distance 0.0 waitsynced 7200 waitunsynced 300
|
||||
----
|
||||
|
||||
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
|
||||
@@ -1791,7 +1876,7 @@ the user under which *chronyd* is running after dropping root privileges. For
|
||||
security reasons, it should not be readable by other users.
|
||||
+
|
||||
This directive can be used multiple times to specify multiple keys. The number
|
||||
of keys must be the same as the number of certificates and the corresponding
|
||||
of keys must be the same as the number of certificates, and the corresponding
|
||||
files must be specified in the same order.
|
||||
|
||||
[[ntsprocesses]]*ntsprocesses* _processes_::
|
||||
@@ -1799,7 +1884,7 @@ This directive specifies how many helper processes will *chronyd* operating
|
||||
as an NTS server start for handling client NTS-KE requests in order to improve
|
||||
performance with multi-core CPUs and multithreading. If set to 0, no helper
|
||||
process will be started and all NTS-KE requests will be handled by the main
|
||||
*chronyd* process. The default value is 1.
|
||||
*chronyd* process. The default value is 1, and the maximum value is 1000.
|
||||
|
||||
[[maxntsconnections]]*maxntsconnections* _connections_::
|
||||
This directive specifies the maximum number of concurrent NTS-KE connections
|
||||
@@ -1820,7 +1905,7 @@ The following IDs are supported:
|
||||
* 30: AES-128-GCM-SIV
|
||||
{blank}::
|
||||
+
|
||||
The default list of IDs is _30 15_. AES-128-GCM-SIV is prefered over
|
||||
The default list of IDs is _30 15_. AES-128-GCM-SIV is preferred over
|
||||
AES-SIV-CMAC-256 for shorter keys, which makes NTS cookies shorter and improves
|
||||
reliability of NTS in networks that block or limit rate of longer NTP messages.
|
||||
+
|
||||
@@ -1833,7 +1918,7 @@ ntsaeads 15
|
||||
This list is used also by the <<ntsaeads1,NTS client>>.
|
||||
+
|
||||
Note the the NTS specification (RFC 8915) requires servers to support
|
||||
AES-SIV-CMAC-256, i.e. 15 should be always included in the specified list.
|
||||
AES-SIV-CMAC-256, i.e. 15 should always be included in the specified list.
|
||||
+
|
||||
The AES-128-GCM-SIV keys used by *chronyd* do not comply to RFC 8915 for
|
||||
compatibility with older *chrony* clients unless the use of compliant keys is
|
||||
@@ -1841,7 +1926,7 @@ negotiated with an
|
||||
https://chrony-project.org/doc/spec/nts-compliant-128gcm.html[NTS-KE record].
|
||||
Support for this record was added in version 4.6.1. As a client, *chronyd* can
|
||||
interoperate with a server that uses compliant keys, but does not support the
|
||||
negotiation, if it responds to incorrectly authenticated requests with an NTS
|
||||
negotiation if it responds to incorrectly authenticated requests with an NTS
|
||||
NAK.
|
||||
|
||||
[[ntsdumpdir2]]*ntsdumpdir* _directory_::
|
||||
@@ -1861,9 +1946,9 @@ This directory is used also by the <<ntsdumpdir1,NTS client>> to save NTS cookie
|
||||
|
||||
[[ntsntpserver]]*ntsntpserver* _hostname_::
|
||||
This directive specifies the hostname (as a fully qualified domain name) or
|
||||
address of the NTP server(s) which is
|
||||
address of the NTP server(s) that is
|
||||
provided in the NTS-KE response to the clients. It allows the NTS-KE server to
|
||||
be separated from the NTP server. However, the servers need to share the keys,
|
||||
be separated from the NTP server. The servers need to share the keys, however,
|
||||
i.e. external key management needs to be enabled by setting
|
||||
<<ntsrotate,*ntsrotate*>> to 0. By default, no hostname or address is provided
|
||||
to the clients, which means they should use the same server for NTS-KE and NTP.
|
||||
@@ -1915,7 +2000,7 @@ addresses. If multiple clients share one IP address (e.g. multiple hosts behind
|
||||
NAT), the sum of their traffic will be limited. If a client that increases its
|
||||
polling rate when it does not receive a reply is detected, its rate limiting
|
||||
will be temporarily suspended to avoid increasing the overall amount of
|
||||
traffic. The maximum number of IP addresses which can be monitored at the same
|
||||
traffic. The maximum number of IP addresses that can be monitored at the same
|
||||
time depends on the memory limit set by the <<clientloglimit,*clientloglimit*>>
|
||||
directive.
|
||||
+
|
||||
@@ -1933,7 +2018,7 @@ than the specified interval).
|
||||
This option sets the maximum number of responses that can be sent in a burst,
|
||||
temporarily exceeding the limit specified by the *interval* option. This is
|
||||
useful for clients that make rapid measurements on start (e.g. *chronyd* with
|
||||
the *iburst* option). The default value is 8. The minimum value is 1 and the
|
||||
the *iburst* option). The default value is 8. The minimum value is 1, and the
|
||||
maximum value is 255.
|
||||
*leak* _rate_:::
|
||||
This option sets the rate at which responses are randomly allowed even if the
|
||||
@@ -1941,7 +2026,7 @@ limits specified by the *interval* and *burst* options are exceeded. This is
|
||||
necessary to prevent an attacker who is sending requests with a spoofed
|
||||
source address from completely blocking responses to that address. The leak
|
||||
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
|
||||
least every fourth request has a response. The minimum value is 1 and the
|
||||
least every fourth request has a response. The minimum value is 1, and the
|
||||
maximum value is 4.
|
||||
*kod* _rate_:::
|
||||
This option sets the rate at which Kiss-o'-Death (KoD) RATE responses are
|
||||
@@ -1949,7 +2034,7 @@ randomly sent when the limits specified by the *interval* and *burst* options
|
||||
are exceeded. It is an additional stream of responses to the *leak* option. A
|
||||
KoD RATE response is a request for the client to reduce its polling rate. Few
|
||||
implementations actually support it. The rate is defined as a power of 1/2. The
|
||||
default value is 0, which means disabled. The minimum value is 0 and the
|
||||
default value is 0, which means disabled. The minimum value is 0, and the
|
||||
maximum value is 4.
|
||||
{blank}::
|
||||
+
|
||||
@@ -1978,13 +2063,13 @@ ntsratelimit interval 3 burst 1
|
||||
The *smoothtime* directive can be used to enable smoothing of the time that
|
||||
*chronyd* serves to its clients to make it easier for them to track it and keep
|
||||
their clocks close together even when large offset or frequency corrections are
|
||||
applied to the server's clock, for example after being offline for a longer
|
||||
applied to the server's clock, for example, after being offline for a longer
|
||||
time.
|
||||
+
|
||||
BE WARNED: The server is intentionally not serving its best estimate of the
|
||||
true time. If a large offset has been accumulated, it can take a very long time
|
||||
to smooth it out. This directive should be used only when the clients are not
|
||||
configured to also poll another NTP server, because they could reject this
|
||||
configured to poll another NTP server also, because they could reject this
|
||||
server as a falseticker or fail to select a source completely.
|
||||
+
|
||||
The smoothing process is implemented with a quadratic spline function with two
|
||||
@@ -1998,7 +2083,7 @@ reset*>> command.
|
||||
The first two arguments of the directive are the maximum frequency offset of
|
||||
the smoothed time to the tracked NTP time (in ppm) and the maximum rate at
|
||||
which the frequency offset is allowed to change (in ppm per second). *leaponly*
|
||||
is an optional third argument which enables a mode where only leap seconds are
|
||||
is an optional third argument that enables a mode where only leap seconds are
|
||||
smoothed out and normal offset and frequency changes are ignored. The *leaponly*
|
||||
option is useful in a combination with the <<leapsecmode,*leapsecmode slew*>>
|
||||
directive to allow the clients to use multiple time smoothing servers safely.
|
||||
@@ -2082,6 +2167,9 @@ The syntax is identical to the *allow* directive.
|
||||
There is also a *cmdallow all* directive with similar behaviour to the *allow
|
||||
all* directive (but applying to monitoring access in this case, of course).
|
||||
+
|
||||
Addresses _127.0.0.1_ and _::1_ (i.e. the loopback interface) are always
|
||||
allowed.
|
||||
+
|
||||
Note that *chronyd* has to be configured with the
|
||||
<<bindcmdaddress,*bindcmdaddress*>> directive to not listen only on the
|
||||
loopback interface to actually allow remote access.
|
||||
@@ -2123,6 +2211,27 @@ An example of the use of the directive is:
|
||||
cmdratelimit interval 2
|
||||
----
|
||||
|
||||
[[opencommands]]*opencommands* [_command_]...::
|
||||
This directive specifies a list of monitoring commands to be enabled for the
|
||||
hosts allowed by the *cmdallow* directive. The following commands can be
|
||||
specified (the naming follows *chronyc*):
|
||||
+
|
||||
*activity*+*+, *authdata*, *clients*, *manual*+*+, *ntpdata*, *rtcdata*+*+,
|
||||
*selectdata*, *serverstats*+*+, *smoothing*+*+, *sourcename*+*+, *sources*+*+,
|
||||
*sourcestats*, *tracking*+*+.
|
||||
+
|
||||
The commands marked with +*+ are enabled by default. The protocol of these
|
||||
commands is considered stable and can be expected to work between different
|
||||
versions of *chronyc* and *chronyd*. The protocol of the other commands is not
|
||||
considered stable and different versions of *chronyc* and *chronyd* may not
|
||||
interoperate. When that happens, *chronyc* will print an '`Invalid command`' or
|
||||
'`Bad reply from daemon`' error.
|
||||
+
|
||||
Note that some of the reported data can be potentially useful to attackers,
|
||||
enabling them to observe and predict better the internal state of *chronyd*.
|
||||
It is recommended to enable only commands that are actually needed for
|
||||
monitoring and limit the access to the hosts that need it.
|
||||
|
||||
=== Real-time clock (RTC)
|
||||
|
||||
[[hwclockfile]]*hwclockfile* _file_::
|
||||
@@ -2188,7 +2297,7 @@ option to *chronyd*) if the following three conditions apply:
|
||||
|
||||
[[rtconutc]]*rtconutc*::
|
||||
*chronyd* assumes by default that the RTC keeps local time (including any
|
||||
daylight saving changes). This is convenient on PCs running Linux which are
|
||||
daylight saving changes). This is convenient on PCs running Linux that are
|
||||
dual-booted with Windows.
|
||||
+
|
||||
If you keep the RTC on local time and your computer is off when daylight saving
|
||||
@@ -2203,7 +2312,8 @@ The directive takes no arguments. It is equivalent to specifying the *-u*
|
||||
switch to the Linux *hwclock* program.
|
||||
+
|
||||
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
|
||||
and is not relevant for the <<rtcsync,*rtcsync*>> directive.
|
||||
and is not relevant for the <<rtcsync,*rtcsync*>> directive or when the RTC
|
||||
is used as reference clock.
|
||||
|
||||
[[rtcsync]]*rtcsync*::
|
||||
The *rtcsync* directive enables a mode where the system time is periodically
|
||||
@@ -2276,7 +2386,7 @@ from the example line above):
|
||||
+
|
||||
*measurements*:::
|
||||
This option is identical to the *rawmeasurements* option, except it logs only
|
||||
valid measurements from synchronised sources, i.e. measurements which passed
|
||||
valid measurements from synchronised sources, i.e. measurements that passed
|
||||
the RFC 5905 tests 1 through 7. This can be useful for producing graphs of the
|
||||
source's performance.
|
||||
+
|
||||
@@ -2317,7 +2427,7 @@ from the example line above):
|
||||
used to prune old samples when it no longer looks like the measurements fit a
|
||||
linear model). [0, i.e. no samples discarded this time]
|
||||
. The number of runs. The number of runs of regression residuals with the same
|
||||
sign is computed. If this is too small it indicates that the measurements are
|
||||
sign is computed. If this is too small, it indicates that the measurements are
|
||||
no longer represented well by a linear model and that some older samples need
|
||||
to be discarded. The number of runs for the data that is being retained is
|
||||
tabulated. Values of approximately half the number of samples are expected.
|
||||
@@ -2353,6 +2463,9 @@ Not considered selectable for synchronisation:
|
||||
* _N_ - has the *noselect* option.
|
||||
* _s_ - is not synchronised.
|
||||
* _M_ - does not have enough measurements.
|
||||
* _r_ - has a stratum outside of the allowed range (configured by the
|
||||
<<minstratum,*minstratum*>> and <<maxstratum,*maxstratum*>>
|
||||
directives).
|
||||
* _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
|
||||
@@ -2536,9 +2649,9 @@ log measurements statistics tracking
|
||||
A banner is periodically written to the log files enabled by the <<log,*log*>>
|
||||
directive to indicate the meanings of the columns.
|
||||
+
|
||||
The *logbanner* directive specifies after how many entries in the log file
|
||||
should be the banner written. The default is 32, and 0 can be used to disable
|
||||
it entirely.
|
||||
The *logbanner* directive specifies the cadence size of how many entries are
|
||||
written to the log file after which the banner is written anew. The default is
|
||||
32, and 0 can be used to disable it entirely.
|
||||
|
||||
[[logchange]]*logchange* _threshold_::
|
||||
This directive sets the threshold for the adjustment of the system clock that
|
||||
@@ -2628,7 +2741,7 @@ sourcedir /var/run/chrony-dhcp
|
||||
[[include]]*include* _pattern_::
|
||||
The *include* directive includes a configuration file, or multiple configuration
|
||||
files if a wildcard pattern is specified. Unlike with the *confdir* directive,
|
||||
the full name of the files needs to be specified and at least one file is
|
||||
the full name of the files needs to be specified, and at least one file is
|
||||
required to exist.
|
||||
+
|
||||
This directive can be used multiple times.
|
||||
@@ -2714,8 +2827,8 @@ timestamp and the actual reception time at the physical layer. This value will
|
||||
be subtracted from receive timestamps obtained from the NIC. The default value
|
||||
is 0.
|
||||
*nocrossts*:::
|
||||
Some hardware can precisely cross timestamp the NIC clock with the system
|
||||
clock. This option disables the use of the cross timestamping.
|
||||
Some hardware can precisely cross-timestamp the NIC clock with the system
|
||||
clock. This option disables the use of the cross-timestamping.
|
||||
*rxfilter* _filter_:::
|
||||
This option selects the receive timestamping filter. The _filter_ can be one of
|
||||
the following:
|
||||
@@ -2755,7 +2868,7 @@ 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
|
||||
configures how long *chronyd* should 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.
|
||||
@@ -2770,7 +2883,7 @@ Note that the maximum timeout is limited by the NTP polling interval.
|
||||
|
||||
[[keyfile]]*keyfile* _file_::
|
||||
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
|
||||
authenticate NTP packets with a message authentication code (MAC) using a
|
||||
cryptographic hash function or cipher.
|
||||
+
|
||||
@@ -2797,7 +2910,7 @@ Each line consists of an ID, optional type, and key.
|
||||
+
|
||||
The ID can be any positive integer in the range 1 through 2^32-1.
|
||||
+
|
||||
The type is a name of a cryptographic hash function or cipher which is used to
|
||||
The type is a name of a cryptographic hash function or cipher that is used to
|
||||
generate and verify the MAC. The default type is *MD5*, which is always
|
||||
supported.
|
||||
If *chronyd* was built with enabled support for hashing using a crypto library
|
||||
@@ -2856,7 +2969,7 @@ Setting this directive to _/_ disables writing and checking of the PID file.
|
||||
[[ptpport]]*ptpport* _port_::
|
||||
The *ptpport* directive enables *chronyd* to send and receive NTP messages
|
||||
contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping
|
||||
on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP
|
||||
on NICs that cannot timestamp NTP packets, but can timestamp unicast PTP
|
||||
packets, and also use corrections provided by PTP one-step end-to-end
|
||||
transparent clocks in network switches and routers. The port recognized by the
|
||||
NICs and PTP transparent clocks is 319 (PTP event port). The default value is 0
|
||||
@@ -2869,7 +2982,7 @@ The PTP port will be open even if *chronyd* is not configured to operate as a
|
||||
server or client. The directive does not change the default protocol of
|
||||
specified NTP sources. Each NTP source that should use NTP-over-PTP needs to
|
||||
be specified with the *port* option set to the PTP port. To actually enable
|
||||
hardware timestamping on NICs which can timestamp PTP packets only, the
|
||||
hardware timestamping on NICs that can timestamp PTP packets only, the
|
||||
*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_. The
|
||||
extension field _F324_ needs to be enabled to use the corrections provided by
|
||||
the PTP transparent clocks.
|
||||
@@ -2926,7 +3039,7 @@ The compiled-in default value is _@DEFAULT_USER@_.
|
||||
=== NTP client with permanent connection to NTP servers
|
||||
|
||||
This section shows how to configure *chronyd* for computers that are connected
|
||||
to the Internet (or to any network containing true NTP servers which ultimately
|
||||
to the Internet (or to any network containing true NTP servers that ultimately
|
||||
derive their time from a reference clock) permanently or most of the time.
|
||||
|
||||
To operate in this mode, you will need to know the names of the NTP servers
|
||||
@@ -2952,7 +3065,7 @@ server ntp2.example.net
|
||||
server ntp3.example.net
|
||||
----
|
||||
|
||||
However, you will probably want to include some of the other directives. The
|
||||
You will probably want to include some of the other directives, however. The
|
||||
<<driftfile,*driftfile*>>, <<makestep,*makestep*>> and <<rtcsync,*rtcsync*>>
|
||||
might be particularly useful. Also, the *iburst* option of the
|
||||
<<server,*server*>> directive is useful to speed up the initial
|
||||
@@ -3041,7 +3154,7 @@ actually connected to the Internet.
|
||||
=== Isolated networks
|
||||
|
||||
This section shows how to configure *chronyd* for computers that never have
|
||||
network connectivity to any computer which ultimately derives its time from a
|
||||
network connectivity to any computer that ultimately derives its time from a
|
||||
reference clock.
|
||||
|
||||
In this situation, one computer is selected to be the primary timeserver. The
|
||||
@@ -3056,7 +3169,7 @@ in the form of the <<manual,*manual*>> directive and the
|
||||
<<chronyc.adoc#settime,*settime*>> command in the *chronyc* program.
|
||||
|
||||
If the server is rebooted, *chronyd* can re-read the drift rate from the drift
|
||||
file. However, the server has no accurate estimate of the current time. To get
|
||||
file. The server has no accurate estimate of the current time, however. To get
|
||||
around this, the system can be configured so that the server can initially set
|
||||
itself to a '`majority-vote`' of selected clients' times; this allows the
|
||||
clients to '`flywheel`' the server while it is rebooting.
|
||||
@@ -3124,7 +3237,7 @@ clients might be configured to poll all three servers.
|
||||
|
||||
=== RTC tracking
|
||||
|
||||
This section considers a computer which has occasional connections to the
|
||||
This section considers a computer that has occasional connections to the
|
||||
Internet and is turned off between '`sessions`'. In this case, *chronyd* relies
|
||||
on the computer's RTC to maintain the time between the periods when it is
|
||||
powered up. It assumes that Linux is run exclusively on the computer. Dual-boot
|
||||
@@ -3135,7 +3248,7 @@ enable the *HPET_EMULATE_RTC* option in your kernel configuration. Otherwise,
|
||||
using it.
|
||||
|
||||
When the computer is connected to the Internet, *chronyd* has access to
|
||||
external NTP servers which it makes measurements from. These measurements are
|
||||
external NTP servers that it makes measurements from. These measurements are
|
||||
saved, and straight-line fits are performed on them to provide an estimate of
|
||||
the computer's time error and rate of gaining or losing time.
|
||||
|
||||
@@ -3178,7 +3291,7 @@ take *chronyd* offline; because *chronyd* free-runs between online sessions, no
|
||||
parameters will change significantly between going offline from the Internet
|
||||
and any power failure.
|
||||
|
||||
A final point regards computers which are left running for extended periods and
|
||||
A final point regards computers that are left running for extended periods and
|
||||
where it is desired to spin down the hard disc when it is not in use (e.g. when
|
||||
not accessed for 15 minutes). *chronyd* has been planned so it supports such
|
||||
operation; this is the reason why the RTC tracking parameters are not saved to
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Stephen Wadeley 2016
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2024
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2025
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -50,7 +50,7 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
|
||||
::1.
|
||||
|
||||
Only the following monitoring commands, which do not affect the behaviour of
|
||||
*chronyd*, are allowed from the network: *activity*, *manual list*,
|
||||
*chronyd*, are allowed from the network by default: *activity*, *manual list*,
|
||||
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
|
||||
*waitsync*. The
|
||||
set of hosts from which *chronyd* will accept these commands can be configured
|
||||
@@ -58,7 +58,10 @@ with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
|
||||
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
|
||||
default, the commands are accepted only from localhost (127.0.0.1 or ::1).
|
||||
|
||||
All other commands are allowed only through the Unix domain socket. When sent
|
||||
Other monitoring commands can be enabled for network access by the
|
||||
<<chrony.conf.adoc#opencommands,*opencommands*>> directive. Monitoring commands
|
||||
with disabled network access and commands that affect the behaviour of
|
||||
*chronyd* are allowed only through the Unix domain socket. If they are sent
|
||||
over the network, *chronyd* will respond with a '`Not authorised`' error, even
|
||||
if it is from localhost.
|
||||
|
||||
@@ -118,6 +121,13 @@ This option allows the user to specify the UDP port number which the target
|
||||
*chronyd* is using for its monitoring connections. This defaults to 323; there
|
||||
would rarely be a need to change this.
|
||||
|
||||
*-u* _user_::
|
||||
This option sets the name of the user to which *chronyc* will switch when it is
|
||||
started under the root user in order to drop its privileges. *chronyc* will not
|
||||
try to change its identity if the option is set to _root_, or the effective
|
||||
user ID of the started process is not 0. The compiled-in default value is
|
||||
_@DEFAULT_CHRONYC_USER@_.
|
||||
|
||||
*-f* _file_::
|
||||
This option is ignored and is provided only for compatibility.
|
||||
|
||||
@@ -364,9 +374,12 @@ a measurement is being made every 64 seconds. *chronyd* automatically varies
|
||||
the polling rate in response to prevailing conditions.
|
||||
*Reach*:::
|
||||
This shows the source's 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.
|
||||
register has 8 bits. It is shifted to left by one bit with each poll and it is
|
||||
updated by 1 when a valid NTP response, or just a sample in case of a reference
|
||||
clock, is received from the source. A value of 377 indicates that a valid
|
||||
response or sample was received for all of the last 8 polls. Note that samples
|
||||
can be dropped if they are not considered good enough for synchronisation, but
|
||||
the reachability register will still have 1s for their polls.
|
||||
*LastRx*:::
|
||||
This column shows how long ago the last good sample (which is shown in the next
|
||||
column) was received from the source. Measurements that failed some tests are
|
||||
@@ -461,12 +474,16 @@ synchronisation:
|
||||
* _N_ - has the *noselect* option.
|
||||
* _M_ - does not have enough measurements.
|
||||
* _s_ - is not synchronised.
|
||||
* _r_ - has a stratum outside of the allowed range (configured by the
|
||||
<<chrony.conf.adoc#minstratum,*minstratum*>> and
|
||||
<<chrony.conf.adoc#maxstratum,*maxstratum*>> directives).
|
||||
* _d_ - has a root distance larger than the maximum distance (configured by the
|
||||
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
|
||||
* _~_ - has a jitter larger than the maximum jitter (configured by the
|
||||
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
|
||||
* _w_ - waits for other sources to get out of the _M_ state.
|
||||
* _S_ - has older measurements than other sources.
|
||||
* _S_ - has only measurements older than reachable sources, or is unreachable
|
||||
for too many polls (configured by the *maxunreach* option).
|
||||
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
|
||||
the <<chrony.conf.adoc#local,*local*>> directive).
|
||||
* _T_ - does not fully agree with sources that have the *trust* option.
|
||||
@@ -1461,6 +1478,13 @@ purged. An example of how to do this is shown below.
|
||||
# chronyc cyclelogs
|
||||
# rm /var/log/chrony/measurements1.log
|
||||
----
|
||||
+
|
||||
Note that log files enabled by the *log* directive are opened when the first
|
||||
entry is made. The message log file specified by the *chronyd*'s *-l* option is
|
||||
opened early on start and closed on the *cyclelogs* command only if opening of
|
||||
the new file succeeds to avoid losing messages. If *chronyd* is configured to
|
||||
drop root privileges and the directory containing the log file is not writable
|
||||
for the specified user, *chronyd* will not be able to open the file again.
|
||||
|
||||
[[dump]]*dump*::
|
||||
The *dump* command causes *chronyd* to write its current history of
|
||||
|
||||
@@ -217,6 +217,14 @@ client requests until the service is able to handle them. The service manager
|
||||
sets the LISTEN_FDS environment variable to the number of passed file
|
||||
descriptors.
|
||||
|
||||
*NOTIFY_SOCKET*::
|
||||
The systemd service manager sets this variable for services of the *notify*
|
||||
type. *chronyd* sends a message to this socket when it it is fully initialised
|
||||
and ready to accept commands (e.g. from *chronyc*), with the clock already set
|
||||
if using the *-s* option or *initstepslew* directive. It is an alternative to
|
||||
the *forking* service type, which does not need the PID file. *chronyd* needs
|
||||
to be started with the *-n* or *-d* option to not fork.
|
||||
|
||||
== FILES
|
||||
|
||||
_@SYSCONFDIR@/chrony.conf_
|
||||
|
||||
19
doc/faq.adoc
19
doc/faq.adoc
@@ -165,6 +165,13 @@ versions or implementations of the libraries might make different system calls.
|
||||
If the filter is missing some system call, `chronyd` could be killed even in
|
||||
normal operation.
|
||||
|
||||
The impact of potential security issues in `chronyc` can be reduced by running
|
||||
`chronyc` under the _chrony_ user instead of root, or another unprivileged user
|
||||
if access to the Unix domain socket is not needed. Since version 4.8, `chronyc`
|
||||
drops root privileges automatically if it is started with the `-u` option
|
||||
specifying the _chrony_ user, or the name was specified to be the compiled-in
|
||||
default by the `--with-chronyc-user` option of the configure script.
|
||||
|
||||
=== How can I make the system clock more secure?
|
||||
|
||||
An NTP client synchronising the system clock to an NTP server is susceptible to
|
||||
@@ -338,10 +345,10 @@ with local NTP server
|
||||
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
|
||||
----
|
||||
|
||||
If your server supports the interleaved mode (e.g. it is running `chronyd`),
|
||||
the `xleave` option should be added to the `server` directive to enable the
|
||||
server to provide the client with more accurate transmit timestamps (kernel or
|
||||
preferably hardware). For example:
|
||||
If your server supports the interleaved mode (RFC 9769), e.g. it is running
|
||||
`chronyd` version 3.0 or later, the `xleave` option should be added to the
|
||||
`server` directive to enable the server to provide the client with more
|
||||
accurate transmit timestamps (kernel or preferably hardware). For example:
|
||||
|
||||
----
|
||||
server ntp.local minpoll 2 maxpoll 4 xleave
|
||||
@@ -897,7 +904,9 @@ measurements from both sources.
|
||||
|
||||
If the first source was significantly better than the second source, it can
|
||||
take many hours before the second source is selected, depending on its polling
|
||||
interval. You can force a faster reselection by increasing the clock error rate
|
||||
interval. You can force a faster reselection by reducing the maximum number of
|
||||
polls the source can still be selected when unreachable (`maxunreach` option
|
||||
supported since `chrony` version 4.8), increasing the clock error rate
|
||||
(`maxclockerror` directive), shortening the polling interval (`maxpoll`
|
||||
option), or reducing the number of samples (`maxsamples` option).
|
||||
|
||||
|
||||
@@ -85,13 +85,6 @@ will be built with support for dropping root privileges. On other systems no
|
||||
extra library is needed. The default user which `chronyd` should run as can be
|
||||
specified with the `--with-user` option of the `configure` script.
|
||||
|
||||
If development files for the POSIX threads library are available, `chronyd`
|
||||
will be built with support for asynchronous resolving of hostnames specified in
|
||||
the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
|
||||
a server to respond to client requests when resolving a hostname. If you don't
|
||||
want to enable the support, specify the `--disable-asyncdns` flag to
|
||||
`configure`.
|
||||
|
||||
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
|
||||
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
|
||||
https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Use public NTP servers from the pool.ntp.org project.
|
||||
# Use four public NTP servers from the pool.ntp.org project.
|
||||
pool pool.ntp.org iburst
|
||||
|
||||
# Record the rate at which the system clock gains/losses time.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Use public servers from the pool.ntp.org project.
|
||||
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
|
||||
# Note: The general recommendation for an NTP client is to have at least
|
||||
# three NTP servers to be able to detect one server providing incorrect
|
||||
# time (falseticker).
|
||||
|
||||
# Use four public NTP servers from the pool.ntp.org project. If this
|
||||
# host has a static public IP address, please consider joining the pool:
|
||||
# https://www.ntppool.org/join.html
|
||||
pool pool.ntp.org iburst
|
||||
|
||||
# Record the rate at which the system clock gains/losses time.
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
#######################################################################
|
||||
### SPECIFY YOUR NTP SERVERS
|
||||
# Most computers using chrony will send measurement requests to one or
|
||||
# more 'NTP servers'. You will probably find that your Internet Service
|
||||
# more NTP servers. The general recommendation is to have at least
|
||||
# three NTP servers to be able to detect one server providing incorrect
|
||||
# time (falseticker). You will probably find that your Internet Service
|
||||
# Provider or company have one or more NTP servers that you can specify.
|
||||
# Failing that, there are a lot of public NTP servers. There is a list
|
||||
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or
|
||||
# you can access at https://support.ntp.org/bin/view/Servers/WebHome or
|
||||
# you can use servers from the pool.ntp.org project.
|
||||
|
||||
! server ntp1.example.net iburst
|
||||
|
||||
@@ -12,10 +12,11 @@ Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
|
||||
ConditionCapability=CAP_SYS_TIME
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
Type=notify
|
||||
PIDFile=/run/chrony/chronyd.pid
|
||||
Environment="OPTIONS="
|
||||
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||
ExecStart=/usr/sbin/chronyd -U $OPTIONS
|
||||
ExecStart=/usr/sbin/chronyd -n -U $OPTIONS
|
||||
|
||||
User=chrony
|
||||
LogsDirectory=chrony
|
||||
|
||||
@@ -6,10 +6,11 @@ Conflicts=ntpd.service systemd-timesyncd.service
|
||||
ConditionCapability=CAP_SYS_TIME
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
Type=notify
|
||||
PIDFile=/run/chrony/chronyd.pid
|
||||
Environment="OPTIONS="
|
||||
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||
ExecStart=/usr/sbin/chronyd $OPTIONS
|
||||
ExecStart=/usr/sbin/chronyd -n $OPTIONS
|
||||
|
||||
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
|
||||
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
|
||||
|
||||
@@ -907,6 +907,7 @@ get_date (const char *p, const time_t *now)
|
||||
yyHour = tmp->tm_hour;
|
||||
yyMinutes = tmp->tm_min;
|
||||
yySeconds = tmp->tm_sec;
|
||||
memset(&tm, 0, sizeof (tm));
|
||||
tm.tm_isdst = tmp->tm_isdst;
|
||||
yyMeridian = MER24;
|
||||
yyRelSeconds = 0;
|
||||
@@ -943,7 +944,6 @@ get_date (const char *p, const time_t *now)
|
||||
tm.tm_hour += yyRelHour;
|
||||
tm.tm_min += yyRelMinutes;
|
||||
tm.tm_sec += yyRelSeconds;
|
||||
tm.tm_wday = 0;
|
||||
|
||||
/* Let mktime deduce tm_isdst if we have an absolute timestamp,
|
||||
or if the relative timestamp mentions days, months, or years. */
|
||||
|
||||
26
hwclock.c
26
hwclock.c
@@ -49,6 +49,7 @@
|
||||
#define DELAY_QUANT_MAX_K 2
|
||||
#define DELAY_QUANT_Q 10
|
||||
#define DELAY_QUANT_REPEAT 7
|
||||
#define DELAY_QUANT_LARGE_STEP_DELAY 1000
|
||||
#define DELAY_QUANT_MIN_STEP 1.0e-9
|
||||
|
||||
struct HCL_Instance_Record {
|
||||
@@ -127,6 +128,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation, doub
|
||||
clock->precision = precision;
|
||||
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
|
||||
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
|
||||
DELAY_QUANT_LARGE_STEP_DELAY,
|
||||
DELAY_QUANT_MIN_STEP);
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_slew, clock);
|
||||
@@ -161,7 +163,8 @@ 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)
|
||||
struct timespec *hw_ts, struct timespec *local_ts, double *err,
|
||||
int *quality)
|
||||
{
|
||||
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
|
||||
double delay_sum, hw_sum, local_sum, local_prec, freq;
|
||||
@@ -199,8 +202,10 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
|
||||
|
||||
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 = QNT_GetQuantile(clock->delay_quants, QNT_GetMinK(clock->delay_quants)) -
|
||||
QNT_GetMinStep(clock->delay_quants) / 2.0;
|
||||
high_delay = QNT_GetQuantile(clock->delay_quants, QNT_GetMaxK(clock->delay_quants)) +
|
||||
QNT_GetMinStep(clock->delay_quants) / 2.0;
|
||||
low_delay = MIN(low_delay, high_delay);
|
||||
high_delay = MAX(high_delay, low_delay + local_prec);
|
||||
|
||||
@@ -224,11 +229,13 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
|
||||
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);
|
||||
*quality = 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Accept the reading with minimum delay if its interval does not contain
|
||||
the current offset predicted from previous samples */
|
||||
/* Indicate acceptable quality of the reading with minimum delay if its
|
||||
interval does not contain the current offset predicted from previous
|
||||
samples, or a new sample is needed to get the tracking working */
|
||||
|
||||
*hw_ts = tss[min_reading][1];
|
||||
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
|
||||
@@ -238,11 +245,14 @@ HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3]
|
||||
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;
|
||||
*quality = 1;
|
||||
} else {
|
||||
*quality = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
DEBUG_LOG("Min-delay reading err=%e prerr=%e ql=%d", *err, pred_err, *quality);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
10
hwclock.h
10
hwclock.h
@@ -38,10 +38,14 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
|
||||
/* Check if a new sample should be accumulated at this time */
|
||||
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 */
|
||||
/* Process new readings of the HW clock in the form of (sys, hw, sys) triplets
|
||||
and produce a sample which can be accumulated by HCL_AccumulateSample().
|
||||
Indicate the quality of the sample relative to already processed samples as
|
||||
a value of 0, 1, or 2, where a sample of quality 0 should normally be
|
||||
dropped. */
|
||||
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||
struct timespec *hw_ts, struct timespec *local_ts, double *err);
|
||||
struct timespec *hw_ts, struct timespec *local_ts, double *err,
|
||||
int *quality);
|
||||
|
||||
/* Accumulate a new sample */
|
||||
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||
|
||||
5
keys.c
5
keys.c
@@ -265,9 +265,6 @@ KEY_Reload(void)
|
||||
if (get_key(i - 1)->id == get_key(i)->id)
|
||||
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
|
||||
}
|
||||
|
||||
/* Erase any passwords from stack */
|
||||
memset(line, 0, sizeof (line));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -405,7 +402,7 @@ check_auth(Key *key, const void *data, int data_len,
|
||||
|
||||
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
|
||||
|
||||
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
|
||||
return MIN(hash_len, trunc_len) == auth_len && UTI_IsMemoryEqual(buf, auth, auth_len);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
4
leapdb.c
4
leapdb.c
@@ -124,13 +124,13 @@ get_list_leap(time_t when, int *tai_offset)
|
||||
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
|
||||
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
|
||||
|
||||
while (fgets(line, sizeof line, f) > 0) {
|
||||
while (fgets(line, sizeof line, f)) {
|
||||
int64_t lsl_when;
|
||||
int lsl_tai_offset;
|
||||
char *p;
|
||||
|
||||
/* Ignore blank lines */
|
||||
for (p = line; *p && isspace(*p); ++p)
|
||||
for (p = line; *p && isspace((unsigned char)*p); ++p)
|
||||
;
|
||||
if (!*p)
|
||||
continue;
|
||||
|
||||
182
local.c
182
local.c
@@ -97,8 +97,142 @@ static double precision_quantum;
|
||||
|
||||
static double max_clock_error;
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* Ask the system for the resolution of the system clock. The Linux
|
||||
clock_getres() is not usable, because it reports the internal timer
|
||||
resolution, which is 1 ns when high-resolution timers are enabled,
|
||||
even when using a lower-resolution clocksource. */
|
||||
|
||||
static int
|
||||
get_clock_resolution(void)
|
||||
{
|
||||
#if defined(HAVE_CLOCK_GETTIME) && !defined(LINUX)
|
||||
struct timespec res;
|
||||
|
||||
if (clock_getres(CLOCK_REALTIME, &res) < 0)
|
||||
return 0;
|
||||
|
||||
return NSEC_PER_SEC * res.tv_sec + res.tv_nsec;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(LINUX) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC_RAW)
|
||||
|
||||
static int
|
||||
compare_ints(const void *a, const void *b)
|
||||
{
|
||||
return *(const int *)a - *(const int *)b;
|
||||
}
|
||||
|
||||
#define READINGS 64
|
||||
|
||||
/* On Linux, try to measure the actual resolution of the system
|
||||
clock by performing a varying amount of busy work between clock
|
||||
readings and finding the minimum change in the measured interval.
|
||||
Require a change of at least two nanoseconds to ignore errors
|
||||
caused by conversion to timespec. Use the raw monotonic clock
|
||||
to avoid the impact of potential frequency changes due to NTP
|
||||
adjustments made by other processes, and the kernel dithering of
|
||||
the 32-bit multiplier. */
|
||||
|
||||
static int
|
||||
measure_clock_resolution(void)
|
||||
{
|
||||
int i, j, b, busy, diffs[READINGS - 1], diff2, min;
|
||||
struct timespec start_ts, ts[READINGS];
|
||||
uint32_t acc;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &start_ts) < 0)
|
||||
return 0;
|
||||
|
||||
for (acc = 0, busy = 1; busy < 100000; busy = busy * 3 / 2 + 1) {
|
||||
for (i = 0, b = busy * READINGS; i < READINGS; i++, b -= busy) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts[i]) < 0)
|
||||
return 0;
|
||||
|
||||
for (j = b; j > 0; j--)
|
||||
acc += (acc & 1) + (uint32_t)ts[i].tv_nsec;
|
||||
}
|
||||
|
||||
/* Give up after 0.1 seconds */
|
||||
if (UTI_DiffTimespecsToDouble(&ts[READINGS - 1], &start_ts) > 0.1) {
|
||||
DEBUG_LOG("Measurement too slow");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < READINGS - 1; i++) {
|
||||
diffs[i] = NSEC_PER_SEC * (ts[i + 1].tv_sec - ts[i].tv_sec) +
|
||||
(ts[i + 1].tv_nsec - ts[i].tv_nsec);
|
||||
|
||||
/* Make sure the differences are sane. A resolution larger than the
|
||||
reading time will be measured in measure_clock_read_delay(). */
|
||||
if (diffs[i] <= 0 || diffs[i] > NSEC_PER_SEC)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sort the differences and keep values unique within 1 ns from the
|
||||
first half of the array, which are less likely to be impacted by CPU
|
||||
interruptions */
|
||||
qsort(diffs, READINGS - 1, sizeof (diffs[0]), compare_ints);
|
||||
for (i = 1, j = 0; i < READINGS / 2; i++) {
|
||||
if (diffs[j] + 1 < diffs[i])
|
||||
diffs[++j] = diffs[i];
|
||||
}
|
||||
j++;
|
||||
|
||||
#if 0
|
||||
for (i = 0; i < j; i++)
|
||||
DEBUG_LOG("busy %d diff %d %d", busy, i, diffs[i]);
|
||||
#endif
|
||||
|
||||
/* Require at least three unique differences to be more confident
|
||||
with the result */
|
||||
if (j < 3)
|
||||
continue;
|
||||
|
||||
/* Find the smallest difference between the unique differences */
|
||||
for (i = 1, min = 0; i < j; i++) {
|
||||
diff2 = diffs[i] - diffs[i - 1];
|
||||
if (min == 0 || min > diff2)
|
||||
min = diff2;
|
||||
}
|
||||
|
||||
if (min == 0)
|
||||
continue;
|
||||
|
||||
/* Prevent the compiler from optimising the busy work out */
|
||||
if (acc == 0)
|
||||
min += 1;
|
||||
|
||||
return min;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
static int
|
||||
measure_clock_resolution(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
/* As a fallback, measure how long it takes to read the clock. It
|
||||
typically takes longer than the resolution of the clock (and it
|
||||
depends on the CPU speed), i.e. every reading gives a different
|
||||
value, but handle also low-resolution clocks that might give
|
||||
the same reading multiple times. */
|
||||
|
||||
/* Define the number of increments of the system clock that we want
|
||||
to see to be fairly sure that we've got something approaching
|
||||
the minimum increment. Even on a crummy implementation that can't
|
||||
@@ -106,10 +240,8 @@ static double max_clock_error;
|
||||
under 1s of busy waiting. */
|
||||
#define NITERS 100
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
static double
|
||||
measure_clock_precision(void)
|
||||
static int
|
||||
measure_clock_read_delay(void)
|
||||
{
|
||||
struct timespec ts, old_ts;
|
||||
int iters, diff, best;
|
||||
@@ -135,7 +267,28 @@ measure_clock_precision(void)
|
||||
|
||||
assert(best > 0);
|
||||
|
||||
return 1.0e-9 * best;
|
||||
return best;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
measure_clock_precision(void)
|
||||
{
|
||||
int res, delay, prec;
|
||||
|
||||
res = get_clock_resolution();
|
||||
if (res <= 0)
|
||||
res = measure_clock_resolution();
|
||||
|
||||
delay = measure_clock_read_delay();
|
||||
|
||||
if (res > 0)
|
||||
prec = MIN(res, delay);
|
||||
else
|
||||
prec = delay;
|
||||
|
||||
return prec / 1.0e9;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -184,10 +337,8 @@ void
|
||||
LCL_Finalise(void)
|
||||
{
|
||||
/* Make sure all handlers have been removed */
|
||||
if (change_list.next != &change_list)
|
||||
assert(0);
|
||||
if (dispersion_notify_list.next != &dispersion_notify_list)
|
||||
assert(0);
|
||||
BRIEF_ASSERT(change_list.next == &change_list);
|
||||
BRIEF_ASSERT(dispersion_notify_list.next == &dispersion_notify_list);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -225,9 +376,7 @@ LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything
|
||||
|
||||
/* Check that the handler is not already registered */
|
||||
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
||||
if (!(ptr->handler != handler || ptr->anything != anything)) {
|
||||
assert(0);
|
||||
}
|
||||
BRIEF_ASSERT(ptr->handler != handler || ptr->anything != anything);
|
||||
}
|
||||
|
||||
new_entry = MallocNew(ChangeListEntry);
|
||||
@@ -301,9 +450,7 @@ LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anythi
|
||||
|
||||
/* Check that the handler is not already registered */
|
||||
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
|
||||
if (!(ptr->handler != handler || ptr->anything != anything)) {
|
||||
assert(0);
|
||||
}
|
||||
BRIEF_ASSERT(ptr->handler != handler || ptr->anything != anything);
|
||||
}
|
||||
|
||||
new_entry = MallocNew(DispersionNotifyListEntry);
|
||||
@@ -701,8 +848,11 @@ LCL_MakeStep(void)
|
||||
|
||||
/* Cancel remaining slew and make the step */
|
||||
LCL_AccumulateOffset(correction, 0.0);
|
||||
if (!LCL_ApplyStepOffset(-correction))
|
||||
if (!LCL_ApplyStepOffset(-correction)) {
|
||||
/* Revert the correction */
|
||||
LCL_AccumulateOffset(-correction, 0.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
|
||||
|
||||
|
||||
48
logging.c
48
logging.c
@@ -46,6 +46,7 @@ static LOG_Context log_contexts;
|
||||
/* Flag indicating we have initialised */
|
||||
static int initialised = 0;
|
||||
|
||||
static char *file_log_path = NULL;
|
||||
static FILE *file_log = NULL;
|
||||
static int system_log = 0;
|
||||
|
||||
@@ -90,8 +91,11 @@ LOG_Finalise(void)
|
||||
if (system_log)
|
||||
closelog();
|
||||
|
||||
if (file_log)
|
||||
if (file_log && file_log != stderr)
|
||||
fclose(file_log);
|
||||
file_log = NULL;
|
||||
Free(file_log_path);
|
||||
file_log_path = NULL;
|
||||
|
||||
LOG_CycleLogFiles();
|
||||
|
||||
@@ -200,27 +204,42 @@ void LOG_Message(LOG_Severity severity,
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LOG_OpenFileLog(const char *log_file)
|
||||
static FILE *
|
||||
open_file_log(const char *log_file, char mode)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
if (log_file) {
|
||||
f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
|
||||
f = UTI_OpenFile(NULL, log_file, NULL, mode, 0640);
|
||||
} else {
|
||||
f = stderr;
|
||||
}
|
||||
|
||||
/* Enable line buffering */
|
||||
setvbuf(f, NULL, _IOLBF, BUFSIZ);
|
||||
if (f)
|
||||
setvbuf(f, NULL, _IOLBF, BUFSIZ);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
LOG_OpenFileLog(const char *log_file)
|
||||
{
|
||||
if (file_log_path)
|
||||
Free(file_log_path);
|
||||
if (log_file)
|
||||
file_log_path = Strdup(log_file);
|
||||
else
|
||||
file_log_path = NULL;
|
||||
|
||||
if (file_log && file_log != stderr)
|
||||
fclose(file_log);
|
||||
|
||||
file_log = f;
|
||||
file_log = open_file_log(file_log_path, 'A');
|
||||
}
|
||||
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
@@ -385,14 +404,29 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
|
||||
void
|
||||
LOG_CycleLogFiles(void)
|
||||
{
|
||||
struct stat st;
|
||||
LOG_FileID i;
|
||||
FILE *f;
|
||||
|
||||
/* The log will be opened later when an entry is logged */
|
||||
for (i = 0; i < n_filelogs; i++) {
|
||||
if (logfiles[i].file)
|
||||
fclose(logfiles[i].file);
|
||||
logfiles[i].file = NULL;
|
||||
logfiles[i].writes = 0;
|
||||
}
|
||||
|
||||
/* Try to open the log specified by the -l option, but only if nothing is
|
||||
present at its path to avoid unnecessary error messages. Keep the
|
||||
original file if that fails (the process might no longer have the
|
||||
necessary privileges to write in the directory). */
|
||||
if (file_log && file_log != stderr && stat(file_log_path, &st) < 0 && errno == ENOENT) {
|
||||
f = open_file_log(file_log_path, 'a');
|
||||
if (f) {
|
||||
fclose(file_log);
|
||||
file_log = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
39
main.c
39
main.c
@@ -107,11 +107,40 @@ delete_pidfile(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
notify_system_manager(int start)
|
||||
{
|
||||
#ifdef LINUX
|
||||
/* The systemd protocol is documented in the sd_notify(3) man page */
|
||||
const char *message, *path = getenv("NOTIFY_SOCKET");
|
||||
int sock_fd;
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
if (path[0] != '/')
|
||||
LOG_FATAL("Unsupported notification socket");
|
||||
|
||||
message = start ? "READY=1" : "STOPPING=1";
|
||||
|
||||
sock_fd = SCK_OpenUnixDatagramSocket(path, NULL, 0);
|
||||
|
||||
if (sock_fd < 0 || SCK_Send(sock_fd, message, strlen(message), 0) != strlen(message))
|
||||
LOG_FATAL("Could not send notification to $NOTIFY_SOCKET");
|
||||
|
||||
SCK_CloseSocket(sock_fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
MAI_CleanupAndExit(void)
|
||||
{
|
||||
if (!initialised) exit(exit_status);
|
||||
|
||||
notify_system_manager(0);
|
||||
|
||||
LCL_CancelOffsetCorrection();
|
||||
SRC_DumpSources();
|
||||
|
||||
@@ -215,6 +244,8 @@ post_init_ntp_hook(void *anything)
|
||||
REF_SetMode(ref_mode);
|
||||
}
|
||||
|
||||
notify_system_manager(1);
|
||||
|
||||
/* Send an empty message to the foreground process so it can exit.
|
||||
If that fails, indicating the process was killed, exit too. */
|
||||
if (!LOG_NotifyParent(""))
|
||||
@@ -339,8 +370,11 @@ go_daemon(void)
|
||||
/* Don't exit before the 'parent' */
|
||||
waitpid(pid, NULL, 0);
|
||||
|
||||
close(pipefd[1]);
|
||||
r = read(pipefd[0], message, sizeof (message));
|
||||
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
|
||||
if (r != 1 || message[0] != '\0') {
|
||||
if (r > 1) {
|
||||
/* Print the error message from the child */
|
||||
@@ -351,8 +385,6 @@ go_daemon(void)
|
||||
} else
|
||||
exit(0);
|
||||
} else {
|
||||
close(pipefd[0]);
|
||||
|
||||
setsid();
|
||||
|
||||
/* Do 2nd fork, as-per recommended practice for launching daemons. */
|
||||
@@ -362,6 +394,7 @@ go_daemon(void)
|
||||
LOG_FATAL("fork() failed : %s", strerror(errno));
|
||||
} else if (pid > 0) {
|
||||
/* In the 'parent' */
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
exit(0);
|
||||
} else {
|
||||
|
||||
16
ntp_core.c
16
ntp_core.c
@@ -286,6 +286,7 @@ static ARR_Instance broadcasts;
|
||||
|
||||
/* Parameters for the peer delay quantile */
|
||||
#define DELAY_QUANT_Q 100
|
||||
#define DELAY_QUANT_LARGE_STEP_DELAY 100
|
||||
#define DELAY_QUANT_REPEAT 7
|
||||
|
||||
/* Minimum and maximum allowed poll interval */
|
||||
@@ -685,11 +686,13 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||
SRC_NTP, NAU_IsAuthEnabled(result->auth),
|
||||
params->sel_options, &result->remote_addr.ip_addr,
|
||||
params->min_samples, params->max_samples,
|
||||
params->min_delay, params->asymmetry);
|
||||
params->min_delay, params->asymmetry,
|
||||
params->max_unreach);
|
||||
|
||||
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,
|
||||
DELAY_QUANT_LARGE_STEP_DELAY,
|
||||
LCL_GetSysPrecisionAsQuantum() / 2.0);
|
||||
} else {
|
||||
result->delay_quant = NULL;
|
||||
@@ -1742,7 +1745,7 @@ check_delay_quant(NCR_Instance inst, double delay)
|
||||
|
||||
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
|
||||
|
||||
if (delay <= quant)
|
||||
if (delay <= quant + QNT_GetMinStep(inst->delay_quant) / 2.0)
|
||||
return 1;
|
||||
|
||||
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
|
||||
@@ -1958,7 +1961,7 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
|
||||
/* The skew and estimated frequency offset relative to the remote source */
|
||||
double skew, source_freq_lo, source_freq_hi;
|
||||
|
||||
/* RFC 5905 packet tests */
|
||||
/* RFC 5905 and RFC 9769 packet tests */
|
||||
int test1, test2n, test2i, test2, test3, test5, test6, test7;
|
||||
int interleaved_packet, valid_packet, synced_packet;
|
||||
|
||||
@@ -2022,7 +2025,7 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
|
||||
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
|
||||
}
|
||||
|
||||
/* Check if the packet is valid per RFC 5905, section 8.
|
||||
/* Check if the packet is valid per RFC 5905 (section 8) and RFC 9769.
|
||||
The test values are 1 when passed and 0 when failed. */
|
||||
|
||||
/* Test 1 checks for duplicate packet */
|
||||
@@ -2335,9 +2338,8 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
|
||||
inst->valid_rx = 1;
|
||||
}
|
||||
|
||||
if ((unsigned int)local_receive.source >= sizeof (tss_chars) ||
|
||||
(unsigned int)local_transmit.source >= sizeof (tss_chars))
|
||||
assert(0);
|
||||
BRIEF_ASSERT((unsigned int)local_receive.source < sizeof (tss_chars) &&
|
||||
(unsigned int)local_transmit.source < sizeof (tss_chars));
|
||||
|
||||
DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%.9f root_disp=%.9f refid=%"PRIx32" [%s]",
|
||||
message->lvm, message->stratum, message->poll, message->precision,
|
||||
|
||||
@@ -220,7 +220,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
||||
|
||||
SCK_CloseSocket(sock_fd);
|
||||
|
||||
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
|
||||
phc_fd = SYS_Linux_OpenPHC(req.ifr_name, O_RDONLY);
|
||||
if (phc_fd < 0)
|
||||
return 0;
|
||||
|
||||
@@ -519,7 +519,7 @@ 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;
|
||||
int n_readings, quality;
|
||||
|
||||
if (!HCL_NeedsNewSample(iface->clock, now))
|
||||
return;
|
||||
@@ -543,7 +543,8 @@ poll_phc(struct Interface *iface, struct timespec *now)
|
||||
return;
|
||||
|
||||
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
|
||||
&sample_phc_ts, &sample_sys_ts, &phc_err))
|
||||
&sample_phc_ts, &sample_sys_ts, &phc_err, &quality) ||
|
||||
quality <= 0)
|
||||
return;
|
||||
|
||||
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
||||
|
||||
@@ -457,9 +457,8 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
|
||||
if (replacement)
|
||||
record->resolved_addr = new_addr->ip_addr;
|
||||
|
||||
if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
|
||||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
|
||||
assert(0);
|
||||
BRIEF_ASSERT(record->remote_addr == NCR_GetRemoteAddress(record->data) &&
|
||||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) == 0);
|
||||
|
||||
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
|
||||
if (auto_start_sources)
|
||||
|
||||
@@ -520,8 +520,7 @@ generate_key(int index)
|
||||
ServerKey *key;
|
||||
int key_length;
|
||||
|
||||
if (index < 0 || index >= MAX_SERVER_KEYS)
|
||||
assert(0);
|
||||
BRIEF_ASSERT(index >= 0 && index < MAX_SERVER_KEYS);
|
||||
|
||||
/* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
|
||||
from ntsdumpdir use a different algorithm, responding to NTP requests
|
||||
@@ -534,11 +533,11 @@ generate_key(int index)
|
||||
key = &server_keys[index];
|
||||
|
||||
key_length = SIV_GetKeyLength(algorithm);
|
||||
if (key_length > sizeof (key->key))
|
||||
assert(0);
|
||||
BRIEF_ASSERT(key_length <= sizeof (key->key));
|
||||
|
||||
UTI_GetRandomBytesUrandom(key->key, key_length);
|
||||
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
|
||||
if (key_length < sizeof (key->key))
|
||||
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
|
||||
UTI_GetRandomBytes(&key->id, sizeof (key->id));
|
||||
|
||||
/* Encode the index in the lowest bits of the ID */
|
||||
@@ -702,7 +701,7 @@ key_timeout(void *arg)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
run_helper(uid_t uid, gid_t gid, int scfilter_level)
|
||||
run_helper(uid_t uid, gid_t gid, int scfilter_level, int sock_fd)
|
||||
{
|
||||
LOG_Severity log_severity;
|
||||
|
||||
@@ -729,10 +728,15 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
|
||||
if (scfilter_level != 0)
|
||||
SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
|
||||
|
||||
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, handle_helper_request, NULL);
|
||||
|
||||
SCH_MainLoop();
|
||||
|
||||
DEBUG_LOG("Helper exiting");
|
||||
|
||||
SCH_RemoveFileHandler(sock_fd);
|
||||
close(sock_fd);
|
||||
|
||||
NKS_Finalise();
|
||||
SCK_Finalise();
|
||||
SYS_Finalise();
|
||||
@@ -792,9 +796,8 @@ NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
|
||||
LOG_CloseParentFd();
|
||||
|
||||
SCK_CloseSocket(sock_fd1);
|
||||
SCH_AddFileHandler(sock_fd2, SCH_FILE_INPUT, handle_helper_request, NULL);
|
||||
|
||||
run_helper(uid, gid, scfilter_level);
|
||||
run_helper(uid, gid, scfilter_level, sock_fd2);
|
||||
}
|
||||
|
||||
SCK_CloseSocket(sock_fd2);
|
||||
@@ -957,8 +960,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
||||
header->key_id = htonl(key->id);
|
||||
|
||||
nonce = cookie->cookie + sizeof (*header);
|
||||
if (key->nonce_length > sizeof (cookie->cookie) - sizeof (*header))
|
||||
assert(0);
|
||||
BRIEF_ASSERT(key->nonce_length <= sizeof (cookie->cookie) - sizeof (*header));
|
||||
UTI_GetRandomBytes(nonce, key->nonce_length);
|
||||
|
||||
plaintext_length = context->c2s.length + context->s2c.length;
|
||||
|
||||
320
nts_ke_session.c
320
nts_ke_session.c
@@ -3,6 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2020-2021
|
||||
* Copyright (C) Anthony Brandon 2025
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -37,11 +38,9 @@
|
||||
#include "siv.h"
|
||||
#include "socket.h"
|
||||
#include "sched.h"
|
||||
#include "tls.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
|
||||
#define INVALID_SOCK_FD (-8)
|
||||
|
||||
struct RecordHeader {
|
||||
@@ -75,7 +74,7 @@ struct NKSN_Instance_Record {
|
||||
KeState state;
|
||||
int sock_fd;
|
||||
char *label;
|
||||
gnutls_session_t tls_session;
|
||||
TLS_Instance tls_session;
|
||||
SCH_TimeoutID timeout_id;
|
||||
int retry_factor;
|
||||
|
||||
@@ -85,8 +84,6 @@ struct NKSN_Instance_Record {
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static gnutls_priority_t priority_cache;
|
||||
|
||||
static int credentials_counter = 0;
|
||||
|
||||
static int clock_updates = 0;
|
||||
@@ -206,71 +203,6 @@ check_message_format(struct Message *message, int eof)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static gnutls_session_t
|
||||
create_tls_session(int server_mode, int sock_fd, const char *server_name,
|
||||
gnutls_certificate_credentials_t credentials,
|
||||
gnutls_priority_t priority)
|
||||
{
|
||||
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
|
||||
gnutls_session_t session;
|
||||
gnutls_datum_t alpn;
|
||||
unsigned int flags;
|
||||
int r;
|
||||
|
||||
r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
|
||||
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
|
||||
if (r < 0) {
|
||||
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!server_mode) {
|
||||
assert(server_name);
|
||||
|
||||
if (!UTI_IsStringIP(server_name)) {
|
||||
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
|
||||
if (clock_updates < CNF_GetNoCertTimeCheck()) {
|
||||
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
|
||||
DEBUG_LOG("Disabled time checks");
|
||||
}
|
||||
|
||||
gnutls_session_set_verify_cert(session, server_name, flags);
|
||||
}
|
||||
|
||||
r = gnutls_priority_set(session, priority);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, credentials);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
memcpy(alpn_name, NKE_ALPN_NAME, sizeof (alpn_name));
|
||||
alpn.data = alpn_name;
|
||||
alpn.size = sizeof (alpn_name) - 1;
|
||||
|
||||
r = gnutls_alpn_set_protocols(session, &alpn, 1, 0);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
gnutls_transport_set_int(session, sock_fd);
|
||||
|
||||
return session;
|
||||
|
||||
error:
|
||||
LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
|
||||
gnutls_deinit(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
stop_session(NKSN_Instance inst)
|
||||
{
|
||||
@@ -286,7 +218,7 @@ stop_session(NKSN_Instance inst)
|
||||
Free(inst->label);
|
||||
inst->label = NULL;
|
||||
|
||||
gnutls_deinit(inst->tls_session);
|
||||
TLS_DestroyInstance(inst->tls_session);
|
||||
inst->tls_session = NULL;
|
||||
|
||||
SCH_RemoveTimeout(inst->timeout_id);
|
||||
@@ -308,21 +240,6 @@ session_timeout(void *arg)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_alpn(NKSN_Instance inst)
|
||||
{
|
||||
gnutls_datum_t alpn;
|
||||
|
||||
if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
|
||||
alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
|
||||
memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
set_input_output(NKSN_Instance inst, int output)
|
||||
{
|
||||
@@ -364,6 +281,7 @@ static int
|
||||
handle_event(NKSN_Instance inst, int event)
|
||||
{
|
||||
struct Message *message = &inst->message;
|
||||
TLS_Status s;
|
||||
int r;
|
||||
|
||||
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
|
||||
@@ -390,56 +308,28 @@ handle_event(NKSN_Instance inst, int event)
|
||||
return 0;
|
||||
|
||||
case KE_HANDSHAKE:
|
||||
r = gnutls_handshake(inst->tls_session);
|
||||
|
||||
if (r < 0) {
|
||||
if (gnutls_error_is_fatal(r)) {
|
||||
gnutls_datum_t cert_error;
|
||||
|
||||
/* Get a description of verification errors */
|
||||
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
|
||||
gnutls_certificate_verification_status_print(
|
||||
gnutls_session_get_verify_cert_status(inst->tls_session),
|
||||
gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
|
||||
cert_error.data = NULL;
|
||||
|
||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
||||
"TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
|
||||
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
|
||||
|
||||
if (cert_error.data)
|
||||
gnutls_free(cert_error.data);
|
||||
s = TLS_DoHandshake(inst->tls_session);
|
||||
|
||||
switch (s) {
|
||||
case TLS_SUCCESS:
|
||||
break;
|
||||
case TLS_AGAIN_OUTPUT:
|
||||
case TLS_AGAIN_INPUT:
|
||||
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
|
||||
return 0;
|
||||
default:
|
||||
stop_session(inst);
|
||||
|
||||
/* Increase the retry interval if the handshake did not fail due
|
||||
to the other end closing the connection */
|
||||
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
|
||||
if (s != TLS_CLOSED)
|
||||
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable output when the handshake is trying to receive data */
|
||||
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
|
||||
return 0;
|
||||
}
|
||||
|
||||
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
|
||||
|
||||
if (DEBUG) {
|
||||
char *description = gnutls_session_get_desc(inst->tls_session);
|
||||
DEBUG_LOG("Handshake with %s completed %s",
|
||||
inst->label, description ? description : "");
|
||||
gnutls_free(description);
|
||||
}
|
||||
|
||||
if (!check_alpn(inst)) {
|
||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
|
||||
stop_session(inst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Client will send a request to the server */
|
||||
change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
|
||||
return 0;
|
||||
@@ -448,16 +338,17 @@ handle_event(NKSN_Instance inst, int event)
|
||||
assert(inst->new_message && message->complete);
|
||||
assert(message->length <= sizeof (message->data) && message->length > message->sent);
|
||||
|
||||
r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
|
||||
message->length - message->sent);
|
||||
s = TLS_Send(inst->tls_session, &message->data[message->sent],
|
||||
message->length - message->sent, &r);
|
||||
|
||||
if (r < 0) {
|
||||
if (gnutls_error_is_fatal(r)) {
|
||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
||||
"Could not send NTS-KE message to %s : %s", inst->label, gnutls_strerror(r));
|
||||
switch (s) {
|
||||
case TLS_SUCCESS:
|
||||
break;
|
||||
case TLS_AGAIN_OUTPUT:
|
||||
return 0;
|
||||
default:
|
||||
stop_session(inst);
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
|
||||
@@ -480,26 +371,24 @@ handle_event(NKSN_Instance inst, int event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
|
||||
sizeof (message->data) - message->length);
|
||||
s = TLS_Receive(inst->tls_session, &message->data[message->length],
|
||||
sizeof (message->data) - message->length, &r);
|
||||
|
||||
if (r < 0) {
|
||||
/* Handle a renegotiation request on both client and server as
|
||||
a protocol error */
|
||||
if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
|
||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
||||
"Could not receive NTS-KE message from %s : %s",
|
||||
inst->label, gnutls_strerror(r));
|
||||
switch (s) {
|
||||
case TLS_SUCCESS:
|
||||
break;
|
||||
case TLS_AGAIN_INPUT:
|
||||
return 0;
|
||||
default:
|
||||
stop_session(inst);
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Received %d bytes from %s", r, inst->label);
|
||||
|
||||
message->length += r;
|
||||
|
||||
} while (gnutls_record_check_pending(inst->tls_session) > 0);
|
||||
} while (TLS_CheckPending(inst->tls_session));
|
||||
|
||||
if (!check_message_format(message, r == 0)) {
|
||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
||||
@@ -519,18 +408,18 @@ handle_event(NKSN_Instance inst, int event)
|
||||
return 1;
|
||||
|
||||
case KE_SHUTDOWN:
|
||||
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
|
||||
s = TLS_Shutdown(inst->tls_session);
|
||||
|
||||
if (r < 0) {
|
||||
if (gnutls_error_is_fatal(r)) {
|
||||
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
|
||||
switch (s) {
|
||||
case TLS_SUCCESS:
|
||||
break;
|
||||
case TLS_AGAIN_OUTPUT:
|
||||
case TLS_AGAIN_INPUT:
|
||||
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
|
||||
return 0;
|
||||
default:
|
||||
stop_session(inst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable output when the TLS shutdown is trying to receive data */
|
||||
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SCK_ShutdownConnection(inst->sock_fd);
|
||||
@@ -592,36 +481,18 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int gnutls_initialised = 0;
|
||||
static int tls_initialised = 0;
|
||||
|
||||
static int
|
||||
init_gnutls(void)
|
||||
init_tls(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (gnutls_initialised)
|
||||
if (tls_initialised)
|
||||
return 1;
|
||||
|
||||
r = gnutls_global_init();
|
||||
if (r < 0)
|
||||
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
|
||||
|
||||
/* Prepare a priority cache for server and client NTS-KE sessions
|
||||
(the NTS specification requires TLS1.3 or later) */
|
||||
r = gnutls_priority_init2(&priority_cache,
|
||||
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
|
||||
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
|
||||
if (r < 0) {
|
||||
LOG(LOGS_ERR, "Could not initialise %s : %s",
|
||||
"priority cache for TLS", gnutls_strerror(r));
|
||||
gnutls_global_deinit();
|
||||
if (!TLS_Initialise(&get_time))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use our clock instead of the system clock in certificate verification */
|
||||
gnutls_global_set_time_function(get_time);
|
||||
|
||||
gnutls_initialised = 1;
|
||||
tls_initialised = 1;
|
||||
DEBUG_LOG("Initialised");
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_step, NULL);
|
||||
@@ -632,16 +503,15 @@ init_gnutls(void)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
deinit_gnutls(void)
|
||||
deinit_tls(void)
|
||||
{
|
||||
if (!gnutls_initialised || credentials_counter > 0)
|
||||
if (!tls_initialised || credentials_counter > 0)
|
||||
return;
|
||||
|
||||
LCL_RemoveParameterChangeHandler(handle_step, NULL);
|
||||
|
||||
gnutls_priority_deinit(priority_cache);
|
||||
gnutls_global_deinit();
|
||||
gnutls_initialised = 0;
|
||||
TLS_Finalise();
|
||||
tls_initialised = 0;
|
||||
DEBUG_LOG("Deinitialised");
|
||||
}
|
||||
|
||||
@@ -652,69 +522,21 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
|
||||
const char **trusted_certs, uint32_t *trusted_certs_ids,
|
||||
int n_trusted_certs, uint32_t trusted_cert_set)
|
||||
{
|
||||
gnutls_certificate_credentials_t credentials = NULL;
|
||||
int i, r;
|
||||
TLS_Credentials credentials;
|
||||
|
||||
if (!init_gnutls())
|
||||
if (!init_tls())
|
||||
return NULL;
|
||||
|
||||
r = gnutls_certificate_allocate_credentials(&credentials);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (certs && keys) {
|
||||
if (trusted_certs || trusted_certs_ids)
|
||||
assert(0);
|
||||
|
||||
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],
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
if (certs || keys || n_certs_keys > 0)
|
||||
assert(0);
|
||||
|
||||
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
|
||||
r = gnutls_certificate_set_x509_system_trust(credentials);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (trusted_certs && trusted_certs_ids) {
|
||||
for (i = 0; i < n_trusted_certs; i++) {
|
||||
struct stat buf;
|
||||
|
||||
if (trusted_certs_ids[i] != trusted_cert_set)
|
||||
continue;
|
||||
|
||||
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
|
||||
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
else
|
||||
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
|
||||
}
|
||||
}
|
||||
credentials = TLS_CreateCredentials(certs, keys, n_certs_keys, trusted_certs,
|
||||
trusted_certs_ids, n_trusted_certs, trusted_cert_set);
|
||||
if (!credentials) {
|
||||
deinit_tls();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
credentials_counter++;
|
||||
|
||||
return (NKSN_Credentials)credentials;
|
||||
|
||||
error:
|
||||
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
|
||||
if (credentials)
|
||||
gnutls_certificate_free_credentials(credentials);
|
||||
deinit_gnutls();
|
||||
return NULL;
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -739,9 +561,9 @@ NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
|
||||
void
|
||||
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
|
||||
{
|
||||
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
|
||||
TLS_DestroyCredentials(credentials);
|
||||
credentials_counter--;
|
||||
deinit_gnutls();
|
||||
deinit_tls();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -791,9 +613,9 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
|
||||
{
|
||||
assert(inst->state == KE_STOPPED);
|
||||
|
||||
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
|
||||
(gnutls_certificate_credentials_t)credentials,
|
||||
priority_cache);
|
||||
inst->tls_session = TLS_CreateInstance(inst->server, sock_fd, inst->server_name,
|
||||
label, NKE_ALPN_NAME, credentials,
|
||||
clock_updates < CNF_GetNoCertTimeCheck());
|
||||
if (!inst->tls_session)
|
||||
return 0;
|
||||
|
||||
@@ -901,19 +723,15 @@ NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter
|
||||
context.algorithm = htons(exporter_algorithm);
|
||||
|
||||
context.is_s2c = 0;
|
||||
if (gnutls_prf_rfc5705(inst->tls_session,
|
||||
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||
sizeof (context) - 1, (char *)&context,
|
||||
length, (char *)c2s->key) < 0) {
|
||||
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||
sizeof (context) - 1, &context, length, c2s->key)) {
|
||||
DEBUG_LOG("Could not export key");
|
||||
return 0;
|
||||
}
|
||||
|
||||
context.is_s2c = 1;
|
||||
if (gnutls_prf_rfc5705(inst->tls_session,
|
||||
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||
sizeof (context) - 1, (char *)&context,
|
||||
length, (char *)s2c->key) < 0) {
|
||||
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||
sizeof (context) - 1, &context, length, s2c->key)) {
|
||||
DEBUG_LOG("Could not export key");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -104,9 +104,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||
body = (unsigned char *)(header + 1);
|
||||
ciphertext = body + nonce_length + nonce_padding;
|
||||
|
||||
if ((unsigned char *)header + auth_length !=
|
||||
ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
|
||||
assert(0);
|
||||
BRIEF_ASSERT((unsigned char *)header + auth_length ==
|
||||
ciphertext + ciphertext_length + ciphertext_padding + additional_padding);
|
||||
|
||||
memcpy(body, nonce, nonce_length);
|
||||
memset(body + nonce_length, 0, nonce_padding);
|
||||
|
||||
@@ -259,8 +259,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
||||
|
||||
/* Make sure this is a response to the request from the last call
|
||||
of NNS_CheckRequestAuth() */
|
||||
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
|
||||
assert(0);
|
||||
BRIEF_ASSERT(UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) == 0);
|
||||
|
||||
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
|
||||
if (!NEF_ParseField(request, req_info->length, parsed,
|
||||
|
||||
@@ -55,7 +55,7 @@ struct request_length {
|
||||
};
|
||||
|
||||
static const struct request_length request_lengths[] = {
|
||||
REQ_LENGTH_ENTRY(null, null), /* NULL */
|
||||
{ 0, 0 }, /* NULL - not supported */
|
||||
REQ_LENGTH_ENTRY(online, null), /* ONLINE */
|
||||
REQ_LENGTH_ENTRY(offline, null), /* OFFLINE */
|
||||
REQ_LENGTH_ENTRY(burst, null), /* BURST */
|
||||
@@ -65,7 +65,7 @@ static const struct request_length request_lengths[] = {
|
||||
REQ_LENGTH_ENTRY(modify_maxdelay, null), /* MODIFY_MAXDELAY */
|
||||
REQ_LENGTH_ENTRY(modify_maxdelayratio, null), /* MODIFY_MAXDELAYRATIO */
|
||||
REQ_LENGTH_ENTRY(modify_maxupdateskew, null), /* MODIFY_MAXUPDATESKEW */
|
||||
REQ_LENGTH_ENTRY(logon, null), /* LOGON */
|
||||
{ 0, 0 }, /* LOGON - not supported */
|
||||
REQ_LENGTH_ENTRY(settime, manual_timestamp), /* SETTIME */
|
||||
{ 0, 0 }, /* LOCAL */
|
||||
REQ_LENGTH_ENTRY(manual, null), /* MANUAL */
|
||||
|
||||
@@ -141,6 +141,7 @@ have_helper(void)
|
||||
/* ======================================================================= */
|
||||
|
||||
/* HELPER - prepare fatal error for daemon */
|
||||
FORMAT_ATTRIBUTE_PRINTF(2, 3)
|
||||
static void
|
||||
res_fatal(PrvResponse *res, const char *fmt, ...)
|
||||
{
|
||||
@@ -546,9 +547,9 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
|
||||
PrvResponse res;
|
||||
|
||||
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
|
||||
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
|
||||
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort())
|
||||
assert(0);
|
||||
BRIEF_ASSERT(ip_saddr.port == 0 || ip_saddr.port == CNF_GetNTPPort() ||
|
||||
ip_saddr.port == CNF_GetAcquisitionPort() ||
|
||||
ip_saddr.port == CNF_GetPtpPort());
|
||||
|
||||
if (!have_helper())
|
||||
return bind(sock, address, address_len);
|
||||
|
||||
55
quantiles.c
55
quantiles.c
@@ -49,20 +49,21 @@ struct QNT_Instance_Record {
|
||||
int q;
|
||||
int min_k;
|
||||
double min_step;
|
||||
double neg_step_limit;
|
||||
int n_set;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
QNT_Instance
|
||||
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
|
||||
QNT_CreateInstance(int min_k, int max_k, int q, int repeat,
|
||||
int large_step_delay, 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);
|
||||
BRIEF_ASSERT(q >= 2 && min_k <= max_k && min_k >= 1 && max_k < q && repeat >= 1 &&
|
||||
repeat <= MAX_REPEAT && min_step > 0.0 && large_step_delay >= 0);
|
||||
|
||||
inst = MallocNew(struct QNT_Instance_Record);
|
||||
inst->n_quants = (max_k - min_k + 1) * repeat;
|
||||
@@ -71,6 +72,7 @@ QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
|
||||
inst->q = q;
|
||||
inst->min_k = min_k;
|
||||
inst->min_step = min_step;
|
||||
inst->neg_step_limit = -large_step_delay * min_step;
|
||||
|
||||
QNT_Reset(inst);
|
||||
|
||||
@@ -114,8 +116,7 @@ insert_initial_value(QNT_Instance inst, double value)
|
||||
{
|
||||
int i, j, r = inst->repeat;
|
||||
|
||||
if (inst->n_set * r >= inst->n_quants)
|
||||
assert(0);
|
||||
BRIEF_ASSERT(inst->n_set * r < inst->n_quants);
|
||||
|
||||
/* Keep the initial estimates repeated and ordered */
|
||||
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
|
||||
@@ -136,29 +137,36 @@ insert_initial_value(QNT_Instance inst, double value)
|
||||
|
||||
static void
|
||||
update_estimate(struct Quantile *quantile, double value, double p, double rand,
|
||||
double min_step)
|
||||
double min_step, double neg_step_limit)
|
||||
{
|
||||
if (value > quantile->est && rand > (1.0 - p)) {
|
||||
if (value >= quantile->est) {
|
||||
if (rand < (1.0 - p))
|
||||
return;
|
||||
quantile->step += quantile->sign > 0 ? min_step : -min_step;
|
||||
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
|
||||
quantile->est += quantile->step > min_step ? quantile->step : min_step;
|
||||
if (quantile->est > value) {
|
||||
quantile->step += value - quantile->est;
|
||||
quantile->est = value;
|
||||
quantile->est = value + min_step / 4.0;
|
||||
}
|
||||
if (quantile->sign < 0 && quantile->step > min_step)
|
||||
quantile->step = min_step;
|
||||
quantile->sign = 1;
|
||||
} else if (value < quantile->est && rand > p) {
|
||||
} else {
|
||||
if (rand < p)
|
||||
return;
|
||||
quantile->step += quantile->sign < 0 ? min_step : -min_step;
|
||||
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
|
||||
quantile->est -= quantile->step > min_step ? quantile->step : min_step;
|
||||
if (quantile->est < value) {
|
||||
quantile->step += quantile->est - value;
|
||||
quantile->est = value;
|
||||
quantile->est = value - min_step / 4.0;
|
||||
}
|
||||
if (quantile->sign > 0 && quantile->step > min_step)
|
||||
quantile->step = min_step;
|
||||
quantile->sign = -1;
|
||||
}
|
||||
|
||||
if (quantile->step < neg_step_limit)
|
||||
quantile->step = neg_step_limit;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -179,7 +187,7 @@ QNT_Accumulate(QNT_Instance inst, double value)
|
||||
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);
|
||||
update_estimate(&inst->quants[i], value, p, rand, inst->min_step, inst->neg_step_limit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,14 +201,29 @@ QNT_GetMinK(QNT_Instance inst)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
QNT_GetMaxK(QNT_Instance inst)
|
||||
{
|
||||
return inst->min_k + (inst->n_quants / inst->repeat) - 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
QNT_GetMinStep(QNT_Instance inst)
|
||||
{
|
||||
return inst->min_step;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
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);
|
||||
BRIEF_ASSERT(k >= inst->min_k && (k - inst->min_k) * inst->repeat < inst->n_quants);
|
||||
|
||||
for (i = 0; i < inst->repeat; i++)
|
||||
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
|
||||
|
||||
@@ -30,12 +30,15 @@
|
||||
|
||||
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 QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat,
|
||||
int large_step_delay, 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 int QNT_GetMaxK(QNT_Instance inst);
|
||||
extern double QNT_GetMinStep(QNT_Instance inst);
|
||||
extern double QNT_GetQuantile(QNT_Instance inst, int k);
|
||||
|
||||
#endif
|
||||
|
||||
59
refclock.c
59
refclock.c
@@ -48,6 +48,7 @@ extern RefclockDriver RCL_SHM_driver;
|
||||
extern RefclockDriver RCL_SOCK_driver;
|
||||
extern RefclockDriver RCL_PPS_driver;
|
||||
extern RefclockDriver RCL_PHC_driver;
|
||||
extern RefclockDriver RCL_RTC_driver;
|
||||
|
||||
struct FilterSample {
|
||||
double offset;
|
||||
@@ -63,6 +64,7 @@ struct RCL_Instance_Record {
|
||||
int driver_poll;
|
||||
int driver_polled;
|
||||
int poll;
|
||||
int reached;
|
||||
int leap_status;
|
||||
int local;
|
||||
int pps_forced;
|
||||
@@ -159,6 +161,8 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->driver = &RCL_PPS_driver;
|
||||
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
||||
inst->driver = &RCL_PHC_driver;
|
||||
} else if (strcmp(params->driver_name, "RTC") == 0) {
|
||||
inst->driver = &RCL_RTC_driver;
|
||||
} else {
|
||||
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
||||
}
|
||||
@@ -175,6 +179,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->driver_poll = params->driver_poll;
|
||||
inst->poll = params->poll;
|
||||
inst->driver_polled = 0;
|
||||
inst->reached = 0;
|
||||
inst->leap_status = LEAP_Normal;
|
||||
inst->local = params->local;
|
||||
inst->pps_forced = params->pps_forced;
|
||||
@@ -226,33 +231,20 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
}
|
||||
|
||||
if (inst->driver->poll) {
|
||||
int max_samples;
|
||||
|
||||
if (inst->driver_poll > inst->poll)
|
||||
inst->driver_poll = inst->poll;
|
||||
|
||||
max_samples = 1 << (inst->poll - inst->driver_poll);
|
||||
if (max_samples < params->filter_length) {
|
||||
if (max_samples < 4) {
|
||||
LOG(LOGS_WARN, "Setting filter length for %s to %d",
|
||||
UTI_RefidToString(inst->ref_id), max_samples);
|
||||
}
|
||||
params->filter_length = max_samples;
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->driver->init && !inst->driver->init(inst))
|
||||
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
|
||||
|
||||
/* Require the filter to have at least 4 samples to produce a filtered
|
||||
sample, or be full for shorter lengths, and combine 60% of samples
|
||||
closest to the median */
|
||||
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
|
||||
params->max_dispersion, 0.6);
|
||||
/* Don't require more than one sample per poll and combine 60% of the
|
||||
samples closest to the median offset */
|
||||
inst->filter = SPF_CreateInstance(1, params->filter_length, params->max_dispersion, 0.6);
|
||||
|
||||
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
|
||||
NULL, params->min_samples, params->max_samples,
|
||||
0.0, 0.0);
|
||||
0.0, 0.0, params->max_unreach);
|
||||
|
||||
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
|
||||
params->driver_name, UTI_RefidToString(inst->ref_id),
|
||||
@@ -435,10 +427,21 @@ convert_tai_offset(struct timespec *sample_time, double *offset)
|
||||
}
|
||||
|
||||
static int
|
||||
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
|
||||
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion,
|
||||
int quality)
|
||||
{
|
||||
NTP_Sample sample;
|
||||
|
||||
instance->reached++;
|
||||
|
||||
/* Don't accumulate the sample if the driver is suggesting it should be
|
||||
dropped due to low quality. The only reason it went so far was to update
|
||||
the reachability. */
|
||||
if (quality < 1) {
|
||||
DEBUG_LOG("refclock sample ignored quality=%d", quality);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample.time = *sample_time;
|
||||
sample.offset = offset;
|
||||
sample.peer_delay = instance->delay;
|
||||
@@ -451,14 +454,14 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
|
||||
|
||||
int
|
||||
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||
struct timespec *ref_time, int leap)
|
||||
struct timespec *ref_time, int leap, int quality)
|
||||
{
|
||||
double correction, dispersion, raw_offset, offset;
|
||||
struct timespec cooked_time;
|
||||
|
||||
if (instance->pps_forced)
|
||||
return RCL_AddPulse(instance, sample_time,
|
||||
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
|
||||
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec), quality);
|
||||
|
||||
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
|
||||
|
||||
@@ -494,7 +497,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
|
||||
if (!accumulate_sample(instance, &cooked_time, offset, dispersion, quality))
|
||||
return 0;
|
||||
|
||||
instance->pps_active = 0;
|
||||
@@ -509,7 +512,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||
}
|
||||
|
||||
int
|
||||
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
||||
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second, int quality)
|
||||
{
|
||||
double correction, dispersion;
|
||||
struct timespec cooked_time;
|
||||
@@ -521,7 +524,7 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
||||
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
|
||||
return 0;
|
||||
|
||||
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
|
||||
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction, quality);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -547,7 +550,7 @@ check_pulse_edge(RCL_Instance instance, double offset, double distance)
|
||||
|
||||
int
|
||||
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
double second, double dispersion, double raw_correction)
|
||||
double second, double dispersion, double raw_correction, int quality)
|
||||
{
|
||||
double offset;
|
||||
int rate;
|
||||
@@ -649,7 +652,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
|
||||
if (!accumulate_sample(instance, cooked_time, offset, dispersion, quality))
|
||||
return 0;
|
||||
|
||||
instance->leap_status = leap;
|
||||
@@ -792,6 +795,9 @@ poll_timeout(void *arg)
|
||||
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
|
||||
inst->driver_polled = 0;
|
||||
|
||||
SRC_UpdateReachability(inst->source, inst->reached > 0);
|
||||
inst->reached = 0;
|
||||
|
||||
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
||||
double local_freq, local_offset;
|
||||
struct timespec local_ref_time;
|
||||
@@ -807,7 +813,6 @@ poll_timeout(void *arg)
|
||||
inst->leap_status = LEAP_Unsynchronised;
|
||||
}
|
||||
|
||||
SRC_UpdateReachability(inst->source, 1);
|
||||
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
|
||||
SRC_AccumulateSample(inst->source, &sample);
|
||||
SRC_SelectSource(inst->source);
|
||||
@@ -816,8 +821,6 @@ poll_timeout(void *arg)
|
||||
follow_local(inst, &local_ref_time, local_freq, local_offset);
|
||||
|
||||
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
||||
} else {
|
||||
SRC_UpdateReachability(inst->source, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ typedef struct {
|
||||
int pps_rate;
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int max_unreach;
|
||||
int sel_options;
|
||||
int max_lock_age;
|
||||
int stratum;
|
||||
@@ -77,10 +78,12 @@ extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
||||
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
||||
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
||||
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);
|
||||
struct timespec *ref_time, int leap, int quality);
|
||||
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second,
|
||||
int quality);
|
||||
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,
|
||||
int quality);
|
||||
extern double RCL_GetPrecision(RCL_Instance instance);
|
||||
extern int RCL_GetDriverPoll(RCL_Instance instance);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ static int phc_initialise(RCL_Instance instance)
|
||||
{
|
||||
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
||||
struct phc_instance *phc;
|
||||
int phc_fd, rising_edge;
|
||||
int rising_edge;
|
||||
struct stat st;
|
||||
char *path, *s;
|
||||
|
||||
@@ -74,19 +74,20 @@ static int phc_initialise(RCL_Instance instance)
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
phc_fd = SYS_Linux_OpenPHC(path, 0);
|
||||
if (phc_fd < 0)
|
||||
LOG_FATAL("Could not open PHC");
|
||||
|
||||
phc = MallocNew(struct phc_instance);
|
||||
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->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
||||
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
||||
UTI_ZeroTimespec(&phc->last_extts);
|
||||
|
||||
phc->fd = SYS_Linux_OpenPHC(path, phc->extpps ? O_RDWR : O_RDONLY);
|
||||
if (phc->fd < 0)
|
||||
LOG_FATAL("Could not open PHC");
|
||||
|
||||
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->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
|
||||
RCL_GetPrecision(instance));
|
||||
|
||||
@@ -158,7 +159,7 @@ static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
|
||||
return;
|
||||
|
||||
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
||||
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
|
||||
UTI_DiffTimespecsToDouble(phc_ts, &local_ts), 1);
|
||||
}
|
||||
|
||||
static void read_ext_pulse(int fd, int event, void *anything)
|
||||
@@ -195,7 +196,7 @@ static int phc_poll(RCL_Instance instance)
|
||||
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
|
||||
struct phc_instance *phc;
|
||||
double phc_err, local_err;
|
||||
int n_readings;
|
||||
int n_readings, quality;
|
||||
|
||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||
|
||||
@@ -204,11 +205,13 @@ static int phc_poll(RCL_Instance instance)
|
||||
if (n_readings < 1)
|
||||
return 0;
|
||||
|
||||
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
|
||||
if (!HCL_ProcessReadings(phc->clock, n_readings, readings,
|
||||
&phc_ts, &sys_ts, &phc_err, &quality))
|
||||
return 0;
|
||||
|
||||
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||
if (quality > 0)
|
||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||
|
||||
if (phc->extpps)
|
||||
return 0;
|
||||
@@ -216,7 +219,7 @@ static int phc_poll(RCL_Instance instance)
|
||||
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
|
||||
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
|
||||
|
||||
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
|
||||
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal, quality);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_PHC_driver = {
|
||||
|
||||
@@ -143,7 +143,7 @@ static int pps_poll(RCL_Instance instance)
|
||||
|
||||
pps->last_seq = seq;
|
||||
|
||||
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
|
||||
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec, 1);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_PPS_driver = {
|
||||
|
||||
180
refclock_rtc.c
Normal file
180
refclock_rtc.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Uwe Kleine-König, Pengutronix 2021
|
||||
* Copyright (C) Ahmad Fatoum, Pengutronix 2024
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
RTC refclock driver.
|
||||
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "refclock.h"
|
||||
|
||||
#ifdef FEAT_RTC
|
||||
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "sched.h"
|
||||
#include "util.h"
|
||||
#include "rtc_linux.h"
|
||||
|
||||
struct refrtc_instance {
|
||||
int fd;
|
||||
int polling;
|
||||
int utc;
|
||||
};
|
||||
|
||||
static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
|
||||
time_t rtc_sec, long rtc_nsec)
|
||||
{
|
||||
struct timespec rtc_ts;
|
||||
int status;
|
||||
|
||||
rtc_ts.tv_sec = rtc_sec;
|
||||
rtc_ts.tv_nsec = rtc_nsec;
|
||||
|
||||
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void refrtc_read_after_uie(int rtcfd, int event, void *data)
|
||||
{
|
||||
RCL_Instance instance = (RCL_Instance)data;
|
||||
struct refrtc_instance *rtc = RCL_GetDriverData(instance);
|
||||
struct timespec now;
|
||||
time_t rtc_sec;
|
||||
int status;
|
||||
|
||||
status = RTC_Linux_CheckInterrupt(rtcfd);
|
||||
if (status < 0) {
|
||||
SCH_RemoveFileHandler(rtcfd);
|
||||
RTC_Linux_SwitchInterrupt(rtcfd, 0); /* Likely to raise error too, but just to be sure... */
|
||||
close(rtcfd);
|
||||
rtc->fd = -1;
|
||||
return;
|
||||
} else if (status == 0) {
|
||||
/* Wait for the next interrupt, this one may be bogus */
|
||||
return;
|
||||
}
|
||||
|
||||
rtc_sec = RTC_Linux_ReadTimeAfterInterrupt(rtcfd, rtc->utc, NULL, &now);
|
||||
if (rtc_sec == (time_t)-1)
|
||||
return;
|
||||
|
||||
refrtc_add_sample(instance, &now, rtc_sec, 0);
|
||||
}
|
||||
|
||||
static int refrtc_initialise(RCL_Instance instance)
|
||||
{
|
||||
const char *options[] = {"utc", NULL};
|
||||
struct refrtc_instance *rtc;
|
||||
int rtcfd, status;
|
||||
const char *path;
|
||||
|
||||
RCL_CheckDriverOptions(instance, options);
|
||||
|
||||
if (CNF_GetRtcSync() || CNF_GetRtcFile())
|
||||
LOG_FATAL("RTC refclock cannot be used together with rtcsync or rtcfile");
|
||||
|
||||
path = RCL_GetDriverParameter(instance);
|
||||
|
||||
rtcfd = open(path, O_RDONLY);
|
||||
if (rtcfd < 0)
|
||||
LOG_FATAL("Could not open RTC device %s : %s", path, strerror(errno));
|
||||
|
||||
/* Close on exec */
|
||||
UTI_FdSetCloexec(rtcfd);
|
||||
|
||||
rtc = MallocNew(struct refrtc_instance);
|
||||
rtc->fd = rtcfd;
|
||||
rtc->utc = RCL_GetDriverOption(instance, "utc") ? 1 : 0;
|
||||
|
||||
RCL_SetDriverData(instance, rtc);
|
||||
|
||||
/* Try to enable update interrupts */
|
||||
status = RTC_Linux_SwitchInterrupt(rtcfd, 1);
|
||||
if (status) {
|
||||
SCH_AddFileHandler(rtcfd, SCH_FILE_INPUT, refrtc_read_after_uie, instance);
|
||||
rtc->polling = 0;
|
||||
} else {
|
||||
LOG(LOGS_INFO, "Falling back to polling for %s", path);
|
||||
rtc->polling = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void refrtc_finalise(RCL_Instance instance)
|
||||
{
|
||||
struct refrtc_instance *rtc;
|
||||
|
||||
rtc = RCL_GetDriverData(instance);
|
||||
|
||||
if (rtc->fd >= 0) {
|
||||
if (!rtc->polling) {
|
||||
SCH_RemoveFileHandler(rtc->fd);
|
||||
RTC_Linux_SwitchInterrupt(rtc->fd, 0);
|
||||
}
|
||||
|
||||
close(rtc->fd);
|
||||
}
|
||||
|
||||
Free(rtc);
|
||||
}
|
||||
|
||||
static int refrtc_poll(RCL_Instance instance)
|
||||
{
|
||||
struct refrtc_instance *rtc;
|
||||
struct timespec now;
|
||||
time_t rtc_sec;
|
||||
|
||||
rtc = RCL_GetDriverData(instance);
|
||||
|
||||
if (!rtc->polling)
|
||||
return 0;
|
||||
|
||||
rtc_sec = RTC_Linux_ReadTimeNow(rtc->fd, rtc->utc, NULL, &now);
|
||||
if (rtc_sec == (time_t)-1)
|
||||
return 0;
|
||||
|
||||
/* As the rtc has a resolution of 1s, only add half a second */
|
||||
return refrtc_add_sample(instance, &now, rtc_sec, 500000000);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_RTC_driver = {
|
||||
refrtc_initialise,
|
||||
refrtc_finalise,
|
||||
refrtc_poll
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
RefclockDriver RCL_RTC_driver = { NULL, NULL, NULL };
|
||||
|
||||
#endif
|
||||
@@ -124,7 +124,7 @@ static int shm_poll(RCL_Instance instance)
|
||||
UTI_NormaliseTimespec(&clock_ts);
|
||||
UTI_NormaliseTimespec(&receive_ts);
|
||||
|
||||
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
|
||||
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap, 1);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_SHM_driver = {
|
||||
|
||||
@@ -135,9 +135,9 @@ static void read_sample(int sockfd, int event, void *anything)
|
||||
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
|
||||
|
||||
if (sample.pulse) {
|
||||
RCL_AddPulse(instance, &sys_ts, sample.offset);
|
||||
RCL_AddPulse(instance, &sys_ts, sample.offset, 1);
|
||||
} else {
|
||||
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
|
||||
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
34
reference.c
34
reference.c
@@ -47,9 +47,6 @@
|
||||
/* The update interval of the reference in the local reference mode */
|
||||
#define LOCAL_REF_UPDATE_INTERVAL 64.0
|
||||
|
||||
/* Interval between updates of the drift file */
|
||||
#define MAX_DRIFTFILE_AGE 3600.0
|
||||
|
||||
static int are_we_synchronised;
|
||||
static int enable_local_stratum;
|
||||
static int local_stratum;
|
||||
@@ -57,6 +54,8 @@ static int local_orphan;
|
||||
static double local_distance;
|
||||
static int local_activate_ok;
|
||||
static double local_activate;
|
||||
static double local_wait_synced;
|
||||
static double local_wait_unsynced;
|
||||
static struct timespec local_ref_time;
|
||||
static NTP_Leap our_leap_status;
|
||||
static int our_leap_sec;
|
||||
@@ -65,6 +64,7 @@ static int our_stratum;
|
||||
static uint32_t our_ref_id;
|
||||
static IPAddr our_ref_ip;
|
||||
static struct timespec our_ref_time;
|
||||
static double unsynchronised_since;
|
||||
static double our_skew;
|
||||
static double our_residual_freq;
|
||||
static double our_root_delay;
|
||||
@@ -110,6 +110,7 @@ static REF_ModeEndHandler mode_end_handler = NULL;
|
||||
/* Filename of the drift file. */
|
||||
static char *drift_file=NULL;
|
||||
static double drift_file_age;
|
||||
static int drift_file_interval;
|
||||
|
||||
static void update_drift_file(double, double);
|
||||
|
||||
@@ -213,7 +214,7 @@ REF_Initialise(void)
|
||||
local_activate_ok = 0;
|
||||
|
||||
/* Now see if we can get the drift file opened */
|
||||
drift_file = CNF_GetDriftFile();
|
||||
drift_file = CNF_GetDriftFile(&drift_file_interval);
|
||||
if (drift_file) {
|
||||
in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
|
||||
if (in) {
|
||||
@@ -250,8 +251,11 @@ REF_Initialise(void)
|
||||
correction_time_ratio = CNF_GetCorrectionTimeRatio();
|
||||
|
||||
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
|
||||
&local_distance, &local_activate);
|
||||
&local_distance, &local_activate,
|
||||
&local_wait_synced,
|
||||
&local_wait_unsynced);
|
||||
UTI_ZeroTimespec(&local_ref_time);
|
||||
unsynchronised_since = SCH_GetLastEventMonoTime();
|
||||
|
||||
leap_when = 0;
|
||||
leap_timeout_id = 0;
|
||||
@@ -987,6 +991,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
if (step_offset != 0.0) {
|
||||
if (LCL_ApplyStepOffset(step_offset))
|
||||
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
|
||||
else
|
||||
LCL_AccumulateOffset(step_offset, 0.0);
|
||||
}
|
||||
|
||||
update_leap_status(leap, raw_now.tv_sec, 0);
|
||||
@@ -1005,7 +1011,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
if (drift_file) {
|
||||
/* Update drift file at most once per hour */
|
||||
drift_file_age += update_interval;
|
||||
if (drift_file_age >= MAX_DRIFTFILE_AGE) {
|
||||
if (drift_file_age >= drift_file_interval) {
|
||||
update_drift_file(local_abs_frequency, our_skew);
|
||||
drift_file_age = 0.0;
|
||||
}
|
||||
@@ -1095,6 +1101,9 @@ REF_SetUnsynchronised(void)
|
||||
our_ref_ip.family = IPADDR_INET4;
|
||||
our_ref_ip.addr.in4 = 0;
|
||||
our_stratum = 0;
|
||||
|
||||
if (are_we_synchronised)
|
||||
unsynchronised_since = SCH_GetLastEventMonoTime();
|
||||
are_we_synchronised = 0;
|
||||
|
||||
LCL_SetSyncStatus(0, 0.0, 0.0);
|
||||
@@ -1138,13 +1147,16 @@ REF_GetReferenceParams
|
||||
)
|
||||
{
|
||||
double dispersion, delta, distance;
|
||||
int wait_local_ok;
|
||||
|
||||
assert(initialised);
|
||||
|
||||
if (are_we_synchronised) {
|
||||
dispersion = get_root_dispersion(local_time);
|
||||
wait_local_ok = UTI_DiffTimespecsToDouble(local_time, &our_ref_time) >= local_wait_synced;
|
||||
} else {
|
||||
dispersion = 0.0;
|
||||
wait_local_ok = SCH_GetLastEventMonoTime() - unsynchronised_since >= local_wait_unsynced;
|
||||
}
|
||||
|
||||
distance = our_root_delay / 2 + dispersion;
|
||||
@@ -1156,7 +1168,8 @@ REF_GetReferenceParams
|
||||
or the root distance exceeds the threshold */
|
||||
|
||||
if (are_we_synchronised &&
|
||||
!(enable_local_stratum && local_activate_ok && distance > local_distance)) {
|
||||
!(enable_local_stratum && local_activate_ok && wait_local_ok &&
|
||||
distance > local_distance)) {
|
||||
|
||||
*is_synchronised = 1;
|
||||
|
||||
@@ -1168,7 +1181,7 @@ REF_GetReferenceParams
|
||||
*root_delay = our_root_delay;
|
||||
*root_dispersion = dispersion;
|
||||
|
||||
} else if (enable_local_stratum && local_activate_ok) {
|
||||
} else if (enable_local_stratum && local_activate_ok && wait_local_ok) {
|
||||
|
||||
*is_synchronised = 0;
|
||||
|
||||
@@ -1268,13 +1281,16 @@ REF_ModifyMakestep(int limit, double threshold)
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_EnableLocal(int stratum, double distance, int orphan, double activate)
|
||||
REF_EnableLocal(int stratum, double distance, int orphan, double activate,
|
||||
double wait_synced, double wait_unsynced)
|
||||
{
|
||||
enable_local_stratum = 1;
|
||||
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
|
||||
local_distance = distance;
|
||||
local_orphan = !!orphan;
|
||||
local_activate = activate;
|
||||
local_wait_synced = wait_synced;
|
||||
local_wait_unsynced = wait_unsynced;
|
||||
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,8 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
|
||||
/* Modify makestep settings */
|
||||
extern void REF_ModifyMakestep(int limit, double threshold);
|
||||
|
||||
extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate);
|
||||
extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate,
|
||||
double wait_synced, double wait_unsynced);
|
||||
extern void REF_DisableLocal(void);
|
||||
|
||||
/* Check if either of the current raw and cooked time, and optionally a
|
||||
|
||||
3
rtc.c
3
rtc.c
@@ -81,8 +81,9 @@ get_driftfile_time(void)
|
||||
{
|
||||
struct stat buf;
|
||||
char *drift_file;
|
||||
int interval;
|
||||
|
||||
drift_file = CNF_GetDriftFile();
|
||||
drift_file = CNF_GetDriftFile(&interval);
|
||||
if (!drift_file)
|
||||
return 0;
|
||||
|
||||
|
||||
305
rtc_linux.c
305
rtc_linux.c
@@ -4,6 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2012-2014
|
||||
* Copyright (C) Ahmad Fatoum, Pengutronix 2024
|
||||
*
|
||||
* 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
|
||||
@@ -296,14 +297,25 @@ slew_samples
|
||||
corresponding real time clock 'DMY HMS' form, taking account of
|
||||
whether the user runs his RTC on the local time zone or UTC */
|
||||
|
||||
static struct tm *
|
||||
rtc_from_t(const time_t *t)
|
||||
static void
|
||||
rtc_from_t(const time_t *t, struct rtc_time *rtc_raw, int utc)
|
||||
{
|
||||
if (rtc_on_utc) {
|
||||
return gmtime(t);
|
||||
struct tm *rtc_tm;
|
||||
if (utc) {
|
||||
rtc_tm = gmtime(t);
|
||||
} else {
|
||||
return localtime(t);
|
||||
rtc_tm = localtime(t);
|
||||
}
|
||||
|
||||
rtc_raw->tm_sec = rtc_tm->tm_sec;
|
||||
rtc_raw->tm_min = rtc_tm->tm_min;
|
||||
rtc_raw->tm_hour = rtc_tm->tm_hour;
|
||||
rtc_raw->tm_mday = rtc_tm->tm_mday;
|
||||
rtc_raw->tm_mon = rtc_tm->tm_mon;
|
||||
rtc_raw->tm_year = rtc_tm->tm_year;
|
||||
rtc_raw->tm_wday = rtc_tm->tm_wday;
|
||||
rtc_raw->tm_yday = rtc_tm->tm_yday;
|
||||
rtc_raw->tm_isdst = rtc_tm->tm_isdst;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -341,17 +353,27 @@ rtc_from_t(const time_t *t)
|
||||
*/
|
||||
|
||||
static time_t
|
||||
t_from_rtc(struct tm *stm) {
|
||||
struct tm temp1, temp2, *tm;
|
||||
t_from_rtc(struct rtc_time *rtc_raw, int utc)
|
||||
{
|
||||
struct tm rtc_tm, temp1, temp2, *tm;
|
||||
long diff;
|
||||
time_t t1, t2;
|
||||
|
||||
temp1 = *stm;
|
||||
/* Convert to seconds since 1970 */
|
||||
memset(&rtc_tm, 0, sizeof (rtc_tm));
|
||||
rtc_tm.tm_sec = rtc_raw->tm_sec;
|
||||
rtc_tm.tm_min = rtc_raw->tm_min;
|
||||
rtc_tm.tm_hour = rtc_raw->tm_hour;
|
||||
rtc_tm.tm_mday = rtc_raw->tm_mday;
|
||||
rtc_tm.tm_mon = rtc_raw->tm_mon;
|
||||
rtc_tm.tm_year = rtc_raw->tm_year;
|
||||
|
||||
temp1 = rtc_tm;
|
||||
temp1.tm_isdst = 0;
|
||||
|
||||
t1 = mktime(&temp1);
|
||||
|
||||
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
|
||||
tm = utc ? gmtime(&t1) : localtime(&t1);
|
||||
if (!tm) {
|
||||
DEBUG_LOG("gmtime()/localtime() failed");
|
||||
return -1;
|
||||
@@ -476,8 +498,8 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
switch_interrupts(int on_off)
|
||||
int
|
||||
RTC_Linux_SwitchInterrupt(int fd, int on_off)
|
||||
{
|
||||
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
|
||||
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
|
||||
@@ -508,7 +530,7 @@ RTC_Linux_Initialise(void)
|
||||
}
|
||||
|
||||
/* Make sure the RTC supports interrupts */
|
||||
if (!switch_interrupts(1) || !switch_interrupts(0)) {
|
||||
if (!RTC_Linux_SwitchInterrupt(fd, 1) || !RTC_Linux_SwitchInterrupt(fd, 0)) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
@@ -557,7 +579,7 @@ RTC_Linux_Finalise(void)
|
||||
/* Remove input file handler */
|
||||
if (fd >= 0) {
|
||||
SCH_RemoveFileHandler(fd);
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
close(fd);
|
||||
|
||||
/* Save the RTC data */
|
||||
@@ -578,7 +600,7 @@ static void
|
||||
measurement_timeout(void *any)
|
||||
{
|
||||
timeout_id = 0;
|
||||
switch_interrupts(1);
|
||||
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -586,21 +608,10 @@ measurement_timeout(void *any)
|
||||
static void
|
||||
set_rtc(time_t new_rtc_time)
|
||||
{
|
||||
struct tm rtc_tm;
|
||||
struct rtc_time rtc_raw;
|
||||
int status;
|
||||
|
||||
rtc_tm = *rtc_from_t(&new_rtc_time);
|
||||
|
||||
rtc_raw.tm_sec = rtc_tm.tm_sec;
|
||||
rtc_raw.tm_min = rtc_tm.tm_min;
|
||||
rtc_raw.tm_hour = rtc_tm.tm_hour;
|
||||
rtc_raw.tm_mday = rtc_tm.tm_mday;
|
||||
rtc_raw.tm_mon = rtc_tm.tm_mon;
|
||||
rtc_raw.tm_year = rtc_tm.tm_year;
|
||||
rtc_raw.tm_wday = rtc_tm.tm_wday;
|
||||
rtc_raw.tm_yday = rtc_tm.tm_yday;
|
||||
rtc_raw.tm_isdst = rtc_tm.tm_isdst;
|
||||
rtc_from_t(&new_rtc_time, &rtc_raw, rtc_on_utc);
|
||||
|
||||
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
||||
if (status < 0) {
|
||||
@@ -750,16 +761,11 @@ process_reading(time_t rtc_time, struct timespec *system_time)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
read_from_device(int fd_, int event, void *any)
|
||||
int
|
||||
RTC_Linux_CheckInterrupt(int fd)
|
||||
{
|
||||
int status;
|
||||
unsigned long data;
|
||||
struct timespec sys_time;
|
||||
struct rtc_time rtc_raw;
|
||||
struct tm rtc_tm;
|
||||
time_t rtc_t;
|
||||
int error = 0;
|
||||
|
||||
status = read(fd, &data, sizeof(data));
|
||||
|
||||
@@ -767,64 +773,79 @@ read_from_device(int fd_, int event, void *any)
|
||||
/* This looks like a bad error : the file descriptor was indicating it was
|
||||
* ready to read but we couldn't read anything. Give up. */
|
||||
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
SCH_RemoveFileHandler(fd);
|
||||
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skip_interrupts > 0) {
|
||||
/* Wait for the next interrupt, this one may be bogus */
|
||||
skip_interrupts--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update interrupt detected? */
|
||||
return (data & RTC_UF) == RTC_UF;
|
||||
}
|
||||
|
||||
time_t
|
||||
RTC_Linux_ReadTimeAfterInterrupt(int fd, int utc,
|
||||
struct timespec *sys_time_cooked,
|
||||
struct timespec *sys_time_raw)
|
||||
{
|
||||
int status;
|
||||
struct rtc_time rtc_raw;
|
||||
|
||||
/* Read RTC time, sandwiched between two polls of the system clock
|
||||
so we can bound any error */
|
||||
|
||||
SCH_GetLastEventTime(sys_time_cooked, NULL, sys_time_raw);
|
||||
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
if (status < 0) {
|
||||
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Convert RTC time into a struct timespec */
|
||||
return t_from_rtc(&rtc_raw, utc);
|
||||
}
|
||||
|
||||
static void
|
||||
read_from_device(int fd_, int event, void *any)
|
||||
{
|
||||
struct timespec sys_time;
|
||||
int status, error = 0;
|
||||
time_t rtc_t;
|
||||
|
||||
status = RTC_Linux_CheckInterrupt(fd);
|
||||
if (status < 0) {
|
||||
SCH_RemoveFileHandler(fd);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0); /* Likely to raise error too, but just to be sure... */
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return;
|
||||
} else if (status == 0) {
|
||||
/* Wait for the next interrupt, this one may be bogus */
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data & RTC_UF) == RTC_UF) {
|
||||
/* Update interrupt detected */
|
||||
rtc_t = RTC_Linux_ReadTimeAfterInterrupt(fd, rtc_on_utc, &sys_time, NULL);
|
||||
if (rtc_t == (time_t)-1) {
|
||||
error = 1;
|
||||
goto turn_off_interrupt;
|
||||
}
|
||||
|
||||
/* Read RTC time, sandwiched between two polls of the system clock
|
||||
so we can bound any error. */
|
||||
|
||||
SCH_GetLastEventTime(&sys_time, NULL, NULL);
|
||||
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
if (status < 0) {
|
||||
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||
error = 1;
|
||||
goto turn_off_interrupt;
|
||||
}
|
||||
|
||||
/* Convert RTC time into a struct timespec */
|
||||
rtc_tm.tm_sec = rtc_raw.tm_sec;
|
||||
rtc_tm.tm_min = rtc_raw.tm_min;
|
||||
rtc_tm.tm_hour = rtc_raw.tm_hour;
|
||||
rtc_tm.tm_mday = rtc_raw.tm_mday;
|
||||
rtc_tm.tm_mon = rtc_raw.tm_mon;
|
||||
rtc_tm.tm_year = rtc_raw.tm_year;
|
||||
rtc_tm.tm_wday = 0;
|
||||
|
||||
rtc_t = t_from_rtc(&rtc_tm);
|
||||
|
||||
if (rtc_t == (time_t)(-1)) {
|
||||
error = 1;
|
||||
goto turn_off_interrupt;
|
||||
}
|
||||
|
||||
process_reading(rtc_t, &sys_time);
|
||||
|
||||
if (n_samples < 4) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD;
|
||||
} else if (n_samples < 6) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
|
||||
} else if (n_samples < 10) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
|
||||
} else if (n_samples < 14) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
|
||||
} else {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
||||
}
|
||||
process_reading(rtc_t, &sys_time);
|
||||
|
||||
if (n_samples < 4) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD;
|
||||
} else if (n_samples < 6) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 1;
|
||||
} else if (n_samples < 10) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 2;
|
||||
} else if (n_samples < 14) {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 3;
|
||||
} else {
|
||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
||||
}
|
||||
|
||||
turn_off_interrupt:
|
||||
@@ -836,7 +857,7 @@ turn_off_interrupt:
|
||||
operating_mode = OM_NORMAL;
|
||||
(after_init_hook)(after_init_hook_arg);
|
||||
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
|
||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||
}
|
||||
@@ -848,7 +869,7 @@ turn_off_interrupt:
|
||||
DEBUG_LOG("Could not complete after trim relock due to errors");
|
||||
operating_mode = OM_NORMAL;
|
||||
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
|
||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||
}
|
||||
@@ -856,7 +877,7 @@ turn_off_interrupt:
|
||||
break;
|
||||
|
||||
case OM_NORMAL:
|
||||
switch_interrupts(0);
|
||||
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||
|
||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||
|
||||
@@ -878,7 +899,7 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
|
||||
|
||||
operating_mode = OM_INITIAL;
|
||||
timeout_id = 0;
|
||||
switch_interrupts(1);
|
||||
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -911,6 +932,33 @@ RTC_Linux_WriteParameters(void)
|
||||
return(retval);
|
||||
}
|
||||
|
||||
time_t
|
||||
RTC_Linux_ReadTimeNow(int fd, int utc,
|
||||
struct timespec *old_sys_cooked,
|
||||
struct timespec *old_sys_raw)
|
||||
{
|
||||
struct rtc_time rtc_raw, rtc_raw_retry;
|
||||
int status;
|
||||
|
||||
/* Retry reading the RTC until both read attempts give the same sec value.
|
||||
This way the race condition is prevented that the RTC has updated itself
|
||||
during the first read operation. */
|
||||
do {
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
if (status >= 0) {
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
||||
}
|
||||
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
||||
|
||||
/* Read system clock */
|
||||
if (old_sys_raw)
|
||||
LCL_ReadRawTime(old_sys_raw);
|
||||
if (old_sys_cooked)
|
||||
LCL_ReadCookedTime(old_sys_cooked, NULL);
|
||||
|
||||
return status >= 0 ? t_from_rtc(&rtc_raw, utc) : -1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
/* Try to set the system clock from the RTC, in the same manner as
|
||||
/sbin/hwclock -s would do. We're not as picky about OS version
|
||||
@@ -920,12 +968,10 @@ RTC_Linux_WriteParameters(void)
|
||||
int
|
||||
RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||
{
|
||||
int fd, status;
|
||||
struct rtc_time rtc_raw, rtc_raw_retry;
|
||||
struct tm rtc_tm;
|
||||
time_t rtc_t;
|
||||
double accumulated_error, sys_offset;
|
||||
struct timespec new_sys_time, old_sys_time;
|
||||
int fd;
|
||||
|
||||
coefs_file_name = CNF_GetRtcFile();
|
||||
|
||||
@@ -938,66 +984,41 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||
return 0; /* Can't open it, and won't be able to later */
|
||||
}
|
||||
|
||||
/* Retry reading the rtc until both read attempts give the same sec value.
|
||||
This way the race condition is prevented that the RTC has updated itself
|
||||
during the first read operation. */
|
||||
do {
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||
if (status >= 0) {
|
||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
||||
}
|
||||
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
||||
|
||||
/* Read system clock */
|
||||
LCL_ReadCookedTime(&old_sys_time, NULL);
|
||||
rtc_t = RTC_Linux_ReadTimeNow(fd, rtc_on_utc, &old_sys_time, NULL);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (status >= 0) {
|
||||
/* Convert to seconds since 1970 */
|
||||
rtc_tm.tm_sec = rtc_raw.tm_sec;
|
||||
rtc_tm.tm_min = rtc_raw.tm_min;
|
||||
rtc_tm.tm_hour = rtc_raw.tm_hour;
|
||||
rtc_tm.tm_mday = rtc_raw.tm_mday;
|
||||
rtc_tm.tm_mon = rtc_raw.tm_mon;
|
||||
rtc_tm.tm_year = rtc_raw.tm_year;
|
||||
if (rtc_t != (time_t)(-1)) {
|
||||
|
||||
rtc_t = t_from_rtc(&rtc_tm);
|
||||
|
||||
if (rtc_t != (time_t)(-1)) {
|
||||
|
||||
/* Work out approximatation to correct time (to about the
|
||||
nearest second) */
|
||||
if (valid_coefs_from_file) {
|
||||
accumulated_error = file_ref_offset +
|
||||
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
|
||||
} else {
|
||||
accumulated_error = 0.0;
|
||||
}
|
||||
|
||||
/* Correct time */
|
||||
|
||||
new_sys_time.tv_sec = rtc_t;
|
||||
/* Average error in the RTC reading */
|
||||
new_sys_time.tv_nsec = 500000000;
|
||||
|
||||
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
|
||||
|
||||
if (new_sys_time.tv_sec < driftfile_time) {
|
||||
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
|
||||
|
||||
/* Set system time only if the step is larger than 1 second */
|
||||
if (fabs(sys_offset) >= 1.0) {
|
||||
if (LCL_ApplyStepOffset(sys_offset))
|
||||
LOG(LOGS_INFO, "System time set from RTC");
|
||||
}
|
||||
/* Work out approximation to correct time (to about the
|
||||
nearest second) */
|
||||
if (valid_coefs_from_file) {
|
||||
accumulated_error = file_ref_offset +
|
||||
(rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm;
|
||||
} else {
|
||||
accumulated_error = 0.0;
|
||||
}
|
||||
|
||||
/* Correct time */
|
||||
|
||||
new_sys_time.tv_sec = rtc_t;
|
||||
/* Average error in the RTC reading */
|
||||
new_sys_time.tv_nsec = 500000000;
|
||||
|
||||
UTI_AddDoubleToTimespec(&new_sys_time, -accumulated_error, &new_sys_time);
|
||||
|
||||
if (new_sys_time.tv_sec < driftfile_time) {
|
||||
LOG(LOGS_WARN, "RTC time before last driftfile modification (ignored)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sys_offset = UTI_DiffTimespecsToDouble(&old_sys_time, &new_sys_time);
|
||||
|
||||
/* Set system time only if the step is larger than 1 second */
|
||||
if (fabs(sys_offset) >= 1.0) {
|
||||
if (LCL_ApplyStepOffset(sys_offset))
|
||||
LOG(LOGS_INFO, "System time set from RTC");
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@@ -1065,7 +1086,7 @@ RTC_Linux_Trim(void)
|
||||
/* And start rapid sampling, interrupts on now */
|
||||
SCH_RemoveTimeout(timeout_id);
|
||||
timeout_id = 0;
|
||||
switch_interrupts(1);
|
||||
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -42,4 +42,13 @@ extern int RTC_Linux_Trim(void);
|
||||
|
||||
extern void RTC_Linux_CycleLogFile(void);
|
||||
|
||||
extern int RTC_Linux_SwitchInterrupt(int fd, int on_off);
|
||||
extern int RTC_Linux_CheckInterrupt(int fd);
|
||||
extern time_t RTC_Linux_ReadTimeAfterInterrupt(int fd, int utc,
|
||||
struct timespec *sys_time_cooked,
|
||||
struct timespec *sys_time_raw);
|
||||
extern time_t RTC_Linux_ReadTimeNow(int fd, int utc,
|
||||
struct timespec *sys_time_cooked,
|
||||
struct timespec *sys_time_raw);
|
||||
|
||||
#endif /* _GOT_RTC_LINUX_H */
|
||||
|
||||
6
sched.c
6
sched.c
@@ -47,12 +47,6 @@ static int initialised = 0;
|
||||
/* One more than the highest file descriptor that is registered */
|
||||
static unsigned int one_highest_fd;
|
||||
|
||||
#ifndef FD_SETSIZE
|
||||
/* If FD_SETSIZE is not defined, assume that fd_set is implemented
|
||||
as a fixed size array of bits, possibly embedded inside a record */
|
||||
#define FD_SETSIZE (sizeof(fd_set) * 8)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
SCH_FileHandler handler;
|
||||
SCH_ArbitraryArgument arg;
|
||||
|
||||
@@ -28,18 +28,14 @@
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#ifdef HAVE_NETTLE_SIV_CMAC
|
||||
#include <nettle/siv-cmac.h>
|
||||
#else
|
||||
#include "siv_nettle_int.c"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETTLE_SIV_GCM
|
||||
#include <nettle/siv-gcm.h>
|
||||
#endif
|
||||
|
||||
#include "memory.h"
|
||||
#include "siv.h"
|
||||
#include "util.h"
|
||||
|
||||
struct SIV_Instance_Record {
|
||||
SIV_Algorithm algorithm;
|
||||
@@ -163,8 +159,7 @@ SIV_GetMaxNonceLength(SIV_Instance instance)
|
||||
int
|
||||
SIV_GetTagLength(SIV_Instance instance)
|
||||
{
|
||||
if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH)
|
||||
assert(0);
|
||||
BRIEF_ASSERT(instance->tag_length >= 1 && instance->tag_length <= SIV_MAX_TAG_LENGTH);
|
||||
return instance->tag_length;
|
||||
}
|
||||
|
||||
|
||||
452
siv_nettle_int.c
452
siv_nettle_int.c
@@ -1,452 +0,0 @@
|
||||
/* This is a single-file implementation of AES-SIV-CMAC-256 based on
|
||||
a patch for GNU Nettle by Nikos Mavrogiannopoulos */
|
||||
|
||||
/*
|
||||
AES-CMAC-128 (rfc 4493)
|
||||
Copyright (C) Stefan Metzmacher 2012
|
||||
Copyright (C) Jeremy Allison 2012
|
||||
Copyright (C) Michael Adam 2012
|
||||
Copyright (C) 2017, Red Hat Inc.
|
||||
|
||||
This file is part of GNU Nettle.
|
||||
|
||||
GNU Nettle is free software: you can redistribute it and/or
|
||||
modify it under the terms of either:
|
||||
|
||||
* the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
or
|
||||
|
||||
* the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
or both in parallel, as here.
|
||||
|
||||
GNU Nettle 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 copies of the GNU General Public License and
|
||||
the GNU Lesser General Public License along with this program. If
|
||||
not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
/* siv-aes128.c, siv-cmac.c, siv.h
|
||||
|
||||
AES-SIV, RFC5297
|
||||
SIV-CMAC, RFC5297
|
||||
|
||||
Copyright (C) 2017 Nikos Mavrogiannopoulos
|
||||
|
||||
This file is part of GNU Nettle.
|
||||
|
||||
GNU Nettle is free software: you can redistribute it and/or
|
||||
modify it under the terms of either:
|
||||
|
||||
* the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
or
|
||||
|
||||
* the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
or both in parallel, as here.
|
||||
|
||||
GNU Nettle 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 copies of the GNU General Public License and
|
||||
the GNU Lesser General Public License along with this program. If
|
||||
not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
/* cmac.h, siv-cmac.h, cmac-aes128.c
|
||||
|
||||
CMAC mode, as specified in RFC4493
|
||||
SIV-CMAC mode, as specified in RFC5297
|
||||
CMAC using AES128 as the underlying cipher.
|
||||
|
||||
Copyright (C) 2017 Red Hat, Inc.
|
||||
|
||||
Contributed by Nikos Mavrogiannopoulos
|
||||
|
||||
This file is part of GNU Nettle.
|
||||
|
||||
GNU Nettle is free software: you can redistribute it and/or
|
||||
modify it under the terms of either:
|
||||
|
||||
* the GNU Lesser General Public License as published by the Free
|
||||
Software Foundation; either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
or
|
||||
|
||||
* the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
or both in parallel, as here.
|
||||
|
||||
GNU Nettle 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 copies of the GNU General Public License and
|
||||
the GNU Lesser General Public License along with this program. If
|
||||
not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
# include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nettle/aes.h"
|
||||
#include "nettle/ctr.h"
|
||||
#include "nettle/macros.h"
|
||||
#include "nettle/memxor.h"
|
||||
#include "nettle/memops.h"
|
||||
|
||||
#include "nettle/nettle-types.h"
|
||||
|
||||
/* For SIV, the block size of the block cipher shall be 128 bits. */
|
||||
#define SIV_BLOCK_SIZE 16
|
||||
#define SIV_DIGEST_SIZE 16
|
||||
#define SIV_MIN_NONCE_SIZE 1
|
||||
|
||||
/*
|
||||
* SIV mode requires the aad and plaintext when building the IV, which
|
||||
* prevents streaming processing and it incompatible with the AEAD API.
|
||||
*/
|
||||
|
||||
/* AES_SIV_CMAC_256 */
|
||||
struct siv_cmac_aes128_ctx {
|
||||
struct aes128_ctx cipher;
|
||||
uint8_t s2vk[AES128_KEY_SIZE];
|
||||
};
|
||||
|
||||
struct cmac128_ctx
|
||||
{
|
||||
/* Key */
|
||||
union nettle_block16 K1;
|
||||
union nettle_block16 K2;
|
||||
|
||||
/* MAC state */
|
||||
union nettle_block16 X;
|
||||
|
||||
/* Block buffer */
|
||||
union nettle_block16 block;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
/* shift one and XOR with 0x87. */
|
||||
static void
|
||||
_cmac128_block_mulx(union nettle_block16 *dst,
|
||||
const union nettle_block16 *src)
|
||||
{
|
||||
uint64_t b1 = READ_UINT64(src->b);
|
||||
uint64_t b2 = READ_UINT64(src->b+8);
|
||||
|
||||
b1 = (b1 << 1) | (b2 >> 63);
|
||||
b2 <<= 1;
|
||||
|
||||
if (src->b[0] & 0x80)
|
||||
b2 ^= 0x87;
|
||||
|
||||
WRITE_UINT64(dst->b, b1);
|
||||
WRITE_UINT64(dst->b+8, b2);
|
||||
}
|
||||
|
||||
static void
|
||||
cmac128_set_key(struct cmac128_ctx *ctx, const void *cipher,
|
||||
nettle_cipher_func *encrypt)
|
||||
{
|
||||
static const uint8_t const_zero[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
union nettle_block16 *L = &ctx->block;
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
/* step 1 - generate subkeys k1 and k2 */
|
||||
encrypt(cipher, 16, L->b, const_zero);
|
||||
|
||||
_cmac128_block_mulx(&ctx->K1, L);
|
||||
_cmac128_block_mulx(&ctx->K2, &ctx->K1);
|
||||
}
|
||||
|
||||
#define MIN(x,y) ((x)<(y)?(x):(y))
|
||||
|
||||
static void
|
||||
cmac128_update(struct cmac128_ctx *ctx, const void *cipher,
|
||||
nettle_cipher_func *encrypt,
|
||||
size_t msg_len, const uint8_t *msg)
|
||||
{
|
||||
union nettle_block16 Y;
|
||||
/*
|
||||
* check if we expand the block
|
||||
*/
|
||||
if (ctx->index < 16)
|
||||
{
|
||||
size_t len = MIN(16 - ctx->index, msg_len);
|
||||
memcpy(&ctx->block.b[ctx->index], msg, len);
|
||||
msg += len;
|
||||
msg_len -= len;
|
||||
ctx->index += len;
|
||||
}
|
||||
|
||||
if (msg_len == 0) {
|
||||
/* if it is still the last block, we are done */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* now checksum everything but the last block
|
||||
*/
|
||||
memxor3(Y.b, ctx->X.b, ctx->block.b, 16);
|
||||
encrypt(cipher, 16, ctx->X.b, Y.b);
|
||||
|
||||
while (msg_len > 16)
|
||||
{
|
||||
memxor3(Y.b, ctx->X.b, msg, 16);
|
||||
encrypt(cipher, 16, ctx->X.b, Y.b);
|
||||
msg += 16;
|
||||
msg_len -= 16;
|
||||
}
|
||||
|
||||
/*
|
||||
* copy the last block, it will be processed in
|
||||
* cmac128_digest().
|
||||
*/
|
||||
memcpy(ctx->block.b, msg, msg_len);
|
||||
ctx->index = msg_len;
|
||||
}
|
||||
|
||||
static void
|
||||
cmac128_digest(struct cmac128_ctx *ctx, const void *cipher,
|
||||
nettle_cipher_func *encrypt,
|
||||
unsigned length,
|
||||
uint8_t *dst)
|
||||
{
|
||||
union nettle_block16 Y;
|
||||
|
||||
memset(ctx->block.b+ctx->index, 0, sizeof(ctx->block.b)-ctx->index);
|
||||
|
||||
/* re-use ctx->block for memxor output */
|
||||
if (ctx->index < 16)
|
||||
{
|
||||
ctx->block.b[ctx->index] = 0x80;
|
||||
memxor(ctx->block.b, ctx->K2.b, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
memxor(ctx->block.b, ctx->K1.b, 16);
|
||||
}
|
||||
|
||||
memxor3(Y.b, ctx->block.b, ctx->X.b, 16);
|
||||
|
||||
assert(length <= 16);
|
||||
if (length == 16)
|
||||
{
|
||||
encrypt(cipher, 16, dst, Y.b);
|
||||
}
|
||||
else
|
||||
{
|
||||
encrypt(cipher, 16, ctx->block.b, Y.b);
|
||||
memcpy(dst, ctx->block.b, length);
|
||||
}
|
||||
|
||||
/* reset state for re-use */
|
||||
memset(&ctx->X, 0, sizeof(ctx->X));
|
||||
ctx->index = 0;
|
||||
}
|
||||
|
||||
|
||||
#define CMAC128_CTX(type) \
|
||||
{ struct cmac128_ctx ctx; type cipher; }
|
||||
|
||||
/* NOTE: Avoid using NULL, as we don't include anything defining it. */
|
||||
#define CMAC128_SET_KEY(self, set_key, encrypt, cmac_key) \
|
||||
do { \
|
||||
(set_key)(&(self)->cipher, (cmac_key)); \
|
||||
if (0) (encrypt)(&(self)->cipher, ~(size_t) 0, \
|
||||
(uint8_t *) 0, (const uint8_t *) 0); \
|
||||
cmac128_set_key(&(self)->ctx, &(self)->cipher, \
|
||||
(nettle_cipher_func *) (encrypt)); \
|
||||
} while (0)
|
||||
|
||||
#define CMAC128_UPDATE(self, encrypt, length, src) \
|
||||
cmac128_update(&(self)->ctx, &(self)->cipher, \
|
||||
(nettle_cipher_func *)encrypt, (length), (src))
|
||||
|
||||
#define CMAC128_DIGEST(self, encrypt, length, digest) \
|
||||
(0 ? (encrypt)(&(self)->cipher, ~(size_t) 0, \
|
||||
(uint8_t *) 0, (const uint8_t *) 0) \
|
||||
: cmac128_digest(&(self)->ctx, &(self)->cipher, \
|
||||
(nettle_cipher_func *) (encrypt), \
|
||||
(length), (digest)))
|
||||
|
||||
struct cmac_aes128_ctx CMAC128_CTX(struct aes128_ctx);
|
||||
|
||||
static void
|
||||
cmac_aes128_set_key(struct cmac_aes128_ctx *ctx, const uint8_t *key)
|
||||
{
|
||||
CMAC128_SET_KEY(ctx, aes128_set_encrypt_key, aes128_encrypt, key);
|
||||
}
|
||||
|
||||
static void
|
||||
cmac_aes128_update (struct cmac_aes128_ctx *ctx,
|
||||
size_t length, const uint8_t *data)
|
||||
{
|
||||
CMAC128_UPDATE (ctx, aes128_encrypt, length, data);
|
||||
}
|
||||
|
||||
static void
|
||||
cmac_aes128_digest(struct cmac_aes128_ctx *ctx,
|
||||
size_t length, uint8_t *digest)
|
||||
{
|
||||
CMAC128_DIGEST(ctx, aes128_encrypt, length, digest);
|
||||
}
|
||||
|
||||
static const uint8_t const_one[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
};
|
||||
|
||||
static const uint8_t const_zero[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static
|
||||
void _siv_s2v(nettle_set_key_func *cmac_set_key,
|
||||
nettle_hash_update_func *cmac_update,
|
||||
nettle_hash_digest_func *cmac_digest,
|
||||
size_t cmac_ctx_size,
|
||||
const uint8_t *s2vk, size_t alength, const uint8_t *adata,
|
||||
size_t nlength, const uint8_t *nonce,
|
||||
size_t plength, const uint8_t *pdata,
|
||||
uint8_t *v)
|
||||
{
|
||||
uint8_t ctx[sizeof(struct cmac128_ctx)+sizeof(struct aes_ctx)];
|
||||
union nettle_block16 D, S, T;
|
||||
|
||||
assert(cmac_ctx_size <= sizeof (ctx));
|
||||
|
||||
cmac_set_key(ctx, s2vk);
|
||||
|
||||
if (nlength == 0 && alength == 0) {
|
||||
cmac_update(ctx, 16, const_one);
|
||||
cmac_digest(ctx, 16, v);
|
||||
return;
|
||||
}
|
||||
|
||||
cmac_update(ctx, 16, const_zero);
|
||||
cmac_digest(ctx, 16, D.b);
|
||||
|
||||
if (1) {
|
||||
_cmac128_block_mulx(&D, &D);
|
||||
cmac_update(ctx, alength, adata);
|
||||
cmac_digest(ctx, 16, S.b);
|
||||
|
||||
memxor(D.b, S.b, 16);
|
||||
}
|
||||
|
||||
if (nlength > 0) {
|
||||
_cmac128_block_mulx(&D, &D);
|
||||
cmac_update(ctx, nlength, nonce);
|
||||
cmac_digest(ctx, 16, S.b);
|
||||
|
||||
memxor(D.b, S.b, 16);
|
||||
}
|
||||
|
||||
/* Sn */
|
||||
if (plength >= 16) {
|
||||
cmac_update(ctx, plength-16, pdata);
|
||||
|
||||
pdata += plength-16;
|
||||
|
||||
memxor3(T.b, pdata, D.b, 16);
|
||||
} else {
|
||||
union nettle_block16 pad;
|
||||
|
||||
_cmac128_block_mulx(&T, &D);
|
||||
memcpy(pad.b, pdata, plength);
|
||||
pad.b[plength] = 0x80;
|
||||
if (plength+1 < 16)
|
||||
memset(&pad.b[plength+1], 0, 16-plength-1);
|
||||
|
||||
memxor(T.b, pad.b, 16);
|
||||
}
|
||||
|
||||
cmac_update(ctx, 16, T.b);
|
||||
cmac_digest(ctx, 16, v);
|
||||
}
|
||||
|
||||
static void
|
||||
siv_cmac_aes128_set_key(struct siv_cmac_aes128_ctx *ctx, const uint8_t *key)
|
||||
{
|
||||
memcpy(ctx->s2vk, key, 16);
|
||||
aes128_set_encrypt_key(&ctx->cipher, key+16);
|
||||
}
|
||||
|
||||
static void
|
||||
siv_cmac_aes128_encrypt_message(struct siv_cmac_aes128_ctx *ctx,
|
||||
size_t nlength, const uint8_t *nonce,
|
||||
size_t alength, const uint8_t *adata,
|
||||
size_t clength, uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
union nettle_block16 siv;
|
||||
size_t slength;
|
||||
|
||||
assert (clength >= SIV_DIGEST_SIZE);
|
||||
slength = clength - SIV_DIGEST_SIZE;
|
||||
|
||||
/* create CTR nonce */
|
||||
_siv_s2v((nettle_set_key_func*)cmac_aes128_set_key,
|
||||
(nettle_hash_update_func*)cmac_aes128_update,
|
||||
(nettle_hash_digest_func*)cmac_aes128_digest,
|
||||
sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata,
|
||||
nlength, nonce, slength, src, siv.b);
|
||||
memcpy(dst, siv.b, SIV_DIGEST_SIZE);
|
||||
siv.b[8] &= ~0x80;
|
||||
siv.b[12] &= ~0x80;
|
||||
|
||||
ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE,
|
||||
siv.b, slength, dst+SIV_DIGEST_SIZE, src);
|
||||
}
|
||||
|
||||
static int
|
||||
siv_cmac_aes128_decrypt_message(struct siv_cmac_aes128_ctx *ctx,
|
||||
size_t nlength, const uint8_t *nonce,
|
||||
size_t alength, const uint8_t *adata,
|
||||
size_t mlength, uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
union nettle_block16 siv;
|
||||
union nettle_block16 ctr;
|
||||
|
||||
memcpy(ctr.b, src, SIV_DIGEST_SIZE);
|
||||
ctr.b[8] &= ~0x80;
|
||||
ctr.b[12] &= ~0x80;
|
||||
|
||||
ctr_crypt(&ctx->cipher, (nettle_cipher_func *)aes128_encrypt, AES_BLOCK_SIZE,
|
||||
ctr.b, mlength, dst, src+SIV_DIGEST_SIZE);
|
||||
|
||||
/* create CTR nonce */
|
||||
_siv_s2v((nettle_set_key_func*)cmac_aes128_set_key,
|
||||
(nettle_hash_update_func*)cmac_aes128_update,
|
||||
(nettle_hash_digest_func*)cmac_aes128_digest,
|
||||
sizeof(struct cmac_aes128_ctx), ctx->s2vk, alength, adata,
|
||||
nlength, nonce, mlength, dst, siv.b);
|
||||
|
||||
return memeql_sec(siv.b, src, SIV_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
95
socket.c
95
socket.c
@@ -213,12 +213,25 @@ get_reusable_socket(int type, IPSockAddr *spec)
|
||||
/* ================================================== */
|
||||
|
||||
#if defined(SOCK_CLOEXEC) || defined(SOCK_NONBLOCK)
|
||||
|
||||
static int
|
||||
get_default_inet_domain(void)
|
||||
{
|
||||
#ifdef FEAT_IPV6
|
||||
if (!ip4_enabled && ip6_enabled)
|
||||
return AF_INET6;
|
||||
#endif
|
||||
return AF_INET;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
|
||||
{
|
||||
int sock_fd, fd_flags, fs_flags;
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_DGRAM | sock_flag, 0);
|
||||
sock_fd = socket(get_default_inet_domain(), SOCK_DGRAM | sock_flag, 0);
|
||||
if (sock_fd < 0)
|
||||
return 0;
|
||||
|
||||
@@ -526,7 +539,7 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *ifac
|
||||
else if (remote_addr)
|
||||
family = remote_addr->ip_addr.family;
|
||||
else
|
||||
family = IPADDR_INET4;
|
||||
family = !ip4_enabled && ip6_enabled ? IPADDR_INET6 : IPADDR_INET4;
|
||||
|
||||
switch (family) {
|
||||
case IPADDR_INET4:
|
||||
@@ -590,35 +603,44 @@ error:
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static socklen_t
|
||||
set_unix_sockaddr(struct sockaddr_un *sa, const char *addr)
|
||||
{
|
||||
size_t len = strlen(addr);
|
||||
|
||||
if (len + 1 > sizeof (sa->sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(sa, 0, sizeof (*sa));
|
||||
sa->sun_family = AF_UNIX;
|
||||
memcpy(sa->sun_path, addr, len);
|
||||
|
||||
return offsetof(struct sockaddr_un, sun_path) + len + 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
bind_unix_address(int sock_fd, const char *addr, int flags)
|
||||
{
|
||||
union sockaddr_all saddr;
|
||||
socklen_t saddr_len;
|
||||
|
||||
memset(&saddr, 0, sizeof (saddr));
|
||||
|
||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
||||
sizeof (saddr.un.sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||
saddr_len = set_unix_sockaddr(&saddr.un, addr);
|
||||
if (saddr_len == 0)
|
||||
return 0;
|
||||
}
|
||||
saddr.un.sun_family = AF_UNIX;
|
||||
|
||||
if (unlink(addr) < 0)
|
||||
DEBUG_LOG("Could not remove %s : %s", addr, strerror(errno));
|
||||
|
||||
/* PRV_BindSocket() doesn't support Unix sockets yet */
|
||||
if (bind(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
|
||||
if (bind(sock_fd, &saddr.sa, saddr_len) < 0) {
|
||||
DEBUG_LOG("Could not bind Unix socket to %s : %s", addr, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allow access to everyone with access to the directory if requested */
|
||||
if (flags & SCK_FLAG_ALL_PERMISSIONS && chmod(addr, 0666) < 0) {
|
||||
DEBUG_LOG("Could not change permissions of %s : %s", addr, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -628,17 +650,13 @@ static int
|
||||
connect_unix_address(int sock_fd, const char *addr)
|
||||
{
|
||||
union sockaddr_all saddr;
|
||||
socklen_t saddr_len;
|
||||
|
||||
memset(&saddr, 0, sizeof (saddr));
|
||||
|
||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
||||
sizeof (saddr.un.sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||
saddr_len = set_unix_sockaddr(&saddr.un, addr);
|
||||
if (saddr_len == 0)
|
||||
return 0;
|
||||
}
|
||||
saddr.un.sun_family = AF_UNIX;
|
||||
|
||||
if (connect(sock_fd, &saddr.sa, sizeof (saddr.un)) < 0) {
|
||||
if (connect(sock_fd, &saddr.sa, saddr_len) < 0) {
|
||||
DEBUG_LOG("Could not connect Unix socket to %s : %s", addr, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
@@ -853,8 +871,10 @@ static int
|
||||
process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
|
||||
SCK_Message *message)
|
||||
{
|
||||
int r = 1, path_len, max_path_len;
|
||||
struct cmsghdr *cmsg;
|
||||
int r = 1;
|
||||
|
||||
init_message_addresses(message, SCK_ADDR_UNSPEC);
|
||||
|
||||
if (msg->msg_namelen <= sizeof (union sockaddr_all) &&
|
||||
msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
|
||||
@@ -867,18 +887,23 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
|
||||
SCK_SockaddrToIPSockAddr(msg->msg_name, msg->msg_namelen, &message->remote_addr.ip);
|
||||
break;
|
||||
case AF_UNIX:
|
||||
/* Make sure the path is terminated by '\0' */
|
||||
max_path_len = sizeof (((struct sockaddr_un *)msg->msg_name)->sun_path);
|
||||
path_len = strnlen(((struct sockaddr_un *)msg->msg_name)->sun_path, max_path_len);
|
||||
if (path_len >= max_path_len) {
|
||||
DEBUG_LOG("Unterminated path");
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
init_message_addresses(message, SCK_ADDR_UNIX);
|
||||
message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path;
|
||||
break;
|
||||
default:
|
||||
init_message_addresses(message, SCK_ADDR_UNSPEC);
|
||||
DEBUG_LOG("Unexpected address");
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
init_message_addresses(message, SCK_ADDR_UNSPEC);
|
||||
|
||||
if (msg->msg_namelen > sizeof (union sockaddr_all)) {
|
||||
DEBUG_LOG("Truncated source address");
|
||||
r = 0;
|
||||
@@ -1050,9 +1075,8 @@ receive_messages(int sock_fd, int flags, int max_messages, int *num_messages)
|
||||
n = ARR_GetSize(recv_headers);
|
||||
n = MIN(n, max_messages);
|
||||
|
||||
if (n < 1 || n > MAX_RECV_MESSAGES ||
|
||||
n > ARR_GetSize(recv_messages) || n > ARR_GetSize(recv_sck_messages))
|
||||
assert(0);
|
||||
BRIEF_ASSERT(n >= 1 && n <= MAX_RECV_MESSAGES &&
|
||||
n <= ARR_GetSize(recv_messages) && n <= ARR_GetSize(recv_sck_messages));
|
||||
|
||||
recv_flags = get_recv_flags(flags);
|
||||
|
||||
@@ -1142,14 +1166,9 @@ send_message(int sock_fd, SCK_Message *message, int flags)
|
||||
(struct sockaddr *)&saddr, sizeof (saddr));
|
||||
break;
|
||||
case SCK_ADDR_UNIX:
|
||||
memset(&saddr, 0, sizeof (saddr));
|
||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s",
|
||||
message->remote_addr.path) >= sizeof (saddr.un.sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", message->remote_addr.path);
|
||||
saddr_len = set_unix_sockaddr(&saddr.un, message->remote_addr.path);
|
||||
if (saddr_len == 0)
|
||||
return 0;
|
||||
}
|
||||
saddr.un.sun_family = AF_UNIX;
|
||||
saddr_len = sizeof (saddr.un);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
|
||||
3
socket.h
3
socket.h
@@ -34,8 +34,7 @@
|
||||
#define SCK_FLAG_BLOCK 1
|
||||
#define SCK_FLAG_BROADCAST 2
|
||||
#define SCK_FLAG_RX_DEST_ADDR 4
|
||||
#define SCK_FLAG_ALL_PERMISSIONS 8
|
||||
#define SCK_FLAG_PRIV_BIND 16
|
||||
#define SCK_FLAG_PRIV_BIND 8
|
||||
|
||||
/* Flags for receiving and sending messages */
|
||||
#define SCK_FLAG_MSG_ERRQUEUE 1
|
||||
|
||||
235
sources.c
235
sources.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2024
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2025
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -54,7 +54,6 @@ static int initialised = 0;
|
||||
/* ================================================== */
|
||||
/* Structure used to hold info for selecting between sources */
|
||||
struct SelectInfo {
|
||||
int select_ok;
|
||||
double std_dev;
|
||||
double root_distance;
|
||||
double lo_limit;
|
||||
@@ -66,10 +65,11 @@ struct SelectInfo {
|
||||
/* This enum contains the flag values that are used to label
|
||||
each source */
|
||||
typedef enum {
|
||||
SRC_OK, /* OK so far, not a final status! */
|
||||
SRC_OK = 0, /* OK so far, not a final status! */
|
||||
SRC_UNSELECTABLE, /* Has noselect option set */
|
||||
SRC_BAD_STATS, /* Doesn't have valid stats data */
|
||||
SRC_UNSYNCHRONISED, /* Provides samples, but not synchronised */
|
||||
SRC_BAD_STRATUM, /* Has stratum outside of allowed range */
|
||||
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
|
||||
SRC_JITTERY, /* Had std dev larger than allowed maximum */
|
||||
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
|
||||
@@ -106,13 +106,20 @@ struct SRC_Instance_Record {
|
||||
/* Number of set bits in the reachability register */
|
||||
int reachability_size;
|
||||
|
||||
/* Updates since last reference update */
|
||||
/* Number of reachability updates with cleared register */
|
||||
int unreachable_run;
|
||||
|
||||
/* Maximum number of reachability updates with cleared register to still
|
||||
allow selection */
|
||||
int max_unreachable_run;
|
||||
|
||||
/* Number of selection updates since last reference update */
|
||||
int updates;
|
||||
|
||||
/* Updates left before allowing combining */
|
||||
/* Number of selection updates left before allowing combining again */
|
||||
int distant;
|
||||
|
||||
/* Updates with a status requiring source replacement */
|
||||
/* Number of selection updates with a status requiring source replacement */
|
||||
int bad;
|
||||
|
||||
/* Flag indicating the status of the source */
|
||||
@@ -144,9 +151,9 @@ struct SRC_Instance_Record {
|
||||
/* Flag indicating the source has a leap second vote */
|
||||
int leap_vote;
|
||||
|
||||
/* Flag indicating the source was already reported as
|
||||
a falseticker since the last selection change */
|
||||
int reported_falseticker;
|
||||
/* Flags indicating which status was already reported for
|
||||
the source since the last change of the system peer */
|
||||
char reported_status[SRC_SELECTED + 1];
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
@@ -191,6 +198,8 @@ static int forced_first_report; /* Flag to allow one failed selection to be
|
||||
|
||||
static double max_distance;
|
||||
static double max_jitter;
|
||||
static int max_stratum;
|
||||
static int min_stratum;
|
||||
static double reselect_distance;
|
||||
static double stratum_weight;
|
||||
static double combine_limit;
|
||||
@@ -206,8 +215,7 @@ static LOG_FileID logfileid;
|
||||
/* Forward prototype */
|
||||
|
||||
static void update_sel_options(void);
|
||||
static void unselect_selected_source(LOG_Severity severity, const char *format,
|
||||
const char *arg);
|
||||
static void unselect_selected_source(LOG_Severity severity, const char *format, ...);
|
||||
static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
double doffset, LCL_ChangeType change_type, void *anything);
|
||||
static void add_dispersion(double dispersion, void *anything);
|
||||
@@ -225,6 +233,8 @@ void SRC_Initialise(void) {
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
max_distance = CNF_GetMaxDistance();
|
||||
max_jitter = CNF_GetMaxJitter();
|
||||
max_stratum = CNF_GetMaxStratum();
|
||||
min_stratum = CNF_GetMinStratum();
|
||||
reselect_distance = CNF_GetReselectDistance();
|
||||
stratum_weight = CNF_GetStratumWeight();
|
||||
combine_limit = CNF_GetCombineLimit();
|
||||
@@ -260,7 +270,8 @@ void SRC_Finalise(void)
|
||||
|
||||
SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
|
||||
int sel_options, IPAddr *addr, int min_samples,
|
||||
int max_samples, double min_delay, double asymmetry)
|
||||
int max_samples, double min_delay, double asymmetry,
|
||||
int max_unreach)
|
||||
{
|
||||
SRC_Instance result;
|
||||
|
||||
@@ -296,6 +307,7 @@ SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authentic
|
||||
result->authenticated = authenticated;
|
||||
result->conf_sel_options = sel_options;
|
||||
result->sel_options = sel_options;
|
||||
result->max_unreachable_run = max_unreach;
|
||||
result->active = 0;
|
||||
|
||||
SRC_SetRefid(result, ref_id, addr);
|
||||
@@ -322,9 +334,8 @@ void SRC_DestroyInstance(SRC_Instance instance)
|
||||
last_updated_inst = NULL;
|
||||
|
||||
assert(initialised);
|
||||
if (instance->index < 0 || instance->index >= n_sources ||
|
||||
instance != sources[instance->index])
|
||||
assert(0);
|
||||
BRIEF_ASSERT(instance->index >= 0 && instance->index < n_sources &&
|
||||
instance == sources[instance->index]);
|
||||
|
||||
SST_DeleteInstance(instance->stats);
|
||||
dead_index = instance->index;
|
||||
@@ -340,7 +351,7 @@ void SRC_DestroyInstance(SRC_Instance instance)
|
||||
if (selected_source_index > dead_index)
|
||||
--selected_source_index;
|
||||
else if (selected_source_index == dead_index)
|
||||
unselect_selected_source(LOGS_INFO, NULL, NULL);
|
||||
unselect_selected_source(LOGS_INFO, NULL);
|
||||
|
||||
SRC_SelectSource(NULL);
|
||||
}
|
||||
@@ -353,6 +364,7 @@ SRC_ResetInstance(SRC_Instance instance)
|
||||
instance->updates = 0;
|
||||
instance->reachability = 0;
|
||||
instance->reachability_size = 0;
|
||||
instance->unreachable_run = 0;
|
||||
instance->distant = 0;
|
||||
instance->bad = 0;
|
||||
instance->status = SRC_BAD_STATS;
|
||||
@@ -360,8 +372,8 @@ SRC_ResetInstance(SRC_Instance instance)
|
||||
instance->stratum = 0;
|
||||
instance->leap = LEAP_Unsynchronised;
|
||||
instance->leap_vote = 0;
|
||||
instance->reported_falseticker = 0;
|
||||
|
||||
memset(instance->reported_status, 0, sizeof (instance->reported_status));
|
||||
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
|
||||
|
||||
SST_ResetInstance(instance->stats);
|
||||
@@ -526,6 +538,13 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
|
||||
if (inst->reachability_size < SOURCE_REACH_BITS)
|
||||
inst->reachability_size++;
|
||||
|
||||
if (inst->reachability == 0) {
|
||||
if (inst->unreachable_run < INT_MAX)
|
||||
inst->unreachable_run++;
|
||||
} else {
|
||||
inst->unreachable_run = 0;
|
||||
}
|
||||
|
||||
/* Check if special reference update mode failed */
|
||||
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
|
||||
REF_SetUnsynchronised();
|
||||
@@ -547,6 +566,7 @@ SRC_ResetReachability(SRC_Instance inst)
|
||||
{
|
||||
inst->reachability = 0;
|
||||
inst->reachability_size = 0;
|
||||
inst->unreachable_run = 0;
|
||||
SRC_UpdateReachability(inst, 0);
|
||||
}
|
||||
|
||||
@@ -620,20 +640,45 @@ update_sel_options(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
FORMAT_ATTRIBUTE_PRINTF(2, 3)
|
||||
static void
|
||||
log_selection_message(LOG_Severity severity, const char *format, const char *arg)
|
||||
log_selection_message(LOG_Severity severity, const char *format, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
if (REF_GetMode() != REF_ModeNormal)
|
||||
return;
|
||||
LOG(severity, format, arg);
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(buf, sizeof (buf), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
LOG(severity, "%s", buf);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
FORMAT_ATTRIBUTE_PRINTF(3, 4)
|
||||
static void
|
||||
log_selection_source(LOG_Severity severity, const char *format, SRC_Instance inst)
|
||||
log_selection_source(LOG_Severity severity, SRC_Instance inst, const char *format, ...)
|
||||
{
|
||||
char buf[320], *name, *ntp_name;
|
||||
char buf[320], buf2[256], *name, *ntp_name, *s;
|
||||
va_list ap;
|
||||
|
||||
if (REF_GetMode() != REF_ModeNormal)
|
||||
return;
|
||||
|
||||
va_start(ap, format);
|
||||
vsnprintf(buf2, sizeof (buf2), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* Replace ## with %s in the formatted string to be the source name */
|
||||
s = strstr(buf2, "##");
|
||||
if (!s || strchr(buf2, '%'))
|
||||
return;
|
||||
s[0] = '%';
|
||||
s[1] = 's';
|
||||
|
||||
name = source_to_string(inst);
|
||||
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
|
||||
@@ -643,7 +688,9 @@ log_selection_source(LOG_Severity severity, const char *format, SRC_Instance ins
|
||||
else
|
||||
snprintf(buf, sizeof (buf), "%s", name);
|
||||
|
||||
log_selection_message(severity, format, buf);
|
||||
LOG(severity, buf2, buf);
|
||||
|
||||
inst->reported_status[inst->status] = 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -686,7 +733,7 @@ source_to_string(SRC_Instance inst)
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
mark_source(SRC_Instance inst, SRC_Status status)
|
||||
set_source_status(SRC_Instance inst, SRC_Status status)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
@@ -696,7 +743,8 @@ mark_source(SRC_Instance inst, SRC_Status status)
|
||||
distance or jitter larger than the allowed maximums */
|
||||
if (inst == last_updated_inst) {
|
||||
if (inst->bad < INT_MAX &&
|
||||
(status == SRC_FALSETICKER || status == SRC_BAD_DISTANCE || status == SRC_JITTERY))
|
||||
(status == SRC_FALSETICKER || status == SRC_BAD_DISTANCE ||
|
||||
status == SRC_BAD_STRATUM || status == SRC_JITTERY))
|
||||
inst->bad++;
|
||||
else
|
||||
inst->bad = 0;
|
||||
@@ -731,6 +779,51 @@ mark_source(SRC_Instance inst, SRC_Status status)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
mark_source(SRC_Instance inst, SRC_Status status)
|
||||
{
|
||||
set_source_status(inst, status);
|
||||
|
||||
BRIEF_ASSERT(status >= SRC_OK && status < sizeof (inst->reported_status));
|
||||
|
||||
if (!inst->reported_status[status]) {
|
||||
switch (status) {
|
||||
case SRC_BAD_STRATUM:
|
||||
if (inst->bad < BAD_HANDLE_THRESHOLD)
|
||||
break;
|
||||
log_selection_source(LOGS_WARN, inst,
|
||||
"Stratum of ## %sstratum of %d",
|
||||
inst->stratum < min_stratum ? "below min" : "above max",
|
||||
inst->stratum < min_stratum ? min_stratum : max_stratum);
|
||||
break;
|
||||
case SRC_BAD_DISTANCE:
|
||||
if (inst->bad < BAD_HANDLE_THRESHOLD)
|
||||
break;
|
||||
log_selection_source(LOGS_WARN, inst,
|
||||
"Root distance of ## exceeds maxdistance of %.3f seconds",
|
||||
max_distance);
|
||||
break;
|
||||
case SRC_JITTERY:
|
||||
if (inst->bad < BAD_HANDLE_THRESHOLD)
|
||||
break;
|
||||
log_selection_source(LOGS_WARN, inst,
|
||||
"Jitter of ## exceeds maxjitter of %.3f seconds",
|
||||
max_jitter);
|
||||
break;
|
||||
case SRC_FALSETICKER:
|
||||
log_selection_source(LOGS_WARN, inst, "Detected falseticker ##");
|
||||
break;
|
||||
case SRC_SELECTED:
|
||||
log_selection_source(LOGS_INFO, inst, "Selected source ##");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
mark_ok_sources(SRC_Status status)
|
||||
{
|
||||
@@ -739,7 +832,8 @@ mark_ok_sources(SRC_Status status)
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
if (sources[i]->status != SRC_OK)
|
||||
continue;
|
||||
mark_source(sources[i], status);
|
||||
/* Don't log the status in this case (multiple sources at once) */
|
||||
set_source_status(sources[i], status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -749,16 +843,24 @@ mark_ok_sources(SRC_Status status)
|
||||
call providing a message or selection of another source, which resets the
|
||||
report_selection_loss flag. */
|
||||
|
||||
FORMAT_ATTRIBUTE_PRINTF(2, 3)
|
||||
static void
|
||||
unselect_selected_source(LOG_Severity severity, const char *format, const char *arg)
|
||||
unselect_selected_source(LOG_Severity severity, const char *format, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
if (selected_source_index != INVALID_SOURCE) {
|
||||
selected_source_index = INVALID_SOURCE;
|
||||
report_selection_loss = 1;
|
||||
}
|
||||
|
||||
if (report_selection_loss && format) {
|
||||
log_selection_message(severity, format, arg);
|
||||
va_start(ap, format);
|
||||
vsnprintf(buf, sizeof (buf), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_selection_message(severity, "%s", buf);
|
||||
report_selection_loss = 0;
|
||||
}
|
||||
}
|
||||
@@ -864,9 +966,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
struct timespec now, ref_time;
|
||||
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
|
||||
int max_badstat_reach, max_badstat_reach_size, n_badstats_sources;
|
||||
int max_sel_reach, max_sel_reach_size;
|
||||
int max_sel_reach, max_sel_reach_size, n_unreach_sources;
|
||||
int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
|
||||
int combined, stratum, min_stratum, max_score_index;
|
||||
int combined, stratum, min_sel_stratum, max_score_index;
|
||||
int orphan_stratum, orphan_source;
|
||||
double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
|
||||
double src_root_delay, src_root_dispersion;
|
||||
@@ -881,7 +983,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
}
|
||||
|
||||
if (n_sources == 0) {
|
||||
unselect_selected_source(LOGS_INFO, "Can't synchronise: no sources", NULL);
|
||||
unselect_selected_source(LOGS_WARN, "Can't synchronise: no sources");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -893,6 +995,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
n_endpoints = 0;
|
||||
n_sel_sources = n_sel_trust_sources = 0;
|
||||
n_badstats_sources = 0;
|
||||
n_unreach_sources = 0;
|
||||
sel_req_source = 0;
|
||||
max_sel_reach = max_badstat_reach = 0;
|
||||
max_sel_reach_size = max_badstat_reach_size = 0;
|
||||
@@ -915,13 +1018,15 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
continue;
|
||||
}
|
||||
|
||||
si = &sources[i]->sel_info;
|
||||
SST_GetSelectionData(sources[i]->stats, &now,
|
||||
&si->lo_limit, &si->hi_limit, &si->root_distance,
|
||||
&si->std_dev, &first_sample_ago,
|
||||
&si->last_sample_ago, &si->select_ok);
|
||||
/* Count unreachable sources for a warning message */
|
||||
if (sources[i]->reachability == 0)
|
||||
n_unreach_sources++;
|
||||
|
||||
if (!si->select_ok) {
|
||||
si = &sources[i]->sel_info;
|
||||
|
||||
if (!SST_GetSelectionData(sources[i]->stats, &now, &si->lo_limit, &si->hi_limit,
|
||||
&si->root_distance, &si->std_dev, &first_sample_ago,
|
||||
&si->last_sample_ago)) {
|
||||
++n_badstats_sources;
|
||||
mark_source(sources[i], SRC_BAD_STATS);
|
||||
if (max_badstat_reach < sources[i]->reachability)
|
||||
@@ -937,6 +1042,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Require the stratum to be in the allowed range */
|
||||
if (sources[i]->stratum < min_stratum || sources[i]->stratum > max_stratum) {
|
||||
mark_source(sources[i], SRC_BAD_STRATUM);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Include extra dispersion in the root distance of sources that don't
|
||||
have new samples (the last sample is older than span of all samples) */
|
||||
if (first_sample_ago < 2.0 * si->last_sample_ago) {
|
||||
@@ -983,8 +1094,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
|
||||
/* Reachability is not a requirement for selection. An unreachable source
|
||||
can still be selected if its newest sample is not older than the oldest
|
||||
sample from reachable sources. */
|
||||
if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) {
|
||||
sample from reachable sources and the number of consecutive unreachable
|
||||
updates does not exceed the configured maximum. */
|
||||
if (sources[i]->reachability == 0 &&
|
||||
(si->last_sample_ago > max_reach_sample_ago ||
|
||||
sources[i]->unreachable_run > sources[i]->max_unreachable_run)) {
|
||||
mark_source(sources[i], SRC_STALE);
|
||||
continue;
|
||||
}
|
||||
@@ -1068,7 +1182,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
|
||||
max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
|
||||
mark_ok_sources(SRC_WAITS_STATS);
|
||||
unselect_selected_source(LOGS_INFO, NULL, NULL);
|
||||
unselect_selected_source(LOGS_INFO, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1082,7 +1196,8 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
|
||||
if (n_endpoints == 0) {
|
||||
/* No sources provided valid endpoints */
|
||||
unselect_selected_source(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
|
||||
unselect_selected_source(LOGS_WARN, "Can't synchronise: no selectable sources"
|
||||
" (%d unreachable sources)", n_unreach_sources);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1156,12 +1271,23 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
assert(depth == 0 && trust_depth == 0);
|
||||
assert(2 * n_sel_sources == n_endpoints);
|
||||
|
||||
if ((best_trust_depth == 0 && best_depth <= n_sel_sources / 2) ||
|
||||
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
|
||||
if (best_trust_depth > 0) {
|
||||
best_depth = best_trust_depth;
|
||||
n_sel_sources = n_sel_trust_sources;
|
||||
}
|
||||
|
||||
if (best_depth <= n_sel_sources / 2) {
|
||||
/* 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);
|
||||
if (best_depth < 2)
|
||||
log_selection_message(LOGS_WARN, "%s (no agreement among %d %ssources)",
|
||||
"Can't synchronise: no majority", n_sel_sources,
|
||||
best_trust_depth > 0 ? "trusted " : "");
|
||||
else
|
||||
log_selection_message(LOGS_WARN, "%s (only %d of %d %ssources agree)",
|
||||
"Can't synchronise: no majority", best_depth,
|
||||
n_sel_sources, best_trust_depth > 0 ? "trusted " : "");
|
||||
reported_no_majority = 1;
|
||||
report_selection_loss = 0;
|
||||
}
|
||||
@@ -1212,15 +1338,11 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
sel_req_source = 0;
|
||||
} else {
|
||||
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()) {
|
||||
unselect_selected_source(LOGS_INFO, "Can't synchronise: %s selectable sources",
|
||||
unselect_selected_source(LOGS_WARN, "Can't synchronise: %s selectable sources",
|
||||
!n_sel_sources ? "no" :
|
||||
sel_req_source ? "no required source in" : "not enough");
|
||||
mark_ok_sources(SRC_WAITS_SOURCES);
|
||||
@@ -1259,12 +1381,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
/* Find minimum stratum */
|
||||
|
||||
index = sel_sources[0];
|
||||
min_stratum = sources[index]->stratum;
|
||||
min_sel_stratum = sources[index]->stratum;
|
||||
for (i = 1; i < n_sel_sources; i++) {
|
||||
index = sel_sources[i];
|
||||
stratum = sources[index]->stratum;
|
||||
if (stratum < min_stratum)
|
||||
min_stratum = stratum;
|
||||
if (min_sel_stratum > stratum)
|
||||
min_sel_stratum = stratum;
|
||||
}
|
||||
|
||||
/* Update scores and find the source with maximum score */
|
||||
@@ -1275,7 +1397,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
|
||||
if (selected_source_index != INVALID_SOURCE)
|
||||
sel_src_distance = sources[selected_source_index]->sel_info.root_distance +
|
||||
(sources[selected_source_index]->stratum - min_stratum) * stratum_weight;
|
||||
(sources[selected_source_index]->stratum - min_sel_stratum) * stratum_weight;
|
||||
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
/* Reset score for non-selectable sources */
|
||||
@@ -1287,7 +1409,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
}
|
||||
|
||||
distance = sources[i]->sel_info.root_distance +
|
||||
(sources[i]->stratum - min_stratum) * stratum_weight;
|
||||
(sources[i]->stratum - min_sel_stratum) * stratum_weight;
|
||||
if (sources[i]->type == SRC_NTP)
|
||||
distance += reselect_distance;
|
||||
|
||||
@@ -1329,19 +1451,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
/* Before selecting the new synchronisation source wait until the reference
|
||||
can be updated */
|
||||
if (sources[max_score_index]->updates == 0) {
|
||||
unselect_selected_source(LOGS_INFO, NULL, NULL);
|
||||
unselect_selected_source(LOGS_INFO, NULL);
|
||||
mark_ok_sources(SRC_WAITS_UPDATE);
|
||||
return;
|
||||
}
|
||||
|
||||
selected_source_index = max_score_index;
|
||||
log_selection_source(LOGS_INFO, "Selected source %s", sources[selected_source_index]);
|
||||
|
||||
/* New source has been selected, reset all scores */
|
||||
for (i = 0; i < n_sources; i++) {
|
||||
sources[i]->sel_score = 1.0;
|
||||
sources[i]->distant = 0;
|
||||
sources[i]->reported_falseticker = 0;
|
||||
memset(sources[i]->reported_status, 0, sizeof (sources[i]->reported_status));
|
||||
}
|
||||
|
||||
reported_no_majority = 0;
|
||||
@@ -1813,6 +1934,8 @@ get_status_char(SRC_Status status)
|
||||
return 'M';
|
||||
case SRC_UNSYNCHRONISED:
|
||||
return 's';
|
||||
case SRC_BAD_STRATUM:
|
||||
return 'r';
|
||||
case SRC_BAD_DISTANCE:
|
||||
return 'd';
|
||||
case SRC_JITTERY:
|
||||
|
||||
@@ -69,7 +69,8 @@ typedef enum {
|
||||
|
||||
extern SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int authenticated,
|
||||
int sel_options, IPAddr *addr, int min_samples,
|
||||
int max_samples, double min_delay, double asymmetry);
|
||||
int max_samples, double min_delay, double asymmetry,
|
||||
int max_unreach);
|
||||
|
||||
/* Function to get rid of a source when it is being unconfigured.
|
||||
This may cause the current reference source to be reselected, if this
|
||||
|
||||
@@ -644,22 +644,16 @@ SST_GetFrequencyRange(SST_Stats inst,
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
double *offset_lo_limit,
|
||||
double *offset_hi_limit,
|
||||
double *root_distance,
|
||||
double *std_dev,
|
||||
double *first_sample_ago,
|
||||
double *last_sample_ago,
|
||||
int *select_ok)
|
||||
int
|
||||
SST_GetSelectionData(SST_Stats inst, struct timespec *now, double *offset_lo_limit,
|
||||
double *offset_hi_limit, double *root_distance, double *std_dev,
|
||||
double *first_sample_ago, double *last_sample_ago)
|
||||
{
|
||||
double offset, sample_elapsed;
|
||||
int i, j;
|
||||
|
||||
if (!inst->n_samples) {
|
||||
*select_ok = 0;
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = get_runsbuf_index(inst, inst->best_single_sample);
|
||||
@@ -675,38 +669,25 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
*offset_lo_limit = offset - *root_distance;
|
||||
*offset_hi_limit = offset + *root_distance;
|
||||
|
||||
#if 0
|
||||
double average_offset, elapsed;
|
||||
int average_ok;
|
||||
/* average_ok ignored for now */
|
||||
elapsed = UTI_DiffTimespecsToDouble(now, &inst->offset_time);
|
||||
average_offset = inst->estimated_offset + inst->estimated_frequency * elapsed;
|
||||
if (fabs(average_offset - offset) <=
|
||||
inst->peer_dispersions[j] + 0.5 * inst->peer_delays[i]) {
|
||||
average_ok = 1;
|
||||
} else {
|
||||
average_ok = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
i = get_runsbuf_index(inst, 0);
|
||||
*first_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
|
||||
i = get_runsbuf_index(inst, inst->n_samples - 1);
|
||||
*last_sample_ago = UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]);
|
||||
|
||||
*select_ok = inst->regression_ok;
|
||||
|
||||
/* 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 */
|
||||
if (!*select_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
|
||||
if (!inst->regression_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
|
||||
inst->n_samples == inst->max_samples) {
|
||||
*std_dev = CNF_GetMaxJitter();
|
||||
*select_ok = 1;
|
||||
} else if (!inst->regression_ok) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f selok=%d",
|
||||
DEBUG_LOG("n=%d off=%f dist=%f sd=%f first_ago=%f last_ago=%f",
|
||||
inst->n_samples, offset, *root_distance, *std_dev,
|
||||
*first_sample_ago, *last_sample_ago, *select_ok);
|
||||
*first_sample_ago, *last_sample_ago);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -67,15 +67,10 @@ extern void SST_DoNewRegression(SST_Stats inst);
|
||||
extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
|
||||
|
||||
/* Get data needed for selection */
|
||||
extern void
|
||||
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
double *offset_lo_limit,
|
||||
double *offset_hi_limit,
|
||||
double *root_distance,
|
||||
double *variance,
|
||||
double *first_sample_ago,
|
||||
double *last_sample_ago,
|
||||
int *select_ok);
|
||||
extern int SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
double *offset_lo_limit, double *offset_hi_limit,
|
||||
double *root_distance, double *variance,
|
||||
double *first_sample_ago, double *last_sample_ago);
|
||||
|
||||
/* Get data needed when setting up tracking on this source */
|
||||
extern void
|
||||
|
||||
@@ -49,6 +49,7 @@ typedef struct {
|
||||
int max_sources;
|
||||
int min_samples;
|
||||
int max_samples;
|
||||
int max_unreach;
|
||||
int filter_length;
|
||||
int interleaved;
|
||||
int sel_options;
|
||||
@@ -79,6 +80,7 @@ typedef struct {
|
||||
#define SRC_DEFAULT_MAXSOURCES 4
|
||||
#define SRC_DEFAULT_MINSAMPLES (-1)
|
||||
#define SRC_DEFAULT_MAXSAMPLES (-1)
|
||||
#define SRC_DEFAULT_MAXUNREACH 100000
|
||||
#define SRC_DEFAULT_ASYMMETRY 1.0
|
||||
#define SRC_DEFAULT_NTSPORT 4460
|
||||
#define SRC_DEFAULT_CERTSET 0
|
||||
|
||||
313
stubs.c
313
stubs.c
@@ -49,66 +49,6 @@
|
||||
#include "sched.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(FEAT_NTP) && !defined(FEAT_ASYNCDNS)
|
||||
|
||||
/* This is a blocking implementation used when asynchronous resolving is not available */
|
||||
|
||||
struct DNS_Async_Instance {
|
||||
const char *name;
|
||||
DNS_NameResolveHandler handler;
|
||||
void *arg;
|
||||
int pipe[2];
|
||||
};
|
||||
|
||||
static void
|
||||
resolve_name(int fd, int event, void *anything)
|
||||
{
|
||||
struct DNS_Async_Instance *inst;
|
||||
IPAddr addrs[DNS_MAX_ADDRESSES];
|
||||
DNS_Status status;
|
||||
int i;
|
||||
|
||||
inst = (struct DNS_Async_Instance *)anything;
|
||||
|
||||
SCH_RemoveFileHandler(inst->pipe[0]);
|
||||
close(inst->pipe[0]);
|
||||
close(inst->pipe[1]);
|
||||
|
||||
status = PRV_Name2IPAddress(inst->name, addrs, DNS_MAX_ADDRESSES);
|
||||
|
||||
for (i = 0; status == DNS_Success && i < DNS_MAX_ADDRESSES &&
|
||||
addrs[i].family != IPADDR_UNSPEC; i++)
|
||||
;
|
||||
|
||||
(inst->handler)(status, i, addrs, inst->arg);
|
||||
|
||||
Free(inst);
|
||||
}
|
||||
|
||||
void
|
||||
DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *anything)
|
||||
{
|
||||
struct DNS_Async_Instance *inst;
|
||||
|
||||
inst = MallocNew(struct DNS_Async_Instance);
|
||||
inst->name = name;
|
||||
inst->handler = handler;
|
||||
inst->arg = anything;
|
||||
|
||||
if (pipe(inst->pipe))
|
||||
LOG_FATAL("pipe() failed");
|
||||
|
||||
UTI_FdSetCloexec(inst->pipe[0]);
|
||||
UTI_FdSetCloexec(inst->pipe[1]);
|
||||
|
||||
SCH_AddFileHandler(inst->pipe[0], SCH_FILE_INPUT, resolve_name, inst);
|
||||
|
||||
if (write(inst->pipe[1], "", 1) < 0)
|
||||
;
|
||||
}
|
||||
|
||||
#endif /* !FEAT_ASYNCDNS */
|
||||
|
||||
#ifndef FEAT_CMDMON
|
||||
|
||||
void
|
||||
@@ -144,259 +84,6 @@ MNL_Finalise(void)
|
||||
|
||||
#endif /* !FEAT_CMDMON */
|
||||
|
||||
#ifndef FEAT_NTP
|
||||
|
||||
void
|
||||
NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NCR_Initialise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NCR_Finalise(void)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
NCR_CheckAccessRestriction(IPAddr *ip_addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
NIO_Initialise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NIO_Finalise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSR_Initialise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSR_Finalise(void)
|
||||
{
|
||||
}
|
||||
|
||||
NSR_Status
|
||||
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||
SourceParameters *params, uint32_t *conf_id)
|
||||
{
|
||||
return NSR_TooManySources;
|
||||
}
|
||||
|
||||
NSR_Status
|
||||
NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
|
||||
SourceParameters *params, uint32_t *conf_id)
|
||||
{
|
||||
return NSR_TooManySources;
|
||||
}
|
||||
|
||||
const char *
|
||||
NSR_StatusToString(NSR_Status status)
|
||||
{
|
||||
return "NTP not supported";
|
||||
}
|
||||
|
||||
NSR_Status
|
||||
NSR_RemoveSource(IPAddr *address)
|
||||
{
|
||||
return NSR_NoSuchSource;
|
||||
}
|
||||
|
||||
void
|
||||
NSR_RemoveSourcesById(uint32_t conf_id)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSR_RemoveAllSources(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSR_HandleBadSource(IPAddr *address)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
NSR_RefreshAddresses(void)
|
||||
{
|
||||
}
|
||||
|
||||
char *
|
||||
NSR_GetName(IPAddr *address)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
|
||||
{
|
||||
if (handler)
|
||||
(handler)();
|
||||
}
|
||||
|
||||
void
|
||||
NSR_ResolveSources(void)
|
||||
{
|
||||
}
|
||||
|
||||
void NSR_StartSources(void)
|
||||
{
|
||||
}
|
||||
|
||||
void NSR_AutoStartSources(void)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples,
|
||||
IPAddr *mask, IPAddr *address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
NSR_GetLocalRefid(IPAddr *address)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyMinpoll(IPAddr *address, int new_minpoll)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyMaxpoll(IPAddr *address, int new_maxpoll)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyMaxdelay(IPAddr *address, double new_max_delay)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyMaxdelayratio(IPAddr *address, double new_max_delay_ratio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_dev_ratio)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyOffset(IPAddr *address, double new_offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
NSR_ReportSource(RPT_SourceReport *report, struct timespec *now)
|
||||
{
|
||||
memset(report, 0, sizeof (*report));
|
||||
}
|
||||
|
||||
int
|
||||
NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
NSR_GetNTPReport(RPT_NTPReport *report)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
NSR_GetActivityReport(RPT_ActivityReport *report)
|
||||
{
|
||||
memset(report, 0, sizeof (*report));
|
||||
}
|
||||
|
||||
void
|
||||
NSR_DumpAuthData(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef FEAT_CMDMON
|
||||
|
||||
void
|
||||
CLG_Initialise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CLG_Finalise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
DNS_SetAddressFamily(int family)
|
||||
{
|
||||
}
|
||||
|
||||
DNS_Status
|
||||
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
|
||||
{
|
||||
return DNS_Failure;
|
||||
}
|
||||
|
||||
void
|
||||
KEY_Initialise(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
KEY_Finalise(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !FEAT_CMDMON */
|
||||
#endif /* !FEAT_NTP */
|
||||
|
||||
#ifndef FEAT_REFCLOCK
|
||||
void
|
||||
RCL_Initialise(void)
|
||||
|
||||
176
sys_linux.c
176
sys_linux.c
@@ -5,6 +5,7 @@
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) John G. Hasler 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2018
|
||||
* Copyright (C) Shachar Raindel 2025
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -38,6 +39,12 @@
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_SCFILTER
|
||||
#include <sys/prctl.h>
|
||||
#include <seccomp.h>
|
||||
@@ -48,9 +55,6 @@
|
||||
#ifdef FEAT_RTC
|
||||
#include <linux/rtc.h>
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
#include <linux/sockios.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_PRIVDROP
|
||||
@@ -59,11 +63,13 @@
|
||||
#endif
|
||||
|
||||
#include "sys_linux.h"
|
||||
#include "sys_linux_scmp.h"
|
||||
#include "sys_timex.h"
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "privops.h"
|
||||
#include "socket.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Frequency scale to convert from ppm to the timex freq */
|
||||
@@ -91,9 +97,6 @@ static int max_tick_bias;
|
||||
static int hz;
|
||||
static double dhz; /* And dbl prec version of same for arithmetic */
|
||||
|
||||
/* Flag indicating whether adjtimex() can step the clock */
|
||||
static int have_setoffset;
|
||||
|
||||
/* The assumed rate at which the effective frequency and tick values are
|
||||
updated in the kernel */
|
||||
static int tick_update_hz;
|
||||
@@ -293,16 +296,11 @@ get_version_specific_details(void)
|
||||
get_kernel_version(&major, &minor, &patch);
|
||||
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
|
||||
|
||||
if (kernelvercmp(major, minor, patch, 2, 2, 0) < 0) {
|
||||
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
|
||||
LOG_FATAL("Kernel version not supported, sorry.");
|
||||
}
|
||||
|
||||
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
|
||||
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
|
||||
/* In tickless kernels before 2.6.33 the frequency is updated in
|
||||
a half-second interval */
|
||||
tick_update_hz = 2;
|
||||
} else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
|
||||
if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
|
||||
/* In kernels before 4.19 the frequency is updated only on internal ticks
|
||||
(CONFIG_HZ). As their rate cannot be reliably detected from the user
|
||||
space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
|
||||
@@ -310,13 +308,6 @@ get_version_specific_details(void)
|
||||
tick_update_hz = 100;
|
||||
}
|
||||
|
||||
/* ADJ_SETOFFSET support */
|
||||
if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
|
||||
have_setoffset = 0;
|
||||
} else {
|
||||
have_setoffset = 1;
|
||||
}
|
||||
|
||||
DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
|
||||
hz, nominal_tick, max_tick_bias, tick_update_hz);
|
||||
}
|
||||
@@ -337,34 +328,6 @@ reset_adjtime_offset(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
test_step_offset(void)
|
||||
{
|
||||
struct timex txc;
|
||||
|
||||
/* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
|
||||
This seems to be the only way how to verify that the kernel really
|
||||
supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
|
||||
mode. */
|
||||
|
||||
txc.modes = MOD_MAXERROR;
|
||||
txc.maxerror = 0;
|
||||
|
||||
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror != 0)
|
||||
return 0;
|
||||
|
||||
txc.modes = ADJ_SETOFFSET | ADJ_NANO;
|
||||
txc.time.tv_sec = 0;
|
||||
txc.time.tv_usec = 0;
|
||||
|
||||
if (SYS_Timex_Adjust(&txc, 1) < 0 || txc.maxerror < 100000)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
report_time_adjust_blockers(void)
|
||||
{
|
||||
@@ -387,15 +350,10 @@ SYS_Linux_Initialise(void)
|
||||
|
||||
reset_adjtime_offset();
|
||||
|
||||
if (have_setoffset && !test_step_offset()) {
|
||||
LOG(LOGS_INFO, "adjtimex() doesn't support ADJ_SETOFFSET");
|
||||
have_setoffset = 0;
|
||||
}
|
||||
|
||||
SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
|
||||
1.0 / tick_update_hz,
|
||||
read_frequency, set_frequency,
|
||||
have_setoffset ? apply_step_offset : NULL,
|
||||
apply_step_offset,
|
||||
0.0, 0.0, NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -771,6 +729,14 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
|
||||
SCMP_A1(SCMP_CMP_EQ, ioctls[i])) < 0)
|
||||
goto add_failed;
|
||||
}
|
||||
|
||||
/* Allow selected ioctls that need to be specified in a separate
|
||||
file to avoid conflicting headers (e.g. TCGETS2) */
|
||||
for (i = 0; SYS_Linux_GetExtraScmpIoctl(i) != 0; i++) {
|
||||
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
|
||||
SCMP_A1(SCMP_CMP_EQ, SYS_Linux_GetExtraScmpIoctl(i))) < 0)
|
||||
goto add_failed;
|
||||
}
|
||||
}
|
||||
|
||||
if (seccomp_load(ctx) < 0)
|
||||
@@ -902,33 +868,99 @@ get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_OpenPHC(const char *path, int phc_index)
|
||||
/* Make sure an FD is a PHC. Return the FD if it is, or close the FD
|
||||
and return -1 if it is not. */
|
||||
|
||||
static int
|
||||
verify_fd_is_phc(int phc_fd)
|
||||
{
|
||||
struct ptp_clock_caps caps;
|
||||
char phc_path[64];
|
||||
int phc_fd;
|
||||
|
||||
if (!path) {
|
||||
if (snprintf(phc_path, sizeof (phc_path), "/dev/ptp%d", phc_index) >= sizeof (phc_path))
|
||||
return -1;
|
||||
path = phc_path;
|
||||
}
|
||||
|
||||
phc_fd = open(path, O_RDONLY);
|
||||
if (phc_fd < 0) {
|
||||
LOG(LOGS_ERR, "Could not open %s : %s", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Make sure it is a PHC */
|
||||
if (ioctl(phc_fd, PTP_CLOCK_GETCAPS, &caps)) {
|
||||
LOG(LOGS_ERR, "ioctl(%s) failed : %s", "PTP_CLOCK_GETCAPS", strerror(errno));
|
||||
close(phc_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
UTI_FdSetCloexec(phc_fd);
|
||||
return phc_fd;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
open_phc_by_iface_name(const char *iface, int flags)
|
||||
{
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
struct ethtool_ts_info ts_info;
|
||||
char phc_device[PATH_MAX];
|
||||
struct ifreq req;
|
||||
int sock_fd;
|
||||
|
||||
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
|
||||
if (sock_fd < 0)
|
||||
return -1;
|
||||
|
||||
memset(&req, 0, sizeof (req));
|
||||
memset(&ts_info, 0, sizeof (ts_info));
|
||||
|
||||
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", iface) >=
|
||||
sizeof (req.ifr_name)) {
|
||||
SCK_CloseSocket(sock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ts_info.cmd = ETHTOOL_GET_TS_INFO;
|
||||
req.ifr_data = (char *)&ts_info;
|
||||
|
||||
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
|
||||
SCK_CloseSocket(sock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Simplify failure paths by closing the socket as early as possible */
|
||||
SCK_CloseSocket(sock_fd);
|
||||
sock_fd = -1;
|
||||
|
||||
if (ts_info.phc_index < 0) {
|
||||
DEBUG_LOG("PHC missing on %s", req.ifr_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (snprintf(phc_device, sizeof (phc_device),
|
||||
"/dev/ptp%d", ts_info.phc_index) >= sizeof (phc_device))
|
||||
return -1;
|
||||
|
||||
return open(phc_device, flags);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_OpenPHC(const char *device, int flags)
|
||||
{
|
||||
int phc_fd = -1;
|
||||
|
||||
if (device[0] == '/') {
|
||||
phc_fd = open(device, flags);
|
||||
if (phc_fd >= 0)
|
||||
phc_fd = verify_fd_is_phc(phc_fd);
|
||||
}
|
||||
|
||||
if (phc_fd < 0) {
|
||||
phc_fd = open_phc_by_iface_name(device, flags);
|
||||
if (phc_fd < 0) {
|
||||
LOG(LOGS_ERR, "Could not open PHC (of) %s", device);
|
||||
return -1;
|
||||
}
|
||||
phc_fd = verify_fd_is_phc(phc_fd);
|
||||
}
|
||||
|
||||
if (phc_fd >= 0)
|
||||
UTI_FdSetCloexec(phc_fd);
|
||||
|
||||
return phc_fd;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ extern void SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext conte
|
||||
|
||||
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 *device, int flags);
|
||||
|
||||
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
|
||||
struct timespec tss[][3]);
|
||||
|
||||
44
sys_linux_scmp.c
Normal file
44
sys_linux_scmp.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2025
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Lists of values that are needed in seccomp filters but need to
|
||||
be compiled separately from sys_linux.c due to conflicting headers.
|
||||
*/
|
||||
|
||||
#include <linux/termios.h>
|
||||
|
||||
#include "sys_linux_scmp.h"
|
||||
|
||||
unsigned long
|
||||
SYS_Linux_GetExtraScmpIoctl(int index)
|
||||
{
|
||||
const unsigned long ioctls[] = {
|
||||
#ifdef TCGETS2
|
||||
/* Conflict between <linux/termios.h> and <sys/ioctl.h> */
|
||||
TCGETS2,
|
||||
#endif
|
||||
0
|
||||
};
|
||||
|
||||
return ioctls[index];
|
||||
}
|
||||
28
sys_linux_scmp.h
Normal file
28
sys_linux_scmp.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2025
|
||||
*
|
||||
* 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 lists that are needed in seccomp filters but need to
|
||||
be compiled separately from sys_linux.c due to conflicting headers.
|
||||
*/
|
||||
|
||||
extern unsigned long SYS_Linux_GetExtraScmpIoctl(int index);
|
||||
21
sys_timex.c
21
sys_timex.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015, 2017
|
||||
* Copyright (C) Miroslav Lichvar 2009-2012, 2014-2015, 2017, 2025
|
||||
*
|
||||
* 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
|
||||
@@ -257,6 +257,8 @@ SYS_Timex_Finalise(void)
|
||||
int
|
||||
SYS_Timex_Adjust(struct timex *txc, int ignore_error)
|
||||
{
|
||||
static long last_constant, last_freq;
|
||||
static int last_status = -1;
|
||||
int state;
|
||||
|
||||
#ifdef SOLARIS
|
||||
@@ -270,7 +272,24 @@ SYS_Timex_Adjust(struct timex *txc, int ignore_error)
|
||||
if (state < 0) {
|
||||
LOG(ignore_error ? LOGS_DEBUG : LOGS_FATAL,
|
||||
NTP_ADJTIME_NAME"(0x%x) failed : %s", txc->modes, strerror(errno));
|
||||
return state;
|
||||
}
|
||||
|
||||
/* This a good place to verify that nothing else is touching the clock,
|
||||
without making an additional timex call. A clock update is normally
|
||||
expected to have four driver calls:
|
||||
- set_sync_status - primarily updating leap status
|
||||
- set_frequency - correcting frequency error
|
||||
- set_frequency - correcting phase error
|
||||
- set_sync_status - updating leap and estimated/maximum error */
|
||||
if (last_status != -1 &&
|
||||
(((last_status ^ txc->status) & STA_PLL && !(txc->modes & MOD_STATUS)) ||
|
||||
(last_constant != txc->constant && !(txc->modes & MOD_TIMECONST)) ||
|
||||
(last_freq != txc->freq && !(txc->modes & MOD_FREQUENCY))))
|
||||
LOG(LOGS_WARN, "System clock interference detected (another NTP client?)");
|
||||
last_status = txc->status;
|
||||
last_constant = txc->constant;
|
||||
last_freq = txc->freq;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ for opts in \
|
||||
"--enable-debug" \
|
||||
"--enable-ntp-signd" \
|
||||
"--enable-scfilter" \
|
||||
"--disable-asyncdns" \
|
||||
"--disable-ipv6" \
|
||||
"--disable-privdrop" \
|
||||
"--disable-readline" \
|
||||
@@ -18,19 +17,13 @@ for opts in \
|
||||
"--disable-sechash" \
|
||||
"--disable-cmdmon" \
|
||||
"--disable-cmdmon --enable-scfilter" \
|
||||
"--disable-ntp" \
|
||||
"--disable-ntp --enable-scfilter" \
|
||||
"--disable-nts" \
|
||||
"--disable-refclock" \
|
||||
"--disable-timestamping" \
|
||||
"--disable-timestamping --disable-ntp" \
|
||||
"--disable-cmdmon --disable-ntp" \
|
||||
"--disable-cmdmon --disable-ntp --enable-scfilter" \
|
||||
"--disable-cmdmon --disable-refclock" \
|
||||
"--disable-cmdmon --disable-ntp --disable-refclock"
|
||||
"--disable-cmdmon --disable-refclock"
|
||||
do
|
||||
./configure $opts || exit 1
|
||||
make clean
|
||||
make "$@" || exit 1
|
||||
make -C test/unit check || exit 1
|
||||
make -C test/unit "$@" check || exit 1
|
||||
done
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Run the unit and simulation tests with different compiler sanitizers
|
||||
# and under valgrind
|
||||
|
||||
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
|
||||
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite --track-fds=yes"
|
||||
|
||||
cd ../..
|
||||
|
||||
@@ -25,6 +25,7 @@ touch Makefile
|
||||
for extra_config_opts in \
|
||||
"--all-privops" \
|
||||
"--disable-ipv6" \
|
||||
"--disable-cmdmon" \
|
||||
"--disable-nts" \
|
||||
"--disable-scfilter" \
|
||||
"--without-aes-gcm-siv" \
|
||||
|
||||
@@ -24,6 +24,12 @@ for falsetickers in 3 4; do
|
||||
# These check are expected to fail
|
||||
check_source_selection && test_fail
|
||||
check_sync && test_fail
|
||||
|
||||
if [ $falsetickers = 3 ]; then
|
||||
check_log_messages "Can't synchronise: no majority (only 2 of 5 sources agree)" 1 1 || test_fail
|
||||
else
|
||||
check_log_messages "Can't synchronise: no majority (no agreement among 5 sources)" 1 1 || test_fail
|
||||
fi
|
||||
done
|
||||
|
||||
# Sources with large asymmetric delay should be excluded
|
||||
@@ -37,4 +43,35 @@ check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
# Sources with large distance should be ignored
|
||||
|
||||
servers=1
|
||||
server_strata=2
|
||||
server_conf="maxclockerror 1000"
|
||||
jitter=1e-7
|
||||
base_delay="(* -1.0 (equal 0.1 (min time 600) 600) (equal 0.1 from 2) (equal 0.1 to 1))"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_source_selection && test_fail
|
||||
check_sync && test_fail
|
||||
|
||||
check_log_messages "Root distance of 192\.168\.123\.2 exceeds maxdistance of 3\." 1 1 || test_fail
|
||||
|
||||
# Sources with large jitter should be ignored
|
||||
|
||||
server_strata=1
|
||||
server_conf=$default_server_conf
|
||||
server_step="(pulse 64 64)"
|
||||
base_delay=$default_base_delay
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_source_selection && test_fail
|
||||
check_sync && test_fail
|
||||
|
||||
check_log_messages "Jitter of 192\.168\.123\.1 exceeds maxjitter of 1\." 1 1 || test_fail
|
||||
|
||||
test_pass
|
||||
|
||||
49
test/simulation/014-intermittent
Executable file
49
test/simulation/014-intermittent
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "intermittent connection"
|
||||
|
||||
# Pass packets only for 1200 seconds every 10000 seconds
|
||||
base_delay=$(cat <<-EOF | tr -d '\n'
|
||||
(+ 1e-4
|
||||
(* -1
|
||||
(equal 0.1 (min (% time 10000) 1200) 1200)))
|
||||
EOF
|
||||
)
|
||||
|
||||
time_max_limit=1e-1
|
||||
freq_max_limit=1e-2
|
||||
time_rms_limit=2e-3
|
||||
freq_rms_limit=2e-5
|
||||
limit=100000
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_log_messages "Can't.*no selectable sources (1 unreachable" 9 10 || test_fail
|
||||
check_log_messages "Selected source 192.168.123.1" 9 10 || test_fail
|
||||
|
||||
# Pass every 20th request
|
||||
base_delay=$(cat <<-EOF | tr -d '\n'
|
||||
(+ 1e-4
|
||||
(* -1
|
||||
(equal 0.1 from 2)
|
||||
(equal 0.1 (min (% (sum 1) 20) 1) 1)))
|
||||
EOF
|
||||
)
|
||||
|
||||
time_max_limit=1e-2
|
||||
freq_max_limit=1e-4
|
||||
time_rms_limit=5e-3
|
||||
max_sync_time=22000
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_log_messages "Can't.*no selectable sources (1 unreachable" 5 15 || test_fail
|
||||
check_log_messages "Selected source 192.168.123.1" 5 15 || test_fail
|
||||
|
||||
test_pass
|
||||
17
test/simulation/015-ipv6
Executable file
17
test/simulation/015-ipv6
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "IPv6 addressing"
|
||||
|
||||
check_config_h 'FEAT_IPV6 1' || test_skip
|
||||
|
||||
ip_family=6
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
test_pass
|
||||
@@ -52,7 +52,7 @@ client_conf="initstepslew 5 192.168.123.1 192.168.123.2"
|
||||
|
||||
min_sync_time=1
|
||||
max_sync_time=500
|
||||
server_conf="deny all"
|
||||
server_conf="deny 192.168.0.0/16"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
|
||||
@@ -18,15 +18,30 @@ max_sync_time=70
|
||||
chronyc_start=70
|
||||
chronyc_conf="tracking"
|
||||
|
||||
for refclock in "SHM 0" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
|
||||
for refclock in "SHM 0" "RTC /dev/rtc:utc" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
|
||||
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
|
||||
logdir tmp
|
||||
log refclocks"
|
||||
|
||||
if [[ $refclock =~ RTC ]]; then
|
||||
check_config_h 'FEAT_RTC 1' || continue
|
||||
wander=0.0
|
||||
freq_offset=0.0
|
||||
client_chronyd_options="-x"
|
||||
else
|
||||
wander=$default_wander
|
||||
freq_offset=$default_freq_offset
|
||||
client_chronyd_options=$default_client_chronyd_options
|
||||
fi
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
if [[ $refclock =~ RTC ]]; then
|
||||
check_chronyc_output "System time *: 0\.10000.... seconds fast" || test_fail
|
||||
else
|
||||
check_sync || test_fail
|
||||
fi
|
||||
check_chronyc_output "^Reference ID.*47505300 \(GPS\)
|
||||
Stratum.*: 4
|
||||
.*
|
||||
@@ -35,11 +50,7 @@ Root delay : 0.001000000 seconds
|
||||
Update interval : 16\.. seconds
|
||||
.*$" || test_fail
|
||||
|
||||
if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
|
||||
check_file_messages "20.* GPS.*[0-9] N " 620 750 refclocks.log || test_fail
|
||||
else
|
||||
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
|
||||
fi
|
||||
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
|
||||
check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
|
||||
rm -f tmp/refclocks.log
|
||||
done
|
||||
@@ -90,8 +101,8 @@ Root delay : 0\.000000001 seconds
|
||||
rm -f tmp/refclocks.log
|
||||
|
||||
min_sync_time=80
|
||||
max_sync_time=180
|
||||
chronyc_start=220
|
||||
max_sync_time=260
|
||||
chronyc_start=270
|
||||
client_conf="
|
||||
refclock SHM 0 refid NMEA offset 0.35 delay 0.1
|
||||
refclock PPS /dev/pps0
|
||||
@@ -112,8 +123,63 @@ Root delay : 0\.000000001 seconds
|
||||
check_file_messages "20.* PPS1.*[0-9] N " 800 960 refclocks.log || test_fail
|
||||
check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail
|
||||
rm -f tmp/refclocks.log
|
||||
|
||||
min_sync_time=80
|
||||
max_sync_time=180
|
||||
chronyc_start=220
|
||||
# Swapped order of SHM and PPS impacts first accepted sample
|
||||
client_conf="
|
||||
refclock PPS /dev/pps0
|
||||
refclock SHM 0 refid NMEA offset 0.35 delay 0.1
|
||||
logdir tmp
|
||||
log refclocks
|
||||
maxupdateskew 10000"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
# This fails occasionally due to the 4th unreachable PPS update
|
||||
# (made just before the SHM update) causing SHM unselection due to
|
||||
# a large root distance
|
||||
#check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
check_chronyc_output "^Reference ID.*50505330 \(PPS0\)
|
||||
Stratum.*: 1
|
||||
.*
|
||||
Root delay : 0\.000000001 seconds
|
||||
.*$" || test_fail
|
||||
|
||||
check_file_messages "20.* PPS0.*[0-9] N " 800 960 refclocks.log || test_fail
|
||||
check_file_messages "20.* PPS0.*- N " 50 63 refclocks.log || test_fail
|
||||
rm -f tmp/refclocks.log
|
||||
fi
|
||||
|
||||
export CLKNETSIM_PHC_JITTER_OFF=$[2 * 25 * 492]
|
||||
export CLKNETSIM_PHC_JITTER_ON=$[2 * 25 * 8]
|
||||
export CLKNETSIM_PHC_JITTER=1e-6
|
||||
refclock_offset=0.0
|
||||
refclock_jitter=1e-9
|
||||
min_sync_time=5
|
||||
max_sync_time=7
|
||||
time_max_limit=1e-7
|
||||
time_rms_limit=1e-8
|
||||
client_conf="refclock PHC /dev/ptp0:nocrossts poll 0
|
||||
logdir tmp
|
||||
log refclocks"
|
||||
chronyc_start=500
|
||||
chronyc_conf="sources"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
check_chronyc_output "^MS.*
|
||||
=*
|
||||
#\* PHC0 0 0 377 8 .*$" || test_fail
|
||||
rm -f tmp/refclocks.log
|
||||
|
||||
unset CLKNETSIM_PHC_JITTER_OFF
|
||||
unset CLKNETSIM_PHC_JITTER_ON
|
||||
export CLKNETSIM_PHC_JITTER=1e-7
|
||||
refclock_offset="(+ 0.399 (sum 1e-3))"
|
||||
refclock_jitter=1e-6
|
||||
servers=1
|
||||
|
||||
@@ -31,6 +31,17 @@ check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
if check_config_h 'FEAT_IPV6 1'; then
|
||||
ip_family=6
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
ip_family=$default_ip_family
|
||||
fi
|
||||
|
||||
freq_max_limit=$default_freq_max_limit
|
||||
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
|
||||
client_peer_options=""
|
||||
|
||||
@@ -114,7 +114,7 @@ limit=1
|
||||
for chronyc_conf in \
|
||||
"accheck 1.2.3.4" \
|
||||
"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 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 extfield F324 ipv6 ipv4" \
|
||||
"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 maxunreach 8 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 extfield F324 ipv6 ipv4" \
|
||||
"add server node1.net1.clk" \
|
||||
"allow 1.2.3.4" \
|
||||
"allow 1.2" \
|
||||
@@ -145,7 +145,7 @@ for chronyc_conf in \
|
||||
"dfreq 1.0e-3" \
|
||||
"doffset -1.0" \
|
||||
"dump" \
|
||||
"local stratum 5 distance 1.0 activate 0.5 orphan" \
|
||||
"local stratum 5 distance 1.0 activate 0.5 orphan waitsynced 100 waitunsynced 20" \
|
||||
"local off" \
|
||||
"makestep 10.0 3" \
|
||||
"makestep" \
|
||||
@@ -195,6 +195,38 @@ do
|
||||
check_chronyc_output "501 Not authorised$" || test_fail
|
||||
done
|
||||
|
||||
for chronyc_conf in \
|
||||
"activity" \
|
||||
"authdata" \
|
||||
"clients" \
|
||||
"manual list" \
|
||||
"ntpdata" \
|
||||
"rtcdata" \
|
||||
"selectdata" \
|
||||
"serverstats" \
|
||||
"smoothing" \
|
||||
"sourcename 192.168.123.1" \
|
||||
"sources" \
|
||||
"sourcestats" \
|
||||
"tracking"
|
||||
do
|
||||
server_conf="opencommands ${chronyc_conf% *}"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_chronyc_output "501 Not authorised$" && test_fail
|
||||
|
||||
server_conf="opencommands"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_chronyc_output "501 Not authorised$" || test_fail
|
||||
|
||||
server_conf="cmddeny 192.168.123.2"
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_chronyc_output "506 Cannot talk to daemon$" || test_fail
|
||||
done
|
||||
|
||||
server_conf="server 192.168.123.1"
|
||||
cmdmon_unix=1
|
||||
|
||||
chronyc_conf="
|
||||
@@ -242,14 +274,14 @@ Jitter asymmetry: \+0\.00
|
||||
NTP tests : 111 111 1110
|
||||
Interleaved : No
|
||||
Authenticated : No
|
||||
TX timestamping : Kernel
|
||||
RX timestamping : Kernel
|
||||
TX timestamping : (Daemon|Kernel)
|
||||
RX timestamping : (Daemon|Kernel)
|
||||
Total TX : 1
|
||||
Total RX : 1
|
||||
Total valid RX : 1
|
||||
Total good RX : 0
|
||||
Total kernel TX : [01]
|
||||
Total kernel RX : 1
|
||||
Total kernel RX : [01]
|
||||
Total HW TX : 0
|
||||
Total HW RX : 0
|
||||
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||
@@ -266,9 +298,9 @@ Authenticated NTP packets : 0
|
||||
Interleaved NTP packets : 0
|
||||
NTP timestamps held : 0
|
||||
NTP timestamp span : 0
|
||||
NTP daemon RX timestamps : 0
|
||||
NTP daemon RX timestamps : [01]
|
||||
NTP daemon TX timestamps : 1
|
||||
NTP kernel RX timestamps : 1
|
||||
NTP kernel RX timestamps : [01]
|
||||
NTP kernel TX timestamps : 0
|
||||
NTP hardware RX timestamps : 0
|
||||
NTP hardware TX timestamps : 0$" || test_fail
|
||||
@@ -392,7 +424,7 @@ cyclelogs
|
||||
dump
|
||||
dfreq 1.0e-3
|
||||
doffset -0.01
|
||||
local stratum 5 distance 1.0 orphan
|
||||
local stratum 5 distance 1.0 orphan waitsynced 100 waitunsynced 10
|
||||
local off
|
||||
makestep 10.0 3
|
||||
makestep
|
||||
|
||||
@@ -7,7 +7,7 @@ test_start "local options"
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
server_strata=3
|
||||
server_conf="local stratum 5 orphan
|
||||
server_conf="local stratum 5 orphan waitunsynced 0
|
||||
server 192.168.123.1
|
||||
server 192.168.123.2
|
||||
server 192.168.123.3"
|
||||
@@ -23,11 +23,62 @@ check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
|
||||
|
||||
limit=1000
|
||||
server_conf="local stratum 5 orphan
|
||||
server 192.168.123.1 minpoll 6 maxpoll 6
|
||||
server 192.168.123.2 minpoll 6 maxpoll 6
|
||||
server 192.168.123.3 minpoll 6 maxpoll 6"
|
||||
server_server_options="minpoll 6 maxpoll 6"
|
||||
client_start=0
|
||||
client_server_conf="
|
||||
server 192.168.123.1 minpoll 6 maxpoll 6
|
||||
server 192.168.123.2 minpoll 6 maxpoll 6
|
||||
server 192.168.123.3 minpoll 6 maxpoll 6"
|
||||
client_conf="logdir tmp
|
||||
log measurements"
|
||||
chronyc_start=700
|
||||
chronyc_conf=""
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_sync || test_fail
|
||||
check_file_messages "20.*123\.1.* 5 111 " 10 11 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.1.* [6-9] 111 " 0 0 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.2.* 5 111 " 2 4 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.2.* 6 111 " 7 9 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.2.* [7-9] 111 " 0 0 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.3.* 5 111 " 2 4 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.3.* 6 111 " 7 9 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.3.* [7-9] 111 " 0 0 measurements.log || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
server_conf="local stratum 5 orphan distance 0.0 waitsynced 150 waitunsynced 0"
|
||||
base_delay=$(cat <<-EOF | tr -d '\n'
|
||||
(+ 1e-4
|
||||
(* -1
|
||||
(equal 0.1 from 1)
|
||||
(equal 0.1 to 2)
|
||||
(equal 0.1 (min time 500) 500)))
|
||||
EOF
|
||||
)
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_sync || test_fail
|
||||
check_file_messages "20.*:1.:.*123\.1.* 5 111 " 6 6 measurements.log || test_fail
|
||||
check_file_messages "20.*:0.:.*123\.2.* 5 111 " 2 3 measurements.log || test_fail
|
||||
check_file_messages "20.*:1.:.*123\.2.* 5 111 " 6 7 measurements.log || test_fail
|
||||
check_file_messages "20.*:0.:.*123\.3.* 5 111 " 7 10 measurements.log || test_fail
|
||||
check_file_messages "20.*:1.:.*123\.3.* 5 111 " 0 1 measurements.log || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
limit=4000
|
||||
wander=0.0
|
||||
jitter=0.0
|
||||
server_strata=1
|
||||
server_conf=""
|
||||
server_server_options=""
|
||||
client_server_conf=""
|
||||
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
|
||||
chronyc_start=1
|
||||
chronyc_conf="timeout 1000000
|
||||
|
||||
@@ -22,6 +22,24 @@ check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
check_chronyc_output "^.*Stratum *: 2.*$" || test_fail
|
||||
check_chronyc_output "^.*Stratum *: 2
|
||||
Ref time.*
|
||||
System time *: 0.000.*
|
||||
Last offset *: [+-]0.000.*$" || test_fail
|
||||
check_log_messages "Could not step" 0 0 || test_fail
|
||||
|
||||
client_conf="makestep 0.01 -1"
|
||||
client_server_options="offset 5.0005"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
check_chronyc_output "^.*Stratum *: 2
|
||||
Ref time.*
|
||||
System time *: 5.000.*
|
||||
Last offset *: [+-]0.000.*$" || test_fail
|
||||
check_log_messages "Could not step" 20 60 || test_fail
|
||||
|
||||
test_pass
|
||||
|
||||
@@ -58,6 +58,21 @@ for client_conf in \
|
||||
fi
|
||||
done
|
||||
|
||||
if check_config_h 'FEAT_IPV6 1'; then
|
||||
server_chronyd_options="-6"
|
||||
client_chronyd_options="-6"
|
||||
ip_family=6
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
server_chronyd_options=$default_server_chronyd_options
|
||||
client_chronyd_options="-d"
|
||||
ip_family=$default_ip_family
|
||||
fi
|
||||
|
||||
server_conf+="
|
||||
server 192.168.123.2 minpoll 1 maxpoll 1 noselect"
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ server_conf="broadcast 64 192.168.123.255"
|
||||
client_server_options="offline"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
if check_config_h 'FEAT_CMDMON 1'; then
|
||||
check_chronyd_exit || test_fail
|
||||
fi
|
||||
check_packet_interval && test_fail
|
||||
|
||||
check_file_messages " 1 2 " 150 160 log.packets || test_fail
|
||||
|
||||
@@ -56,6 +56,23 @@ check_file_messages " 2 1 .* 4460 " 260 300 log.packets || test_fail
|
||||
check_file_messages "." 6 6 ntskeys || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
if check_config_h 'FEAT_IPV6 1'; then
|
||||
ip_family=6
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages "20.*:123:1.* 111 111 1111" 75 80 measurements.log || test_fail
|
||||
check_file_messages "20.*:123:1.* 111 001 0000" 37 39 measurements.log || test_fail
|
||||
check_file_messages " 2 1 .* 4460 " 260 300 log.packets || test_fail
|
||||
check_file_messages "." 6 6 ntskeys || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
ip_family=$default_ip_family
|
||||
fi
|
||||
|
||||
client_conf+="
|
||||
ntsrefresh 120
|
||||
ntsdumpdir tmp"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
test_start "address refreshment"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
limit=1000
|
||||
servers=5
|
||||
client_conf="logdir tmp
|
||||
|
||||
@@ -68,7 +68,7 @@ check_source_selection && test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_log_messages "Can't synchronise: no majority" 1 1 || test_fail
|
||||
check_log_messages "Can't synchronise: no majority (no agreement among 2 sources)" 1 1 || test_fail
|
||||
check_log_messages "Detected falseticker" 0 2 || test_fail
|
||||
check_log_messages "Source 192.168.123.. replaced with" 3 60 || test_fail
|
||||
check_log_messages "Source 192.168.123.1 replaced with" 1 25 || test_fail
|
||||
|
||||
189
test/simulation/149-sourcedir
Executable file
189
test/simulation/149-sourcedir
Executable file
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "sourcedir directive"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
servers=4
|
||||
limit=191
|
||||
update_executable="tmp/update-sourcedir"
|
||||
client_server_conf="sourcedir tmp"
|
||||
base_delay="(+ 1e-4 (* 5 (equal 0.1 from $[servers + 2])))"
|
||||
chronyc_start=1
|
||||
chronyc_conf="timeout 6000
|
||||
activity
|
||||
$(for i in $(seq 1 18); do echo "reload sources"; echo activity; done)"
|
||||
|
||||
cat > tmp/sources.sources <<EOF
|
||||
pool nodes-1-2-3-4.net1.clk iburst
|
||||
EOF
|
||||
|
||||
cat > tmp/update-sourcedir <<EOF
|
||||
#!/usr/bin/env bash
|
||||
case "\$1" in
|
||||
19) s="pool nodes-1-2-3-4.net1.clk";;
|
||||
39) s="pool nodes-1-2-3-4.net1.clk maxsources 3";;
|
||||
59) s="pool nodes-1-2-3.net1.clk";;
|
||||
79) s="pool nodes-1-2-3.net1.clk
|
||||
server nodes-3-4.net1.clk";;
|
||||
99) s="server nodes-3-4.net1.clk";;
|
||||
119) s="server nodes-1-2-3.net1.clk";;
|
||||
139) s="server 192.168.123.2";;
|
||||
159) s="server 192.168.123.2 maxdelay 0.1";;
|
||||
179) s="";;
|
||||
*) exit 0;;
|
||||
esac
|
||||
echo "\$s" > tmp/sources.sources
|
||||
EOF
|
||||
chmod 755 tmp/update-sourcedir
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
|
||||
check_log_messages "T00:0.:[135].Z \(Added\|Removed\)" 0 0 || test_fail
|
||||
check_log_messages "T00:0.:..Z Added pool nodes-1-2-3-4\." 3 3 || test_fail
|
||||
check_log_messages "T00:0.:..Z Removed pool nodes-1-2-3-4\." 3 3 || test_fail
|
||||
check_log_messages "T00:0.:..Z Added pool nodes-1-2-3\." 1 1 || test_fail
|
||||
check_log_messages "T00:0.:..Z Removed pool nodes-1-2-3\." 1 1 || test_fail
|
||||
check_log_messages "T00:0.:..Z Added source ID#" 2 2 || test_fail
|
||||
check_log_messages "T00:0.:..Z Added source 192.168.123.[1234]" 2 2 || test_fail
|
||||
check_log_messages "T00:0.:..Z Removed source 192.168.123.[1234]" 4 4 || test_fail
|
||||
|
||||
check_chronyc_output "^200 OK
|
||||
0 sources online
|
||||
0 sources offline
|
||||
4 sources doing burst \(return to online\)
|
||||
0 sources doing burst \(return to offline\)
|
||||
0 sources with unknown address
|
||||
200 OK
|
||||
200 OK
|
||||
4 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
|
||||
4 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
|
||||
4 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
|
||||
3 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
|
||||
3 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
|
||||
3 sources online
|
||||
0 sources offline
|
||||
0 sources doing burst \(return to online\)
|
||||
0 sources doing burst \(return to offline\)
|
||||
5 sources with unknown address
|
||||
200 OK
|
||||
200 OK
|
||||
3 sources online
|
||||
0 sources offline
|
||||
0 sources doing burst \(return to online\)
|
||||
0 sources doing burst \(return to offline\)
|
||||
5 sources with unknown address
|
||||
200 OK
|
||||
200 OK
|
||||
4 sources online
|
||||
0 sources offline
|
||||
0 sources doing burst \(return to online\)
|
||||
0 sources doing burst \(return to offline\)
|
||||
5 sources with unknown address
|
||||
200 OK
|
||||
200 OK
|
||||
4 sources online
|
||||
0 sources offline
|
||||
0 sources doing burst \(return to online\)
|
||||
0 sources doing burst \(return to offline\)
|
||||
5 sources with unknown address
|
||||
200 OK
|
||||
200 OK
|
||||
1 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
|
||||
1 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
|
||||
1 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
|
||||
1 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
|
||||
1 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
|
||||
1 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
|
||||
1 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
|
||||
1 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
|
||||
0 sources offline
|
||||
0 sources doing burst \(return to online\)
|
||||
0 sources doing burst \(return to offline\)
|
||||
0 sources with unknown address$" || test_fail
|
||||
|
||||
check_packet_interval || test_fail
|
||||
|
||||
test_pass
|
||||
43
test/simulation/150-maxunreach
Executable file
43
test/simulation/150-maxunreach
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "maxunreach option"
|
||||
|
||||
limit=5000
|
||||
servers=2
|
||||
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
|
||||
base_delay=$(cat <<-EOF | tr -d '\n'
|
||||
(+ 1e-4
|
||||
(* -1
|
||||
(equal 0.1 from 3)
|
||||
(equal 0.1 to 1)
|
||||
(equal 0.1 (min time 2000) 2000))
|
||||
(* 0.5
|
||||
(+ (equal 0.1 from 2)
|
||||
(equal 0.1 to 2))))
|
||||
EOF
|
||||
)
|
||||
|
||||
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 "Selected source 192.168.123.1" 1 1 || test_fail
|
||||
check_log_messages "Selected source 192.168.123.2" 0 0 || test_fail
|
||||
|
||||
client_server_options="minpoll 6 maxpoll 6 minsamples 64 maxunreach 10"
|
||||
|
||||
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 "Selected source 192.168.123.1" 1 1 || test_fail
|
||||
check_log_messages "Selected source 192.168.123.2" 1 1 || test_fail
|
||||
check_log_messages "00:52:..Z Selected source 192.168.123.2" 1 1 || test_fail
|
||||
|
||||
test_pass
|
||||
38
test/simulation/151-stratumlimit
Executable file
38
test/simulation/151-stratumlimit
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
. ./test.common
|
||||
|
||||
test_start "minstratum and maxstratum options"
|
||||
|
||||
client_conf="
|
||||
minstratum 3
|
||||
maxstratum 5"
|
||||
|
||||
for s in 3 5; do
|
||||
server_conf="local stratum $s"
|
||||
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 "Stratum of .* stratum" 0 0 || test_fail
|
||||
done
|
||||
|
||||
for s in 2 6; do
|
||||
server_conf="local stratum $s"
|
||||
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 "Stratum of .* stratum" 0 0 || test_fail
|
||||
if [ $s -lt 3 ]; then
|
||||
check_log_messages "Stratum of .* below minstratum of 3" 1 1 || test_fail
|
||||
else
|
||||
check_log_messages "Stratum of .* above maxstratum of 5" 1 1 || test_fail
|
||||
fi
|
||||
done
|
||||
|
||||
test_pass
|
||||
@@ -1,7 +1,7 @@
|
||||
This is a collection of simulation tests using the clknetsim simulator
|
||||
(supported on Linux only).
|
||||
|
||||
https://github.com/mlichvar/clknetsim
|
||||
https://gitlab.com/chrony/clknetsim
|
||||
|
||||
The CLKNETSIM_PATH environment variable should point to the directory where
|
||||
clknetsim was downloaded and compiled. If the variable is not set, the tests
|
||||
|
||||
@@ -39,6 +39,7 @@ default_refclock_jitter=""
|
||||
default_refclock_offset=0.0
|
||||
|
||||
default_update_interval=0
|
||||
default_update_executable=""
|
||||
default_shift_pll=2
|
||||
|
||||
default_server_strata=1
|
||||
@@ -78,6 +79,7 @@ default_max_sync_time=210
|
||||
default_client_min_mean_out_interval=0.0
|
||||
default_client_max_min_out_interval=inf
|
||||
|
||||
default_ip_family=4
|
||||
default_cmdmon_unix=1
|
||||
default_pcap_dumps=0
|
||||
default_dns=0
|
||||
@@ -91,8 +93,6 @@ done
|
||||
test_start() {
|
||||
rm -rf tmp/*
|
||||
echo "Testing $@:"
|
||||
|
||||
check_config_h 'FEAT_NTP 1' || test_skip
|
||||
}
|
||||
|
||||
test_pass() {
|
||||
@@ -192,6 +192,8 @@ get_node_name() {
|
||||
|
||||
if [ $dns -ne 0 ]; then
|
||||
echo "node$index.net1.clk"
|
||||
elif [ $ip_family -eq 6 ]; then
|
||||
printf "fc00::123:%x" $index
|
||||
else
|
||||
echo "192.168.123.$index"
|
||||
fi
|
||||
@@ -239,7 +241,7 @@ check_config_h() {
|
||||
|
||||
# Check if the clock was well synchronized
|
||||
check_sync() {
|
||||
local i sync_time max_time_error max_freq_error ret=0
|
||||
local i msg sync_time max_time_error max_freq_error r ret=0
|
||||
local rms_time_error rms_freq_error
|
||||
|
||||
test_message 2 1 "checking clock sync time, max/rms time/freq error:"
|
||||
@@ -254,17 +256,37 @@ check_sync() {
|
||||
rms_time_error=$(get_stat 'RMS offset' $i)
|
||||
rms_freq_error=$(get_stat 'RMS frequency' $i)
|
||||
|
||||
test_message 3 0 "node $i: $sync_time $(printf '%.2e %.2e %.2e %.2e' \
|
||||
$max_time_error $max_freq_error $rms_time_error $rms_freq_error)"
|
||||
r=0
|
||||
msg="$sync_time"
|
||||
if ! check_stat $sync_time $min_sync_time $max_sync_time; then
|
||||
msg+="!"
|
||||
r=1
|
||||
fi
|
||||
msg+="$(printf ' %.2e' $max_time_error)"
|
||||
if ! check_stat $max_time_error 0.0 $time_max_limit; then
|
||||
msg+="!"
|
||||
r=1
|
||||
fi
|
||||
msg+="$(printf ' %.2e' $max_freq_error)"
|
||||
if ! check_stat $max_freq_error 0.0 $freq_max_limit; then
|
||||
msg+="!"
|
||||
r=1
|
||||
fi
|
||||
msg+="$(printf ' %.2e' $rms_time_error)"
|
||||
if ! check_stat $rms_time_error 0.0 $time_rms_limit; then
|
||||
msg+="!"
|
||||
r=1
|
||||
fi
|
||||
msg+="$(printf ' %.2e' $rms_freq_error)"
|
||||
if ! check_stat $rms_freq_error 0.0 $freq_rms_limit; then
|
||||
msg+="!"
|
||||
r=1
|
||||
fi
|
||||
|
||||
check_stat $sync_time $min_sync_time $max_sync_time && \
|
||||
check_stat $max_time_error 0.0 $time_max_limit && \
|
||||
check_stat $max_freq_error 0.0 $freq_max_limit && \
|
||||
check_stat $rms_time_error 0.0 $time_rms_limit && \
|
||||
check_stat $rms_freq_error 0.0 $freq_rms_limit && \
|
||||
test_ok || test_bad
|
||||
test_message 3 0 "node $i: $msg"
|
||||
|
||||
[ $? -eq 0 ] || ret=1
|
||||
[ $r -eq 0 ] || ret=1
|
||||
[ $r -eq 0 ] && test_ok || test_bad
|
||||
done
|
||||
|
||||
return $ret
|
||||
@@ -442,7 +464,9 @@ run_simulation() {
|
||||
-o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
|
||||
-R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
|
||||
-r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
|
||||
-l $(awk "BEGIN {print $limit * 2^$update_interval}") && test_ok || test_error
|
||||
-l $(awk "BEGIN {print $limit * 2^$update_interval}") \
|
||||
$([ "$update_executable" != "" ] && printf "%s" "-e $update_executable") && \
|
||||
test_ok || test_error
|
||||
}
|
||||
|
||||
run_test() {
|
||||
@@ -454,6 +478,7 @@ run_test() {
|
||||
nodes=$(get_chronyd_nodes)
|
||||
[ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
|
||||
|
||||
export CLKNETSIM_IP_FAMILY=$ip_family
|
||||
export CLKNETSIM_UNIX_SUBNET=$[$cmdmon_unix != 0 ? 2 : 0]
|
||||
|
||||
for i in $(seq 1 $nodes); do
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
check_chronyd_features PRIVDROP || test_skip "PRIVDROP support disabled"
|
||||
|
||||
user="nobody"
|
||||
priv_drop=1
|
||||
|
||||
test_start "dropping of root privileges"
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atm
|
||||
run_chronyc "clients" || test_fail
|
||||
check_chronyc_output "^Hostname NTP Drop Int IntL Last Cmd Drop Int Last
|
||||
===============================================================================
|
||||
127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [ 0-9]+ 0 0 - -$" \
|
||||
.*127\.0\.0\.1 [0-9 ]+ 0 [-0-9 ]+ - [-0-9 ]+ 0 0 - -.*$" \
|
||||
|| test_fail
|
||||
|
||||
run_chronyc "ntpdata $server" || test_fail
|
||||
@@ -107,7 +107,7 @@ Total HW RX : 0$" || test_fail
|
||||
run_chronyc "selectdata" || test_fail
|
||||
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||
=======================================================================
|
||||
M 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
|
||||
M 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns [\?N]$" || test_fail
|
||||
|
||||
run_chronyc "serverstats" || test_fail
|
||||
check_chronyc_output "^NTP packets received : [0-9]+
|
||||
|
||||
@@ -22,7 +22,7 @@ certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
|
||||
&> $TEST_DIR/certtool.log
|
||||
certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
|
||||
--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
|
||||
chown $user $TEST_DIR/server.*
|
||||
chown "$(get_user)" $TEST_DIR/server.*
|
||||
|
||||
ntpport=$(get_free_port)
|
||||
ntsport=$(get_free_port)
|
||||
|
||||
@@ -26,7 +26,7 @@ certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
|
||||
&> $TEST_DIR/certtool.log
|
||||
certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
|
||||
--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
|
||||
chown $user $TEST_DIR/server.*
|
||||
chown "$(get_user)" $TEST_DIR/server.*
|
||||
|
||||
ntpport=$(get_free_port)
|
||||
ntsport=$(get_free_port)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
|
||||
|
||||
[[ $CHRONYD_WRAPPER == *valgrind* ]] && test_skip "SCFILTER breaks valgrind"
|
||||
|
||||
test_start "system call filter in non-destructive tests"
|
||||
|
||||
for level in 1 2 -1 -2; do
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
TEST_LIBDIR=${CHRONY_LIBDIR:-/var/lib/chrony}
|
||||
TEST_LOGDIR=${CHRONY_LOGDIR:-/var/log/chrony}
|
||||
TEST_RUNDIR=${CHRONY_RUNDIR:-/var/run/chrony}
|
||||
TEST_PRIVDROP_USER=$(ls -ld "$TEST_RUNDIR" 2> /dev/null | awk '{print $3}')
|
||||
|
||||
. ./test.common
|
||||
|
||||
user=$(ls -ld "$TEST_RUNDIR" 2> /dev/null | awk '{print $3}')
|
||||
priv_drop=1
|
||||
|
||||
test_start "system directories"
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
|
||||
|
||||
[[ $CHRONYD_WRAPPER == *valgrind* ]] && test_skip "SCFILTER breaks valgrind"
|
||||
|
||||
test_start "system call filter in destructive tests"
|
||||
|
||||
for level in 1 2 -1 -2; do
|
||||
|
||||
@@ -21,9 +21,13 @@ TEST_LIBDIR=${TEST_LIBDIR:-$TEST_DIR}
|
||||
TEST_LOGDIR=${TEST_LOGDIR:-$TEST_DIR}
|
||||
TEST_RUNDIR=${TEST_RUNDIR:-$TEST_DIR}
|
||||
TEST_SCFILTER=${TEST_SCFILTER:-0}
|
||||
TEST_ROOT_USER=${TEST_ROOT_USER:-root}
|
||||
TEST_PRIVDROP_USER=${TEST_PRIVDROP_USER:-nobody}
|
||||
|
||||
test_start() {
|
||||
check_chronyd_features NTP CMDMON || test_skip "NTP/CMDMON support disabled"
|
||||
local user=$(get_user)
|
||||
|
||||
check_chronyd_features CMDMON || test_skip "CMDMON support disabled"
|
||||
|
||||
[ "${#TEST_DIR}" -ge 5 ] || test_skip "invalid TEST_DIR"
|
||||
|
||||
@@ -36,7 +40,7 @@ test_start() {
|
||||
|
||||
rm -f "$TEST_LIBDIR"/* "$TEST_LOGDIR"/* "$TEST_RUNDIR"/*
|
||||
|
||||
if [ "$user" != "root" ]; then
|
||||
if [ "$user" != "$TEST_ROOT_USER" ]; then
|
||||
id -u "$user" > /dev/null 2> /dev/null || test_skip "missing user $user"
|
||||
chown "$user:$(id -g "$user")" "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
|
||||
su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \
|
||||
@@ -108,7 +112,7 @@ default_clock_control=0
|
||||
default_server=127.0.0.1
|
||||
default_server_name=127.0.0.1
|
||||
default_server_options=""
|
||||
default_user=root
|
||||
default_priv_drop=0
|
||||
|
||||
# Initialize test settings from their defaults
|
||||
for defoptname in ${!default_*}; do
|
||||
@@ -183,6 +187,14 @@ get_cmdsocket() {
|
||||
echo "$TEST_RUNDIR/chronyd.sock"
|
||||
}
|
||||
|
||||
get_user() {
|
||||
if [ "$priv_drop" -ne 0 ]; then
|
||||
echo "$TEST_PRIVDROP_USER"
|
||||
else
|
||||
echo "$TEST_ROOT_USER"
|
||||
fi
|
||||
}
|
||||
|
||||
# Find a free port in the 10000-20000 range (their use is racy)
|
||||
get_free_port() {
|
||||
local port
|
||||
@@ -197,15 +209,19 @@ get_free_port() {
|
||||
}
|
||||
|
||||
generate_chrony_conf() {
|
||||
local ntpport cmdport
|
||||
local user ntpport cmdport
|
||||
|
||||
user=$(get_user)
|
||||
ntpport=$(get_free_port)
|
||||
cmdport=$(get_free_port)
|
||||
while true; do
|
||||
cmdport=$(get_free_port)
|
||||
[ "$ntpport" -ne "$cmdport" ] && break
|
||||
done
|
||||
|
||||
echo "0.0 10000" > "$TEST_LIBDIR/driftfile"
|
||||
echo "1 MD5 abcdefghijklmnopq" > "$TEST_DIR/keys"
|
||||
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR/keys"
|
||||
echo "0.0" > "$TEST_DIR/tempcomp"
|
||||
chown "$user:$(id -g "$user")" "$TEST_LIBDIR/driftfile" "$TEST_DIR"/{keys,tempcomp}
|
||||
|
||||
(
|
||||
echo "pidfile $(get_pidfile)"
|
||||
@@ -246,14 +262,14 @@ get_chronyd_options() {
|
||||
[ "$clock_control" -eq 0 ] && echo "-x"
|
||||
echo "-l $(get_logfile)"
|
||||
echo "-f $(get_conffile)"
|
||||
echo "-u $user"
|
||||
echo "-u $(get_user)"
|
||||
echo "-F $TEST_SCFILTER"
|
||||
echo "$extra_chronyd_options"
|
||||
}
|
||||
|
||||
# Start a chronyd instance
|
||||
start_chronyd() {
|
||||
local pid pidfile=$(get_pidfile)
|
||||
local pid pidfile=$(get_pidfile) wrapper_options=""
|
||||
|
||||
print_nondefaults
|
||||
test_message 1 0 "starting chronyd"
|
||||
@@ -264,7 +280,12 @@ start_chronyd() {
|
||||
|
||||
rm -f "$TEST_LOGDIR"/*.log
|
||||
|
||||
$CHRONYD_WRAPPER "$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
|
||||
if [[ $CHRONYD_WRAPPER == *valgrind* ]]; then
|
||||
wrapper_options="--log-file=$TEST_DIR/chronyd.valgrind --enable-debuginfod=no"
|
||||
fi
|
||||
|
||||
$CHRONYD_WRAPPER $wrapper_options \
|
||||
"$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
|
||||
|
||||
[ $? -eq 0 ] && [ -f "$pidfile" ] && ps -p "$(cat "$pidfile")" > /dev/null && test_ok || test_error
|
||||
}
|
||||
@@ -311,6 +332,12 @@ stop_chronyd() {
|
||||
done
|
||||
|
||||
test_ok
|
||||
|
||||
if [ -f "$TEST_DIR/chronyd.valgrind" ]; then
|
||||
test_message 2 0 "checking valgrind report"
|
||||
! grep -q 'ERROR SUMMARY: [^0]' "$TEST_DIR/chronyd.valgrind" && \
|
||||
test_ok || test_bad
|
||||
fi
|
||||
}
|
||||
|
||||
# Check chronyd log for expected and unexpected messages
|
||||
@@ -351,7 +378,7 @@ check_chronyd_files() {
|
||||
|
||||
# Run a chronyc command
|
||||
run_chronyc() {
|
||||
local host=$chronyc_host options="-n -m"
|
||||
local host=$chronyc_host options="-n -m" wrapper_options="" ret=0
|
||||
|
||||
test_message 1 0 "running chronyc $([ -n "$host" ] && echo "@$host ")$*"
|
||||
|
||||
@@ -361,8 +388,23 @@ run_chronyc() {
|
||||
options="$options -p $(grep cmdport "$(get_conffile)" | awk '{print $2}')"
|
||||
fi
|
||||
|
||||
$CHRONYC_WRAPPER "$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \
|
||||
test_ok || test_error
|
||||
if [[ $CHRONYC_WRAPPER == *valgrind* ]]; then
|
||||
wrapper_options="--log-file=$TEST_DIR/chronyc.valgrind --enable-debuginfod=no"
|
||||
fi
|
||||
|
||||
$CHRONYC_WRAPPER $wrapper_options \
|
||||
"$chronyc" -h "$host" -u "$(get_user)" $options "$@" > "$TEST_DIR/chronyc.out" && \
|
||||
test_ok || test_error
|
||||
[ $? -ne 0 ] && ret=1
|
||||
|
||||
if [ -f "$TEST_DIR/chronyc.valgrind" ]; then
|
||||
test_message 2 0 "checking valgrind report"
|
||||
! grep -q 'ERROR SUMMARY: [^0]' "$TEST_DIR/chronyc.valgrind" && \
|
||||
test_ok || test_bad
|
||||
[ $? -ne 0 ] && ret=1
|
||||
fi
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Compare chronyc output with specified pattern
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
#include <config.h>
|
||||
#include "test.h"
|
||||
|
||||
#if defined(FEAT_NTP) || defined(FEAT_CMDMON)
|
||||
|
||||
#include <clientlog.c>
|
||||
|
||||
static uint64_t
|
||||
@@ -321,10 +319,3 @@ test_unit(void)
|
||||
LCL_Finalise();
|
||||
CNF_Finalise();
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
TEST_REQUIRE(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user