mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 17:35:06 -05:00
Compare commits
60 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 |
17
NEWS
17
NEWS
@@ -1,3 +1,18 @@
|
||||
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
|
||||
==================
|
||||
|
||||
@@ -16,9 +31,11 @@ Enhancements
|
||||
* 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
|
||||
|
||||
1
README
1
README
@@ -78,6 +78,7 @@ 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>
|
||||
|
||||
2
candm.h
2
candm.h
@@ -306,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;
|
||||
|
||||
|
||||
183
client.c
183
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;
|
||||
@@ -207,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);
|
||||
@@ -257,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;
|
||||
}
|
||||
|
||||
@@ -987,7 +1104,7 @@ 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;
|
||||
|
||||
@@ -2459,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");
|
||||
@@ -3447,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",
|
||||
@@ -3473,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);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -3492,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++) {
|
||||
@@ -3511,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':
|
||||
@@ -3547,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;
|
||||
@@ -3556,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;
|
||||
}
|
||||
|
||||
3
cmdmon.c
3
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
|
||||
@@ -686,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);
|
||||
|
||||
@@ -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
|
||||
@@ -66,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;
|
||||
@@ -158,6 +159,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
} else if (!strcasecmp(cmd, "maxsources")) {
|
||||
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 CPS_InvalidValue;
|
||||
|
||||
40
conf.c
40
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
|
||||
@@ -115,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;
|
||||
@@ -702,12 +704,16 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||
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, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "minsources")) {
|
||||
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, 0, INT_MAX);
|
||||
} else if (!strcasecmp(command, "noclientlog")) {
|
||||
@@ -958,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;
|
||||
@@ -972,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;
|
||||
@@ -1036,6 +1043,9 @@ parse_refclock(char *line)
|
||||
} else if (!strcasecmp(cmd, "maxsamples")) {
|
||||
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)
|
||||
break;
|
||||
@@ -1085,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;
|
||||
@@ -1872,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)
|
||||
@@ -1912,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,
|
||||
@@ -2303,6 +2317,22 @@ CNF_GetMaxJitter(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetMaxStratum(void)
|
||||
{
|
||||
return max_stratum;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetMinStratum(void)
|
||||
{
|
||||
return min_stratum;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
CNF_GetReselectDistance(void)
|
||||
{
|
||||
|
||||
2
conf.h
2
conf.h
@@ -106,6 +106,8 @@ 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);
|
||||
|
||||
31
configure
vendored
31
configure
vendored
@@ -133,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]
|
||||
@@ -250,6 +251,7 @@ try_timestamping=0
|
||||
feat_ntp_signd=0
|
||||
ntp_era_split=""
|
||||
default_user="root"
|
||||
default_chronyc_user="root"
|
||||
default_hwclockfile=""
|
||||
default_pidfile="/var/run/chrony/chronyd.pid"
|
||||
default_rtcdevice="/dev/rtc"
|
||||
@@ -354,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/^.*=//;'`
|
||||
;;
|
||||
@@ -661,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"
|
||||
@@ -694,13 +701,16 @@ else
|
||||
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
|
||||
@@ -798,6 +808,7 @@ then
|
||||
# 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
|
||||
@@ -1011,7 +1022,7 @@ if [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
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"
|
||||
@@ -1080,6 +1091,7 @@ 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\""
|
||||
|
||||
@@ -1123,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
|
||||
@@ -70,8 +71,10 @@ 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 the 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
|
||||
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
|
||||
@@ -217,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
|
||||
@@ -251,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
|
||||
@@ -311,7 +320,8 @@ 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 that should be used only when other
|
||||
sources are unreachable.
|
||||
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
|
||||
@@ -730,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
|
||||
@@ -1089,6 +1105,16 @@ 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
|
||||
@@ -1120,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-up 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. It generally overestimates the
|
||||
precision and it can be sensitive to the CPU speed, however, 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_::
|
||||
@@ -2431,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
|
||||
|
||||
@@ -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
|
||||
@@ -121,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.
|
||||
|
||||
@@ -467,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.
|
||||
@@ -1472,7 +1483,7 @@ 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 containg the log file is not writable
|
||||
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*::
|
||||
|
||||
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).
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
18
hwclock.c
18
hwclock.c
@@ -163,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;
|
||||
@@ -228,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);
|
||||
@@ -242,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,
|
||||
|
||||
3
keys.c
3
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));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
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;
|
||||
|
||||
163
local.c
163
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;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -91,7 +91,7 @@ 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);
|
||||
|
||||
2
main.c
2
main.c
@@ -126,7 +126,7 @@ notify_system_manager(int start)
|
||||
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");
|
||||
LOG_FATAL("Could not send notification to $NOTIFY_SOCKET");
|
||||
|
||||
SCK_CloseSocket(sock_fd);
|
||||
#endif
|
||||
|
||||
@@ -686,7 +686,8 @@ 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);
|
||||
@@ -1960,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;
|
||||
|
||||
@@ -2024,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 */
|
||||
|
||||
@@ -220,7 +220,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
||||
|
||||
SCK_CloseSocket(sock_fd);
|
||||
|
||||
phc_fd = SYS_Linux_OpenPHC(req.ifr_name);
|
||||
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);
|
||||
|
||||
318
nts_ke_session.c
318
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,67 +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) {
|
||||
BRIEF_ASSERT(!trusted_certs && !trusted_certs_ids);
|
||||
|
||||
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 {
|
||||
BRIEF_ASSERT(!certs && !keys && n_certs_keys <= 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;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -737,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();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -789,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;
|
||||
|
||||
@@ -899,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;
|
||||
}
|
||||
|
||||
35
refclock.c
35
refclock.c
@@ -244,7 +244,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
|
||||
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),
|
||||
@@ -427,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;
|
||||
@@ -443,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);
|
||||
|
||||
@@ -486,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;
|
||||
@@ -501,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;
|
||||
@@ -513,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
|
||||
@@ -539,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;
|
||||
@@ -641,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;
|
||||
@@ -657,12 +668,6 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
RCL_UpdateReachability(RCL_Instance instance)
|
||||
{
|
||||
instance->reached++;
|
||||
}
|
||||
|
||||
double
|
||||
RCL_GetPrecision(RCL_Instance instance)
|
||||
{
|
||||
|
||||
10
refclock.h
10
refclock.h
@@ -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,11 +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);
|
||||
extern void RCL_UpdateReachability(RCL_Instance instance);
|
||||
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);
|
||||
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));
|
||||
|
||||
@@ -154,13 +155,11 @@ static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
|
||||
}
|
||||
phc->last_extts = *phc_ts;
|
||||
|
||||
RCL_UpdateReachability(instance);
|
||||
|
||||
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
|
||||
return;
|
||||
|
||||
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
||||
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
|
||||
UTI_DiffTimespecsToDouble(phc_ts, &local_ts), 1);
|
||||
}
|
||||
|
||||
static void read_ext_pulse(int fd, int event, void *anything)
|
||||
@@ -197,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);
|
||||
|
||||
@@ -206,14 +205,13 @@ static int phc_poll(RCL_Instance instance)
|
||||
if (n_readings < 1)
|
||||
return 0;
|
||||
|
||||
if (!phc->extpps)
|
||||
RCL_UpdateReachability(instance);
|
||||
|
||||
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;
|
||||
@@ -221,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,9 +143,7 @@ static int pps_poll(RCL_Instance instance)
|
||||
|
||||
pps->last_seq = seq;
|
||||
|
||||
RCL_UpdateReachability(instance);
|
||||
|
||||
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 = {
|
||||
|
||||
@@ -58,9 +58,7 @@ static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
|
||||
rtc_ts.tv_sec = rtc_sec;
|
||||
rtc_ts.tv_nsec = rtc_nsec;
|
||||
|
||||
RCL_UpdateReachability(instance);
|
||||
|
||||
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal);
|
||||
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal, 1);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -138,12 +136,15 @@ static void refrtc_finalise(RCL_Instance instance)
|
||||
|
||||
rtc = RCL_GetDriverData(instance);
|
||||
|
||||
if (!rtc->polling) {
|
||||
SCH_RemoveFileHandler(rtc->fd);
|
||||
RTC_Linux_SwitchInterrupt(rtc->fd, 0);
|
||||
if (rtc->fd >= 0) {
|
||||
if (!rtc->polling) {
|
||||
SCH_RemoveFileHandler(rtc->fd);
|
||||
RTC_Linux_SwitchInterrupt(rtc->fd, 0);
|
||||
}
|
||||
|
||||
close(rtc->fd);
|
||||
}
|
||||
|
||||
close(rtc->fd);
|
||||
Free(rtc);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,8 +109,6 @@ static int shm_poll(RCL_Instance instance)
|
||||
|
||||
shm->valid = 0;
|
||||
|
||||
RCL_UpdateReachability(instance);
|
||||
|
||||
receive_ts.tv_sec = t.receiveTimeStampSec;
|
||||
clock_ts.tv_sec = t.clockTimeStampSec;
|
||||
|
||||
@@ -126,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 = {
|
||||
|
||||
@@ -129,17 +129,15 @@ static void read_sample(int sockfd, int event, void *anything)
|
||||
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
|
||||
UTI_NormaliseTimespec(&sys_ts);
|
||||
|
||||
RCL_UpdateReachability(instance);
|
||||
|
||||
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
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;
|
||||
|
||||
16
socket.c
16
socket.c
@@ -604,18 +604,18 @@ error:
|
||||
/* ================================================== */
|
||||
|
||||
static socklen_t
|
||||
set_unix_sockaddr(struct sockaddr_un *sun, const char *addr)
|
||||
set_unix_sockaddr(struct sockaddr_un *sa, const char *addr)
|
||||
{
|
||||
size_t len = strlen(addr);
|
||||
|
||||
if (len + 1 > sizeof (sun->sun_path)) {
|
||||
if (len + 1 > sizeof (sa->sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(sun, 0, sizeof (*sun));
|
||||
sun->sun_family = AF_UNIX;
|
||||
memcpy(sun->sun_path, addr, len);
|
||||
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;
|
||||
}
|
||||
@@ -641,12 +641,6 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
80
sources.c
80
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;
|
||||
@@ -70,6 +69,7 @@ typedef enum {
|
||||
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 */
|
||||
@@ -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;
|
||||
@@ -224,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();
|
||||
@@ -259,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;
|
||||
|
||||
@@ -295,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);
|
||||
@@ -351,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;
|
||||
@@ -524,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();
|
||||
@@ -545,6 +566,7 @@ SRC_ResetReachability(SRC_Instance inst)
|
||||
{
|
||||
inst->reachability = 0;
|
||||
inst->reachability_size = 0;
|
||||
inst->unreachable_run = 0;
|
||||
SRC_UpdateReachability(inst, 0);
|
||||
}
|
||||
|
||||
@@ -721,7 +743,8 @@ set_source_status(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;
|
||||
@@ -765,6 +788,14 @@ mark_source(SRC_Instance inst, SRC_Status 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;
|
||||
@@ -937,7 +968,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
int max_badstat_reach, max_badstat_reach_size, n_badstats_sources;
|
||||
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;
|
||||
@@ -992,12 +1023,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
n_unreach_sources++;
|
||||
|
||||
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);
|
||||
|
||||
if (!si->select_ok) {
|
||||
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)
|
||||
@@ -1013,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) {
|
||||
@@ -1059,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;
|
||||
}
|
||||
@@ -1343,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 */
|
||||
@@ -1359,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 */
|
||||
@@ -1371,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;
|
||||
|
||||
@@ -1896,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
|
||||
|
||||
77
sys_linux.c
77
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
|
||||
@@ -62,6 +63,7 @@
|
||||
#endif
|
||||
|
||||
#include "sys_linux.h"
|
||||
#include "sys_linux_scmp.h"
|
||||
#include "sys_timex.h"
|
||||
#include "conf.h"
|
||||
#include "local.h"
|
||||
@@ -95,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;
|
||||
@@ -297,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),
|
||||
@@ -314,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);
|
||||
}
|
||||
@@ -341,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)
|
||||
{
|
||||
@@ -391,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);
|
||||
}
|
||||
|
||||
@@ -775,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)
|
||||
@@ -926,7 +888,7 @@ verify_fd_is_phc(int phc_fd)
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
open_phc_by_iface_name(const char *iface)
|
||||
open_phc_by_iface_name(const char *iface, int flags)
|
||||
{
|
||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||
struct ethtool_ts_info ts_info;
|
||||
@@ -969,7 +931,7 @@ open_phc_by_iface_name(const char *iface)
|
||||
"/dev/ptp%d", ts_info.phc_index) >= sizeof (phc_device))
|
||||
return -1;
|
||||
|
||||
return open(phc_device, O_RDONLY);
|
||||
return open(phc_device, flags);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
@@ -978,21 +940,20 @@ open_phc_by_iface_name(const char *iface)
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_OpenPHC(const char *device)
|
||||
SYS_Linux_OpenPHC(const char *device, int flags)
|
||||
{
|
||||
int phc_fd = -1;
|
||||
|
||||
if (device[0] == '/') {
|
||||
phc_fd = open(device, O_RDONLY);
|
||||
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);
|
||||
phc_fd = open_phc_by_iface_name(device, flags);
|
||||
if (phc_fd < 0) {
|
||||
LOG(LOGS_ERR, "Could not open PHC of iface %s : %s",
|
||||
device, strerror(errno));
|
||||
LOG(LOGS_ERR, "Could not open PHC (of) %s", device);
|
||||
return -1;
|
||||
}
|
||||
phc_fd = verify_fd_is_phc(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 *device);
|
||||
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);
|
||||
@@ -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
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -43,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
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
test_start "IPv6 addressing"
|
||||
|
||||
check_config_h 'FEAT_IPV6 1' || test_skip
|
||||
|
||||
ip_family=6
|
||||
|
||||
run_test || test_fail
|
||||
|
||||
@@ -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
|
||||
.*
|
||||
@@ -85,7 +100,7 @@ Root delay : 0\.000000001 seconds
|
||||
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
|
||||
rm -f tmp/refclocks.log
|
||||
|
||||
min_sync_time=180
|
||||
min_sync_time=80
|
||||
max_sync_time=260
|
||||
chronyc_start=270
|
||||
client_conf="
|
||||
@@ -122,7 +137,10 @@ maxupdateskew 10000"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || 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
|
||||
|
||||
@@ -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" \
|
||||
@@ -219,6 +219,11 @@ do
|
||||
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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -64,8 +64,11 @@ if check_config_h 'FEAT_IPV6 1'; then
|
||||
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
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
test_start "address refreshment"
|
||||
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
limit=1000
|
||||
servers=5
|
||||
client_conf="logdir tmp
|
||||
|
||||
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
|
||||
@@ -463,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() {
|
||||
|
||||
@@ -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]+
|
||||
|
||||
@@ -213,12 +213,15 @@ generate_chrony_conf() {
|
||||
|
||||
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)"
|
||||
@@ -390,7 +393,7 @@ run_chronyc() {
|
||||
fi
|
||||
|
||||
$CHRONYC_WRAPPER $wrapper_options \
|
||||
"$chronyc" -h "$host" $options "$@" > "$TEST_DIR/chronyc.out" && \
|
||||
"$chronyc" -h "$host" -u "$(get_user)" $options "$@" > "$TEST_DIR/chronyc.out" && \
|
||||
test_ok || test_error
|
||||
[ $? -ne 0 ] && ret=1
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@ test_unit(void)
|
||||
{
|
||||
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
|
||||
struct timespec readings[MAX_READINGS][3];
|
||||
int i, j, k, l, new_sample, n_readings, count, quality;
|
||||
HCL_Instance clock;
|
||||
double freq, jitter, interval, dj, err, sum;
|
||||
int i, j, k, l, new_sample, n_readings, count;
|
||||
|
||||
LCL_Initialise();
|
||||
TST_RegisterDummyDrivers();
|
||||
@@ -84,11 +84,13 @@ test_unit(void)
|
||||
|
||||
UTI_ZeroTimespec(&hw_ts);
|
||||
UTI_ZeroTimespec(&local_ts);
|
||||
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err)) {
|
||||
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
|
||||
new_sample = 1;
|
||||
} else {
|
||||
new_sample = 0;
|
||||
new_sample = 0;
|
||||
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err, &quality)) {
|
||||
TEST_CHECK(quality >= 0 && quality <= 2);
|
||||
if (quality > 0) {
|
||||
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
|
||||
new_sample = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2017
|
||||
* Copyright (C) Miroslav Lichvar 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
|
||||
|
||||
@@ -193,6 +193,8 @@ test_unit(void)
|
||||
|
||||
server_cred = NKSN_CreateServerCertCredentials(&cert, &key, 1);
|
||||
client_cred = NKSN_CreateClientCertCredentials(&cert, &cert_id, 1, 0);
|
||||
TEST_CHECK(server_cred);
|
||||
TEST_CHECK(client_cred);
|
||||
|
||||
TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds) == 0);
|
||||
TEST_CHECK(fcntl(sock_fds[0], F_SETFL, O_NONBLOCK) == 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2020
|
||||
* Copyright (C) Miroslav Lichvar 2020, 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2022
|
||||
* Copyright (C) Miroslav Lichvar 2022, 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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Luke Valenta 2023
|
||||
* Copyright (C) Miroslav Lichvar 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
|
||||
@@ -190,6 +191,7 @@ test_unit(void)
|
||||
s3 = SCK_AcceptConnection(s1, &sa2);
|
||||
TEST_CHECK(UTI_CompareIPs(&sa1.ip_addr, &sa2.ip_addr, NULL) == 0);
|
||||
|
||||
fcntl(s3, F_SETFL, fcntl(s3, F_GETFL) & ~O_NONBLOCK);
|
||||
send_and_recv(SCK_ADDR_IP, 1, 1, s3, s2);
|
||||
|
||||
SCK_ShutdownConnection(s2);
|
||||
@@ -226,6 +228,7 @@ test_unit(void)
|
||||
s3 = SCK_AcceptConnection(s1, &sa2);
|
||||
TEST_CHECK(sa2.ip_addr.family == IPADDR_UNSPEC);
|
||||
|
||||
fcntl(s3, F_SETFL, fcntl(s3, F_GETFL) & ~O_NONBLOCK);
|
||||
send_and_recv(SCK_ADDR_UNIX, 1, i % 2, s3, s2);
|
||||
|
||||
if (i % 4)
|
||||
|
||||
@@ -28,7 +28,8 @@ create_source(SRC_Type type, IPAddr *addr, int authenticated, int sel_options)
|
||||
|
||||
return SRC_CreateNewInstance(UTI_IPToRefid(addr), type, authenticated, sel_options,
|
||||
type == SRC_NTP ? addr : NULL,
|
||||
SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES, 0.0, 1.0);
|
||||
SRC_DEFAULT_MINSAMPLES, SRC_DEFAULT_MAXSAMPLES, 0.0, 1.0,
|
||||
SRC_DEFAULT_MAXUNREACH);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
93
tls.h
Normal file
93
tls.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* 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
|
||||
* 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 the TLS session
|
||||
*/
|
||||
|
||||
#ifndef GOT_TLS_H
|
||||
#define GOT_TLS_H
|
||||
|
||||
struct TLS_Instance_Record;
|
||||
|
||||
typedef struct TLS_Instance_Record *TLS_Instance;
|
||||
|
||||
typedef void *TLS_Credentials;
|
||||
|
||||
typedef enum {
|
||||
/* TLS operation succeeded */
|
||||
TLS_SUCCESS,
|
||||
/* TLS operation failed.
|
||||
No more operations should be called and the session should be destroyed. */
|
||||
TLS_FAILED,
|
||||
/* TLS session closed by other end */
|
||||
TLS_CLOSED,
|
||||
/* The last TLS operation should be called again when input is ready */
|
||||
TLS_AGAIN_INPUT,
|
||||
/* The last TLS operation should be called again when output is ready */
|
||||
TLS_AGAIN_OUTPUT,
|
||||
} TLS_Status;
|
||||
|
||||
/* Initialize TLS */
|
||||
extern int TLS_Initialise(time_t (*get_time)(time_t *t));
|
||||
|
||||
/* Deinitialize TLS */
|
||||
extern void TLS_Finalise(void);
|
||||
|
||||
/* Create new TLS credentials instance */
|
||||
extern TLS_Credentials TLS_CreateCredentials(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);
|
||||
|
||||
/* Destroy TLS credentials instance */
|
||||
extern void TLS_DestroyCredentials(TLS_Credentials credentials);
|
||||
|
||||
/* Create new TLS session instance */
|
||||
extern TLS_Instance TLS_CreateInstance(int server_mode, int sock_fd, const char *server_name,
|
||||
const char *label, const char *alpn_name,
|
||||
TLS_Credentials credentials, int disable_time_checks);
|
||||
|
||||
/* Destroy TLS instance */
|
||||
extern void TLS_DestroyInstance(TLS_Instance inst);
|
||||
|
||||
/* Perform TLS handshake */
|
||||
extern TLS_Status TLS_DoHandshake(TLS_Instance inst);
|
||||
|
||||
/* Send data over TLS */
|
||||
extern TLS_Status TLS_Send(TLS_Instance inst, const void *data, int length, int *sent);
|
||||
|
||||
/* Receive data over TLS */
|
||||
extern TLS_Status TLS_Receive(TLS_Instance inst, void *data, int length, int *received);
|
||||
|
||||
/* Check if there is data pending to read */
|
||||
extern int TLS_CheckPending(TLS_Instance inst);
|
||||
|
||||
/* Perform TLS shutdown */
|
||||
extern TLS_Status TLS_Shutdown(TLS_Instance inst);
|
||||
|
||||
/* Export key from TLS instance */
|
||||
extern int TLS_ExportKey(TLS_Instance inst, int label_length, const char *label,
|
||||
int context_length, const void *context, int key_length,
|
||||
unsigned char *key);
|
||||
|
||||
#endif
|
||||
409
tls_gnutls.c
Normal file
409
tls_gnutls.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Routines implementing TLS session handling using the gnutls library.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "tls.h"
|
||||
|
||||
#include "conf.h"
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/x509.h>
|
||||
|
||||
struct TLS_Instance_Record {
|
||||
gnutls_session_t session;
|
||||
int server;
|
||||
char *label;
|
||||
char *alpn_name;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static gnutls_priority_t priority_cache;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
TLS_Initialise(time_t (*get_time)(time_t *t))
|
||||
{
|
||||
int 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();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use our clock instead of the system clock in certificate verification */
|
||||
gnutls_global_set_time_function(get_time);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
TLS_Finalise(void)
|
||||
{
|
||||
gnutls_priority_deinit(priority_cache);
|
||||
gnutls_global_deinit();
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
TLS_Credentials
|
||||
TLS_CreateCredentials(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;
|
||||
|
||||
r = gnutls_certificate_allocate_credentials(&credentials);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (certs && keys) {
|
||||
BRIEF_ASSERT(!trusted_certs && !trusted_certs_ids);
|
||||
|
||||
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 {
|
||||
BRIEF_ASSERT(!certs && !keys && n_certs_keys <= 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return credentials;
|
||||
|
||||
error:
|
||||
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
|
||||
if (credentials)
|
||||
gnutls_certificate_free_credentials(credentials);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
TLS_DestroyCredentials(TLS_Credentials credentials)
|
||||
{
|
||||
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
TLS_Instance
|
||||
TLS_CreateInstance(int server_mode, int sock_fd, const char *server_name, const char *label,
|
||||
const char *alpn_name, TLS_Credentials credentials, int disable_time_checks)
|
||||
{
|
||||
gnutls_datum_t alpn;
|
||||
unsigned int flags;
|
||||
int r;
|
||||
|
||||
TLS_Instance inst = MallocNew(struct TLS_Instance_Record);
|
||||
|
||||
inst->session = NULL;
|
||||
inst->server = server_mode;
|
||||
inst->label = Strdup(label);
|
||||
inst->alpn_name = Strdup(alpn_name);
|
||||
|
||||
r = gnutls_init(&inst->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));
|
||||
inst->session = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!server_mode) {
|
||||
assert(server_name);
|
||||
|
||||
if (!UTI_IsStringIP(server_name)) {
|
||||
r = gnutls_server_name_set(inst->session, GNUTLS_NAME_DNS, server_name,
|
||||
strlen(server_name));
|
||||
if (r < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
|
||||
if (disable_time_checks) {
|
||||
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
|
||||
DEBUG_LOG("Disabled time checks");
|
||||
}
|
||||
|
||||
gnutls_session_set_verify_cert(inst->session, server_name, flags);
|
||||
}
|
||||
|
||||
r = gnutls_priority_set(inst->session, priority_cache);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
r = gnutls_credentials_set(inst->session, GNUTLS_CRD_CERTIFICATE, credentials);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
alpn.data = (unsigned char *)inst->alpn_name;
|
||||
alpn.size = strlen(inst->alpn_name);
|
||||
|
||||
r = gnutls_alpn_set_protocols(inst->session, &alpn, 1, 0);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
gnutls_transport_set_int(inst->session, sock_fd);
|
||||
|
||||
return inst;
|
||||
|
||||
error:
|
||||
LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
|
||||
TLS_DestroyInstance(inst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
TLS_DestroyInstance(TLS_Instance inst)
|
||||
{
|
||||
if (inst->session)
|
||||
gnutls_deinit(inst->session);
|
||||
|
||||
Free(inst->label);
|
||||
Free(inst->alpn_name);
|
||||
|
||||
Free(inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_alpn(TLS_Instance inst)
|
||||
{
|
||||
gnutls_datum_t alpn;
|
||||
int length = strlen(inst->alpn_name);
|
||||
|
||||
if (gnutls_alpn_get_selected_protocol(inst->session, &alpn) < 0 ||
|
||||
alpn.size != length || memcmp(alpn.data, inst->alpn_name, length) != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
TLS_Status
|
||||
TLS_DoHandshake(TLS_Instance inst)
|
||||
{
|
||||
int r = gnutls_handshake(inst->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->session),
|
||||
gnutls_certificate_type_get(inst->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);
|
||||
|
||||
/* 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)
|
||||
return TLS_FAILED;
|
||||
|
||||
return TLS_CLOSED;
|
||||
}
|
||||
|
||||
return gnutls_record_get_direction(inst->session) ? TLS_AGAIN_OUTPUT : TLS_AGAIN_INPUT;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
char *description = gnutls_session_get_desc(inst->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);
|
||||
return TLS_FAILED;
|
||||
}
|
||||
|
||||
return TLS_SUCCESS;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
TLS_Status
|
||||
TLS_Send(TLS_Instance inst, const void *data, int length, int *sent)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (length < 0)
|
||||
return TLS_FAILED;
|
||||
|
||||
r = gnutls_record_send(inst->session, data, length);
|
||||
|
||||
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));
|
||||
return TLS_FAILED;
|
||||
}
|
||||
|
||||
return TLS_AGAIN_OUTPUT;
|
||||
}
|
||||
|
||||
*sent = r;
|
||||
|
||||
return TLS_SUCCESS;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
TLS_Status
|
||||
TLS_Receive(TLS_Instance inst, void *data, int length, int *received)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (length < 0)
|
||||
return TLS_FAILED;
|
||||
|
||||
r = gnutls_record_recv(inst->session, data, length);
|
||||
|
||||
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));
|
||||
return TLS_FAILED;
|
||||
}
|
||||
|
||||
return TLS_AGAIN_INPUT;
|
||||
}
|
||||
|
||||
*received = r;
|
||||
|
||||
return TLS_SUCCESS;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
TLS_CheckPending(TLS_Instance inst)
|
||||
{
|
||||
return gnutls_record_check_pending(inst->session) > 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
TLS_Status
|
||||
TLS_Shutdown(TLS_Instance inst)
|
||||
{
|
||||
int r = gnutls_bye(inst->session, GNUTLS_SHUT_RDWR);
|
||||
|
||||
if (r < 0) {
|
||||
if (gnutls_error_is_fatal(r)) {
|
||||
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
|
||||
return TLS_FAILED;
|
||||
}
|
||||
|
||||
return gnutls_record_get_direction(inst->session) ? TLS_AGAIN_OUTPUT : TLS_AGAIN_INPUT;
|
||||
}
|
||||
|
||||
return TLS_SUCCESS;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
TLS_ExportKey(TLS_Instance inst, int label_length, const char *label, int context_length,
|
||||
const void *context, int key_length, unsigned char *key)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (label_length < 0 || context_length < 0 || key_length < 0)
|
||||
return 0;
|
||||
|
||||
r = gnutls_prf_rfc5705(inst->session, label_length, label, context_length, (char *)context,
|
||||
key_length, (char *)key);
|
||||
|
||||
return r >= 0;
|
||||
}
|
||||
17
util.c
17
util.c
@@ -545,12 +545,14 @@ UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask)
|
||||
char *
|
||||
UTI_IPSockAddrToString(const IPSockAddr *sa)
|
||||
{
|
||||
char *result;
|
||||
char buf[BUFFER_LENGTH], *result;
|
||||
|
||||
/* Copy to a separate buffer to avoid a compiler warning */
|
||||
snprintf(buf, sizeof (buf), "%s", UTI_IPToString(&sa->ip_addr));
|
||||
|
||||
result = NEXT_BUFFER;
|
||||
snprintf(result, BUFFER_LENGTH,
|
||||
sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu",
|
||||
UTI_IPToString(&sa->ip_addr), sa->port);
|
||||
sa->ip_addr.family != IPADDR_INET6 ? "%s:%hu" : "[%s]:%hu", buf, sa->port);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1201,7 +1203,7 @@ create_dir(char *p, mode_t mode, uid_t uid, gid_t gid)
|
||||
}
|
||||
|
||||
/* Set its owner */
|
||||
if (chown(p, uid, gid) < 0) {
|
||||
if (lchown(p, uid, gid) < 0) {
|
||||
LOG(LOGS_ERR, "Could not change ownership of %s : %s", p, strerror(errno));
|
||||
/* Don't leave it there with incorrect ownership */
|
||||
rmdir(p);
|
||||
@@ -1364,6 +1366,7 @@ FILE *
|
||||
UTI_OpenFile(const char *basedir, const char *name, const char *suffix,
|
||||
char mode, mode_t perm)
|
||||
{
|
||||
uint64_t attempts = 0, warn_attempts = 100;
|
||||
const char *file_mode;
|
||||
char path[PATH_MAX];
|
||||
LOG_Severity severity;
|
||||
@@ -1407,6 +1410,12 @@ try_again:
|
||||
return NULL;
|
||||
}
|
||||
DEBUG_LOG("Removed %s", path);
|
||||
|
||||
if (++attempts == warn_attempts) {
|
||||
LOG(LOGS_WARN, "Failing to replace %s (%"PRIu64" attempts)", path, attempts);
|
||||
warn_attempts *= 10;
|
||||
}
|
||||
|
||||
goto try_again;
|
||||
}
|
||||
LOG(severity, "Could not open %s : %s", path, strerror(errno));
|
||||
|
||||
Reference in New Issue
Block a user