Compare commits

..

2 Commits

Author SHA1 Message Date
Miroslav Lichvar
04328ceead doc: update NEWS 2020-08-19 16:24:04 +02:00
Miroslav Lichvar
f00fed2009 main: create new file when writing pidfile
When writing the pidfile, open the file with the O_CREAT|O_EXCL flags
to avoid following a symlink and writing the PID to an unexpected file,
when chronyd still has the root privileges.

The Linux open(2) man page warns about O_EXCL not working as expected on
NFS versions before 3 and Linux versions before 2.6. Saving pidfiles on
a distributed filesystem like NFS is not generally expected, but if
there is a reason to do that, these old kernel and NFS versions are not
considered to be supported for saving files by chronyd.

This is a minimal backport specific to this issue of the following
commits:
- commit 2fc8edacb8 ("use PATH_MAX")
- commit f4c6a00b2a ("logging: call exit() in LOG_Message()")
- commit 7a4c396bba ("util: add functions for common file operations")
- commit e18903a6b5 ("switch to new util file functions")

Reported-by: Matthias Gerstner <mgerstner@suse.de>
2020-08-06 11:46:04 +02:00
243 changed files with 5753 additions and 26265 deletions

View File

@@ -21,50 +21,48 @@
#
# Makefile template
SYSCONFDIR = @SYSCONFDIR@
BINDIR = @BINDIR@
SBINDIR = @SBINDIR@
LOCALSTATEDIR = @LOCALSTATEDIR@
CHRONYVARDIR = @CHRONYVARDIR@
DESTDIR =
SYSCONFDIR=@SYSCONFDIR@
BINDIR=@BINDIR@
SBINDIR=@SBINDIR@
LOCALSTATEDIR=@LOCALSTATEDIR@
CHRONYVARDIR=@CHRONYVARDIR@
CC = @CC@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
GETDATE_CFLAGS = @GETDATE_CFLAGS@
DESTDIR=
EXTRA_OBJS = @EXTRA_OBJS@
HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_CLI_OBJS = @EXTRA_CLI_OBJS@
EXTRA_OBJS=@EXTRA_OBJECTS@
CLI_OBJS = array.o client.o cmdparse.o getdate.o memory.o nameserv.o \
pktlength.o socket.o util.o $(EXTRA_CLI_OBJS)
pktlength.o util.o $(HASH_OBJ)
ALL_OBJS = $(OBJS) $(CLI_OBJS)
ALL_OBJS = $(OBJS) $(EXTRA_OBJS) $(CLI_OBJS)
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
EXTRA_LIBS = @EXTRA_LIBS@
EXTRA_CLI_LIBS = @EXTRA_CLI_LIBS@
EXTRA_LIBS=@EXTRA_LIBS@
EXTRA_CLI_LIBS=@EXTRA_CLI_LIBS@
# Until we have a main procedure we can link, just build object files
# to test compilation
all : chronyd chronyc
chronyd : $(OBJS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyd : $(OBJS) $(EXTRA_OBJS)
$(CC) $(CFLAGS) -o chronyd $(OBJS) $(EXTRA_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_LIBS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
getdate.o: CFLAGS += $(GETDATE_CFLAGS)
distclean : clean
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean
@@ -72,7 +70,6 @@ distclean : clean
-rm -f Makefile config.h config.log
clean :
$(MAKE) -C test/unit clean
-rm -f *.o *.s chronyc chronyd core.* *~
-rm -f *.gcda *.gcno
-rm -rf .deps
@@ -124,7 +121,7 @@ check : chronyd chronyc
cd test/system && ./run
print-chronyd-objects :
@echo $(OBJS)
@echo $(OBJS) $(EXTRA_OBJS)
Makefile : Makefile.in configure
@echo
@@ -138,6 +135,4 @@ Makefile : Makefile.in configure
.deps/%.d: %.c | .deps
@$(CC) -MM $(CPPFLAGS) -MT '$(<:%.c=%.o) $@' $< -o $@
ifndef NODEPS
-include $(ALL_OBJS:%.o=.deps/%.d)
endif

141
NEWS
View File

@@ -1,144 +1,3 @@
New in version 4.4
==================
Enhancements
------------
* Add support for AES-GCM-SIV with Nettle >= 3.9 to shorten NTS
cookies to avoid some length-specific blocking of NTP on Internet
* Add support for multiple refclocks using extpps option on one PHC
* Add maxpoll option to hwtimestamp directive to improve PHC tracking
with low packet rates
* Add hwtstimeout directive to configure timeout for late timestamps
* Handle late hardware transmit timestamps of NTP requests on all sockets
* Handle mismatched 32/64-bit time_t in SOCK refclock samples
* Improve source replacement
* Log important changes made by command requests (chronyc)
* Refresh address of NTP sources periodically
* Request nanosecond kernel RX timestamping on FreeBSD
* Set DSCP for IPv6 packets
* Shorten NTS-KE retry interval when network is down
* Update seccomp filter for musl
* Warn if loading keys from file with unexpected permissions
* Warn if source selection fails or falseticker is detected
* Add selectopts command to modify source-specific selection options
* Add timestamp sources to serverstats report and make its fields 64-bit
* Add -e option to chronyc to indicate end of response
New in version 4.3
==================
Enhancements
------------
* Add local option to refclock directive to stabilise system clock
with more stable free-running clock (e.g. TCXO, OCXO)
* Add maxdelayquant option to server/pool/peer directive to replace
maxdelaydevratio filter with long-term quantile-based filtering
* Add selection option to log directive
* Allow external PPS in PHC refclock without configurable pin
* Don't accept first interleaved response to minimise error in delay
* Don't use arc4random on Linux to avoid server performance loss
* Improve filter option to better handle missing NTP samples
* Improve stability with hardware timestamping and PHC refclock
* Update seccomp filter
Bug fixes
---------
* Fix waitsync command to reconnect when not getting response
New in version 4.2
==================
Enhancements
------------
* Add support for NTPv4 extension field improving synchronisation
stability and resolution of root delay and dispersion (experimental)
* Add support for NTP over PTP (experimental)
* Add support for AES-CMAC and hash functions in GnuTLS
* Improve server interleaved mode to be more reliable and support
multiple clients behind NAT
* Update seccomp filter
* Add statistics about interleaved mode to serverstats report
Bug fixes
---------
* Fix RTC support with 64-bit time_t on 32-bit Linux
* Fix seccomp filter to work correctly with bind*device directives
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
Other changes
-------------
* Switch Solaris support to illumos
New in version 4.1
==================
Enhancements
------------
* Add support for NTS servers specified by IP address (matching
Subject Alternative Name in server certificate)
* Add source-specific configuration of trusted certificates
* Allow multiple files and directories with trusted certificates
* Allow multiple pairs of server keys and certificates
* Add copy option to server/pool directive
* Increase PPS lock limit to 40% of pulse interval
* Perform source selection immediately after loading dump files
* Reload dump files for addresses negotiated by NTS-KE server
* Update seccomp filter and add less restrictive level
* Restart ongoing name resolution on online command
Bug fixes
---------
* Fix responding to IPv4 command requests on FreeBSD
* Fix dump files to not include uncorrected offset
* Fix initstepslew to accept time from own NTP clients
* Reset NTP address and port when no longer negotiated by NTS-KE server
New in version 4.0
==================
Enhancements
------------
* Add support for Network Time Security (NTS) authentication
* Add support for AES-CMAC keys (AES128, AES256) with Nettle
* Add authselectmode directive to control selection of unauthenticated sources
* Add binddevice, bindacqdevice, bindcmddevice directives
* Add confdir directive to better support fragmented configuration
* Add sourcedir directive and "reload sources" command to support dynamic
NTP sources specified in files
* Add clockprecision directive
* Add dscp directive to set Differentiated Services Code Point (DSCP)
* Add -L option to limit log messages by severity
* Add -p option to print whole configuration with included files
* Add -U option to allow start under non-root user
* Allow maxsamples to be set to 1 for faster update with -q/-Q option
* Avoid replacing NTP sources with sources that have unreachable address
* Improve pools to repeat name resolution to get "maxsources" sources
* Improve source selection with trusted sources
* Improve NTP loop test to prevent synchronisation to itself
* Repeat iburst when NTP source is switched from offline state to online
* Update clock synchronisation status and leap status more frequently
* Update seccomp filter
* Add "add pool" command
* Add "reset sources" command to drop all measurements
* Add authdata command to print details about NTP authentication
* Add selectdata command to print details about source selection
* Add -N option and sourcename command to print original names of sources
* Add -a option to some commands to print also unresolved sources
* Add -k, -p, -r options to clients command to select, limit, reset data
Bug fixes
---------
* Don't set interface for NTP responses to allow asymmetric routing
* Handle RTCs that don't support interrupts
* Respond to command requests with correct address on multihomed hosts
Removed features
----------------
* Drop support for RIPEMD keys (RMD128, RMD160, RMD256, RMD320)
* Drop support for long (non-standard) MACs in NTPv4 packets (chrony 2.x
clients using non-MD5/SHA1 keys need to use option "version 3")
* Drop support for line editing with GNU Readline
New in version 3.5.1
====================

64
README
View File

@@ -28,8 +28,10 @@ What will chrony run on?
========================
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
illumos. Closely related systems may work too. Any other system will
likely require a porting exercise.
Solaris. Closely related systems may work too. Any other system will
likely require a porting exercise. You would need to start from one
of the existing system-specific drivers and look into the quirks of
certain system calls and the kernel on your target system.
How do I set it up?
===================
@@ -47,7 +49,42 @@ Frequently Asked Questions (FAQ).
The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony-project.org/
https://chrony.tuxfamily.org/
Where are new versions announced?
=================================
There is a low volume mailing list where new versions and other
important news relating to chrony is announced. You can join this list
by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org
These messages will be copied to chrony-users (see below).
How can I get support for chrony?
and where can I discuss new features, possible bugs etc?
========================================================
There are 3 mailing lists relating to chrony. chrony-announce was
mentioned above. chrony-users is a users' discussion list, e.g. for
general questions and answers about using chrony. chrony-dev is a more
technical list, e.g. for discussing how new features should be
implemented, exchange of information between developers etc. To
subscribe to either of these lists, send a message with the subject
"subscribe" to
chrony-users-request@chrony.tuxfamily.org
or
chrony-dev-request@chrony.tuxfamily.org
as applicable.
When you are reporting a bug, please send us all the information you can.
Unfortunately, chrony has proven to be one of those programs where it is very
difficult to reproduce bugs in a different environment. So we may have to
interact with you quite a lot to obtain enough extra logging and tracing to
pin-point the problem in some cases. Please be patient and plan for this!
License
=======
@@ -63,31 +100,26 @@ Miroslav Lichvar <mlichvar@redhat.com>
Acknowledgements
================
In writing the chronyd program, extensive use has been made of the NTPv3 (RFC
1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd
implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and
others has been used to check the details of the protocol.
In writing the chronyd program, extensive use has been made of RFC 1305
and RFC 5905, written by David Mills. The source code of the NTP reference
implementation has been used to check the details of the protocol.
The following people have provided patches and other major contributions
to chrony:
to the program :
Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
Leigh Brown <leigh@solinno.co.uk>
Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Stefan R. Filipek <srfilipek@gmail.com>
Mike Fleetwood <mike@rockover.demon.co.uk>
Alexander Gretencord <arutha@gmx.de>
@@ -97,12 +129,10 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
John Hasler <john@dhh.gt.org>
Tjalling Hattink <t.hattink@fugro.nl>
Liam Hatton <me@liamhatton.com>
Holger Hoffstätte <holger@applied-asynchrony.com>
Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi>
Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Eric Lammerts <eric@lammerts.org>
Stefan Lucke <stefan@lucke.in-berlin.de>
Victor Lum <viclum@vanu.com>
@@ -113,14 +143,9 @@ Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi>
@@ -132,7 +157,6 @@ Bernhard M. Wiedemann <bwiedemann@suse.de>
Joachim Wiedorn <ad_debian@joonet.de>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
Michael Witten <mfwitten@gmail.com>
Doug Woodward <dougw@whistler.com>
Thomas Zajic <zlatko@zlatko.fdns.net>

View File

@@ -30,19 +30,16 @@
#include "sysincl.h"
/* This type is used to represent an IPv4 address or IPv6 address.
Addresses which are not resolved yet can be represented with an ID.
All parts are in HOST order, NOT network order. */
#define IPADDR_UNSPEC 0
#define IPADDR_INET4 1
#define IPADDR_INET6 2
#define IPADDR_ID 3
typedef struct {
union {
uint32_t in4;
uint8_t in6[16];
uint32_t id;
} addr;
uint16_t family;
uint16_t _pad;
@@ -50,10 +47,8 @@ typedef struct {
typedef struct {
IPAddr ip_addr;
uint16_t port;
} IPSockAddr;
typedef IPSockAddr NTP_Remote_Address;
unsigned short port;
} NTP_Remote_Address;
#define INVALID_IF_INDEX -1

View File

@@ -247,8 +247,6 @@ set_subnet_(ADF_AuthTable table,
set_subnet(&table->base6, ip6, 4, 0, new_state, delete_children) == ADF_SUCCESS)
return ADF_SUCCESS;
break;
default:
break;
}
return ADF_BADSUBNET;
@@ -361,9 +359,9 @@ ADF_IsAllowed(ADF_AuthTable table,
case IPADDR_INET6:
split_ip6(ip_addr, ip6);
return check_ip_in_node(&table->base6, ip6);
default:
return 0;
}
return 0;
}
/* ================================================== */

15
array.c
View File

@@ -116,21 +116,6 @@ ARR_AppendElement(ARR_Instance array, void *element)
memcpy(e, element, array->elem_size);
}
void
ARR_RemoveElement(ARR_Instance array, unsigned int index)
{
void *e, *l;
e = ARR_GetElement(array, index);
l = ARR_GetElement(array, array->used - 1);
if (e < l)
memmove(e, (char *)e + array->elem_size, (char *)l - (char *)e);
array->used--;
realloc_array(array, array->used);
}
void
ARR_SetSize(ARR_Instance array, unsigned int size)
{

View File

@@ -47,9 +47,6 @@ extern void *ARR_GetElements(ARR_Instance array);
/* Add a new element to the end of the array */
extern void ARR_AppendElement(ARR_Instance array, void *element);
/* Remove element with given index */
extern void ARR_RemoveElement(ARR_Instance array, unsigned int index);
/* Set the size of the array */
extern void ARR_SetSize(ARR_Instance array, unsigned int size);

179
candm.h
View File

@@ -101,16 +101,7 @@
#define REQ_ADD_PEER3 61
#define REQ_SHUTDOWN 62
#define REQ_ONOFFLINE 63
#define REQ_ADD_SOURCE 64
#define REQ_NTP_SOURCE_NAME 65
#define REQ_RESET_SOURCES 66
#define REQ_AUTH_DATA 67
#define REQ_CLIENT_ACCESSES_BY_INDEX3 68
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
#define REQ_MODIFY_SELECTOPTS 72
#define N_REQUEST_TYPES 73
#define N_REQUEST_TYPES 64
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -122,12 +113,6 @@ typedef struct {
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
/* Structure for 64-bit integers (not requiring 64-bit alignment) */
typedef struct {
uint32_t high;
uint32_t low;
} Integer64;
/* 32-bit floating-point format consisting of 7-bit signed exponent
and 25-bit signed coefficient without hidden bit.
The result is calculated as: 2^(exp - 25) * coef */
@@ -260,11 +245,6 @@ typedef struct {
int32_t EOR;
} REQ_Ac_Check;
/* Source types in NTP source requests */
#define REQ_ADDSRC_SERVER 1
#define REQ_ADDSRC_PEER 2
#define REQ_ADDSRC_POOL 3
/* Flags used in NTP source requests */
#define REQ_ADDSRC_ONLINE 0x1
#define REQ_ADDSRC_AUTOOFFLINE 0x2
@@ -275,13 +255,9 @@ typedef struct {
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP1 0x800
typedef struct {
uint32_t type;
uint8_t name[256];
IPAddr ip_addr;
uint32_t port;
int32_t minpoll;
int32_t maxpoll;
@@ -293,7 +269,6 @@ typedef struct {
int32_t min_samples;
int32_t max_samples;
uint32_t authkey;
uint32_t nts_port;
Float max_delay;
Float max_delay_ratio;
Float max_delay_dev_ratio;
@@ -302,9 +277,7 @@ typedef struct {
Float offset;
uint32_t flags;
int32_t filter_length;
uint32_t cert_set;
Float max_delay_quant;
uint32_t reserved[1];
uint32_t reserved[3];
int32_t EOR;
} REQ_NTP_Source;
@@ -319,7 +292,8 @@ typedef struct {
} REQ_Dfreq;
typedef struct {
Float doffset;
int32_t sec;
int32_t usec;
int32_t EOR;
} REQ_Doffset;
@@ -335,8 +309,6 @@ typedef struct {
typedef struct {
uint32_t first_index;
uint32_t n_clients;
uint32_t min_hits;
uint32_t reset;
int32_t EOR;
} REQ_ClientAccessesByIndex;
@@ -363,30 +335,6 @@ typedef struct {
int32_t EOR;
} REQ_NTPData;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_NTPSourceName;
typedef struct {
IPAddr ip_addr;
int32_t EOR;
} REQ_AuthData;
typedef struct {
uint32_t index;
int32_t EOR;
} REQ_SelectData;
/* Mask and options reuse the REQ_ADDSRC flags */
typedef struct {
IPAddr address;
uint32_t ref_id;
uint32_t mask;
uint32_t options;
int32_t EOR;
} REQ_Modify_SelectOpts;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -423,10 +371,9 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(two times), delta offset, and manual timestamp, added new fields and
(using new request/reply types) and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant,
added new commands: authdata, ntpdata, onoffline, refresh, reset,
selectdata, serverstats, shutdown, sourcename
added new commands: ntpdata, refresh, serverstats, shutdown
*/
#define PROTO_VERSION_NUMBER 6
@@ -440,8 +387,8 @@ typedef struct {
#define PROTO_VERSION_PADDING 6
/* The maximum length of padding in request packet, currently
defined by CLIENT_ACCESSES_BY_INDEX3 */
#define MAX_PADDING_LENGTH 484
defined by MANUAL_LIST */
#define MAX_PADDING_LENGTH 396
/* ================================================== */
@@ -490,10 +437,6 @@ typedef struct {
REQ_ReselectDistance reselect_distance;
REQ_SmoothTime smoothtime;
REQ_NTPData ntp_data;
REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
REQ_Modify_SelectOpts modify_select_opts;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -530,14 +473,7 @@ typedef struct {
#define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
#define RPY_MANUAL_LIST2 18
#define RPY_NTP_SOURCE_NAME 19
#define RPY_AUTH_DATA 20
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
#define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24
#define RPY_SERVER_STATS4 25
#define N_REPLY_TYPES 26
#define N_REPLY_TYPES 19
/* Status codes */
#define STT_SUCCESS 0
@@ -550,7 +486,8 @@ typedef struct {
#define STT_BADSUBNET 7
#define STT_ACCESSALLOWED 8
#define STT_ACCESSDENIED 9
#define STT_NOHOSTACCESS 10 /* Deprecated */
/* Deprecated */
#define STT_NOHOSTACCESS 10
#define STT_SOURCEALREADYKNOWN 11
#define STT_TOOMANYSOURCES 12
#define STT_NORTC 13
@@ -560,7 +497,6 @@ typedef struct {
#define STT_INVALIDAF 17
#define STT_BADPKTVERSION 18
#define STT_BADPKTLENGTH 19
#define STT_INVALIDNAME 21
typedef struct {
int32_t EOR;
@@ -575,12 +511,17 @@ typedef struct {
#define RPY_SD_MD_PEER 1
#define RPY_SD_MD_REF 2
#define RPY_SD_ST_SELECTED 0
#define RPY_SD_ST_NONSELECTABLE 1
#define RPY_SD_ST_SYNC 0
#define RPY_SD_ST_UNREACH 1
#define RPY_SD_ST_FALSETICKER 2
#define RPY_SD_ST_JITTERY 3
#define RPY_SD_ST_UNSELECTED 4
#define RPY_SD_ST_SELECTABLE 5
#define RPY_SD_ST_CANDIDATE 4
#define RPY_SD_ST_OUTLIER 5
#define RPY_SD_FLAG_NOSELECT 0x1
#define RPY_SD_FLAG_PREFER 0x2
#define RPY_SD_FLAG_TRUST 0x4
#define RPY_SD_FLAG_REQUIRE 0x8
typedef struct {
IPAddr ip_addr;
@@ -649,17 +590,14 @@ typedef struct {
typedef struct {
IPAddr ip;
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops;
int8_t ntp_interval;
int8_t nke_interval;
int8_t cmd_interval;
int8_t ntp_timeout_interval;
int8_t pad;
uint32_t last_ntp_hit_ago;
uint32_t last_nke_hit_ago;
uint32_t last_cmd_hit_ago;
} RPY_ClientAccesses_Client;
@@ -672,24 +610,11 @@ typedef struct {
} RPY_ClientAccessesByIndex;
typedef struct {
Integer64 ntp_hits;
Integer64 nke_hits;
Integer64 cmd_hits;
Integer64 ntp_drops;
Integer64 nke_drops;
Integer64 cmd_drops;
Integer64 log_drops;
Integer64 ntp_auth_hits;
Integer64 ntp_interleaved_hits;
Integer64 ntp_timestamps;
Integer64 ntp_span_seconds;
Integer64 ntp_daemon_rx_timestamps;
Integer64 ntp_daemon_tx_timestamps;
Integer64 ntp_kernel_rx_timestamps;
Integer64 ntp_kernel_tx_timestamps;
Integer64 ntp_hw_rx_timestamps;
Integer64 ntp_hw_tx_timestamps;
Integer64 reserved[4];
uint32_t ntp_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t cmd_drops;
uint32_t log_drops;
int32_t EOR;
} RPY_ServerStats;
@@ -759,55 +684,10 @@ typedef struct {
uint32_t total_tx_count;
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
uint32_t reserved[3];
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
typedef struct {
uint8_t name[256];
int32_t EOR;
} RPY_NTPSourceName;
#define RPY_AD_MD_NONE 0
#define RPY_AD_MD_SYMMETRIC 1
#define RPY_AD_MD_NTS 2
typedef struct {
uint16_t mode;
uint16_t key_type;
uint32_t key_id;
uint16_t key_length;
uint16_t ke_attempts;
uint32_t last_ke_ago;
uint16_t cookies;
uint16_t cookie_length;
uint16_t nak;
uint16_t pad;
int32_t EOR;
} RPY_AuthData;
#define RPY_SD_OPTION_NOSELECT 0x1
#define RPY_SD_OPTION_PREFER 0x2
#define RPY_SD_OPTION_TRUST 0x4
#define RPY_SD_OPTION_REQUIRE 0x8
typedef struct {
uint32_t ref_id;
IPAddr ip_addr;
uint8_t state_char;
uint8_t authentication;
uint8_t leap;
uint8_t pad;
uint16_t conf_options;
uint16_t eff_options;
uint32_t last_sample_ago;
Float score;
Float lo_limit;
Float hi_limit;
int32_t EOR;
} RPY_SelectData;
typedef struct {
uint8_t version;
uint8_t pkt_type;
@@ -837,9 +717,6 @@ typedef struct {
RPY_Activity activity;
RPY_Smoothing smoothing;
RPY_NTPData ntp_data;
RPY_NTPSourceName ntp_source_name;
RPY_AuthData auth_data;
RPY_SelectData select_data;
} data; /* Reply specific parameters */
} CMD_Reply;

1343
client.c

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -38,24 +38,28 @@
#include "array.h"
#include "clientlog.h"
#include "conf.h"
#include "local.h"
#include "memory.h"
#include "ntp.h"
#include "reports.h"
#include "util.h"
#include "logging.h"
#define MAX_SERVICES 3
typedef struct {
IPAddr ip_addr;
uint32_t last_hit[MAX_SERVICES];
uint32_t hits[MAX_SERVICES];
uint16_t drops[MAX_SERVICES];
uint16_t tokens[MAX_SERVICES];
int8_t rate[MAX_SERVICES];
uint32_t last_ntp_hit;
uint32_t last_cmd_hit;
uint32_t ntp_hits;
uint32_t cmd_hits;
uint16_t ntp_drops;
uint16_t cmd_drops;
uint16_t ntp_tokens;
uint16_t cmd_tokens;
int8_t ntp_rate;
int8_t cmd_rate;
int8_t ntp_timeout_rate;
uint8_t drop_flags;
uint8_t flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record;
/* Hash table of records, there is a fixed number of records per slot */
@@ -100,12 +104,15 @@ static uint32_t ts_offset;
#define MIN_LIMIT_BURST 1
#define MAX_LIMIT_BURST 255
static uint16_t max_tokens[MAX_SERVICES];
static uint16_t tokens_per_hit[MAX_SERVICES];
static uint16_t max_ntp_tokens;
static uint16_t max_cmd_tokens;
static uint16_t ntp_tokens_per_packet;
static uint16_t cmd_tokens_per_packet;
/* Reduction of token rates to avoid overflow of 16-bit counters. Negative
shift is used for coarse limiting with intervals shorter than -TS_FRAC. */
static int token_shift[MAX_SERVICES];
static int ntp_token_shift;
static int cmd_token_shift;
/* Rates at which responses are randomly allowed (in log2) when the
buckets don't have enough tokens. This is necessary in order to
@@ -115,66 +122,30 @@ static int token_shift[MAX_SERVICES];
#define MIN_LEAK_RATE 1
#define MAX_LEAK_RATE 4
static int leak_rate[MAX_SERVICES];
static int ntp_leak_rate;
static int cmd_leak_rate;
/* Limit intervals in log2 */
static int limit_interval[MAX_SERVICES];
/* Flag indicating whether the last response was dropped */
#define FLAG_NTP_DROPPED 0x1
/* NTP limit interval in log2 */
static int ntp_limit_interval;
/* Flag indicating whether facility is turned on or not */
static int active;
/* RX and TX timestamp saved for clients using interleaved mode */
typedef struct {
uint64_t rx_ts;
uint8_t flags;
uint8_t tx_ts_source;
uint16_t slew_epoch;
int32_t tx_ts_offset;
} NtpTimestamps;
/* Flags for NTP timestamps */
#define NTPTS_DISABLED 1
#define NTPTS_VALID_TX 2
/* RX->TX map using a circular buffer with ordered timestamps */
typedef struct {
ARR_Instance timestamps;
uint32_t first;
uint32_t size;
uint32_t max_size;
uint32_t cached_index;
uint64_t cached_rx_ts;
uint16_t slew_epoch;
double slew_offset;
} NtpTimestampMap;
static NtpTimestampMap ntp_ts_map;
/* Maximum interval of NTP timestamps in future after a backward step */
#define NTPTS_FUTURE_LIMIT (1LL << 32) /* 1 second */
/* Maximum number of timestamps moved in the array to insert a new timestamp */
#define NTPTS_INSERT_LIMIT 64
/* Maximum expected value of the timestamp source */
#define MAX_NTP_TS NTP_TS_HARDWARE
/* Global statistics */
static uint64_t total_hits[MAX_SERVICES];
static uint64_t total_drops[MAX_SERVICES];
static uint64_t total_ntp_auth_hits;
static uint64_t total_ntp_interleaved_hits;
static uint64_t total_record_drops;
static uint64_t total_ntp_rx_timestamps[MAX_NTP_TS + 1];
static uint64_t total_ntp_tx_timestamps[MAX_NTP_TS + 1];
static uint32_t total_ntp_hits;
static uint32_t total_cmd_hits;
static uint32_t total_ntp_drops;
static uint32_t total_cmd_drops;
static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U
/* ================================================== */
static int expand_hashtable(void);
static void handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
/* ================================================== */
@@ -190,28 +161,12 @@ compare_ts(uint32_t x, uint32_t y)
/* ================================================== */
static int
compare_total_hits(Record *x, Record *y)
{
uint32_t x_hits, y_hits;
int i;
for (i = 0, x_hits = y_hits = 0; i < MAX_SERVICES; i++) {
x_hits += x->hits[i];
y_hits += y->hits[i];
}
return x_hits > y_hits ? 1 : -1;
}
/* ================================================== */
static Record *
get_record(IPAddr *ip)
{
uint32_t last_hit = 0, oldest_hit = 0;
unsigned int first, i;
time_t last_hit, oldest_hit = 0;
Record *record, *oldest_record;
unsigned int first, i, j;
if (!active || (ip->family != IPADDR_INET4 && ip->family != IPADDR_INET6))
return NULL;
@@ -229,13 +184,12 @@ get_record(IPAddr *ip)
if (record->ip_addr.family == IPADDR_UNSPEC)
break;
for (j = 0; j < MAX_SERVICES; j++) {
if (j == 0 || compare_ts(last_hit, record->last_hit[j]) < 0)
last_hit = record->last_hit[j];
}
last_hit = compare_ts(record->last_ntp_hit, record->last_cmd_hit) > 0 ?
record->last_ntp_hit : record->last_cmd_hit;
if (!oldest_record || compare_ts(oldest_hit, last_hit) > 0 ||
(oldest_hit == last_hit && compare_total_hits(oldest_record, record) > 0)) {
(oldest_hit == last_hit && record->ntp_hits + record->cmd_hits <
oldest_record->ntp_hits + oldest_record->cmd_hits)) {
oldest_record = record;
oldest_hit = last_hit;
}
@@ -257,18 +211,16 @@ get_record(IPAddr *ip)
}
record->ip_addr = *ip;
for (i = 0; i < MAX_SERVICES; i++)
record->last_hit[i] = INVALID_TS;
for (i = 0; i < MAX_SERVICES; i++)
record->hits[i] = 0;
for (i = 0; i < MAX_SERVICES; i++)
record->drops[i] = 0;
for (i = 0; i < MAX_SERVICES; i++)
record->tokens[i] = max_tokens[i];
for (i = 0; i < MAX_SERVICES; i++)
record->rate[i] = INVALID_RATE;
record->last_ntp_hit = record->last_cmd_hit = INVALID_TS;
record->ntp_hits = record->cmd_hits = 0;
record->ntp_drops = record->cmd_drops = 0;
record->ntp_tokens = max_ntp_tokens;
record->cmd_tokens = max_cmd_tokens;
record->ntp_rate = record->cmd_rate = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE;
record->drop_flags = 0;
record->flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record;
}
@@ -354,56 +306,39 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void
CLG_Initialise(void)
{
int i, interval, burst, lrate, slots2;
int interval, burst, leak_rate;
for (i = 0; i < MAX_SERVICES; i++) {
max_tokens[i] = 0;
tokens_per_hit[i] = 0;
token_shift[i] = 0;
leak_rate[i] = 0;
limit_interval[i] = MIN_LIMIT_INTERVAL;
max_ntp_tokens = max_cmd_tokens = 0;
ntp_tokens_per_packet = cmd_tokens_per_packet = 0;
ntp_token_shift = cmd_token_shift = 0;
ntp_leak_rate = cmd_leak_rate = 0;
ntp_limit_interval = MIN_LIMIT_INTERVAL;
switch (i) {
case CLG_NTP:
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
continue;
break;
case CLG_NTSKE:
if (!CNF_GetNtsRateLimit(&interval, &burst, &lrate))
continue;
break;
case CLG_CMDMON:
if (!CNF_GetCommandRateLimit(&interval, &burst, &lrate))
continue;
break;
default:
assert(0);
}
if (CNF_GetNTPRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_ntp_tokens, &ntp_tokens_per_packet,
&ntp_token_shift);
ntp_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
ntp_limit_interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
if (CNF_GetCommandRateLimit(&interval, &burst, &leak_rate)) {
set_bucket_params(interval, burst, &max_cmd_tokens, &cmd_tokens_per_packet,
&cmd_token_shift);
cmd_leak_rate = CLAMP(MIN_LEAK_RATE, leak_rate, MAX_LEAK_RATE);
}
active = !CNF_GetNoClientLog();
if (!active) {
for (i = 0; i < MAX_SERVICES; i++) {
if (leak_rate[i] != 0)
LOG_FATAL("Rate limiting cannot be enabled with noclientlog");
}
if (ntp_leak_rate || cmd_leak_rate)
LOG_FATAL("ratelimit cannot be used with noclientlog");
return;
}
/* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash
table where two copies exist at the same time. */
max_slots = CNF_GetClientLogLimit() /
((sizeof (Record) + sizeof (NtpTimestamps)) * SLOT_SIZE * 3 / 2);
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2);
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
for (slots2 = 0; 1U << (slots2 + 1) <= max_slots; slots2++)
;
DEBUG_LOG("Max records %u", 1U << (slots2 + SLOT_BITS));
slots = 0;
records = NULL;
@@ -412,17 +347,6 @@ CLG_Initialise(void)
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
ntp_ts_map.timestamps = NULL;
ntp_ts_map.first = 0;
ntp_ts_map.size = 0;
ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
ntp_ts_map.cached_index = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
ntp_ts_map.slew_epoch = 0;
ntp_ts_map.slew_offset = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
}
/* ================================================== */
@@ -434,10 +358,6 @@ CLG_Finalise(void)
return;
ARR_DestroyInstance(records);
if (ntp_ts_map.timestamps)
ARR_DestroyInstance(ntp_ts_map.timestamps);
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
}
/* ================================================== */
@@ -460,33 +380,30 @@ get_ts_from_timespec(struct timespec *ts)
/* ================================================== */
static void
update_record(CLG_Service service, Record *record, struct timespec *now)
update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits,
uint16_t *tokens, uint32_t max_tokens, int token_shift, int8_t *rate)
{
uint32_t interval, now_ts, prev_hit, tokens;
int interval2, tshift, mtokens;
int8_t *rate;
uint32_t interval, now_ts, prev_hit, new_tokens;
int interval2;
now_ts = get_ts_from_timespec(now);
prev_hit = record->last_hit[service];
record->last_hit[service] = now_ts;
record->hits[service]++;
prev_hit = *last_hit;
*last_hit = now_ts;
(*hits)++;
interval = now_ts - prev_hit;
if (prev_hit == INVALID_TS || (int32_t)interval < 0)
return;
tshift = token_shift[service];
mtokens = max_tokens[service];
if (tshift >= 0)
tokens = (now_ts >> tshift) - (prev_hit >> tshift);
else if (now_ts - prev_hit > mtokens)
tokens = mtokens;
if (token_shift >= 0)
new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift);
else if (now_ts - prev_hit > max_tokens)
new_tokens = max_tokens;
else
tokens = (now_ts - prev_hit) << -tshift;
record->tokens[service] = MIN(record->tokens[service] + tokens, mtokens);
new_tokens = (now_ts - prev_hit) << -token_shift;
*tokens = MIN(*tokens + new_tokens, max_tokens);
/* Convert the interval to scaled and rounded log2 */
if (interval) {
@@ -501,11 +418,6 @@ update_record(CLG_Service service, Record *record, struct timespec *now)
interval2 = -RATE_SCALE * (TS_FRAC + 1);
}
/* For the NTP service, update one of the two rates depending on whether
the previous request of the client had a reply or it timed out */
rate = service == CLG_NTP && record->drop_flags & (1U << service) ?
&record->ntp_timeout_rate : &record->rate[service];
/* Update the rate in a rough approximation of exponential moving average */
if (*rate == INVALID_RATE) {
*rate = -interval2;
@@ -545,33 +457,50 @@ CLG_GetClientIndex(IPAddr *client)
/* ================================================== */
static void
check_service_number(CLG_Service service)
{
assert(service >= 0 && service <= MAX_SERVICES);
}
/* ================================================== */
int
CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
CLG_LogNTPAccess(IPAddr *client, struct timespec *now)
{
Record *record;
check_service_number(service);
total_hits[service]++;
total_ntp_hits++;
record = get_record(client);
if (record == NULL)
return -1;
update_record(service, record, now);
/* Update one of the two rates depending on whether the previous request
of the client had a reply or it timed out */
update_record(now, &record->last_ntp_hit, &record->ntp_hits,
&record->ntp_tokens, max_ntp_tokens, ntp_token_shift,
record->flags & FLAG_NTP_DROPPED ?
&record->ntp_timeout_rate : &record->ntp_rate);
DEBUG_LOG("service %d hits %"PRIu32" rate %d trate %d tokens %d",
(int)service, record->hits[service], record->rate[service],
service == CLG_NTP ? record->ntp_timeout_rate : INVALID_RATE,
record->tokens[service]);
DEBUG_LOG("NTP hits %"PRIu32" rate %d trate %d tokens %d",
record->ntp_hits, record->ntp_rate, record->ntp_timeout_rate,
record->ntp_tokens);
return get_index(record);
}
/* ================================================== */
int
CLG_LogCommandAccess(IPAddr *client, struct timespec *now)
{
Record *record;
total_cmd_hits++;
record = get_record(client);
if (record == NULL)
return -1;
update_record(now, &record->last_cmd_hit, &record->cmd_hits,
&record->cmd_tokens, max_cmd_tokens, cmd_token_shift,
&record->cmd_rate);
DEBUG_LOG("Cmd hits %"PRIu32" rate %d tokens %d",
record->cmd_hits, record->cmd_rate, record->cmd_tokens);
return get_index(record);
}
@@ -601,58 +530,83 @@ limit_response_random(int leak_rate)
/* ================================================== */
int
CLG_LimitServiceRate(CLG_Service service, int index)
CLG_LimitNTPResponseRate(int index)
{
Record *record;
int drop;
check_service_number(service);
if (tokens_per_hit[service] == 0)
if (!ntp_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
record->drop_flags &= ~(1U << service);
record->flags &= ~FLAG_NTP_DROPPED;
if (record->tokens[service] >= tokens_per_hit[service]) {
record->tokens[service] -= tokens_per_hit[service];
if (record->ntp_tokens >= ntp_tokens_per_packet) {
record->ntp_tokens -= ntp_tokens_per_packet;
return 0;
}
drop = limit_response_random(leak_rate[service]);
drop = limit_response_random(ntp_leak_rate);
/* Poorly implemented NTP clients can send requests at a higher rate
/* Poorly implemented clients may send new requests at even a higher rate
when they are not getting replies. If the request rate seems to be more
than twice as much as when replies are sent, give up on rate limiting to
reduce the amount of traffic. Invert the sense of the leak to respond to
most of the requests, but still keep the estimated rate updated. */
if (service == CLG_NTP && record->ntp_timeout_rate != INVALID_RATE &&
record->ntp_timeout_rate > record->rate[service] + RATE_SCALE)
if (record->ntp_timeout_rate != INVALID_RATE &&
record->ntp_timeout_rate > record->ntp_rate + RATE_SCALE)
drop = !drop;
if (!drop) {
record->tokens[service] = 0;
record->ntp_tokens = 0;
return 0;
}
record->drop_flags |= 1U << service;
record->drops[service]++;
total_drops[service]++;
record->flags |= FLAG_NTP_DROPPED;
record->ntp_drops++;
total_ntp_drops++;
return 1;
}
/* ================================================== */
void
CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src, NTP_Timestamp_Source tx_ts_src)
int
CLG_LimitCommandResponseRate(int index)
{
if (auth)
total_ntp_auth_hits++;
if (rx_ts_src >= 0 && rx_ts_src <= MAX_NTP_TS)
total_ntp_rx_timestamps[rx_ts_src]++;
if (tx_ts_src >= 0 && tx_ts_src <= MAX_NTP_TS)
total_ntp_tx_timestamps[tx_ts_src]++;
Record *record;
if (!cmd_tokens_per_packet)
return 0;
record = ARR_GetElement(records, index);
if (record->cmd_tokens >= cmd_tokens_per_packet) {
record->cmd_tokens -= cmd_tokens_per_packet;
return 0;
}
if (!limit_response_random(cmd_leak_rate)) {
record->cmd_tokens = 0;
return 0;
}
record->cmd_drops++;
total_cmd_drops++;
return 1;
}
/* ================================================== */
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
{
Record *record;
record = ARR_GetElement(records, index);
*rx_ts = &record->ntp_rx_ts;
*tx_ts = &record->ntp_tx_ts;
}
/* ================================================== */
@@ -660,337 +614,7 @@ CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src, NTP_Timestamp_Sourc
int
CLG_GetNtpMinPoll(void)
{
return limit_interval[CLG_NTP];
}
/* ================================================== */
static NtpTimestamps *
get_ntp_tss(uint32_t index)
{
return ARR_GetElement(ntp_ts_map.timestamps,
(ntp_ts_map.first + index) & (ntp_ts_map.max_size - 1));
}
/* ================================================== */
static int
find_ntp_rx_ts(uint64_t rx_ts, uint32_t *index)
{
uint64_t rx_x, rx_lo, rx_hi, step;
uint32_t i, x, lo, hi;
if (ntp_ts_map.cached_rx_ts == rx_ts && rx_ts != 0ULL) {
*index = ntp_ts_map.cached_index;
return 1;
}
if (ntp_ts_map.size == 0) {
*index = 0;
return 0;
}
lo = 0;
hi = ntp_ts_map.size - 1;
rx_lo = get_ntp_tss(lo)->rx_ts;
rx_hi = get_ntp_tss(hi)->rx_ts;
/* Check for ts < lo before ts > hi to trim timestamps from "future" later
if both conditions are true to not break the order of the endpoints.
Compare timestamps by their difference to allow adjacent NTP eras. */
if ((int64_t)(rx_ts - rx_lo) < 0) {
*index = 0;
return 0;
} else if ((int64_t)(rx_ts - rx_hi) > 0) {
*index = ntp_ts_map.size;
return 0;
}
/* Perform a combined linear interpolation and binary search */
for (i = 0; ; i++) {
if (rx_ts == rx_hi) {
*index = ntp_ts_map.cached_index = hi;
ntp_ts_map.cached_rx_ts = rx_ts;
return 1;
} else if (rx_ts == rx_lo) {
*index = ntp_ts_map.cached_index = lo;
ntp_ts_map.cached_rx_ts = rx_ts;
return 1;
} else if (lo + 1 == hi) {
*index = hi;
return 0;
}
if (hi - lo > 3 && i % 2 == 0) {
step = (rx_hi - rx_lo) / (hi - lo);
if (step == 0)
step = 1;
x = lo + (rx_ts - rx_lo) / step;
} else {
x = lo + (hi - lo) / 2;
}
if (x <= lo)
x = lo + 1;
else if (x >= hi)
x = hi - 1;
rx_x = get_ntp_tss(x)->rx_ts;
if ((int64_t)(rx_x - rx_ts) <= 0) {
lo = x;
rx_lo = rx_x;
} else {
hi = x;
rx_hi = rx_x;
}
}
}
/* ================================================== */
static uint64_t
ntp64_to_int64(NTP_int64 *ts)
{
return (uint64_t)ntohl(ts->hi) << 32 | ntohl(ts->lo);
}
/* ================================================== */
static void
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
{
ntp_ts->hi = htonl(ts >> 32);
ntp_ts->lo = htonl(ts);
}
/* ================================================== */
static uint32_t
push_ntp_tss(uint32_t index)
{
if (ntp_ts_map.size < ntp_ts_map.max_size) {
ntp_ts_map.size++;
} else {
ntp_ts_map.first = (ntp_ts_map.first + 1) % (ntp_ts_map.max_size);
if (index > 0)
index--;
}
return index;
}
/* ================================================== */
static void
set_ntp_tx(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src)
{
struct timespec ts;
if (!tx_ts) {
tss->flags &= ~NTPTS_VALID_TX;
return;
}
UTI_Ntp64ToTimespec(rx_ts, &ts);
UTI_DiffTimespecs(&ts, tx_ts, &ts);
if (ts.tv_sec < -2 || ts.tv_sec > 1) {
tss->flags &= ~NTPTS_VALID_TX;
return;
}
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
tss->flags |= NTPTS_VALID_TX;
tss->tx_ts_source = tx_src;
}
/* ================================================== */
static void
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts, NTP_Timestamp_Source *tx_src)
{
int32_t offset = tss->tx_ts_offset;
NTP_int64 ntp_ts;
if (tss->flags & NTPTS_VALID_TX) {
int64_to_ntp64(tss->rx_ts, &ntp_ts);
UTI_Ntp64ToTimespec(&ntp_ts, tx_ts);
if (offset >= (int32_t)NSEC_PER_SEC) {
offset -= NSEC_PER_SEC;
tx_ts->tv_sec++;
}
tx_ts->tv_nsec += offset;
UTI_NormaliseTimespec(tx_ts);
} else {
UTI_ZeroTimespec(tx_ts);
}
*tx_src = tss->tx_ts_source;
}
/* ================================================== */
void
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts, NTP_Timestamp_Source tx_src)
{
NtpTimestamps *tss;
uint32_t i, index;
uint64_t rx;
if (!active)
return;
/* Allocate the array on first use */
if (!ntp_ts_map.timestamps) {
ntp_ts_map.timestamps = ARR_CreateInstance(sizeof (NtpTimestamps));
ARR_SetSize(ntp_ts_map.timestamps, ntp_ts_map.max_size);
}
rx = ntp64_to_int64(rx_ts);
if (rx == 0ULL)
return;
/* Disable the RX timestamp if it already exists to avoid responding
with a wrong TX timestamp */
if (find_ntp_rx_ts(rx, &index)) {
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
return;
}
assert(index <= ntp_ts_map.size);
if (index == ntp_ts_map.size) {
/* Increase the size or drop the oldest timestamp to make room for
the new timestamp */
index = push_ntp_tss(index);
} else {
/* Trim timestamps in distant future after backward step */
while (index < ntp_ts_map.size &&
get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - rx > NTPTS_FUTURE_LIMIT)
ntp_ts_map.size--;
/* Insert the timestamp if it is close to the latest timestamp.
Otherwise, replace the closest older or the oldest timestamp. */
if (index + NTPTS_INSERT_LIMIT >= ntp_ts_map.size) {
index = push_ntp_tss(index);
for (i = ntp_ts_map.size - 1; i > index; i--)
*get_ntp_tss(i) = *get_ntp_tss(i - 1);
} else {
if (index > 0)
index--;
}
}
ntp_ts_map.cached_index = index;
ntp_ts_map.cached_rx_ts = rx;
tss = get_ntp_tss(index);
tss->rx_ts = rx;
tss->flags = 0;
tss->slew_epoch = ntp_ts_map.slew_epoch;
set_ntp_tx(tss, rx_ts, tx_ts, tx_src);
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
index, ntp_ts_map.first, ntp_ts_map.size);
}
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
/* Drop all timestamps on unknown step */
if (change_type == LCL_ChangeUnknownStep) {
ntp_ts_map.size = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
}
ntp_ts_map.slew_epoch++;
ntp_ts_map.slew_offset = doffset;
}
/* ================================================== */
void
CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
/* If the RX timestamp was captured before the last correction of the clock,
remove the adjustment from the TX timestamp */
if ((uint16_t)(get_ntp_tss(index)->slew_epoch + 1U) == ntp_ts_map.slew_epoch)
UTI_AddDoubleToTimespec(tx_ts, ntp_ts_map.slew_offset, tx_ts);
}
/* ================================================== */
void
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
set_ntp_tx(get_ntp_tss(index), rx_ts, tx_ts, tx_src);
}
/* ================================================== */
int
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source *tx_src)
{
NtpTimestamps *tss;
uint32_t index;
if (!ntp_ts_map.timestamps)
return 0;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return 0;
tss = get_ntp_tss(index);
if (tss->flags & NTPTS_DISABLED)
return 0;
get_ntp_tx(tss, tx_ts, tx_src);
return 1;
}
/* ================================================== */
void
CLG_DisableNtpTimestamps(NTP_int64 *rx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
/* This assumes the function is called only to prevent multiple
interleaved responses to the same timestamp */
total_ntp_interleaved_hits++;
return ntp_limit_interval;
}
/* ================================================== */
@@ -1029,12 +653,10 @@ static uint32_t get_last_ago(uint32_t x, uint32_t y)
/* ================================================== */
int
CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
RPT_ClientAccessByIndex_Report *report, struct timespec *now)
CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now)
{
Record *record;
uint32_t now_ts;
int i, r;
if (!active || index < 0 || index >= ARR_GetSize(records))
return 0;
@@ -1044,44 +666,20 @@ CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
if (record->ip_addr.family == IPADDR_UNSPEC)
return 0;
if (min_hits == 0) {
r = 1;
} else {
for (i = r = 0; i < MAX_SERVICES; i++) {
if (record->hits[i] >= min_hits) {
r = 1;
break;
}
}
}
now_ts = get_ts_from_timespec(now);
if (r) {
now_ts = get_ts_from_timespec(now);
report->ip_addr = record->ip_addr;
report->ntp_hits = record->ntp_hits;
report->cmd_hits = record->cmd_hits;
report->ntp_drops = record->ntp_drops;
report->cmd_drops = record->cmd_drops;
report->ntp_interval = get_interval(record->ntp_rate);
report->cmd_interval = get_interval(record->cmd_rate);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_ntp_hit);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_cmd_hit);
report->ip_addr = record->ip_addr;
report->ntp_hits = record->hits[CLG_NTP];
report->nke_hits = record->hits[CLG_NTSKE];
report->cmd_hits = record->hits[CLG_CMDMON];
report->ntp_drops = record->drops[CLG_NTP];
report->nke_drops = record->drops[CLG_NTSKE];
report->cmd_drops = record->drops[CLG_CMDMON];
report->ntp_interval = get_interval(record->rate[CLG_NTP]);
report->nke_interval = get_interval(record->rate[CLG_NTSKE]);
report->cmd_interval = get_interval(record->rate[CLG_CMDMON]);
report->ntp_timeout_interval = get_interval(record->ntp_timeout_rate);
report->last_ntp_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTP]);
report->last_nke_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_NTSKE]);
report->last_cmd_hit_ago = get_last_ago(now_ts, record->last_hit[CLG_CMDMON]);
}
if (reset) {
for (i = 0; i < MAX_SERVICES; i++) {
record->hits[i] = 0;
record->drops[i] = 0;
}
}
return r;
return 1;
}
/* ================================================== */
@@ -1089,23 +687,9 @@ CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
void
CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
{
report->ntp_hits = total_hits[CLG_NTP];
report->nke_hits = total_hits[CLG_NTSKE];
report->cmd_hits = total_hits[CLG_CMDMON];
report->ntp_drops = total_drops[CLG_NTP];
report->nke_drops = total_drops[CLG_NTSKE];
report->cmd_drops = total_drops[CLG_CMDMON];
report->ntp_hits = total_ntp_hits;
report->cmd_hits = total_cmd_hits;
report->ntp_drops = total_ntp_drops;
report->cmd_drops = total_cmd_drops;
report->log_drops = total_record_drops;
report->ntp_auth_hits = total_ntp_auth_hits;
report->ntp_interleaved_hits = total_ntp_interleaved_hits;
report->ntp_timestamps = ntp_ts_map.size;
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) >> 32 : 0;
report->ntp_daemon_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_DAEMON];
report->ntp_daemon_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_DAEMON];
report->ntp_kernel_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_KERNEL];
report->ntp_kernel_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_KERNEL];
report->ntp_hw_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_HARDWARE];
report->ntp_hw_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_HARDWARE];
}

View File

@@ -31,37 +31,20 @@
#include "sysincl.h"
#include "reports.h"
typedef enum {
CLG_NTP = 0,
CLG_NTSKE,
CLG_CMDMON,
} CLG_Service;
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
NTP_Timestamp_Source tx_ts_src);
extern int CLG_LogNTPAccess(IPAddr *client, struct timespec *now);
extern int CLG_LogCommandAccess(IPAddr *client, struct timespec *now);
extern int CLG_LimitNTPResponseRate(int index);
extern int CLG_LimitCommandResponseRate(int index);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_GetNtpMinPoll(void);
/* Functions to save and retrieve timestamps for server interleaved mode */
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src);
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src);
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source *tx_src);
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
/* And some reporting functions, for use by chronyc. */
extern int CLG_GetNumberOfIndices(void);
extern int CLG_GetClientAccessReportByIndex(int index, int reset, uint32_t min_hits,
RPT_ClientAccessByIndex_Report *report,
struct timespec *now);
extern int CLG_GetClientAccessReportByIndex(int index, RPT_ClientAccessByIndex_Report *report, struct timespec *now);
extern void CLG_GetServerStatsReport(RPT_ServerStatsReport *report);
#endif /* GOT_CLIENTLOG_H */

48
cmac.h
View File

@@ -1,48 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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 CMAC.
*/
#ifndef GOT_CMAC_H
#define GOT_CMAC_H
/* Avoid overlapping with the hash enumeration */
typedef enum {
CMC_INVALID = 0,
CMC_AES128 = 13,
CMC_AES256 = 14,
} CMC_Algorithm;
typedef struct CMC_Instance_Record *CMC_Instance;
extern int CMC_GetKeyLength(CMC_Algorithm algorithm);
extern CMC_Instance CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key,
int length);
extern int CMC_Hash(CMC_Instance inst, const void *in, int in_len,
unsigned char *out, int out_len);
extern void CMC_DestroyInstance(CMC_Instance inst);
#endif

View File

@@ -1,189 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
CMAC using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "cmac.h"
#include "hash.h"
#include "logging.h"
#include "memory.h"
struct CMC_Instance_Record {
gnutls_mac_algorithm_t algorithm;
gnutls_hmac_hd_t mac;
};
/* ================================================== */
static int instance_counter = 0;
static int gnutls_initialised = 0;
/* ================================================== */
static void
init_gnutls(void)
{
int r;
if (gnutls_initialised)
return;
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
DEBUG_LOG("Initialised");
gnutls_initialised = 1;
}
/* ================================================== */
static void
deinit_gnutls(void)
{
assert(gnutls_initialised);
gnutls_global_deinit();
gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
/* ================================================== */
static gnutls_mac_algorithm_t
get_mac_algorithm(CMC_Algorithm algorithm)
{
switch (algorithm) {
case CMC_AES128:
return GNUTLS_MAC_AES_CMAC_128;
case CMC_AES256:
return GNUTLS_MAC_AES_CMAC_256;
default:
return GNUTLS_MAC_UNKNOWN;
}
}
/* ================================================== */
int
CMC_GetKeyLength(CMC_Algorithm algorithm)
{
gnutls_mac_algorithm_t malgo = get_mac_algorithm(algorithm);
int len;
if (malgo == GNUTLS_MAC_UNKNOWN)
return 0;
len = gnutls_hmac_get_key_size(malgo);
if (len < 0)
return 0;
return len;
}
/* ================================================== */
CMC_Instance
CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
{
gnutls_hmac_hd_t handle;
CMC_Instance inst;
int r;
if (instance_counter == 0)
init_gnutls();
if (length <= 0 || length != CMC_GetKeyLength(algorithm))
goto error;
r = gnutls_hmac_init(&handle, get_mac_algorithm(algorithm), key, length);
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "mac", gnutls_strerror(r));
goto error;
}
inst = MallocNew(struct CMC_Instance_Record);
inst->algorithm = get_mac_algorithm(algorithm);
inst->mac = handle;
instance_counter++;
return inst;
error:
if (instance_counter == 0)
deinit_gnutls();
return NULL;
}
/* ================================================== */
int
CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
if (in_len < 0 || out_len < 0)
return 0;
hash_len = gnutls_hmac_get_len(inst->algorithm);
if (out_len > hash_len)
out_len = hash_len;
if (hash_len > sizeof (buf))
return 0;
if (gnutls_hmac(inst->mac, in, in_len) < 0) {
/* Reset the state */
gnutls_hmac_output(inst->mac, buf);
return 0;
}
gnutls_hmac_output(inst->mac, buf);
memcpy(out, buf, out_len);
return out_len;
}
/* ================================================== */
void
CMC_DestroyInstance(CMC_Instance inst)
{
gnutls_hmac_deinit(inst->mac, NULL);
Free(inst);
instance_counter--;
if (instance_counter == 0)
deinit_gnutls();
}

View File

@@ -1,117 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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.
*
**********************************************************************
=======================================================================
Support for AES128 and AES256 CMAC in Nettle.
*/
#include "config.h"
#include "sysincl.h"
#include <nettle/cmac.h>
#include "cmac.h"
#include "memory.h"
struct CMC_Instance_Record {
int key_length;
union {
struct cmac_aes128_ctx aes128;
struct cmac_aes256_ctx aes256;
} context;
};
/* ================================================== */
int
CMC_GetKeyLength(CMC_Algorithm algorithm)
{
if (algorithm == CMC_AES128)
return AES128_KEY_SIZE;
else if (algorithm == CMC_AES256)
return AES256_KEY_SIZE;
return 0;
}
/* ================================================== */
CMC_Instance
CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
{
CMC_Instance inst;
if (length <= 0 || length != CMC_GetKeyLength(algorithm))
return NULL;
inst = MallocNew(struct CMC_Instance_Record);
inst->key_length = length;
switch (length) {
case AES128_KEY_SIZE:
cmac_aes128_set_key(&inst->context.aes128, key);
break;
case AES256_KEY_SIZE:
cmac_aes256_set_key(&inst->context.aes256, key);
break;
default:
assert(0);
}
return inst;
}
/* ================================================== */
int
CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
{
if (in_len < 0 || out_len < 0)
return 0;
if (out_len > CMAC128_DIGEST_SIZE)
out_len = CMAC128_DIGEST_SIZE;
switch (inst->key_length) {
case AES128_KEY_SIZE:
cmac_aes128_update(&inst->context.aes128, in_len, in);
cmac_aes128_digest(&inst->context.aes128, out_len, out);
break;
case AES256_KEY_SIZE:
cmac_aes256_update(&inst->context.aes256, in_len, in);
cmac_aes256_digest(&inst->context.aes256, out_len, out);
break;
default:
assert(0);
}
return out_len;
}
/* ================================================== */
void
CMC_DestroyInstance(CMC_Instance inst)
{
Free(inst);
}

737
cmdmon.c

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@
#include "addressing.h"
extern void CAM_Initialise(void);
extern void CAM_Initialise(int family);
extern void CAM_Finalise(void);

View File

@@ -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
*
* 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
@@ -43,8 +43,7 @@ int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
uint32_t ef_type;
int n, sel_option;
int n;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
@@ -63,16 +62,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.copy = 0;
src->params.ext_fields = 0;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.cert_set = SRC_DEFAULT_CERTSET;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.max_delay_quant = 0.0;
src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0;
@@ -95,15 +88,18 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1;
} else if (!strcasecmp(cmd, "copy")) {
src->params.copy = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY)
@@ -111,16 +107,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "extfield")) {
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0;
switch (ef_type) {
case NTP_EF_EXP1:
src->params.ext_fields |= NTP_EF_FLAG_EXP1;
break;
default:
return 0;
}
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
@@ -133,9 +119,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxdelayquant")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
@@ -157,16 +140,11 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "minstratum")) {
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "nts")) {
src->params.nts = 1;
} else if (!strcasecmp(cmd, "ntsport")) {
if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "offset")) {
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "port")) {
if (sscanf(line, "%d%n", &src->port, &n) != 1)
if (sscanf(line, "%hu%n", &src->port, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "polltarget")) {
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
@@ -179,8 +157,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
src->params.sel_options |= sel_option;
} else {
return 0;
}
@@ -191,102 +167,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */
int
CPS_GetSelectOption(char *option)
{
if (!strcasecmp(option, "noselect")) {
return SRC_SELECT_NOSELECT;
} else if (!strcasecmp(option, "prefer")) {
return SRC_SELECT_PREFER;
} else if (!strcasecmp(option, "require")) {
return SRC_SELECT_REQUIRE;
} else if (!strcasecmp(option, "trust")) {
return SRC_SELECT_TRUST;
}
return 0;
}
/* ================================================== */
int
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
{
char *p, *net, *slash;
uint32_t a, b, c;
int bits, len, n;
p = CPS_SplitWord(line);
if (strcmp(line, "all") == 0) {
*all = 1;
net = p;
p = CPS_SplitWord(p);
} else {
*all = 0;
net = line;
}
/* Make sure there are no other arguments */
if (*p)
return 0;
/* No specified address or network means all IPv4 and IPv6 addresses */
if (!*net) {
ip->family = IPADDR_UNSPEC;
*subnet_bits = 0;
return 1;
}
slash = strchr(net, '/');
if (slash) {
if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
return 0;
*slash = '\0';
} else {
bits = -1;
}
if (UTI_StringToIP(net, ip)) {
if (bits >= 0)
*subnet_bits = bits;
else
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
return 1;
}
/* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
numbers. This is different than the numbers-and-dots notation accepted
by inet_aton()! */
a = b = c = 0;
n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
if (n > 0 && !net[len]) {
if (a > 255 || b > 255 || c > 255)
return 0;
ip->family = IPADDR_INET4;
ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
if (bits >= 0)
*subnet_bits = bits;
else
*subnet_bits = n * 8;
return 1;
}
/* The last possibility is a hostname */
if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
return 1;
}
return 0;
}
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
{
@@ -381,7 +261,7 @@ CPS_SplitWord(char *line)
/* ================================================== */
int
CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key)
{
char *s1, *s2, *s3, *s4;
@@ -398,28 +278,12 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
return 0;
if (*s3) {
*type = s2;
*hash = s2;
*key = s3;
} else {
*type = "MD5";
*hash = "MD5";
*key = s2;
}
return 1;
}
/* ================================================== */
int
CPS_ParseRefid(char *line, uint32_t *ref_id)
{
int i;
for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
if (i >= 4)
return 0;
*ref_id |= (uint32_t)line[i] << (24 - i * 8);
}
return i;
}

View File

@@ -32,19 +32,13 @@
typedef struct {
char *name;
int port;
unsigned short port;
SourceParameters params;
} CPS_NTP_Source;
/* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get an NTP/refclock select option */
extern int CPS_GetSelectOption(char *option);
/* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
@@ -55,9 +49,6 @@ extern void CPS_NormalizeLine(char *line);
extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
/* Parse a refclock reference ID (returns number of characters) */
extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
extern int CPS_ParseKey(char *line, uint32_t *id, const char **hash, char **key);
#endif /* GOT_CMDPARSE_H */

919
conf.c

File diff suppressed because it is too large Load Diff

34
conf.h
View File

@@ -30,13 +30,10 @@
#include "addressing.h"
#include "reference.h"
#include "sources.h"
extern void CNF_Initialise(int restarted, int client_only);
extern void CNF_Finalise(void);
extern void CNF_EnablePrint(void);
extern char *CNF_GetRtcDevice(void);
extern void CNF_ReadFile(const char *filename);
@@ -44,15 +41,11 @@ extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
extern void CNF_CheckReadOnlyAccess(void);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
extern void CNF_AddRefclocks(void);
extern void CNF_ReloadSources(void);
extern int CNF_GetAcquisitionPort(void);
extern int CNF_GetNTPPort(void);
extern char *CNF_GetDriftFile(void);
@@ -60,7 +53,6 @@ extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogSelection(void);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -82,11 +74,7 @@ extern void CNF_GetFallbackDrifts(int *min, int *max);
extern void CNF_GetBindAddress(int family, IPAddr *addr);
extern void CNF_GetBindAcquisitionAddress(int family, IPAddr *addr);
extern void CNF_GetBindCommandAddress(int family, IPAddr *addr);
extern char *CNF_GetBindNtpInterface(void);
extern char *CNF_GetBindAcquisitionInterface(void);
extern char *CNF_GetBindCommandInterface(void);
extern char *CNF_GetBindCommandPath(void);
extern int CNF_GetNtpDscp(void);
extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
@@ -98,9 +86,7 @@ extern double CNF_GetMaxClockError(void);
extern double CNF_GetMaxDrift(void);
extern double CNF_GetCorrectionTimeRatio(void);
extern double CNF_GetMaxSlewRate(void);
extern double CNF_GetClockPrecision(void);
extern SRC_AuthSelectMode CNF_GetAuthSelectMode(void);
extern double CNF_GetMaxDistance(void);
extern double CNF_GetMaxJitter(void);
extern double CNF_GetReselectDistance(void);
@@ -115,7 +101,6 @@ extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
extern void CNF_GetTempComp(char **file, double *interval, char **point_file, double *T0, double *k0, double *k1, double *k2);
@@ -137,14 +122,12 @@ typedef enum {
CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_PTP,
CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter;
typedef struct {
char *name;
int minpoll;
int maxpoll;
int min_samples;
int max_samples;
int nocrossts;
@@ -155,22 +138,5 @@ typedef struct {
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
extern int CNF_GetRefresh(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
extern int CNF_GetNtsServerPort(void);
extern int CNF_GetNtsServerProcesses(void);
extern int CNF_GetNtsServerConnections(void);
extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(void);
extern int CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids);
extern int CNF_GetNoSystemCert(void);
extern int CNF_GetNoCertTimeCheck(void);
#endif /* GOT_CONF_H */

331
configure vendored
View File

@@ -5,7 +5,7 @@
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2022
# Copyright (C) Miroslav Lichvar 2009, 2012-2018
# Copyright (C) Stefan R. Filipek 2019
#
# =======================================================================
@@ -33,13 +33,13 @@ test_code () {
echo "int main(int argc, char **argv) {"
echo "$code"
echo "return 0; }"
) > conftest.c
) > docheck.c
echo "conftest.c:" >> config.log
cat conftest.c >> config.log
echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
echo "docheck.c:" >> config.log
cat docheck.c >> config.log
echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \
$MYLDFLAGS >> config.log
$MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
$MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \
$MYLDFLAGS >> config.log 2>&1
if [ $? -eq 0 ]
@@ -50,39 +50,11 @@ test_code () {
echo "No"
result=1
fi
rm -f conftest.c conftest
rm -f docheck.c docheck
echo >> config.log
return $result
}
#}}}
#{{{ test_executable
test_executable () {
name=$1
executable=$2
options=$3
printf "%s" "Checking for $name : "
echo $executable $options >> config.log
$executable $options >> config.log 2>&1
if [ $? -eq 0 ]
then
echo "Yes"
result=0
else
echo "No"
result=1
fi
echo >> config.log
return $result
}
#}}}
#{{{ pkg_config
pkg_config () {
$PKG_CONFIG "$@" 2>> config.log
}
#}}}
#{{{ usage
usage () {
cat <<EOF
@@ -108,13 +80,15 @@ for instance \`--prefix=$HOME'.
For better control, use the options below.
--disable-readline Disable line editing support
--without-readline Don't use GNU readline even if it is available
--without-editline Don't use editline even if it is available
--with-readline-includes=DIR Specify where readline include directory is
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support
--without-gnutls Don't use gnutls even if it is available
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
@@ -128,7 +102,6 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
@@ -164,11 +137,6 @@ Some influential environment variables:
headers in a nonstandard directory <include dir>
LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
nonstandard directory <lib dir>
PKG_CONFIG path to pkg-config utility
PKG_CONFIG_PATH
directories to add to pkg-config's search path
PKG_CONFIG_LIBDIR
path overriding pkg-config's built-in search path
Use these variables to override the choices made by \`configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -186,6 +154,13 @@ add_def () {
fi
}
#}}}
#{{{ pkg_config
pkg_config () {
type pkg-config > /dev/null 2> /dev/null || return 1
pkg-config $@ 2> /dev/null
}
#}}}
#{{{ get_features
get_features () {
ff=1
@@ -211,24 +186,23 @@ OPERATINGSYSTEM=`uname -s`
VERSION=`uname -r`
MACHINE=`uname -m`
LIBS=""
EXTRA_LIBS=""
EXTRA_CLI_LIBS=""
EXTRA_OBJECTS=""
EXTRA_CLI_OBJECTS=""
EXTRA_DEFS=""
SYSDEFS=""
feat_debug=0
feat_cmdmon=1
feat_ntp=1
feat_refclock=1
feat_readline=1
try_readline=1
try_editline=1
feat_sechash=1
try_nettle=1
try_nss=1
try_tomcrypt=1
feat_nts=1
try_gnutls=1
feat_rtc=1
try_rtc=0
feat_droproot=1
@@ -237,6 +211,9 @@ try_clockctl=0
feat_scfilter=0
try_seccomp=-1
priv_ops=""
readline_lib=""
readline_inc=""
ncurses_lib=""
feat_ipv6=1
feat_phc=1
try_phc=0
@@ -245,9 +222,7 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_aes_gcm_siv=1
try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
@@ -269,9 +244,21 @@ do
--disable-readline )
feat_readline=0
;;
--without-readline )
try_readline=0
;;
--without-editline )
try_editline=0
;;
--with-readline-library=* )
readline_lib=-L`echo $option | sed -e 's/^.*=//;'`
;;
--with-readline-includes=* )
readline_inc=-I`echo $option | sed -e 's/^.*=//;'`
;;
--with-ncurses-library=* )
ncurses_lib=-L`echo $option | sed -e 's/^.*=//;'`
;;
--prefix=* | --install_prefix=* )
SETPREFIX=`echo $option | sed -e 's/[^=]*=//;'`
;;
@@ -347,9 +334,6 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
--without-aes-gcm-siv)
try_aes_gcm_siv=0
;;
--without-clock-gettime)
try_clock_gettime=0
;;
@@ -389,12 +373,6 @@ do
--without-tomcrypt )
try_tomcrypt=0
;;
--disable-nts )
feat_nts=0
;;
--without-gnutls )
try_gnutls=0
;;
--host-system=* )
OPERATINGSYSTEM=`echo $option | sed -e 's/^.*=//;'`
;;
@@ -427,7 +405,6 @@ case $OPERATINGSYSTEM in
try_setsched=1
try_lockmem=1
try_phc=1
try_arc4random=0
add_def LINUX
echo "Configuring for " $SYSTEM
;;
@@ -455,7 +432,8 @@ case $OPERATINGSYSTEM in
;;
Darwin)
EXTRA_OBJECTS="sys_macosx.o"
LIBS="$LIBS -lresolv"
EXTRA_LIBS="-lresolv"
EXTRA_CLI_LIBS="-lresolv"
add_def MACOSX
if [ $feat_droproot = "1" ]; then
add_def FEAT_PRIVDROP
@@ -474,7 +452,8 @@ case $OPERATINGSYSTEM in
;;
SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
EXTRA_LIBS="-lsocket -lnsl -lresolv"
EXTRA_CLI_LIBS="-lsocket -lnsl -lresolv"
try_setsched=1
try_lockmem=1
add_def SOLARIS
@@ -486,7 +465,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
;;
* )
echo "error: $SYSTEM is not supported (yet?)"
@@ -506,7 +485,7 @@ fi
if [ $feat_ntp = "1" ]; then
add_def FEAT_NTP
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_core.o ntp_io.o ntp_sources.o"
if [ $feat_ntp_signd = "1" ]; then
add_def FEAT_SIGND
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
@@ -570,27 +549,10 @@ if [ "x$MYCFLAGS" = "x" ]; then
fi
fi
TESTCFLAGS="-fwrapv"
if test_code '-fwrapv' '' "$TESTCFLAGS" '' ''; then
GETDATE_CFLAGS="-fwrapv"
else
GETDATE_CFLAGS=""
fi
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
if [ "x$PKG_CONFIG" = "x" ]; then
PKG_CONFIG=pkg-config
fi
if ! test_executable "pkg-config" $PKG_CONFIG --version; then
try_nettle=0
try_nss=0
try_gnutls=0
fi
if test_code '64-bit time_t' 'time.h' '' '' '
char x[sizeof(time_t) > 4 ? 1 : -1] = {0};
return x[0];'
@@ -630,9 +592,11 @@ then
fi
MATHCODE='return (int) pow(2.0, log(sqrt((double)argc)));'
if ! test_code 'math' 'math.h' '' '' "$MATHCODE"; then
if test_code 'math' 'math.h' '' '' "$MATHCODE"; then
LIBS=""
else
if test_code 'math in -lm' 'math.h' '' '-lm' "$MATHCODE"; then
LIBS="$LIBS -lm"
LIBS="-lm"
else
echo "error: could not compile/link a program which uses sqrt(), log(), pow()"
exit 1
@@ -647,11 +611,10 @@ then
fi
if [ $feat_ipv6 = "1" ] && \
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$LIBS" '
test_code 'IPv6 support' 'arpa/inet.h sys/socket.h netinet/in.h' '' "$EXTRA_LIBS" '
struct sockaddr_in6 n;
char p[100];
n.sin6_addr = in6addr_any;
n.sin6_scope_id = 0;
return !inet_ntop(AF_INET6, &n.sin6_addr.s6_addr, p, sizeof(p));'
then
add_def FEAT_IPV6
@@ -669,28 +632,14 @@ then
fi
fi
if ! test_code 'O_NOFOLLOW flag' 'sys/types.h sys/stat.h fcntl.h' '' "$LIBS" \
'return open("/dev/null", O_NOFOLLOW);'
then
if test_code 'O_NOFOLLOW flag with _GNU_SOURCE' 'sys/types.h sys/stat.h fcntl.h' \
'-D_GNU_SOURCE' "$LIBS" \
'return open("/dev/null", O_NOFOLLOW);'
then
add_def _GNU_SOURCE
else
echo "error: open() does not support O_NOFOLLOW flag"
exit 1
fi
fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, (void *)1);'
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, (void *)1);'
'clock_gettime(CLOCK_REALTIME, NULL);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -698,17 +647,15 @@ if [ $try_clock_gettime = "1" ]; then
fi
fi
if ! test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$EXTRA_LIBS" \
'return getaddrinfo(0, 0, 0, 0);'
then
echo "error: getaddrinfo() not found"
exit 1
add_def HAVE_GETADDRINFO
fi
if [ $feat_asyncdns = "1" ] && \
test_code 'pthread' 'pthread.h' '-pthread' '' '
pthread_t thread;
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
test_code 'pthread' 'pthread.h' '-pthread' '' \
'return (int)pthread_create((void *)1, NULL, (void *)1, NULL);'
then
add_def FEAT_ASYNCDNS
add_def USE_PTHREAD_ASYNCDNS
@@ -716,27 +663,24 @@ then
use_pthread=1
fi
if [ $try_arc4random = "1" ] && \
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
'arc4random_buf((void *)1, 1);'
then
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
add_def HAVE_ARC4RANDOM
else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom((void *)1, 1, 0);'; then
add_def HAVE_GETRANDOM
fi
fi
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then
add_def HAVE_GETRANDOM
fi
RECVMMSG_CODE='
struct mmsghdr hdr;
return !recvmmsg(0, &hdr, 1, MSG_DONTWAIT, 0);'
if [ $try_recvmmsg = "1" ]; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$LIBS" "$RECVMMSG_CODE"; then
if test_code 'recvmmsg()' 'sys/socket.h' '' "$EXTRA_LIBS" "$RECVMMSG_CODE"; then
add_def HAVE_RECVMMSG
else
if test_code 'recvmmsg() with _GNU_SOURCE' 'sys/socket.h' '-D_GNU_SOURCE' \
"$LIBS" "$RECVMMSG_CODE"
"$EXTRA_LIBS" "$RECVMMSG_CODE"
then
add_def _GNU_SOURCE
add_def HAVE_RECVMMSG
@@ -787,7 +731,6 @@ if [ "x$timepps_h" != "x" ] && \
pps_handle_t h = 0;
pps_info_t i;
struct timespec ts;
ts.tv_sec = ts.tv_nsec = 0;
return time_pps_fetch(h, PPS_TSFMT_TSPEC, &i, &ts);'
then
add_def FEAT_PPS
@@ -816,12 +759,10 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
'seccomp_init(SCMP_ACT_KILL);'
then
add_def FEAT_SCFILTER
if [ $feat_ntp = "1" ]; then
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
# used by the main thread as the helper process works on one request at
# a time and the async resolver would block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
fi
# NAME2IPADDRESS shouldn't be enabled with other operations as the helper
# process works on one request at the time and the async resolver could
# block the main thread
priv_ops="NAME2IPADDRESS RELOADDNS"
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
fi
@@ -876,7 +817,6 @@ if [ $try_lockmem = "1" ] && \
'setrlimit(RLIMIT_MEMLOCK, ...)' \
'sys/resource.h' '' '' '
struct rlimit rlim;
rlim.rlim_max = rlim.rlim_cur = RLIM_INFINITY;
setrlimit(RLIMIT_MEMLOCK, &rlim);'
then
add_def HAVE_SETRLIMIT_MEMLOCK
@@ -890,11 +830,37 @@ fi
READLINE_LINK=""
if [ $feat_readline = "1" ]; then
if [ $try_editline = "1" ]; then
if test_code editline 'stdio.h editline/readline.h' '' '-ledit' \
if test_code editline 'stdio.h editline/readline.h' \
"$readline_inc" "$readline_lib -ledit" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
READLINE_LINK="-ledit"
add_def USE_EDITLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -ledit"
fi
fi
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code readline 'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib -lreadline" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib -lreadline"
fi
fi
if [ "x$READLINE_LINK" = "x" ] && [ $try_readline = "1" ]; then
if test_code 'readline with -lncurses' \
'stdio.h readline/readline.h readline/history.h' \
"$readline_inc" "$readline_lib $ncurses_lib -lreadline -lncurses" \
'add_history(readline("prompt"));'
then
add_def FEAT_READLINE
MYCPPFLAGS="$MYCPPFLAGS $readline_inc"
READLINE_LINK="$readline_lib $ncurses_lib -lreadline -lncurses"
fi
fi
@@ -913,28 +879,22 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
then
HASH_OBJ="hash_nettle.o"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o"
fi
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
"$test_cflags" "$test_link" \
'NSSLOWHASH_Begin(NSSLOWHASH_NewContext(NSSLOW_Init(), HASH_AlgSHA512));'
then
HASH_OBJ="hash_nss.o"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
fi
@@ -942,102 +902,16 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
then
HASH_OBJ="hash_tomcrypt.o"
HASH_LINK="-ltomcrypt"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS -I/usr/include/tomcrypt"
add_def FEAT_SECHASH
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
test_cflags=""
test_link=""
else
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
fi
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then
if test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
'nettle/siv-gcm.h' "" "$LIBS" \
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
(void *)4);'
then
add_def HAVE_NETTLE_SIV_GCM
fi
else
if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
then
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
fi
else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
fi
fi
fi
if grep '#define HAVE_SIV' config.h > /dev/null; then
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
LIBS="$LIBS $test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_NTS
fi
fi
fi
if [ $use_pthread = "1" ]; then
MYCFLAGS="$MYCFLAGS -pthread"
fi
@@ -1107,7 +981,7 @@ add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features SECHASH IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
@@ -1123,16 +997,15 @@ add_def CHRONY_VERSION "\"${CHRONY_VERSION}\""
for f in Makefile doc/Makefile test/unit/Makefile
do
echo Creating $f
sed -e "s%@EXTRA_OBJS@%${EXTRA_OBJECTS}%;\
s%@EXTRA_CLI_OBJS@%${EXTRA_CLI_OBJECTS}%;\
sed -e "s%@EXTRA_OBJECTS@%${EXTRA_OBJECTS}%;\
s%@CC@%${MYCC}%;\
s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@GETDATE_CFLAGS@%${GETDATE_CFLAGS}%;\
s%@LIBS@%${LIBS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
s%@HASH_OBJ@%${HASH_OBJ}%;\
s%@SYSCONFDIR@%${SYSCONFDIR}%;\
s%@BINDIR@%${BINDIR}%;\
s%@SBINDIR@%${SBINDIR}%;\

View File

@@ -60,8 +60,8 @@ Support files
Dates and sizes may differ
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.chrony-project.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.chrony-project.chronyd.plist
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist
If you have used chrony support directories other than those suggested, you
will need to edit each file and make the appropriate changes.
@@ -83,21 +83,21 @@ sudo chmod +x /usr/local/bin/chronylogrotate.sh
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
2. org.chrony-project.chronyc.plist
2. org.tuxfamily.chronyc.plist
This file is the launchd plist that runs logrotation each day. You may
wish to edit this file to change the time of day at which the rotation
will run, currently 04:05 am
sudo cp org.chrony-project.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
3. org.chrony-project.chronyd.plist
3. org.tuxfamily.chronyd.plist
This file is the launchd plist that runs chronyd when the Macintosh starts.
sudo cp org.chrony-project.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.chrony-project.logrotate</string>
<string>org.tuxfamily.logrotate</string>
<key>KeepAlive</key>
<false/>
<key>ProgramArguments</key>

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.chrony-project.chronyd</string>
<string>org.tuxfamily.chronyd</string>
<key>Program</key>
<string>/usr/local/sbin/chronyd</string>
<key>ProgramArguments</key>

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2023
// Copyright (C) Miroslav Lichvar 2009-2017
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -39,7 +39,7 @@ running.
If no commands are specified on the command line, *chronyc* will expect input
from the user. The prompt _chronyc>_ will be displayed when it is being run
from a terminal. If *chronyc*'s input or output are redirected from or to a file,
the prompt will not be shown.
the prompt is not shown.
There are two ways *chronyc* can access *chronyd*. One is the Internet
Protocol (IPv4 or IPv6) and the other is a Unix domain socket, which is
@@ -51,8 +51,7 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
Only the following monitoring commands, which do not affect the behaviour of
*chronyd*, are allowed from the network: *activity*, *manual list*,
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
*waitsync*. The
*rtcdata*, *smoothing*, *sources*, *sourcestats*, *tracking*, *waitsync*. The
set of hosts from which *chronyd* will accept these commands can be configured
with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
@@ -60,7 +59,9 @@ default, the commands are accepted only from localhost (127.0.0.1 or ::1).
All other commands are allowed only through the Unix domain socket. When sent
over the network, *chronyd* will respond with a '`Not authorised`' error, even
if it is from localhost.
if it is from localhost. In chrony versions before 2.2 they were allowed
from the network if they were authenticated with a password, but that is no
longer supported.
Having full access to *chronyd* via *chronyc* is more or less equivalent to
being able to modify the *chronyd*'s configuration file and restart it.
@@ -77,21 +78,11 @@ With this option hostnames will be resolved only to IPv6 addresses.
This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
DNS lookups. Long addresses will not be truncated to fit into the column.
*-N*::
This option enables printing of original hostnames or IP addresses of NTP
sources that were specified in the configuration file, or *chronyc* commands.
Without the *-n* and *-N* option, the printed hostnames are obtained from
reverse DNS lookups and can be different from the specified hostnames.
*-c*::
This option enables printing of reports in a comma-separated values (CSV)
format. Reverse DNS lookups will be disabled, time will be printed as number of
seconds since the epoch, and values in seconds will not be converted to other
units.
*-e*::
With this option each *chronyc* response will end with a line containing a
single dot.
format. IP addresses will not be resolved to hostnames, time will be printed as
number of seconds since the epoch and values in seconds will not be converted
to other units.
*-d*::
This option enables printing of debugging messages if *chronyc* was compiled
@@ -103,15 +94,12 @@ With this option multiple commands can be specified. Each argument will be
interpreted as a whole command.
*-h* _host_::
This option specifies the host to be contacted by *chronyc*. It can be
specified with a hostname, IP address, or path to the local Unix domain socket.
Multiple values can be specified as a comma-separated list to provide a
fallback.
This option allows the user to specify which host (or comma-separated list of
addresses) running the *chronyd* program is to be contacted. This allows for
remote monitoring, without having to connect over SSH to the other host first.
+
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
where *chronyc* is being run. First, it tries to connect to the Unix domain
socket and if that fails (e.g. due to running under a non-root user), it
will try to connect to 127.0.0.1 and then ::1.
The default is to contact *chronyd* running on the same host where
*chronyc* is being run.
*-p* _port_::
This option allows the user to specify the UDP port number which the target
@@ -124,14 +112,10 @@ This option is ignored and is provided only for compatibility.
*-a*::
This option is ignored and is provided only for compatibility.
*-v*, *--version*::
*-v*::
With this option *chronyc* displays its version number on the terminal and
exits.
*--help*::
With this option *chronyc* displays a help message on the terminal and
exits.
== COMMANDS
This section describes each of the commands available within the *chronyc*
@@ -184,23 +168,17 @@ stratum-2 and is synchronised from a stratum-1).
This is the time (UTC) at which the last measurement from the reference
source was processed.
*System time*:::
This is the current offset between the NTP clock and system clock. The NTP
clock is a software (virtual) clock maintained by *chronyd*, which is
synchronised to the configured time sources and provides time to NTP clients.
The system clock is synchronised to the NTP clock. To avoid steps in the
system time, which might have adverse consequences for certain applications,
the system clock is normally corrected only by speeding up or slowing down (up
to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
directive). If the offset is too large, this correction will take a very long
time. A step can be forced by the <<makestep,*makestep*>> command, or the
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
+
Note that all other offsets reported by *chronyc* and most offsets in the log
files are relative to the NTP clock, not the system clock.
In normal operation, *chronyd* by default never steps the system clock, because
any jump in the time can have adverse consequences for certain application
programs. Instead, any error in the system clock is corrected by slightly
speeding up or slowing down the system clock until the error has been removed,
and then returning to the system clock's normal speed. A consequence of this is
that there will be a period when the system clock (as read by other programs)
will be different from *chronyd*'s estimate of the current true time (which it
reports to NTP clients when it is operating in server mode). The value reported
on this line is the difference due to this effect.
*Last offset*:::
This is the estimated local offset on the last clock update. A positive value
indicates the local time (as previously estimated true time) was ahead of the
time sources.
This is the estimated local offset on the last clock update.
*RMS offset*:::
This is a long-term average of the offset value.
*Frequency*:::
@@ -306,18 +284,15 @@ milliseconds.
=== Time sources
[[sources]]*sources* [*-a*] [*-v*]::
[[sources]]*sources* [*-v*]::
This command displays information about the current time sources that *chronyd*
is accessing.
+
If the *-a* option is specified, all sources are displayed, including those that
do not have a known address yet. Such sources have an identifier in the format
_ID#XXXXXXXXXX_, which can be used in other commands expecting a source address.
+
The *-v* option enables a verbose output. In this case,
The optional argument *-v* can be specified, meaning _verbose_. In this case,
extra caption lines are shown as a reminder of the meanings of the columns.
+
----
210 Number of sources = 3
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns
@@ -331,23 +306,18 @@ The columns are as follows:
This indicates the mode of the source. _^_ means a server, _=_ means a peer
and _#_ indicates a locally connected reference clock.
*S*:::
This column indicates the selection state of the source.
* _*_ indicates the best source which is currently selected for
synchronisation.
* _+_ indicates other sources selected for synchronisation, which are combined
with the best source.
* _-_ indicates a source which is considered to be selectable for
synchronisation, but not currently selected.
* _x_ indicates a source which *chronyd* thinks is a falseticker (i.e. its
time is inconsistent with a majority of other sources, or sources specified
with the *trust* option).
This column indicates the state of the source.
* _*_ indicates the source to which *chronyd* is currently synchronised.
* _+_ indicates acceptable sources which are combined with the selected
source.
* _-_ indicates acceptable sources which are excluded by the combining
algorithm.
* _?_ indicates sources to which connectivity has been lost or whose packets
do not pass all tests. It is also shown at start-up, until at least 3 samples
have been gathered from it.
* _x_ indicates a clock which *chronyd* thinks is a falseticker (i.e. its
time is inconsistent with a majority of other sources).
* _~_ indicates a source whose time appears to have too much variability.
* _?_ indicates a source which is not considered to be selectable for
synchronisation for other reasons (e.g. unreachable, not synchronised, or
does not have enough measurements).
{blank}:::
The <<selectdata,*selectdata*>> command can be used to get more details about
the selection state.
*Name/IP address*:::
This shows the name or the IP address of the source, or reference ID for reference
clocks.
@@ -379,25 +349,22 @@ offset. This can be suffixed by _ns_ (indicating nanoseconds), _us_
(indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating
seconds). The number to the left of the square brackets shows the original
measurement, adjusted to allow for any slews applied to the local clock
since. Positive offsets indicate that the local clock is ahead of the source.
The number following the _+/-_ indicator shows the margin of error in the
measurement (NTP root distance).
since. The number following the _+/-_ indicator shows the margin of error in
the measurement. Positive offsets indicate that the local clock is ahead of
the source.
[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
[[sourcestats]]*sourcestats* [*-v*]::
The *sourcestats* command displays information about the drift rate and offset
estimation process for each of the sources currently being examined by
*chronyd*.
+
If the *-a* option is specified, all sources are displayed, including those that
do not have a known address yet. Such sources have an identifier in the format
_ID#XXXXXXXXXX_, which can be used in other commands expecting a source address.
+
The *-v* option enables a verbose output. In this case,
The optional argument *-v* can be specified, meaning _verbose_. In this case,
extra caption lines are shown as a reminder of the meanings of the columns.
+
An example report is:
+
----
210 Number of sources = 1
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
===============================================================================
foo.example.net 11 5 46m -0.001 0.045 1us 25us
@@ -433,116 +400,6 @@ This is the estimated offset of the source.
*Std Dev*:::
This is the estimated sample standard deviation.
[[selectdata]]*selectdata* [*-a*] [*-v*]::
The *selectdata* command displays information specific to the selection of time
sources. If the *-a* option is specified, all sources are displayed, including
those that do not have a known address yet. With the *-v* option, extra caption
lines are shown as a reminder of the meanings of the columns.
+
An example of the output is shown below.
+
----
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us N
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us N
----
+
The columns are as follows:
+
*S*:::
This column indicates the state of the source after the last source selection.
It is similar to the state reported by the *sources* command, but more
states are reported.
{blank}:::
The following states indicate the source is not considered selectable for
synchronisation:
* _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<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.
* _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.
* _x_ - does not agree with other sources (falseticker).
{blank}:::
The following states indicate the source is considered selectable, but it is
not currently used for synchronisation:
* _W_ - waits for other sources to be selectable (required by the
<<chrony.conf.adoc#minsources,*minsources*>> directive, or
the *require* option of another source).
* _P_ - another selectable source is preferred due to the *prefer* option.
* _U_ - waits for a new measurement (after selecting a different best source).
* _D_ - has, or recently had, a root distance which is too large to be combined
with other sources (configured by the
<<chrony.conf.adoc#combinelimit,*combinelimit*>> directive).
{blank}:::
The following states indicate the source is used for synchronisation of the
local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
*Name/IP address*:::
This column shows the name or IP address of the source if it is an NTP server,
or the reference ID if it is a reference clock.
*Auth*:::
This column indicites whether an authentication mechanism is enabled for the
source. _Y_ means yes and _N_ means no.
*COpts*:::
This column displays the configured selection options of the source.
* _N_ indicates the *noselect* option.
* _P_ indicates the *prefer* option.
* _T_ indicates the *trust* option.
* _R_ indicates the *require* option.
*EOpts*:::
This column displays the current effective selection options of the source,
which can be different from the configured options due to the authentication
selection mode (configured by the
<<chrony.conf.adoc#authselectmode,*authselectmode*>> directive). The symbols
are the same as in the *COpts* column.
*Last*:::
This column displays how long ago was the last measurement of the source made
when the selection was performed.
*Score*:::
This column displays the current score against the source in the _*_ state. The
scoring system avoids frequent reselection when multiple sources have a similar
root distance. A value larger than 1 indicates this source was better than the
_*_ source in recent selections. If the score reaches 10, the best source will
be reselected and the scores will be reset to 1.
*Interval*:::
This column displays the lower and upper endpoint of the interval which was
expected to contain the true offset of the local clock considering the root
distance at the time of the selection.
*Leap*:::
This column displays the current leap status of the source.
* _N_ indicates the normal status (no leap second).
* _+_ indicates that a leap second will be inserted at the end of the month.
* _-_ indicates that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made).
[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
The *selectopts* command modifies the configured selection options of an NTP
source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
unknown addresses), or a reference clock specified by reference ID as a string.
+
The selection options can be added with the *+* symbol or removed with the *-*
symbol. The *selectdata* command can be used to verify the configuration. The
modified options will be applied in the next source selection, e.g. when a new
measurement is made, or the *reselect* command is executed.
+
An example of using this command is shown below.
+
----
selectopts 1.2.3.4 -noselect +prefer
selectopts GPS +trust
----
[[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised
to a source even when it is not currently the best one among the available
@@ -583,83 +440,10 @@ the offline state.
the name of the server or peer was not resolved to an address yet; this source is
not visible in the *sources* and *sourcestats* reports.
[[authdata]]*authdata* [*-a*]::
The *authdata* command displays information specific to authentication of NTP
sources. If the *-a* option is specified, all sources are displayed, including
those that do not have a known address yet. An example of the output is
shown below.
+
----
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
foo.example.net NTS 1 15 256 135m 0 0 8 100
bar.example.net SK 30 13 128 - 0 0 0 0
baz.example.net - 0 0 0 - 0 0 0 0
----
+
The columns are as follows:
+
*Name/IP address*:::
This column shows the name or the IP address of the source.
*Mode*:::
This column shows which mechanism authenticates NTP packets received from the
source. _NTS_ means Network Time Security, _SK_ means a symmetric key, and _-_
means authentication is disabled.
*KeyID*:::
This column shows an identifier of the key used for authentication. With a
symmetric key, it is the ID from the <<chrony.conf.adoc#keyfile,key file>>.
With NTS, it is a number starting at zero and incremented by one with each
successful key establishment using the NTS-KE protocol, i.e. it shows how many
times the key establishment was performed with this source.
*Type*:::
This columns shows an identifier of the algorithm used for authentication.
With a symmetric key, it is the hash function or cipher specified in the key
file. With NTS, it is an authenticated encryption with associated data (AEAD)
algorithm, which is negotiated in the NTS-KE protocol. The following values can
be reported:
* 1: MD5
* 2: SHA1
* 3: SHA256
* 4: SHA384
* 5: SHA512
* 6: SHA3-224
* 7: SHA3-256
* 8: SHA3-384
* 9: SHA3-512
* 10: TIGER
* 11: WHIRLPOOL
* 13: AES128
* 14: AES256
* 15: AEAD-AES-SIV-CMAC-256
* 30: AEAD-AES-128-GCM-SIV
*KLen*:::
This column shows the length of the key in bits.
*Last*:::
This column shows how long ago the last successful key establishment was
performed. It is in seconds, or letters _m_, _h_, _d_ or _y_ indicate minutes,
hours, days, or years.
*Atmp*:::
This column shows the number of attempts to perform the key establishment since
the last successful key establishment. A number larger than 1 indicates a
problem with the network or server.
*NAK*:::
This column shows whether an NTS NAK was received since the last request.
A NAK indicates that authentication failed on the server side due to
*chronyd* using a cookie which is no longer valid and that it needs to perform
the key establishment again in order to get new cookies.
*Cook*:::
This column shows the number of NTS cookies that *chronyd* currently has. If
the key establishment was successful, a number smaller than 8 indicates a
problem with the network or server.
*CLen*:::
This column shows the length in bytes of the NTS cookie which will be used in
the next request.
[[ntpdata]]*ntpdata* [_address_]::
The *ntpdata* command displays the last valid measurement and other
NTP-specific information about the specified NTP source, or all NTP sources
(with a known address) if no address was specified. An example of the output is
shown below.
NTP-specific information about the specified NTP source, or all NTP sources if
no address was specified. An example of the output is shown below.
+
----
Remote address : 203.0.113.15 (CB00710F)
@@ -688,7 +472,6 @@ RX timestamping : Kernel
Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
----
+
The fields are explained as follows:
@@ -726,8 +509,7 @@ packets sent to the source is more variable than the delay of packets sent
from the source back.
*NTP tests*:::
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
loop.
delay, delay ratio, delay dev ratio, and synchronisation loop.
*Interleaved*:::
This shows if the response was in the interleaved mode.
*Authenticated*:::
@@ -742,18 +524,17 @@ The number of packets sent to the source.
*Total RX*:::
The number of all packets received from the source.
*Total valid RX*:::
The number of packets which passed the first two groups of NTP tests.
*Total good RX*:::
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
The number of valid packets received from the source.
[[add_peer]]*add peer* _name_ [_option_]...::
[[add_peer]]*add peer* _address_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
*chronyd* is running.
+
Following the words *add peer*, the syntax of the following
parameters and options is identical to that for the
parameters and options is similar to that for the
<<chrony.conf.adoc#peer,*peer*>> directive in the configuration file.
The following peer options can be set in the command: *port*, *minpoll*,
*maxpoll*, *presend*, *maxdelayratio*, *maxdelay*, *key*.
+
An example of using this command is shown below.
+
@@ -761,27 +542,15 @@ An example of using this command is shown below.
add peer foo.example.net minpoll 6 maxpoll 10 key 25
----
[[add_pool]]*add pool* _name_ [_option_]...::
The *add pool* command allows a pool of NTP servers to be added whilst
*chronyd* is running.
+
Following the words *add pool*, the syntax of the following parameters and
options is identical to that for the <<chrony.conf.adoc#pool,*pool*>>
directive in the configuration file.
+
An example of using this command is shown below:
+
----
add pool foo.example.net maxsources 3 iburst
----
[[add_server]]*add server* _name_ [_option_]...::
[[add_server]]*add server* _address_ [_option_]...::
The *add server* command allows a new NTP server to be added whilst
*chronyd* is running.
+
Following the words *add server*, the syntax of the following parameters and
options is identical to that for the <<chrony.conf.adoc#server,*server*>>
options is similar to that for the <<chrony.conf.adoc#server,*server*>>
directive in the configuration file.
The following server options can be set in the command: *port*, *minpoll*,
*maxpoll*, *presend*, *maxdelayratio*, *maxdelay*, *key*.
+
An example of using this command is shown below:
+
@@ -832,7 +601,7 @@ alternative to the form with mask.
_address_:::
This is an IP address or a hostname. The burst command is applied only to
that source.
{blank}::
::
+
If no _mask_ or _masked-address_ arguments are provided, every source will be
matched.
@@ -919,8 +688,7 @@ the loaded periods. The *offline* and *online* commands can be used to achieve
this.
+
There are four forms of the *offline* command. The first form is a wildcard,
meaning all sources (including sources that do not have a known address yet).
The second form allows an IP address mask and a masked
meaning all sources. The second form allows an IP address mask and a masked
address to be specified. The third form uses CIDR notation. The fourth form
uses an IP address or a hostname. These forms are illustrated below.
+
@@ -957,11 +725,10 @@ The syntax is identical to that of the <<offline,*offline*>> command.
[[onoffline]]
*onoffline*::
The *onoffline* command tells *chronyd* to switch all sources that have a known
address to the online or
The *onoffline* command tells *chronyd* to switch all sources to the online or
offline status according to the current network configuration. A source is
considered online if it is possible to send requests to it, i.e. a network
route to the source is present.
considered online if it is possible to send requests to it, i.e. a route to the
network is present.
[[polltarget]]*polltarget* _address_ _polltarget_::
The *polltarget* command is used to modify the poll target for one of the
@@ -970,31 +737,12 @@ current set of sources. It is equivalent to the *polltarget* option in the
[[refresh]]*refresh*::
The *refresh* command can be used to force *chronyd* to resolve the names of
configured NTP sources to IP addresses again and replace any addresses missing
in the list of resolved addresses.
configured sources to IP addresses again, e.g. after suspending and resuming
the machine in a different network.
+
Sources that stop responding are replaced with newly resolved addresses
automatically after 8 polling intervals. This command can be used to replace
them immediately, e.g. after suspending and resuming the machine in a different
network.
+
Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
addresses are included in a single DNS response (e.g. pool.ntp.org), this
command might replace the addresses even if they are still in the pool.
[[reload]]*reload* *sources*::
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
from the directories specified by the
<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive.
[[sourcename]]*sourcename* _address_::
The *sourcename* command prints the original hostname or address that was
specified for an NTP source in the configuration file, or the *add* command.
This command is an alternative to the *-N* option, which can be useful in
scripts.
+
Note that different NTP sources can share the same name, e.g. servers from a
pool.
Sources that stop responding will be replaced with newly resolved addresses
automatically after 8 polling intervals, but this command can still be useful
to replace them immediately and not wait until they are marked as unreachable.
=== Manual time input
@@ -1032,7 +780,7 @@ The columns are as as follows:
. The regression residual at this point, in seconds. This allows '`outliers`'
to be easily spotted, so that they can be deleted using the *manual delete*
command.
{blank}::
::
+
The *delete* form of the command deletes a single sample. The parameter is the
index of the sample, as shown in the first column of the output from *manual
@@ -1103,17 +851,10 @@ This command can be used to examine the effect of a series of *allow*, *allow
all*, *deny*, and *deny all* commands specified either via *chronyc*, or in
*chronyd*'s configuration file.
[[clients]]*clients* [*-p* _packets_] [*-k*] [*-r*]::
[[clients]]*clients*::
This command shows a list of clients that have accessed the server, through
the NTP, command, or NTS-KE port. It does not include accesses over the Unix
domain command socket.
+
The *-p* option specifies the minimum number of received NTP or command
packets, or accepted NTS-KE connections, needed to include a client in the
list. The default value is 0, i.e. all clients are reported. With the *-k*
option the last four columns will show the NTS-KE accesses instead of command
accesses. If the *-r* option is specified, *chronyd* will reset the counters of
received and dropped packets or connections after reporting the current values.
either the NTP or command ports. It does not include accesses over
the Unix domain command socket. There are no arguments.
+
An example of the output is:
+
@@ -1138,18 +879,20 @@ The columns are as follows:
. The average interval between NTP packets.
. The average interval between NTP packets after limiting the response rate.
. Time since the last NTP packet was received
. The number of command packets or NTS-KE connections received/accepted from
the client.
. The number of command packets or NTS-KE connections dropped to limit the
response rate.
. The average interval between command packets or NTS-KE connections.
. Time since the last command packet or NTS-KE connection was
received/accepted.
. The number of command packets received from the client.
. The number of command packets dropped to limit the response rate.
. The average interval between command packets.
. Time since the last command packet was received.
[[serverstats]]*serverstats*::
The *serverstats* command displays NTP and command server statistics.
+
An example of the output is shown below.
The *serverstats* command displays how many valid NTP and command requests
*chronyd* as a server received from clients, how many of them were dropped to
limit the response rate as configured by the
<<chrony.conf.adoc#ratelimit,*ratelimit*>> and
<<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directives, and how many
client log records were dropped due to the memory limit configured by the
<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive. An example of
the output is shown below.
+
----
NTP packets received : 1598
@@ -1157,70 +900,7 @@ NTP packets dropped : 8
Command packets received : 19
Command packets dropped : 0
Client log records dropped : 0
NTS-KE connections accepted: 3
NTS-KE connections dropped : 0
Authenticated NTP packets : 189
Interleaved NTP packets : 43
NTP timestamps held : 44
NTP timestamp span : 120
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : 1537
NTP kernel RX timestamps : 1590
NTP kernel TX timestamps : 43
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0
----
+
The fields have the following meaning:
+
*NTP packets received*:::
The number of valid NTP requests received by the server.
*NTP packets dropped*:::
The number of NTP requests dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#ratelimit,*ratelimit*>> directive).
*Command packets received*:::
The number of command requests received by the server.
*Command packets dropped*:::
The number of command requests dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directive).
*Client log records dropped*:::
The number of client log records dropped by the server to limit the memory use
(configured by the <<chrony.conf.adoc#clientloglimit,*clientloglimit*>>
directive).
*NTS-KE connections accepted*:::
The number of NTS-KE connections accepted by the server.
*NTS-KE connections dropped*:::
The number of NTS-KE connections dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#ntsratelimit,*ntsratelimit*>> directive).
*Authenticated NTP packets*:::
The number of received NTP requests that were authenticated (with a symmetric
key or NTS).
*Interleaved NTP packets*:::
The number of received NTP requests that were detected to be in the interleaved
mode.
*NTP timestamps held*:::
The number of pairs of receive and transmit timestamps that the server is
currently holding in memory for clients using the interleaved mode.
*NTP timestamp span*:::
The interval (in seconds) covered by the currently held NTP timestamps.
*NTP daemon RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
daemon.
*NTP daemon TX timestamps*:::
The number of NTP responses which included a transmit timestamp captured by the
daemon.
*NTP kernel RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
kernel.
*NTP kernel TX timestamps*:::
The number of NTP responses (in the interleaved mode) which included a transmit
timestamp captured by the kernel.
*NTP hardware RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
NIC.
*NTP hardware TX timestamps*:::
The number of NTP responses (in the interleaved mode) which included a transmit
timestamp captured by the NIC.
[[allow]]*allow* [*all*] [_subnet_]::
The effect of the allow command is identical to the
@@ -1229,8 +909,11 @@ The effect of the allow command is identical to the
The syntax is illustrated in the following examples:
+
----
allow 1.2.3.4
allow all 3.4.5.0/24
allow foo.example.net
allow all 1.2
allow 3.4.5
allow 6.7.8/22
allow 6.7.8.9/22
allow 2001:db8:789a::/48
allow 0/0
allow ::/0
@@ -1245,8 +928,11 @@ The effect of the allow command is identical to the
The syntax is illustrated in the following examples:
+
----
deny 1.2.3.4
deny all 3.4.5.0/24
deny foo.example.net
deny all 1.2
deny 3.4.5
deny 6.7.8/22
deny 6.7.8.9/22
deny 2001:db8:789a::/48
deny 0/0
deny ::/0
@@ -1259,8 +945,8 @@ deny all
*local* *off*::
The *local* command allows *chronyd* to be told that it is to appear as a
reference source, even if it is not itself properly synchronised to an external
source. This can be used on isolated networks, to allow a computer to be the
primary time server for other computers.
source. (This can be used on isolated networks, to allow one computer to be a
master time server with the other computers slaving to it.)
+
The first form enables the local reference mode on the host. The syntax is
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
@@ -1399,7 +1085,7 @@ more than 1 second away from the system clock):
error).
. Save the RTC parameters to the RTC file (specified with the
<<chrony.conf.adoc#rtcfile,*rtcfile*>> directive in the configuration file).
{blank}::
::
+
The last step is done as a precaution against the computer suffering a power
failure before either the daemon exits or the <<writertc,*writertc*>> command
@@ -1432,36 +1118,25 @@ purged. An example of how to do this is shown below.
----
# mv /var/log/chrony/measurements.log /var/log/chrony/measurements1.log
# chronyc cyclelogs
# rm /var/log/chrony/measurements1.log
# ls -l /var/log/chrony
-rw-r--r-- 1 root root 0 Jun 8 18:17 measurements.log
-rw-r--r-- 1 root root 12345 Jun 8 18:17 measurements1.log
# rm -f measurements1.log
----
[[dump]]*dump*::
The *dump* command causes *chronyd* to write its current history of
measurements for each of its sources to dump files in the directory specified
in the configuration file by the <<chrony.conf.adoc#dumpdir,*dumpdir*>>
directive and also write server NTS keys and client NTS cookies to the
directory specified by the <<chrony.conf.adoc#ntsdumpdir1,*ntsdumpdir*>>
directive. Note that *chronyd* does this automatically when it exits. This
command is mainly useful for inspection whilst *chronyd* is running.
command is mainly useful for inspection of the history whilst *chronyd* is
running.
[[rekey]]*rekey*::
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive. It
also re-reads the server NTS keys if
<<chrony.conf.adoc#ntsdumpdir2,*ntsdumpdir*>> is specified and
<<chrony.conf.adoc#ntsrotate,automatic rotation>> is disabled in the
configuration file.
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
[[reset]]*reset* *sources*::
The *reset sources* command causes *chronyd* to drop all measurements and
switch to the unsynchronised state. This command can help *chronyd* with
recovery when the measurements are known to be no longer valid or accurate,
e.g. due to moving the computer to a different network, or resuming the
computer from a low-power state (which resets the system clock). *chronyd* will
drop the measurements automatically when it detects the clock has made an
unexpected jump, but the detection is not completely reliable.
[[shutdown]]*shutdown*::
[[rekey]]*shutdown*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
the process the SIGTERM signal.
@@ -1513,10 +1188,10 @@ generated from the _/dev/urandom_ device and it is printed to standard output.
+
The command has three optional arguments. The first argument is the key number
(by default 1), which will be specified with the *key* option of the *server*
or *peer* directives in the configuration file. The second argument is the name
of the hash function or cipher (by default SHA1, or MD5 if SHA1 is not
available). The third argument is the length of the key in bits if a hash
function was selected, between 80 and 4096 bits (by default 160 bits).
or *peer* directives in the configuration file. The second argument is the hash
function (by default SHA1 or MD5 if SHA1 is not available) and the third
argument is the number of bits the key should have, between 80 and 4096 bits
(by default 160 bits).
+
An example is:
+
@@ -1526,13 +1201,7 @@ keygen 73 SHA1 256
+
which generates a 256-bit SHA1 key with number 73. The printed line should
then be securely transferred and added to the key files on both server and
client, or peers. A different key should be generated for each client or peer.
+
An example using the AES128 cipher is:
+
----
keygen 151 AES128
----
client, or peers.
[[exit]]*exit*::
[[quit]]*quit*::
@@ -1548,7 +1217,7 @@ The *help* command displays a summary of the commands and their arguments.
== BUGS
For instructions on how to report bugs, please visit
https://chrony-project.org/.
https://chrony.tuxfamily.org/.
== AUTHORS

View File

@@ -41,7 +41,7 @@ If no configuration directives are specified on the command line, *chronyd*
will read them from a configuration file. The compiled-in default location of
the file is _@SYSCONFDIR@/chrony.conf_.
Informational messages, warnings, and errors will be logged to syslog.
Information messages and warnings will be logged to syslog.
== OPTIONS
@@ -55,32 +55,20 @@ IPv6 sockets will be created.
*-f* _file_::
This option can be used to specify an alternate location for the configuration
file. The compiled-in default value is _@SYSCONFDIR@/chrony.conf_.
file (default _@SYSCONFDIR@/chrony.conf_).
*-n*::
When run in this mode, the program will not detach itself from the terminal.
*-d*::
When run in this mode, the program will not detach itself from the terminal,
and all messages will be written to the terminal instead of syslog. If
*chronyd* was compiled with enabled support for debugging, this option can be
used twice to enable debug messages.
and all messages will be written to the terminal instead of syslog. When
*chronyd* was compiled with debugging support, this option can be used twice to
print also debugging messages.
*-l* _file_::
This option enables writing of log messages to a file instead of syslog or the
terminal.
*-L* _level_::
This option specifies the minimum severity level of messages to be written to
the log file, syslog, or terminal. The following levels can be specified:
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
default value is 0.
*-p*::
When run in this mode, *chronyd* will print the configuration and exit. It will
not detach from the terminal. This option can be used to verify the syntax of
the configuration and get the whole configuration, even if it is split into
multiple files and read by the *include* or *confdir* directive.
This option specifies a file which should be used for logging instead of syslog
or terminal.
*-q*::
When run in this mode, *chronyd* will set the system clock once and exit. It
@@ -88,10 +76,8 @@ will not detach from the terminal.
*-Q*::
This option is similar to the *-q* option, except it only prints the offset
without making any corrections of the clock and disables server ports to allow
*chronyd* to be started without root privileges, assuming the configuration
does not have any directives which would require them (e.g. *refclock*,
*hwtimestamp*, *rtcfile*, etc).
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
*-r*::
This option will try to reload and then delete files containing sample
@@ -102,7 +88,7 @@ directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
illumos, and macOS 10.13 or later).
Solaris, and macOS 10.13 or later).
*-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -139,73 +125,49 @@ running, but still allow it to adjust the frequency of the system clock.
*-u* _user_::
This option sets the name of the system user to which *chronyd* will switch
after start in order to drop root privileges. It overrides the
<<chrony.conf.adoc#user,*user*>> directive. The compiled-in default value is
_@DEFAULT_USER@_.
<<chrony.conf.adoc#user,*user*>> directive (default _@DEFAULT_USER@_).
+
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent.
*-U*::
This option disables a check for root privileges to allow *chronyd* to be
started under a non-root user, assuming the process will have all capabilities
(e.g. provided by the service manager) and access to all files, directories,
and devices, needed to operate correctly in the specified configuration. Note
that different capabilities might be needed with different configurations and
different Linux kernel versions. Starting *chronyd* under a non-root user is
not recommended when the configuration is not known, or at least limited to
specific directives.
*-F* _level_::
This option configures system call filters loaded by *chronyd* processes if it
was compiled with support for the Linux secure computing (seccomp) facility.
Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
levels 1 and 2, *chronyd* will be killed if it makes a system call which is
blocked by the filters. The level can be specified as a negative number to
trigger the SIGSYS signal instead of SIGKILL, which can be useful for
debugging. The default value is 0.
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled (default 0).
+
At level 1, the filters allow only selected system calls that are normally
expected to be made by *chronyd*. Other system calls are blocked. This level is
recommended only if it is known to work on the version of the system where
*chrony* is installed. The filters need to allow also system calls made by
libraries that *chronyd* is using (e.g. libc), but different versions or
implementations of the libraries might make different system calls. If the
filters are missing a system call, *chronyd* could be killed even in normal
operation.
+
At level 2, the filters block only a small number of specific system calls
(e.g. fork and exec). This approach should avoid false positives, but the
protection of the system against a compromised *chronyd* process is much more
limited.
+
The filters cannot be enabled with the *mailonchange* directive.
It's recommended to enable the filter only when it's known to work on the
version of the system where *chrony* is installed as the filter needs to allow
also system calls made from libraries that *chronyd* is using (e.g. libc) and
different versions or implementations of the libraries may make different
system calls. If the filter is missing some system call, *chronyd* could be
killed even in normal operation.
*-P* _priority_::
On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
real-time scheduler at the specified priority (which must be between 0 and
100). On macOS, this option must have either a value of 0 to disable the thread
time constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0.
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
specified priority (which must be between 0 and 100). On macOS, this option
must have either a value of 0 (the default) to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
support this option.
*-m*::
This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
This mode is only supported on Linux.
*-x*::
This option disables the control of the system clock. *chronyd* will not try to
make any adjustments of the clock. It will assume the clock is free running and
still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to be started without the capability to adjust or set
the system clock (e.g. in some containers) to operate as an NTP server.
option allows *chronyd* to run without the capability to adjust or set the
system clock (e.g. in some containers) in order to operate as an NTP server. It
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
*-v*, *--version*::
*-v*::
With this option *chronyd* will print version number to the terminal and exit.
*-h*, *--help*::
With this option *chronyd* will print a help message to the terminal and exit.
== FILES
_@SYSCONFDIR@/chrony.conf_
@@ -217,7 +179,7 @@ _@SYSCONFDIR@/chrony.conf_
== BUGS
For instructions on how to report bugs, please visit
https://chrony-project.org/.
https://chrony.tuxfamily.org/.
== AUTHORS

File diff suppressed because it is too large Load Diff

View File

@@ -22,25 +22,18 @@ The software is distributed as source code which has to be compiled. The source
code is supplied in the form of a gzipped tar file, which unpacks to a
subdirectory identifying the name and version of the program.
A C compiler (e.g. `gcc` or `clang`) and GNU Make are needed to build `chrony`.
The following libraries with their development files, and programs, are needed
to enable optional features:
The following programs and libraries with their development files are needed to
build `chrony`:
* pkg-config: detection of development libraries
* Nettle, GnuTLS, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
* libcap: dropping root privileges on Linux (`DROPROOT`)
* libseccomp: system call filter on Linux (`SCFILTER`)
* GnuTLS and Nettle: Network Time Security (`NTS`)
* Editline: line editing in `chronyc` (`READLINE`)
* timepps.h header: PPS reference clock
* Asciidoctor: documentation in HTML format
* Bash: test suite
The following programs are needed when building `chrony` from the git
repository instead of a released tar file:
* Asciidoctor: manual pages
* Bison: parser for chronyc settime command
* C compiler (gcc or clang recommended)
* GNU Make
* Nettle, NSS, or LibTomCrypt (optional)
* Editline (optional)
* libcap (Linux only, optional)
* libseccomp (Linux only, optional)
* timepps.h header (optional)
* Asciidoctor (for HTML documentation)
* Bash (for testing)
After unpacking the source code, change directory into it, and type
@@ -94,13 +87,13 @@ want to enable the support, specify the `--disable-asyncdns` flag to
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
`chronyd` will be built with support for other cryptographic hash functions
than MD5, which can be used for NTP authentication with a symmetric key. If you
don't want to enable the support, specify the `--disable-sechash` flag to
`configure`.
If development files for the editline library are available,
If development files for the editline or readline library are available,
`chronyc` will be built with line editing support. If you don't want this,
specify the `--disable-readline` flag to `configure`.
@@ -170,6 +163,43 @@ https://github.com/seccomp/libseccomp[libseccomp] library and the
the kernel attack surface and possibly prevent kernel exploits from `chronyd`
if it is compromised.
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
the cursor keys to replay and edit old commands. Two libraries are supported
which provide such functionality, editline and GNU readline.
Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
The `configure` script will automatically enable the line editing support if
one of the supported libraries is available. If they are both available, the
editline library will be used.
If you don't want to use it (in which case `chronyc` will use a minimal command
line interface), invoke `configure` like this:
----
./configure --disable-readline other-options...
----
If you have editline, readline or ncurses installed in locations that aren't
normally searched by the compiler and linker, you need to use extra options:
`--with-readline-includes=directory_name`::
This defines the name of the directory above the one where `readline.h` is.
`readline.h` is assumed to be in `editline` or `readline` subdirectory of the
named directory.
`--with-readline-library=directory_name`::
This defines the directory containing the `libedit.a` or `libedit.so` file,
or `libreadline.a` or `libreadline.so` file.
`--with-ncurses-library=directory_name`::
This defines the directory containing the `libncurses.a` or `libncurses.so`
file.
== Extra options for package builders
The `configure` and `make` procedures have some extra options that may be

View File

@@ -8,39 +8,11 @@ Wants=time-sync.target
[Service]
Type=oneshot
# Wait for chronyd to update the clock and the remaining
# correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 0 0.1 0.0 1
# Wait for at most 3 minutes
TimeoutStartSec=180
# Wait up to ~10 minutes for chronyd to synchronize and the remaining
# clock correction to be less than 0.1 seconds
ExecStart=/usr/bin/chronyc -h 127.0.0.1,::1 waitsync 600 0.1 0.0 1
RemainAfterExit=yes
StandardOutput=null
CapabilityBoundingSet=
DevicePolicy=closed
DynamicUser=yes
IPAddressAllow=localhost
IPAddressDeny=any
LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
UMask=0777
[Install]
WantedBy=multi-user.target

View File

@@ -1,5 +1,5 @@
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
pool pool.ntp.org iburst
# Record the rate at which the system clock gains/losses time.
@@ -25,18 +25,9 @@ rtcsync
# Serve time even if not synchronized to a time source.
#local stratum 10
# Require authentication (nts or key option) for all NTP sources.
#authselectmode require
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Save NTS keys and cookies.
ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC

View File

@@ -57,20 +57,6 @@
! maxdrift 100
# By default, chronyd allows synchronisation to an unauthenticated NTP
# source (i.e. specified without the nts and key options) if it agrees with
# a majority of authenticated NTP sources, or if no authenticated source is
# specified. If you don't want chronyd to ever synchronise to an
# unauthenticated NTP source, uncomment the first from the following lines.
# If you don't want to synchronise to an unauthenticated NTP source only
# when an authenticated source is specified, uncomment the second line.
# If you want chronyd to ignore authentication in the source selection,
# uncomment the third line.
! authselectmode require
! authselectmode prefer
! authselectmode ignore
#######################################################################
### FILENAMES ETC
# Chrony likes to keep information about your computer's clock in files.
@@ -86,37 +72,22 @@ driftfile /var/lib/chrony/drift
! keyfile /etc/chrony.keys
# If you specify an NTP server with the nts option to enable authentication
# with the Network Time Security (NTS) mechanism, or enable server NTS with
# the ntsservercert and ntsserverkey directives below, the following line will
# allow the client/server to save the NTS keys and cookies in order to reduce
# the number of key establishments (NTS-KE sessions).
ntsdumpdir /var/lib/chrony
# If chronyd is configured to act as an NTP server and you want to enable NTS
# for its clients, you will need a TLS certificate and private key. Uncomment
# and edit the following lines to specify the locations of the certificate and
# key.
! ntsservercert /etc/.../foo.example.net.crt
! ntsserverkey /etc/.../foo.example.net.key
# chronyd can save the measurement history for the servers to files when
# it exits. This is useful in 2 situations:
# it it exits. This is useful in 2 situations:
#
# 1. If you stop chronyd and restart it with the '-r' option (e.g. after
# 1. On Linux, if you stop chronyd and restart it with '-r' (e.g. after
# an upgrade), the old measurements will still be relevant when chronyd
# is restarted. This will reduce the time needed to get accurate
# gain/loss measurements.
# gain/loss measurements, especially with a dial-up link.
#
# 2. On Linux, if you use the RTC support and start chronyd with
# 2. Again on Linux, if you use the RTC support and start chronyd with
# '-r -s' on bootup, measurements from the last boot will still be
# useful (the real time clock is used to 'flywheel' chronyd between
# boots).
#
# Uncomment the following line to use this.
# Enable these two options to use this.
! dumponexit
! dumpdir /var/lib/chrony
# chronyd writes its process ID to a file. If you try to start a second
@@ -145,18 +116,6 @@ ntsdumpdir /var/lib/chrony
! makestep 1.0 3
#######################################################################
### LEAP SECONDS
# A leap second is an occasional one-second correction of the UTC
# time scale. By default, chronyd tells the kernel to insert/delete
# the leap second, which makes a backward/forward step to correct the
# clock for it. As with the makestep directive, this jump can upset
# some applications. If you prefer chronyd to make a gradual
# correction, causing the clock to be off for a longer time, uncomment
# the following line.
! leapsecmode slew
#######################################################################
### LOGGING
# If you want to log information about the time measurements chronyd has
@@ -176,6 +135,8 @@ ntsdumpdir /var/lib/chrony
#######################################################################
### ACTING AS AN NTP SERVER
# You might want the computer to be an NTP server for other computers.
# e.g. you might be running chronyd on a dial-up machine that has a LAN
# sitting behind it with several 'satellite' computers on it.
#
# By default, chronyd does not allow any clients to access it. You need
# to explicitly enable access using 'allow' and 'deny' directives.
@@ -191,6 +152,15 @@ ntsdumpdir /var/lib/chrony
# You can have as many allow and deny directives as you need. The order
# is unimportant.
# If you want chronyd to act as an NTP broadcast server, enable and edit
# (and maybe copy) the following line. This means that a broadcast
# packet is sent to the address 192.168.1.255 every 60 seconds. The
# address MUST correspond to the broadcast address of one of the network
# interfaces on your machine. If you have multiple network interfaces,
# add a broadcast line for each.
! broadcast 60 192.168.1.255
# If you want to present your computer's time for others to synchronise
# with, even if you don't seem to be synchronised to any NTP servers
# yourself, enable the following line. The value 10 may be varied

View File

@@ -11,5 +11,3 @@
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
#4 AES128 HEX:2DA837C4B6573748CA692B8C828E4891
#5 AES256 HEX:2666B8099BFF2D5BA20876121788ED24D2BE59111B8FFB562F0F56AE6EC7246E

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -1,49 +0,0 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to update
# its NTP sources with servers from DHCP options passed by NetworkManager
# in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
export LC_ALL=C
interface=$1
action=$2
chronyc=/usr/bin/chronyc
server_options=iburst
server_dir=/var/run/chrony-dhcp
dhcp_server_file=$server_dir/$interface.sources
dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
add_servers_from_dhcp() {
rm -f "$dhcp_server_file"
for server in $dhcp_ntp_servers; do
# Check for invalid characters (from the DHCPv6 NTP FQDN suboption)
len1=$(printf '%s' "$server" | wc -c)
len2=$(printf '%s' "$server" | tr -d -c 'A-Za-z0-9:.-' | wc -c)
if [ "$len1" -ne "$len2" ] || [ "$len2" -lt 1 ] || [ "$len2" -gt 255 ]; then
continue
fi
printf 'server %s %s\n' "$server" "$server_options" >> "$dhcp_server_file"
done
$chronyc reload sources > /dev/null 2>&1 || :
}
clear_servers_from_dhcp() {
if [ -f "$dhcp_server_file" ]; then
rm -f "$dhcp_server_file"
$chronyc reload sources > /dev/null 2>&1 || :
fi
}
mkdir -p $server_dir
case "$action" in
up|dhcp4-change|dhcp6-change)
add_servers_from_dhcp;;
down)
clear_servers_from_dhcp;;
esac
exit 0

View File

@@ -1,29 +0,0 @@
#!/bin/sh
# This is a NetworkManager dispatcher / networkd-dispatcher script for
# chronyd to set its NTP sources online or offline when a network interface
# is configured or removed
export LC_ALL=C
chronyc=/usr/bin/chronyc
# For NetworkManager consider only selected events
if [ $# -ge 2 ]; then
case "$2" in
up|down|connectivity-change)
;;
dhcp4-change|dhcp6-change)
# Actions "up" and "connectivity-change" in some cases do not
# guarantee that the interface has a route (e.g. a bond).
# dhcp(x)-change handles at least cases that use DHCP.
;;
*)
exit 0;;
esac
fi
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
$chronyc onoffline > /dev/null 2>&1
exit 0

View File

@@ -1,58 +0,0 @@
# This is a more restricted version of the chronyd service intended for
# minimal NTP/NTS client configurations. The daemon is started without root
# privileges and is allowed to write only to its own runtime, state, and log
# directories. It cannot bind to privileged ports in order to operate as an
# NTP server, or provide monitoring access over IPv4/IPv6. It cannot use
# reference clocks, HW timestamping, RTC tracking, and other features.
[Unit]
Description=NTP client (restricted)
Documentation=man:chronyd(8) man:chrony.conf(5)
After=chronyd.service ntpdate.service sntp.service ntpd.service
Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd -U $OPTIONS
User=chrony
LogsDirectory=chrony
LogsDirectoryMode=0750
RuntimeDirectory=chrony
RuntimeDirectoryMode=0750
RuntimeDirectoryPreserve=restart
StateDirectory=chrony
StateDirectoryMode=0750
AmbientCapabilities=CAP_SYS_TIME
CapabilityBoundingSet=CAP_SYS_TIME
DevicePolicy=closed
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
# This breaks adjtimex()
#PrivateUsers=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io
SystemCallFilter=~@reboot @resources @swap
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@@ -10,39 +10,9 @@ Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_MKNOD CAP_SYS_ADMIN
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_PACCT
CapabilityBoundingSet=~CAP_SYS_PTRACE CAP_SYS_RAWIO CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
DeviceAllow=char-pps rw
DeviceAllow=char-ptp rw
DeviceAllow=char-rtc rw
DevicePolicy=closed
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateTmp=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
ReadWritePaths=/run /var/lib/chrony -/var/log
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @swap
# Adjust restrictions for /usr/sbin/sendmail (mailonchange directive)
NoNewPrivileges=no
ReadWritePaths=-/var/spool
RestrictAddressFamilies=AF_NETLINK
ProtectSystem=full
[Install]
WantedBy=multi-user.target

View File

@@ -448,9 +448,9 @@ o_merid : /* NULL */
the same signature as the function definition does. */
#include "getdate.h"
extern struct tm *gmtime (const time_t *timep);
extern struct tm *localtime (const time_t *timep);
extern time_t mktime (struct tm *tm);
extern struct tm *gmtime ();
extern struct tm *localtime ();
extern time_t mktime ();
/* Month and day table. */
static TABLE const MonthDayTable[] = {
@@ -641,13 +641,16 @@ static TABLE const MilitaryTable[] = {
/* ARGSUSED */
static int
yyerror (char *s ATTRIBUTE_UNUSED)
yyerror (s)
char *s ATTRIBUTE_UNUSED;
{
return 0;
}
static int
ToHour (int Hours, MERIDIAN Meridian)
ToHour (Hours, Meridian)
int Hours;
MERIDIAN Meridian;
{
switch (Meridian)
{
@@ -674,7 +677,8 @@ ToHour (int Hours, MERIDIAN Meridian)
}
static int
ToYear (int Year)
ToYear (Year)
int Year;
{
if (Year < 0)
Year = -Year;
@@ -690,7 +694,8 @@ ToYear (int Year)
}
static int
LookupWord (char *buff)
LookupWord (buff)
char *buff;
{
register char *p;
register char *q;

24
hash.h
View File

@@ -31,26 +31,12 @@
/* length of hash values produced by SHA512 */
#define MAX_HASH_LENGTH 64
typedef enum {
HSH_INVALID = 0,
HSH_MD5 = 1,
HSH_SHA1 = 2,
HSH_SHA256 = 3,
HSH_SHA384 = 4,
HSH_SHA512 = 5,
HSH_SHA3_224 = 6,
HSH_SHA3_256 = 7,
HSH_SHA3_384 = 8,
HSH_SHA3_512 = 9,
HSH_TIGER = 10,
HSH_WHIRLPOOL = 11,
HSH_MD5_NONCRYPTO = 10000, /* For NTPv4 reference ID */
} HSH_Algorithm;
extern int HSH_GetHashId(const char *name);
extern int HSH_GetHashId(HSH_Algorithm algorithm);
extern int HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len);
extern unsigned int HSH_Hash(int id,
const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len);
extern void HSH_Finalise(void);

View File

@@ -1,145 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
Crypto hashing using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "hash.h"
#include "logging.h"
struct hash {
const HSH_Algorithm algorithm;
const gnutls_digest_algorithm_t type;
gnutls_hash_hd_t handle;
};
static struct hash hashes[] = {
{ HSH_MD5_NONCRYPTO, GNUTLS_DIG_MD5, NULL },
{ HSH_MD5, GNUTLS_DIG_MD5, NULL },
{ HSH_SHA1, GNUTLS_DIG_SHA1, NULL },
{ HSH_SHA256, GNUTLS_DIG_SHA256, NULL },
{ HSH_SHA384, GNUTLS_DIG_SHA384, NULL },
{ HSH_SHA512, GNUTLS_DIG_SHA512, NULL },
{ HSH_SHA3_224, GNUTLS_DIG_SHA3_224, NULL },
{ HSH_SHA3_256, GNUTLS_DIG_SHA3_256, NULL },
{ HSH_SHA3_384, GNUTLS_DIG_SHA3_384, NULL },
{ HSH_SHA3_512, GNUTLS_DIG_SHA3_512, NULL },
{ 0, 0, NULL }
};
static int gnutls_initialised = 0;
int
HSH_GetHashId(HSH_Algorithm algorithm)
{
int id, r;
if (!gnutls_initialised) {
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
gnutls_initialised = 1;
}
for (id = 0; hashes[id].algorithm != 0; id++) {
if (hashes[id].algorithm == algorithm)
break;
}
if (hashes[id].algorithm == 0)
return -1;
if (hashes[id].handle)
return id;
if (algorithm == HSH_MD5_NONCRYPTO)
GNUTLS_FIPS140_SET_LAX_MODE();
r = gnutls_hash_init(&hashes[id].handle, hashes[id].type);
if (algorithm == HSH_MD5_NONCRYPTO)
GNUTLS_FIPS140_SET_STRICT_MODE();
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "hash", gnutls_strerror(r));
hashes[id].handle = NULL;
return -1;
}
return id;
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
gnutls_hash_hd_t handle;
int hash_len;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
handle = hashes[id].handle;
hash_len = gnutls_hash_get_len(hashes[id].type);
if (out_len > hash_len)
out_len = hash_len;
if (hash_len > sizeof (buf))
return 0;
if (gnutls_hash(handle, in1, in1_len) < 0 ||
(in2 && gnutls_hash(handle, in2, in2_len) < 0)) {
/* Reset the state */
gnutls_hash_output(handle, buf);
return 0;
}
gnutls_hash_output(handle, buf);
memcpy(out, buf, out_len);
return out_len;
}
void
HSH_Finalise(void)
{
int i;
if (!gnutls_initialised)
return;
for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].handle)
gnutls_hash_deinit(hashes[i].handle, NULL);
}
gnutls_global_deinit();
}

View File

@@ -36,22 +36,20 @@
static MD5_CTX ctx;
int
HSH_GetHashId(HSH_Algorithm algorithm)
HSH_GetHashId(const char *name)
{
/* only MD5 is supported */
if (algorithm != HSH_MD5 && algorithm != HSH_MD5_NONCRYPTO)
if (strcmp(name, "MD5"))
return -1;
return 0;
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
unsigned int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
MD5Init(&ctx);
MD5Update(&ctx, in1, in1_len);
if (in2)

View File

@@ -35,39 +35,37 @@
#include "memory.h"
struct hash {
const HSH_Algorithm algorithm;
const char *name;
const char *int_name;
const struct nettle_hash *nettle_hash;
void *context;
};
static struct hash hashes[] = {
{ HSH_MD5, "md5", NULL, NULL },
{ HSH_SHA1, "sha1", NULL, NULL },
{ HSH_SHA256, "sha256", NULL, NULL },
{ HSH_SHA384, "sha384", NULL, NULL },
{ HSH_SHA512, "sha512", NULL, NULL },
{ HSH_SHA3_224, "sha3_224", NULL, NULL },
{ HSH_SHA3_256, "sha3_256", NULL, NULL },
{ HSH_SHA3_384, "sha3_384", NULL, NULL },
{ HSH_SHA3_512, "sha3_512", NULL, NULL },
{ 0, NULL, NULL, NULL }
{ "MD5", "md5", NULL, NULL },
{ "RMD160", "ripemd160", NULL, NULL },
{ "SHA1", "sha1", NULL, NULL },
{ "SHA256", "sha256", NULL, NULL },
{ "SHA384", "sha384", NULL, NULL },
{ "SHA512", "sha512", NULL, NULL },
{ "SHA3-224", "sha3_224", NULL, NULL },
{ "SHA3-256", "sha3_256", NULL, NULL },
{ "SHA3-384", "sha3_384", NULL, NULL },
{ "SHA3-512", "sha3_512", NULL, NULL },
{ NULL, NULL, NULL, NULL }
};
int
HSH_GetHashId(HSH_Algorithm algorithm)
HSH_GetHashId(const char *name)
{
int id, nid;
if (algorithm == HSH_MD5_NONCRYPTO)
algorithm = HSH_MD5;
for (id = 0; hashes[id].algorithm != 0; id++) {
if (hashes[id].algorithm == algorithm)
for (id = 0; hashes[id].name; id++) {
if (!strcmp(name, hashes[id].name))
break;
}
if (hashes[id].algorithm == 0)
if (!hashes[id].name)
return -1;
if (hashes[id].context)
@@ -87,16 +85,14 @@ HSH_GetHashId(HSH_Algorithm algorithm)
return id;
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
unsigned int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
const struct nettle_hash *hash;
void *context;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
hash = hashes[id].nettle_hash;
context = hashes[id].context;
@@ -117,7 +113,7 @@ HSH_Finalise(void)
{
int i;
for (i = 0; hashes[i].algorithm != 0; i++) {
for (i = 0; hashes[i].name; i++) {
if (hashes[i].context)
Free(hashes[i].context);
}

View File

@@ -38,33 +38,30 @@ static NSSLOWInitContext *ictx;
struct hash {
HASH_HashType type;
HSH_Algorithm algorithm;
const char *name;
NSSLOWHASHContext *context;
};
static struct hash hashes[] = {
{ HASH_AlgMD5, HSH_MD5, NULL },
{ HASH_AlgSHA1, HSH_SHA1, NULL },
{ HASH_AlgSHA256, HSH_SHA256, NULL },
{ HASH_AlgSHA384, HSH_SHA384, NULL },
{ HASH_AlgSHA512, HSH_SHA512, NULL },
{ 0, 0, NULL }
{ HASH_AlgMD5, "MD5", NULL },
{ HASH_AlgSHA1, "SHA1", NULL },
{ HASH_AlgSHA256, "SHA256", NULL },
{ HASH_AlgSHA384, "SHA384", NULL },
{ HASH_AlgSHA512, "SHA512", NULL },
{ 0, NULL, NULL }
};
int
HSH_GetHashId(HSH_Algorithm algorithm)
HSH_GetHashId(const char *name)
{
int i;
if (algorithm == HSH_MD5_NONCRYPTO)
algorithm = HSH_MD5;
for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].algorithm == algorithm)
for (i = 0; hashes[i].name; i++) {
if (!strcmp(name, hashes[i].name))
break;
}
if (hashes[i].algorithm == 0)
if (!hashes[i].name)
return -1; /* not found */
if (!ictx && !(ictx = NSSLOW_Init()))
@@ -77,16 +74,14 @@ HSH_GetHashId(HSH_Algorithm algorithm)
return i;
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
unsigned int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
unsigned int ret = 0;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
NSSLOWHASH_Begin(hashes[id].context);
NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
if (in2)
@@ -104,7 +99,7 @@ HSH_Finalise(void)
{
int i;
for (i = 0; hashes[i].algorithm != 0; i++) {
for (i = 0; hashes[i].name; i++) {
if (hashes[i].context)
NSSLOWHASH_Destroy(hashes[i].context);
}

View File

@@ -32,54 +32,63 @@
#include "util.h"
struct hash {
HSH_Algorithm algorithm;
const char *name;
const char *int_name;
const struct ltc_hash_descriptor *desc;
};
static const struct hash hashes[] = {
{ HSH_MD5, "md5", &md5_desc },
{ "MD5", "md5", &md5_desc },
#ifdef LTC_RIPEMD128
{ "RMD128", "rmd128", &rmd128_desc },
#endif
#ifdef LTC_RIPEMD160
{ "RMD160", "rmd160", &rmd160_desc },
#endif
#ifdef LTC_RIPEMD256
{ "RMD256", "rmd256", &rmd256_desc },
#endif
#ifdef LTC_RIPEMD320
{ "RMD320", "rmd320", &rmd320_desc },
#endif
#ifdef LTC_SHA1
{ HSH_SHA1, "sha1", &sha1_desc },
{ "SHA1", "sha1", &sha1_desc },
#endif
#ifdef LTC_SHA256
{ HSH_SHA256, "sha256", &sha256_desc },
{ "SHA256", "sha256", &sha256_desc },
#endif
#ifdef LTC_SHA384
{ HSH_SHA384, "sha384", &sha384_desc },
{ "SHA384", "sha384", &sha384_desc },
#endif
#ifdef LTC_SHA512
{ HSH_SHA512, "sha512", &sha512_desc },
{ "SHA512", "sha512", &sha512_desc },
#endif
#ifdef LTC_SHA3
{ HSH_SHA3_224, "sha3-224", &sha3_224_desc },
{ HSH_SHA3_256, "sha3-256", &sha3_256_desc },
{ HSH_SHA3_384, "sha3-384", &sha3_384_desc },
{ HSH_SHA3_512, "sha3-512", &sha3_512_desc },
{ "SHA3-224", "sha3-224", &sha3_224_desc },
{ "SHA3-256", "sha3-256", &sha3_256_desc },
{ "SHA3-384", "sha3-384", &sha3_384_desc },
{ "SHA3-512", "sha3-512", &sha3_512_desc },
#endif
#ifdef LTC_TIGER
{ HSH_TIGER, "tiger", &tiger_desc },
{ "TIGER", "tiger", &tiger_desc },
#endif
#ifdef LTC_WHIRLPOOL
{ HSH_WHIRLPOOL, "whirlpool", &whirlpool_desc },
{ "WHIRLPOOL", "whirlpool", &whirlpool_desc },
#endif
{ 0, NULL, NULL }
{ NULL, NULL, NULL }
};
int
HSH_GetHashId(HSH_Algorithm algorithm)
HSH_GetHashId(const char *name)
{
int i, h;
if (algorithm == HSH_MD5_NONCRYPTO)
algorithm = HSH_MD5;
for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].algorithm == algorithm)
for (i = 0; hashes[i].name; i++) {
if (!strcmp(name, hashes[i].name))
break;
}
if (hashes[i].algorithm == 0)
if (!hashes[i].name)
return -1; /* not found */
h = find_hash(hashes[i].int_name);
@@ -92,17 +101,15 @@ HSH_GetHashId(HSH_Algorithm algorithm)
return find_hash(hashes[i].int_name);
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
unsigned int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
unsigned long len;
int r;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
len = sizeof (buf);
if (in2)
r = hash_memory_multi(id, buf, &len,

111
hwclock.c
View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2018, 2022
* Copyright (C) Miroslav Lichvar 2016-2018
*
* 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
@@ -33,7 +33,6 @@
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
@@ -44,13 +43,6 @@
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
/* Quantiles for filtering readings by delay */
#define DELAY_QUANT_MIN_K 1
#define DELAY_QUANT_MAX_K 2
#define DELAY_QUANT_Q 10
#define DELAY_QUANT_REPEAT 7
#define DELAY_QUANT_MIN_STEP 1.0e-9
struct HCL_Instance_Record {
/* HW and local reference timestamp */
struct timespec hw_ref;
@@ -72,18 +64,12 @@ struct HCL_Instance_Record {
/* Minimum interval between samples */
double min_separation;
/* Expected precision of readings */
double precision;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
double frequency;
/* Estimated quantiles of reading delay */
QNT_Instance delay_quants;
};
/* ================================================== */
@@ -106,7 +92,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
HCL_Instance
HCL_CreateInstance(int min_samples, int max_samples, double min_separation, double precision)
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
{
HCL_Instance clock;
@@ -124,10 +110,6 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation, doub
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
clock->precision = precision;
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
DELAY_QUANT_MIN_STEP);
LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -139,7 +121,6 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation, doub
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
QNT_DestroyInstance(clock->delay_quants);
Free(clock->y_data);
Free(clock->x_data);
Free(clock);
@@ -159,94 +140,6 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
/* ================================================== */
int
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err)
{
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
double delay_sum, hw_sum, local_sum, local_prec, freq;
int i, min_reading, combined;
struct timespec ts1, ts2;
if (n_readings < 1)
return 0;
/* Work out the current correction multiplier needed to get cooked delays */
LCL_CookTime(&tss[0][0], &ts1, NULL);
LCL_CookTime(&tss[n_readings - 1][2], &ts2, NULL);
if (UTI_CompareTimespecs(&tss[0][0], &tss[n_readings - 1][2]) < 0)
freq = UTI_DiffTimespecsToDouble(&ts1, &ts2) /
UTI_DiffTimespecsToDouble(&tss[0][0], &tss[n_readings - 1][2]);
else
freq = 1.0;
for (i = 0; i < n_readings; i++) {
delay = freq * UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
if (delay < 0.0) {
/* Step in the middle of a reading? */
DEBUG_LOG("Bad reading delay=%e", delay);
return 0;
}
if (i == 0 || min_delay > delay) {
min_delay = delay;
min_reading = i;
}
QNT_Accumulate(clock->delay_quants, delay);
}
local_prec = LCL_GetSysPrecisionAsQuantum();
low_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MIN_K);
high_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MAX_K);
low_delay = MIN(low_delay, high_delay);
high_delay = MAX(high_delay, low_delay + local_prec);
/* Combine readings with delay in the expected interval */
for (i = combined = 0, delay_sum = hw_sum = local_sum = 0.0; i < n_readings; i++) {
raw_delay = UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
delay = freq * raw_delay;
if (delay < low_delay || delay > high_delay)
continue;
delay_sum += delay;
hw_sum += UTI_DiffTimespecsToDouble(&tss[i][1], &tss[0][1]);
local_sum += UTI_DiffTimespecsToDouble(&tss[i][0], &tss[0][0]) + raw_delay / 2.0;
combined++;
}
DEBUG_LOG("Combined %d readings lo=%e hi=%e", combined, low_delay, high_delay);
if (combined > 0) {
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
*err = MAX(delay_sum / combined / 2.0, clock->precision);
return 1;
}
/* Accept the reading with minimum delay if its interval does not contain
the current offset predicted from previous samples */
*hw_ts = tss[min_reading][1];
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
*err = MAX(min_delay / 2.0, clock->precision);
pred_err = 0.0;
LCL_CookTime(local_ts, &ts1, NULL);
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err);
return 1;
}
return 0;
}
/* ================================================== */
void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err)

View File

@@ -30,7 +30,7 @@ typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
double min_separation, double precision);
double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
@@ -38,11 +38,6 @@ 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 */
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err);
/* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);

238
keys.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2012-2016, 2019-2020
* Copyright (C) Miroslav Lichvar 2012-2016
*
* 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
@@ -32,7 +32,6 @@
#include "array.h"
#include "keys.h"
#include "cmac.h"
#include "cmdparse.h"
#include "conf.h"
#include "memory.h"
@@ -43,23 +42,12 @@
/* Consider 80 bits as the absolute minimum for a secure key */
#define MIN_SECURE_KEY_LENGTH 10
typedef enum {
NTP_MAC,
CMAC,
} KeyClass;
typedef struct {
uint32_t id;
int type;
int length;
KeyClass class;
union {
struct {
unsigned char *value;
int hash_id;
} ntp_mac;
CMC_Instance cmac;
} data;
char *val;
int len;
int hash_id;
int auth_delay;
} Key;
static ARR_Instance keys;
@@ -74,21 +62,9 @@ static void
free_keys(void)
{
unsigned int i;
Key *key;
for (i = 0; i < ARR_GetSize(keys); i++) {
key = ARR_GetElement(keys, i);
switch (key->class) {
case NTP_MAC:
Free(key->data.ntp_mac.value);
break;
case CMAC:
CMC_DestroyInstance(key->data.cmac);
break;
default:
assert(0);
}
}
for (i = 0; i < ARR_GetSize(keys); i++)
Free(((Key *)ARR_GetElement(keys, i))->val);
ARR_SetSize(keys, 0);
cache_valid = 0;
@@ -122,18 +98,62 @@ get_key(unsigned int index)
}
/* ================================================== */
/* Decode key encoded in ASCII or HEX */
static int
decode_key(char *key)
determine_hash_delay(uint32_t key_id)
{
int len = strlen(key);
NTP_Packet pkt;
struct timespec before, after;
double diff, min_diff;
int i, nsecs;
memset(&pkt, 0, sizeof (pkt));
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH,
(unsigned char *)&pkt.auth_data, sizeof (pkt.auth_data));
LCL_ReadRawTime(&after);
diff = UTI_DiffTimespecsToDouble(&after, &before);
if (i == 0 || min_diff > diff)
min_diff = diff;
}
/* Add on a bit extra to allow for copying, conversions etc */
nsecs = 1.0625e9 * min_diff;
DEBUG_LOG("authentication delay for key %"PRIu32": %d nsecs", key_id, nsecs);
return nsecs;
}
/* ================================================== */
/* Decode password encoded in ASCII or HEX */
static int
decode_password(char *key)
{
int i, j, len = strlen(key);
char buf[3], *p;
if (!strncmp(key, "ASCII:", 6)) {
memmove(key, key + 6, len - 6);
return len - 6;
} else if (!strncmp(key, "HEX:", 4)) {
return UTI_HexToBytes(key + 4, key, len);
if ((len - 4) % 2)
return 0;
for (i = 0, j = 4; j + 1 < len; i++, j += 2) {
buf[0] = key[j], buf[1] = key[j + 1], buf[2] = '\0';
key[i] = strtol(buf, &p, 16);
if (p != buf + 2)
return 0;
}
return i;
} else {
/* assume ASCII */
return len;
@@ -165,13 +185,11 @@ compare_keys_by_id(const void *a, const void *b)
void
KEY_Reload(void)
{
unsigned int i, line_number, key_length, cmac_key_length;
unsigned int i, line_number;
FILE *in;
char line[2048], *key_file, *key_value;
const char *key_type;
HSH_Algorithm hash_algorithm;
CMC_Algorithm cmac_algorithm;
int hash_id;
uint32_t key_id;
char line[2048], *keyval, *key_file;
const char *hashname;
Key key;
free_keys();
@@ -182,10 +200,7 @@ KEY_Reload(void)
if (!key_file)
return;
if (!UTI_CheckFilePermissions(key_file, 0771))
;
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
in = fopen(key_file, "r");
if (!in) {
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
return;
@@ -198,56 +213,26 @@ KEY_Reload(void)
if (!*line)
continue;
memset(&key, 0, sizeof (key));
if (!CPS_ParseKey(line, &key.id, &key_type, &key_value)) {
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}
key_length = decode_key(key_value);
if (key_length == 0) {
LOG(LOGS_WARN, "Could not decode key %"PRIu32, key.id);
key.hash_id = HSH_GetHashId(hashname);
if (key.hash_id < 0) {
LOG(LOGS_WARN, "Unknown hash function in key %"PRIu32, key_id);
continue;
}
hash_algorithm = UTI_HashNameToAlgorithm(key_type);
cmac_algorithm = UTI_CmacNameToAlgorithm(key_type);
if (hash_algorithm != 0) {
hash_id = HSH_GetHashId(hash_algorithm);
if (hash_id < 0) {
LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "hash function", key.id);
continue;
}
key.class = NTP_MAC;
key.type = hash_algorithm;
key.length = key_length;
key.data.ntp_mac.value = MallocArray(unsigned char, key_length);
memcpy(key.data.ntp_mac.value, key_value, key_length);
key.data.ntp_mac.hash_id = hash_id;
} else if (cmac_algorithm != 0) {
cmac_key_length = CMC_GetKeyLength(cmac_algorithm);
if (cmac_key_length == 0) {
LOG(LOGS_WARN, "Unsupported %s in key %"PRIu32, "cipher", key.id);
continue;
} else if (cmac_key_length != key_length) {
LOG(LOGS_WARN, "Invalid length of %s key %"PRIu32" (expected %u bits)",
key_type, key.id, 8 * cmac_key_length);
continue;
}
key.class = CMAC;
key.type = cmac_algorithm;
key.length = key_length;
key.data.cmac = CMC_CreateInstance(cmac_algorithm, (unsigned char *)key_value,
key_length);
assert(key.data.cmac);
} else {
LOG(LOGS_WARN, "Invalid type in key %"PRIu32, key.id);
key.len = decode_password(keyval);
if (!key.len) {
LOG(LOGS_WARN, "Could not decode password in key %"PRIu32, key_id);
continue;
}
key.id = key_id;
key.val = MallocArray(char, key.len);
memcpy(key.val, keyval, key.len);
ARR_AppendElement(keys, &key);
}
@@ -258,8 +243,6 @@ KEY_Reload(void)
more careful! */
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
LOG(LOGS_INFO, "Loaded %u symmetric keys", ARR_GetSize(keys));
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)
@@ -268,6 +251,9 @@ KEY_Reload(void)
/* Erase any passwords from stack */
memset(line, 0, sizeof (line));
for (i = 0; i < ARR_GetSize(keys); i++)
get_key(i)->auth_delay = determine_hash_delay(get_key(i)->id);
}
/* ================================================== */
@@ -323,6 +309,21 @@ KEY_KeyKnown(uint32_t key_id)
/* ================================================== */
int
KEY_GetAuthDelay(uint32_t key_id)
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
return key->auth_delay;
}
/* ================================================== */
int
KEY_GetAuthLength(uint32_t key_id)
{
@@ -334,15 +335,7 @@ KEY_GetAuthLength(uint32_t key_id)
if (!key)
return 0;
switch (key->class) {
case NTP_MAC:
return HSH_Hash(key->data.ntp_mac.hash_id, buf, 0, buf, 0, buf, sizeof (buf));
case CMAC:
return CMC_Hash(key->data.cmac, buf, 0, buf, sizeof (buf));
default:
assert(0);
return 0;
}
return HSH_Hash(key->hash_id, buf, 0, buf, 0, buf, sizeof (buf));
}
/* ================================================== */
@@ -357,53 +350,30 @@ KEY_CheckKeyLength(uint32_t key_id)
if (!key)
return 0;
return key->length >= MIN_SECURE_KEY_LENGTH;
}
/* ================================================== */
int
KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits)
{
Key *key;
key = get_key_by_id(key_id);
if (!key)
return 0;
*type = key->type;
*bits = 8 * key->length;
return 1;
return key->len >= MIN_SECURE_KEY_LENGTH;
}
/* ================================================== */
static int
generate_auth(Key *key, const void *data, int data_len, unsigned char *auth, int auth_len)
generate_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
{
switch (key->class) {
case NTP_MAC:
return HSH_Hash(key->data.ntp_mac.hash_id, key->data.ntp_mac.value,
key->length, data, data_len, auth, auth_len);
case CMAC:
return CMC_Hash(key->data.cmac, data, data_len, auth, auth_len);
default:
return 0;
}
return HSH_Hash(hash_id, key, key_len, data, data_len, auth, auth_len);
}
/* ================================================== */
static int
check_auth(Key *key, const void *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
check_ntp_auth(int hash_id, const unsigned char *key, int key_len,
const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
hash_len = generate_ntp_auth(hash_id, key, key_len, data, data_len, buf, sizeof (buf));
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
}
@@ -411,8 +381,8 @@ check_auth(Key *key, const void *data, int data_len,
/* ================================================== */
int
KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
unsigned char *auth, int auth_len)
KEY_GenerateAuth(uint32_t key_id, const unsigned char *data, int data_len,
unsigned char *auth, int auth_len)
{
Key *key;
@@ -421,13 +391,14 @@ KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
if (!key)
return 0;
return generate_auth(key, data, data_len, auth, auth_len);
return generate_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
data, data_len, auth, auth_len);
}
/* ================================================== */
int
KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len)
{
Key *key;
@@ -437,5 +408,6 @@ KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
if (!key)
return 0;
return check_auth(key, data, data_len, auth, auth_len, trunc_len);
return check_ntp_auth(key->hash_id, (unsigned char *)key->val, key->len,
data, data_len, auth, auth_len, trunc_len);
}

9
keys.h
View File

@@ -34,14 +34,15 @@ extern void KEY_Finalise(void);
extern void KEY_Reload(void);
extern int KEY_GetKey(uint32_t key_id, char **key, int *len);
extern int KEY_KeyKnown(uint32_t key_id);
extern int KEY_GetAuthDelay(uint32_t key_id);
extern int KEY_GetAuthLength(uint32_t key_id);
extern int KEY_CheckKeyLength(uint32_t key_id);
extern int KEY_GetKeyInfo(uint32_t key_id, int *type, int *bits);
extern int KEY_GenerateAuth(uint32_t key_id, const void *data, int data_len,
unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const void *data, int data_len,
extern int KEY_GenerateAuth(uint32_t key_id, const unsigned char *data,
int data_len, unsigned char *auth, int auth_len);
extern int KEY_CheckAuth(uint32_t key_id, const unsigned char *data, int data_len,
const unsigned char *auth, int auth_len, int trunc_len);
#endif /* GOT_KEYS_H */

85
local.c
View File

@@ -108,8 +108,8 @@ static double max_clock_error;
#define NSEC_PER_SEC 1000000000
static double
measure_clock_precision(void)
static void
calculate_sys_precision(void)
{
struct timespec ts, old_ts;
int iters, diff, best;
@@ -135,7 +135,18 @@ measure_clock_precision(void)
assert(best > 0);
return 1.0e-9 * best;
precision_quantum = 1.0e-9 * best;
/* Get rounded log2 value of the measured precision */
precision_log = 0;
while (best < 707106781) {
precision_log--;
best *= 2;
}
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
}
/* ================================================== */
@@ -159,16 +170,7 @@ LCL_Initialise(void)
current_freq_ppm = 0.0;
temp_comp_ppm = 0.0;
precision_quantum = CNF_GetClockPrecision();
if (precision_quantum <= 0.0)
precision_quantum = measure_clock_precision();
precision_quantum = CLAMP(1.0e-9, precision_quantum, 1.0);
precision_log = round(log(precision_quantum) / log(2.0));
/* NTP code doesn't support smaller log than -30 */
assert(precision_log >= -30);
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
calculate_sys_precision();
/* This is the maximum allowed frequency offset in ppm, the time must
never stop or run backwards */
@@ -183,11 +185,13 @@ LCL_Initialise(void)
void
LCL_Finalise(void)
{
/* Make sure all handlers have been removed */
if (change_list.next != &change_list)
assert(0);
if (dispersion_notify_list.next != &dispersion_notify_list)
assert(0);
while (change_list.next != &change_list)
LCL_RemoveParameterChangeHandler(change_list.next->handler,
change_list.next->anything);
while (dispersion_notify_list.next != &dispersion_notify_list)
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
dispersion_notify_list.next->anything);
}
/* ================================================== */
@@ -505,7 +509,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
/* ================================================== */
int
void
LCL_AccumulateOffset(double offset, double corr_rate)
{
struct timespec raw, cooked;
@@ -517,14 +521,12 @@ LCL_AccumulateOffset(double offset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, offset))
return 0;
return;
(*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
return 1;
}
/* ================================================== */
@@ -563,8 +565,6 @@ void
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
LCL_CancelOffsetCorrection();
/* Dispatch to all handlers */
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
@@ -590,7 +590,7 @@ LCL_NotifyLeap(int leap)
/* ================================================== */
int
void
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{
struct timespec raw, cooked;
@@ -602,7 +602,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, doffset))
return 0;
return;
old_freq_ppm = current_freq_ppm;
@@ -624,26 +624,6 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
return 1;
}
/* ================================================== */
int
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *first_handler;
int r;
first_handler = change_list.next;
change_list.next = &change_list;
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
change_list.next = first_handler;
return r;
}
/* ================================================== */
@@ -711,19 +691,6 @@ LCL_MakeStep(void)
/* ================================================== */
void
LCL_CancelOffsetCorrection(void)
{
struct timespec raw;
double correction;
LCL_ReadRawTime(&raw);
LCL_GetOffsetCorrection(&raw, &correction, NULL);
LCL_AccumulateOffset(correction, 0.0);
}
/* ================================================== */
int
LCL_CanSystemLeap(void)
{

12
local.h
View File

@@ -149,7 +149,7 @@ extern void LCL_AccumulateDeltaFrequency(double dfreq);
forwards (i.e. it is currently slow of true time). Provided is also
a suggested correction rate (correction time * offset). */
extern int LCL_AccumulateOffset(double offset, double corr_rate);
extern void LCL_AccumulateOffset(double offset, double corr_rate);
/* Routine to apply an immediate offset by doing a sudden step if
possible. (Intended for use after an initial estimate of offset has
@@ -171,12 +171,7 @@ extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying
a slew, in one easy step */
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
/* Same as the routine above, except it does not call the registered
parameter change handlers */
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
double corr_rate);
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
/* Routine to read the system precision as a log to base 2 value. */
extern int LCL_GetSysPrecisionAsLog(void);
@@ -202,9 +197,6 @@ extern void LCL_Finalise(void);
to a timezone problem. */
extern int LCL_MakeStep(void);
/* Routine to cancel the outstanding system clock correction */
extern void LCL_CancelOffsetCorrection(void);
/* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap
does something */
extern int LCL_CanSystemLeap(void);

114
logging.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014, 2018-2020
* Copyright (C) Miroslav Lichvar 2011-2014, 2018
*
* 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
@@ -29,28 +29,26 @@
#include "sysincl.h"
#include <syslog.h>
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "util.h"
/* This is used by DEBUG_LOG macro */
LOG_Severity log_min_severity = LOGS_INFO;
/* Current logging contexts */
static LOG_Context log_contexts;
int log_debug_enabled = 0;
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
static FILE *file_log = NULL;
static FILE *file_log;
static int system_log = 0;
static int parent_fd = 0;
#define DEBUG_LEVEL_PRINT_FUNCTION 2
#define DEBUG_LEVEL_PRINT_DEBUG 2
static int debug_level = 0;
struct LogFile {
const char *name;
const char *banner;
@@ -65,20 +63,14 @@ static int n_filelogs = 0;
static struct LogFile logfiles[MAX_FILELOGS];
/* Global prefix for debug messages */
static char *debug_prefix;
/* ================================================== */
/* Init function */
void
LOG_Initialise(void)
{
debug_prefix = Strdup("");
log_contexts = 0;
initialised = 1;
LOG_OpenFileLog(NULL);
file_log = stderr;
}
/* ================================================== */
@@ -95,8 +87,6 @@ LOG_Finalise(void)
LOG_CycleLogFiles();
Free(debug_prefix);
initialised = 0;
}
@@ -144,9 +134,7 @@ void LOG_Message(LOG_Severity severity,
time_t t;
struct tm *tm;
assert(initialised);
if (!system_log && file_log && severity >= log_min_severity) {
if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
tm = gmtime(&t);
@@ -155,8 +143,8 @@ void LOG_Message(LOG_Severity severity,
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG)
fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name);
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
#endif
}
@@ -169,12 +157,10 @@ void LOG_Message(LOG_Severity severity,
case LOGS_INFO:
case LOGS_WARN:
case LOGS_ERR:
if (severity >= log_min_severity)
log_message(0, severity, buf);
log_message(0, severity, buf);
break;
case LOGS_FATAL:
if (severity >= log_min_severity)
log_message(1, severity, buf);
log_message(1, severity, buf);
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
@@ -200,7 +186,9 @@ LOG_OpenFileLog(const char *log_file)
FILE *f;
if (log_file) {
f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
f = fopen(log_file, "a");
if (!f)
LOG_FATAL("Could not open log file %s", log_file);
} else {
f = stderr;
}
@@ -226,51 +214,12 @@ LOG_OpenSystemLog(void)
/* ================================================== */
void LOG_SetMinSeverity(LOG_Severity severity)
void LOG_SetDebugLevel(int level)
{
/* Don't print any debug messages in a non-debug build */
log_min_severity = CLAMP(DEBUG > 0 ? LOGS_DEBUG : LOGS_INFO, severity, LOGS_FATAL);
}
/* ================================================== */
LOG_Severity
LOG_GetMinSeverity(void)
{
return log_min_severity;
}
/* ================================================== */
void
LOG_SetContext(LOG_Context context)
{
log_contexts |= context;
}
/* ================================================== */
void
LOG_UnsetContext(LOG_Context context)
{
log_contexts &= ~context;
}
/* ================================================== */
LOG_Severity
LOG_GetContextSeverity(LOG_Context contexts)
{
return log_contexts & contexts ? LOGS_INFO : LOGS_DEBUG;
}
/* ================================================== */
void
LOG_SetDebugPrefix(const char *prefix)
{
Free(debug_prefix);
debug_prefix = Strdup(prefix);
debug_level = level;
if (level >= DEBUG_LEVEL_PRINT_DEBUG) {
log_debug_enabled = 1;
}
}
/* ================================================== */
@@ -298,10 +247,7 @@ LOG_CloseParentFd()
LOG_FileID
LOG_FileOpen(const char *name, const char *banner)
{
if (n_filelogs >= MAX_FILELOGS) {
assert(0);
return -1;
}
assert(n_filelogs < MAX_FILELOGS);
logfiles[n_filelogs].name = name;
logfiles[n_filelogs].banner = banner;
@@ -323,20 +269,24 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
return;
if (!logfiles[id].file) {
char *logdir = CNF_GetLogDir();
char filename[512], *logdir = CNF_GetLogDir();
if (!logdir) {
if (logdir[0] == '\0') {
LOG(LOGS_WARN, "logdir not specified");
logfiles[id].name = NULL;
return;
}
logfiles[id].file = UTI_OpenFile(logdir, logfiles[id].name, ".log", 'a', 0644);
if (!logfiles[id].file) {
/* Disable the log */
if (snprintf(filename, sizeof(filename), "%s/%s.log",
logdir, logfiles[id].name) >= sizeof (filename) ||
!(logfiles[id].file = fopen(filename, "a"))) {
LOG(LOGS_WARN, "Could not open log file %s", filename);
logfiles[id].name = NULL;
return;
}
/* Close on exec */
UTI_FdSetCloexec(fileno(logfiles[id].file));
}
banner = CNF_GetLogBanner();
@@ -344,7 +294,7 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
char bannerline[256];
int i, bannerlen;
bannerlen = MIN(strlen(logfiles[id].banner), sizeof (bannerline) - 1);
bannerlen = strlen(logfiles[id].banner);
for (i = 0; i < bannerlen; i++)
bannerline[i] = '=';

View File

@@ -31,6 +31,9 @@
#include "sysincl.h"
/* Flag indicating whether debug messages are logged */
extern int log_debug_enabled;
/* Line logging macros. If the compiler is GNU C, we take advantage of
being able to get the function name also. */
@@ -52,7 +55,7 @@
#define DEBUG_LOG(...) \
do { \
if (DEBUG && log_min_severity == LOGS_DEBUG) \
if (DEBUG && log_debug_enabled) \
LOG_MESSAGE(LOGS_DEBUG, __VA_ARGS__); \
} while (0)
@@ -66,16 +69,13 @@
/* Definition of severity */
typedef enum {
LOGS_DEBUG = -1,
LOGS_INFO = 0,
LOGS_INFO,
LOGS_WARN,
LOGS_ERR,
LOGS_FATAL,
LOGS_DEBUG
} LOG_Severity;
/* Minimum severity of messages to be logged */
extern LOG_Severity log_min_severity;
/* Init function */
extern void LOG_Initialise(void);
@@ -92,30 +92,12 @@ FORMAT_ATTRIBUTE_PRINTF(2, 3)
extern void LOG_Message(LOG_Severity severity, const char *format, ...);
#endif
/* Set the minimum severity of a message to be logged or printed to terminal.
If the severity is LOGS_DEBUG and DEBUG is enabled, all messages will be
prefixed with the filename, line number, and function name. */
extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void);
/* Flags for info messages that should be logged only in specific contexts */
typedef enum {
LOGC_Command = 1,
LOGC_SourceFile = 2,
} LOG_Context;
/* Modify current contexts */
extern void LOG_SetContext(LOG_Context context);
extern void LOG_UnsetContext(LOG_Context context);
/* Get severity depending on the current active contexts: INFO if they contain
at least one of the specified contexts, DEBUG otherwise */
extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
/* Set a prefix for debug messages */
extern void LOG_SetDebugPrefix(const char *prefix);
/* Set debug level:
0, 1 - only non-debug messages are logged
2 - debug messages are logged too, all messages are prefixed with
filename, line, and function name
*/
extern void LOG_SetDebugLevel(int level);
/* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file);

138
main.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) John G. Hasler 2009
* Copyright (C) Miroslav Lichvar 2012-2020
* Copyright (C) Miroslav Lichvar 2012-2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -38,9 +38,6 @@
#include "ntp_signd.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "nts_ke_server.h"
#include "nts_ntp_server.h"
#include "socket.h"
#include "sources.h"
#include "sourcestats.h"
#include "reference.h"
@@ -76,18 +73,11 @@ static REF_Mode ref_mode = REF_ModeNormal;
static void
do_platform_checks(void)
{
struct timespec ts;
/* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4);
assert(-1 == ~0);
assert((int32_t)4294967295U == (int32_t)-1);
/* Require time_t and tv_nsec in timespec to be signed */
ts.tv_sec = -1;
ts.tv_nsec = -1;
assert(ts.tv_sec < 0 && ts.tv_nsec < 0);
}
/* ================================================== */
@@ -97,11 +87,11 @@ delete_pidfile(void)
{
const char *pidfile = CNF_GetPidFile();
if (!pidfile)
if (!pidfile[0])
return;
if (!UTI_RemoveFile(NULL, pidfile, NULL))
;
/* Don't care if this fails, there's not a lot we can do */
unlink(pidfile);
}
/* ================================================== */
@@ -111,8 +101,9 @@ MAI_CleanupAndExit(void)
{
if (!initialised) exit(exit_status);
LCL_CancelOffsetCorrection();
SRC_DumpSources();
if (CNF_GetDumpDir()[0] != '\0') {
SRC_DumpSources();
}
/* Don't update clock when removing sources */
REF_SetMode(REF_ModeIgnore);
@@ -121,23 +112,18 @@ MAI_CleanupAndExit(void)
TMC_Finalise();
MNL_Finalise();
CLG_Finalise();
NKS_Finalise();
NNS_Finalise();
NSD_Finalise();
NSR_Finalise();
SST_Finalise();
NCR_Finalise();
NIO_Finalise();
CAM_Finalise();
KEY_Finalise();
RCL_Finalise();
SRC_Finalise();
REF_Finalise();
RTC_Finalise();
SYS_Finalise();
SCK_Finalise();
SCH_Finalise();
LCL_Finalise();
PRV_Finalise();
@@ -148,8 +134,6 @@ MAI_CleanupAndExit(void)
HSH_Finalise();
LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(exit_status);
}
@@ -158,6 +142,7 @@ MAI_CleanupAndExit(void)
static void
signal_cleanup(int x)
{
if (!initialised) exit(0);
SCH_QuitProgram();
}
@@ -166,8 +151,6 @@ signal_cleanup(int x)
static void
quit_timeout(void *arg)
{
LOG(LOGS_INFO, "Timeout reached");
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();
@@ -195,7 +178,7 @@ ntp_source_resolving_end(void)
NSR_AutoStartSources();
/* Special modes can end only when sources update their reachability.
Give up immediately if there are no active sources. */
Give up immediatelly if there are no active sources. */
if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
REF_SetUnsynchronised();
}
@@ -270,10 +253,7 @@ check_pidfile(void)
FILE *in;
int pid, count;
if (!pidfile)
return;
in = UTI_OpenFile(NULL, pidfile, NULL, 'r', 0);
in = fopen(pidfile, "r");
if (!in)
return;
@@ -298,7 +278,7 @@ write_pidfile(void)
const char *pidfile = CNF_GetPidFile();
FILE *out;
if (!pidfile)
if (!pidfile[0])
return;
out = UTI_OpenFile(NULL, pidfile, NULL, 'W', 0644);
@@ -331,9 +311,6 @@ go_daemon(void)
char message[1024];
int r;
/* Don't exit before the 'parent' */
waitpid(pid, NULL, 0);
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
if (r) {
@@ -356,9 +333,7 @@ go_daemon(void)
if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
/* In the 'parent' */
close(pipefd[1]);
exit(0);
exit(0); /* In the 'parent' */
} else {
/* In the child we want to leave running as the daemon */
@@ -391,34 +366,8 @@ go_daemon(void)
static void
print_help(const char *progname)
{
printf("Usage: %s [OPTION]... [DIRECTIVE]...\n\n"
"Options:\n"
" -4\t\tUse IPv4 addresses only\n"
" -6\t\tUse IPv6 addresses only\n"
" -f FILE\tSpecify configuration file (%s)\n"
" -n\t\tDon't run as daemon\n"
" -d\t\tDon't run as daemon and log to stderr\n"
#if DEBUG > 0
" -d -d\t\tEnable debug messages\n"
#endif
" -l FILE\tLog to file\n"
" -L LEVEL\tSet logging threshold (0)\n"
" -p\t\tPrint configuration and exit\n"
" -q\t\tSet clock and exit\n"
" -Q\t\tLog offset and exit\n"
" -r\t\tReload dump files\n"
" -R\t\tAdapt configuration for restart\n"
" -s\t\tSet clock from RTC\n"
" -t SECONDS\tExit after elapsed time\n"
" -u USER\tSpecify user (%s)\n"
" -U\t\tDon't check for root\n"
" -F LEVEL\tSet system call filter level (0)\n"
" -P PRIORITY\tSet process priority (0)\n"
" -m\t\tLock memory\n"
" -x\t\tDon't control clock\n"
" -v, --version\tPrint version and exit\n"
" -h, --help\tPrint usage and exit\n",
progname, DEFAULT_CONF_FILE, DEFAULT_USER);
printf("Usage: %s [-4|-6] [-n|-d] [-q|-Q] [-r] [-R] [-s] [-t TIMEOUT] [-f FILE|COMMAND...]\n",
progname);
}
/* ================================================== */
@@ -451,16 +400,16 @@ int main
char *user = NULL, *log_file = NULL;
struct passwd *pw;
int opt, debug = 0, nofork = 0, address_family = IPADDR_UNSPEC;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = -1;
int do_init_rtc = 0, restarted = 0, client_only = 0, timeout = 0;
int scfilter_level = 0, lock_memory = 0, sched_priority = 0;
int clock_control = 1, system_log = 1, log_severity = LOGS_INFO;
int user_check = 1, config_args = 0, print_config = 0;
int clock_control = 1, system_log = 1;
int config_args = 0;
do_platform_checks();
LOG_Initialise();
/* Parse long command-line options */
/* Parse (undocumented) long command-line options */
for (optind = 1; optind < argc; optind++) {
if (!strcmp("--help", argv[optind])) {
print_help(progname);
@@ -474,7 +423,7 @@ int main
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "46df:F:hl:L:mnpP:qQrRst:u:Uvx")) != -1) {
while ((opt = getopt(argc, argv, "46df:F:hl:mnP:qQrRst:u:vx")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -494,22 +443,12 @@ int main
case 'l':
log_file = optarg;
break;
case 'L':
log_severity = parse_int_arg(optarg);
break;
case 'm':
lock_memory = 1;
break;
case 'n':
nofork = 1;
break;
case 'p':
print_config = 1;
user_check = 0;
nofork = 1;
system_log = 0;
log_severity = LOGS_WARN;
break;
case 'P':
sched_priority = parse_int_arg(optarg);
break;
@@ -523,7 +462,6 @@ int main
ref_mode = REF_ModePrintOnce;
nofork = 1;
client_only = 1;
user_check = 0;
clock_control = 0;
system_log = 0;
break;
@@ -542,9 +480,6 @@ int main
case 'u':
user = optarg;
break;
case 'U':
user_check = 0;
break;
case 'v':
print_version();
return 0;
@@ -557,7 +492,7 @@ int main
}
}
if (user_check && getuid() != 0)
if (getuid() && !client_only)
LOG_FATAL("Not superuser");
/* Turn into a daemon */
@@ -571,15 +506,13 @@ int main
LOG_OpenSystemLog();
}
LOG_SetMinSeverity(debug >= 2 ? LOGS_DEBUG : log_severity);
LOG_SetDebugLevel(debug);
LOG(LOGS_INFO, "chronyd version %s starting (%s)", CHRONY_VERSION, CHRONYD_FEATURES);
DNS_SetAddressFamily(address_family);
CNF_Initialise(restarted, client_only);
if (print_config)
CNF_EnablePrint();
/* Parse the config file or the remaining command line arguments */
config_args = argc - optind;
@@ -590,9 +523,6 @@ int main
CNF_ParseLine(NULL, config_args + optind - argc + 1, argv[optind]);
}
if (print_config)
return 0;
/* Check whether another chronyd may already be running */
check_pidfile();
@@ -612,11 +542,6 @@ int main
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
SCK_Initialise(address_family);
/* Start helper processes if needed */
NKS_PreInitialise(pw->pw_uid, pw->pw_gid, scfilter_level);
SYS_Initialise(clock_control);
RTC_Initialise(do_init_rtc);
SRC_Initialise();
@@ -624,8 +549,8 @@ int main
KEY_Initialise();
/* Open privileged ports before dropping root */
CAM_Initialise();
NIO_Initialise();
CAM_Initialise(address_family);
NIO_Initialise(address_family);
NCR_Initialise();
CNF_SetupAccessRestrictions();
@@ -642,22 +567,13 @@ int main
}
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
/* Warn if missing read access or having write access to keys */
CNF_CheckReadOnlyAccess();
}
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
REF_Initialise();
SST_Initialise();
NSR_Initialise();
NSD_Initialise();
NNS_Initialise();
NKS_Initialise();
CLG_Initialise();
MNL_Initialise();
TMC_Initialise();
@@ -671,7 +587,7 @@ int main
CAM_OpenUnixSocket();
if (scfilter_level)
SYS_EnableSystemCallFilter(scfilter_level, SYS_MAIN_PROCESS);
SYS_EnableSystemCallFilter(scfilter_level);
if (ref_mode == REF_ModeNormal && CNF_GetInitSources() > 0) {
ref_mode = REF_ModeInitStepSlew;
@@ -680,7 +596,7 @@ int main
REF_SetModeEndHandler(reference_mode_end);
REF_SetMode(ref_mode);
if (timeout >= 0)
if (timeout > 0)
SCH_AddTimeoutByDelay(timeout, quit_timeout, NULL);
if (do_init_rtc) {

View File

@@ -92,7 +92,6 @@ MNL_Initialise(void)
void
MNL_Finalise(void)
{
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
}
/* ================================================== */

15
md5.c
View File

@@ -117,7 +117,8 @@ inline UINT4 ROTATE_LEFT(UINT4 x, int n)
/* The routine MD5Init initializes the message-digest context
mdContext. All fields are set to zero.
*/
void MD5Init (MD5_CTX *mdContext)
void MD5Init (mdContext)
MD5_CTX *mdContext;
{
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
@@ -133,7 +134,10 @@ void MD5Init (MD5_CTX *mdContext)
account for the presence of each of the characters inBuf[0..inLen-1]
in the message whose digest is being computed.
*/
void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
void MD5Update (mdContext, inBuf, inLen)
MD5_CTX *mdContext;
unsigned const char *inBuf;
unsigned int inLen;
{
UINT4 in[16];
int mdi;
@@ -169,7 +173,8 @@ void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inL
ends with the desired message digest in mdContext->digest[0...15].
*/
void MD5Final (MD5_CTX *mdContext)
void MD5Final (mdContext)
MD5_CTX *mdContext;
{
UINT4 in[16];
int mdi;
@@ -209,7 +214,9 @@ void MD5Final (MD5_CTX *mdContext)
/* Basic MD5 step. Transforms buf based on in.
*/
static void Transform (UINT4 *buf, UINT4 *in)
static void Transform (buf, in)
UINT4 *buf;
UINT4 *in;
{
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];

View File

@@ -47,13 +47,8 @@ Realloc(void *ptr, size_t size)
{
void *r;
if (size == 0) {
Free(ptr);
return NULL;
}
r = realloc(ptr, size);
if (!r)
if (!r && size)
LOG_FATAL("Could not allocate memory");
return r;

View File

@@ -34,7 +34,6 @@
#include <resolv.h>
#include "nameserv.h"
#include "socket.h"
#include "util.h"
/* ================================================== */
@@ -50,24 +49,12 @@ DNS_SetAddressFamily(int family)
DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai;
int i, result;
IPAddr ip;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
for (i = 0; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
/* Avoid calling getaddrinfo() if the name is an IP address */
if (UTI_StringToIP(name, &ip)) {
if (address_family != IPADDR_UNSPEC && ip.family != address_family)
return DNS_Failure;
if (max_addrs >= 1)
ip_addrs[0] = ip;
return DNS_Success;
}
memset(&hints, 0, sizeof (hints));
switch (address_family) {
@@ -82,7 +69,7 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
default:
hints.ai_family = AF_UNSPEC;
}
hints.ai_socktype = SOCK_DGRAM;
hints.ai_socktype = SOCK_STREAM;
result = getaddrinfo(name, NULL, &hints, &res);
@@ -107,9 +94,6 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
case AF_INET6:
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET6)
continue;
/* Don't return an address that would lose a scope ID */
if (((struct sockaddr_in6 *)ai->ai_addr)->sin6_scope_id != 0)
continue;
ip_addrs[i].family = IPADDR_INET6;
memcpy(&ip_addrs[i].addr.in6, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
sizeof (ip_addrs->addr.in6));
@@ -119,9 +103,48 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
}
}
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
freeaddrinfo(res);
return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure;
#else
struct hostent *host;
int i;
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
return DNS_Failure;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
host = gethostbyname(name);
if (host == NULL) {
if (h_errno == TRY_AGAIN)
return DNS_TryAgain;
} else {
if (host->h_addrtype != AF_INET || !host->h_addr_list[0])
return DNS_Failure;
for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) {
ip_addrs[i].family = IPADDR_INET4;
ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]);
}
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
return DNS_Success;
}
#ifdef FORCE_DNSRETRY
return DNS_TryAgain;
#else
return DNS_Failure;
#endif
#endif
}
/* ================================================== */
@@ -130,21 +153,35 @@ int
DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
{
char *result = NULL;
#ifdef FEAT_IPV6
struct sockaddr_in6 saddr;
#else
struct sockaddr_in saddr;
#endif
IPSockAddr ip_saddr;
struct sockaddr_in6 in6;
socklen_t slen;
char hbuf[NI_MAXHOST];
ip_saddr.ip_addr = *ip_addr;
ip_saddr.port = 0;
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&saddr, sizeof (saddr));
if (!getnameinfo((struct sockaddr *)&saddr, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
slen = UTI_IPAndPortToSockaddr(ip_addr, 0, (struct sockaddr *)&in6);
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf;
#else
struct hostent *host;
uint32_t addr;
switch (ip_addr->family) {
case IPADDR_INET4:
addr = htonl(ip_addr->addr.in4);
host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET);
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6);
break;
#endif
default:
host = NULL;
}
if (host)
result = host->h_name;
#endif
if (result == NULL)
result = UTI_IPToString(ip_addr);

View File

@@ -51,7 +51,7 @@ struct DNS_Async_Instance {
int pipe[2];
};
static pthread_mutex_t privops_lock = PTHREAD_MUTEX_INITIALIZER;
static int resolving_threads = 0;
/* ================================================== */
@@ -60,9 +60,7 @@ start_resolving(void *anything)
{
struct DNS_Async_Instance *inst = (struct DNS_Async_Instance *)anything;
pthread_mutex_lock(&privops_lock);
inst->status = PRV_Name2IPAddress(inst->name, inst->addresses, DNS_MAX_ADDRESSES);
pthread_mutex_unlock(&privops_lock);
/* Notify the main thread that the result is ready */
if (write(inst->pipe[1], "", 1) < 0)
@@ -83,6 +81,8 @@ end_resolving(int fd, int event, void *anything)
LOG_FATAL("pthread_join() failed");
}
resolving_threads--;
SCH_RemoveFileHandler(inst->pipe[0]);
close(inst->pipe[0]);
close(inst->pipe[1]);
@@ -116,6 +116,9 @@ DNS_Name2IPAddressAsync(const char *name, DNS_NameResolveHandler handler, void *
UTI_FdSetCloexec(inst->pipe[0]);
UTI_FdSetCloexec(inst->pipe[1]);
resolving_threads++;
assert(resolving_threads <= 1);
if (pthread_create(&inst->thread, NULL, start_resolving, inst)) {
LOG_FATAL("pthread_create() failed");
}

94
ntp.h
View File

@@ -47,21 +47,18 @@ typedef uint32_t NTP_int32;
/* Maximum stratum number (infinity) */
#define NTP_MAX_STRATUM 16
/* Invalid stratum number */
#define NTP_INVALID_STRATUM 0
/* The minimum valid length of an extension field */
#define NTP_MIN_EXTENSION_LENGTH 16
/* The maximum assumed length of all extension fields in received
packets (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH 1024
/* The minimum and maximum supported length of MAC */
#define NTP_MIN_MAC_LENGTH (4 + 16)
#define NTP_MAX_MAC_LENGTH (4 + MAX_HASH_LENGTH)
/* The minimum valid length of an extension field */
#define NTP_MIN_EF_LENGTH 16
/* The maximum assumed length of all extension fields in an NTP packet,
including a MAC (RFC 5905 doesn't specify a limit on length or number of
extension fields in one packet) */
#define NTP_MAX_EXTENSIONS_LENGTH (1024 + NTP_MAX_MAC_LENGTH)
/* The maximum length of MAC in NTPv4 packets which allows deterministic
parsing of extension fields (RFC 7822) */
#define NTP_MAX_V4_MAC_LENGTH (4 + 20)
@@ -96,10 +93,21 @@ typedef struct {
NTP_int64 receive_ts;
NTP_int64 transmit_ts;
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
/* Optional extension fields, we don't send packets with them yet */
/* uint8_t extensions[] */
/* Optional message authentication code (MAC) */
NTP_int32 auth_keyid;
uint8_t auth_data[NTP_MAX_MAC_LENGTH - 4];
} NTP_Packet;
#define NTP_HEADER_LENGTH (int)offsetof(NTP_Packet, extensions)
#define NTP_NORMAL_PACKET_LENGTH (int)offsetof(NTP_Packet, auth_keyid)
/* The buffer used to hold a datagram read from the network */
typedef struct {
NTP_Packet ntp_pkt;
uint8_t extensions[NTP_MAX_EXTENSIONS_LENGTH];
} NTP_Receive_Buffer;
/* Macros to work with the lvm field */
#define NTP_LVM_TO_LEAP(lvm) (((lvm) >> 6) & 0x3)
@@ -113,59 +121,6 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Non-authentication extension fields and corresponding internal flags */
#define NTP_EF_EXP1 0xF323
#define NTP_EF_FLAG_EXP1 0x1
/* Pre-NTPv5 experimental extension field */
typedef struct {
uint32_t magic;
NTP_int32 root_delay;
NTP_int32 root_dispersion;
NTP_int64 mono_receive_ts;
uint32_t mono_epoch;
} NTP_ExtFieldExp1;
#define NTP_EF_EXP1_MAGIC 0xF5BEDD9AU
/* Authentication extension fields */
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
#define NTP_EF_NTS_COOKIE 0x0204
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
/* Enumeration for authentication modes of NTP packets */
typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */
NTP_AUTH_SYMMETRIC, /* NTP MAC or CMAC using a symmetric key
(RFC 1305, RFC 5905, RFC 8573) */
NTP_AUTH_MSSNTP, /* MS-SNTP authenticator field */
NTP_AUTH_MSSNTP_EXT, /* MS-SNTP extended authenticator field */
NTP_AUTH_NTS, /* Network Time Security (RFC 8915) */
} NTP_AuthMode;
/* Structure describing an NTP packet */
typedef struct {
int length;
int version;
NTP_Mode mode;
int ext_fields;
int ext_field_flags;
struct {
NTP_AuthMode mode;
struct {
int start;
int length;
uint32_t key_id;
} mac;
} auth;
} NTP_PacketInfo;
/* Structure used to save NTP measurements. time is the local time at which
the sample is to be considered to have been made and offset is the offset at
the time (positive indicates that the local clock is slow relative to the
@@ -177,13 +132,8 @@ typedef struct {
double peer_dispersion;
double root_delay;
double root_dispersion;
int stratum;
NTP_Leap leap;
} NTP_Sample;
/* Possible sources of timestamps */
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
#endif /* GOT_NTP_H */

View File

@@ -1,386 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019-2020
*
* 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.
*
**********************************************************************
=======================================================================
NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "keys.h"
#include "logging.h"
#include "memory.h"
#include "ntp_auth.h"
#include "ntp_signd.h"
#include "nts_ntp.h"
#include "nts_ntp_client.h"
#include "nts_ntp_server.h"
#include "srcparams.h"
#include "util.h"
/* Structure to hold authentication configuration and state */
struct NAU_Instance_Record {
NTP_AuthMode mode; /* Authentication mode of NTP packets */
uint32_t key_id; /* Identifier of a symmetric key */
NNC_Instance nts; /* Client NTS state */
};
/* ================================================== */
static int
generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info)
{
int auth_len, max_auth_len;
if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) {
DEBUG_LOG("Packet too long");
return 0;
}
/* Truncate long MACs in NTPv4 packets to allow deterministic parsing
of extension fields (RFC 7822) */
max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4;
max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4);
auth_len = KEY_GenerateAuth(key_id, packet, info->length,
(unsigned char *)packet + info->length + 4, max_auth_len);
if (auth_len < NTP_MIN_MAC_LENGTH - 4) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0;
}
*(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
info->auth.mac.start = info->length;
info->auth.mac.length = 4 + auth_len;
info->auth.mac.key_id = key_id;
info->length += info->auth.mac.length;
return 1;
}
/* ================================================== */
static int
check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
{
int trunc_len;
if (info->auth.mac.length < NTP_MIN_MAC_LENGTH)
return 0;
trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ?
NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH;
if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start,
(unsigned char *)packet + info->auth.mac.start + 4,
info->auth.mac.length - 4, trunc_len - 4))
return 0;
return 1;
}
/* ================================================== */
static NAU_Instance
create_instance(NTP_AuthMode mode)
{
NAU_Instance instance;
instance = MallocNew(struct NAU_Instance_Record);
instance->mode = mode;
instance->key_id = INACTIVE_AUTHKEY;
instance->nts = NULL;
assert(sizeof (instance->key_id) == 4);
return instance;
}
/* ================================================== */
NAU_Instance
NAU_CreateNoneInstance(void)
{
return create_instance(NTP_AUTH_NONE);
}
/* ================================================== */
NAU_Instance
NAU_CreateSymmetricInstance(uint32_t key_id)
{
NAU_Instance instance = create_instance(NTP_AUTH_SYMMETRIC);
instance->key_id = key_id;
if (!KEY_KeyKnown(key_id))
LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "missing");
else if (!KEY_CheckKeyLength(key_id))
LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "too short");
return instance;
}
/* ================================================== */
NAU_Instance
NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set,
uint16_t ntp_port)
{
NAU_Instance instance = create_instance(NTP_AUTH_NTS);
instance->nts = NNC_CreateInstance(nts_address, name, cert_set, ntp_port);
return instance;
}
/* ================================================== */
void
NAU_DestroyInstance(NAU_Instance instance)
{
if (instance->mode == NTP_AUTH_NTS)
NNC_DestroyInstance(instance->nts);
Free(instance);
}
/* ================================================== */
int
NAU_IsAuthEnabled(NAU_Instance instance)
{
return instance->mode != NTP_AUTH_NONE;
}
/* ================================================== */
int
NAU_GetSuggestedNtpVersion(NAU_Instance instance)
{
/* If the MAC in NTPv4 packets would be truncated, prefer NTPv3 for
compatibility with older chronyd servers */
if (instance->mode == NTP_AUTH_SYMMETRIC &&
KEY_GetAuthLength(instance->key_id) + sizeof (instance->key_id) > NTP_MAX_V4_MAC_LENGTH)
return 3;
return NTP_VERSION;
}
/* ================================================== */
int
NAU_PrepareRequestAuth(NAU_Instance instance)
{
switch (instance->mode) {
case NTP_AUTH_NTS:
if (!NNC_PrepareForAuth(instance->nts))
return 0;
break;
default:
break;
}
return 1;
}
/* ================================================== */
int
NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info)
{
switch (instance->mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
if (!generate_symmetric_auth(instance->key_id, request, info))
return 0;
break;
case NTP_AUTH_NTS:
if (!NNC_GenerateRequestAuth(instance->nts, request, info))
return 0;
break;
default:
assert(0);
}
info->auth.mode = instance->mode;
return 1;
}
/* ================================================== */
int
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
{
*kod = 0;
switch (info->auth.mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
if (!check_symmetric_auth(request, info))
return 0;
break;
case NTP_AUTH_MSSNTP:
/* MS-SNTP requests are not authenticated */
break;
case NTP_AUTH_MSSNTP_EXT:
/* Not supported yet */
return 0;
case NTP_AUTH_NTS:
if (!NNS_CheckRequestAuth(request, info, kod))
return 0;
break;
default:
return 0;
}
return 1;
}
/* ================================================== */
int
NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
uint32_t kod)
{
switch (request_info->auth.mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
if (!generate_symmetric_auth(request_info->auth.mac.key_id, response, response_info))
return 0;
break;
case NTP_AUTH_MSSNTP:
/* Sign the packet asynchronously by ntp_signd */
if (!NSD_SignAndSendPacket(request_info->auth.mac.key_id, response, response_info,
remote_addr, local_addr))
return 0;
/* Don't send the original packet */
return 0;
case NTP_AUTH_NTS:
if (!NNS_GenerateResponseAuth(request, request_info, response, response_info, kod))
return 0;
break;
default:
DEBUG_LOG("Could not authenticate response auth_mode=%d", (int)request_info->auth.mode);
return 0;
}
response_info->auth.mode = request_info->auth.mode;
return 1;
}
/* ================================================== */
int
NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response, NTP_PacketInfo *info)
{
/* The authentication must match the expected mode */
if (info->auth.mode != instance->mode)
return 0;
switch (info->auth.mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
/* Check if it is authenticated with the specified key */
if (info->auth.mac.key_id != instance->key_id)
return 0;
/* and that the MAC is valid */
if (!check_symmetric_auth(response, info))
return 0;
break;
case NTP_AUTH_NTS:
if (!NNC_CheckResponseAuth(instance->nts, response, info))
return 0;
break;
default:
return 0;
}
return 1;
}
/* ================================================== */
void
NAU_ChangeAddress(NAU_Instance instance, IPAddr *address)
{
switch (instance->mode) {
case NTP_AUTH_NONE:
case NTP_AUTH_SYMMETRIC:
break;
case NTP_AUTH_NTS:
NNC_ChangeAddress(instance->nts, address);
break;
default:
assert(0);
}
}
/* ================================================== */
void
NAU_DumpData(NAU_Instance instance)
{
switch (instance->mode) {
case NTP_AUTH_NTS:
NNC_DumpData(instance->nts);
break;
default:
break;
}
}
/* ================================================== */
void
NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report)
{
memset(report, 0, sizeof (*report));
report->mode = instance->mode;
report->last_ke_ago = -1;
switch (instance->mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
report->key_id = instance->key_id;
KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length);
break;
case NTP_AUTH_NTS:
NNC_GetReport(instance->nts, report);
break;
default:
assert(0);
}
}

View File

@@ -1,84 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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 NTP authentication
*/
#ifndef GOT_NTP_AUTH_H
#define GOT_NTP_AUTH_H
#include "addressing.h"
#include "ntp.h"
#include "reports.h"
typedef struct NAU_Instance_Record *NAU_Instance;
/* Create an authenticator instance in a specific mode */
extern NAU_Instance NAU_CreateNoneInstance(void);
extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id);
extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name,
uint32_t cert_set, uint16_t ntp_port);
/* Destroy an instance */
extern void NAU_DestroyInstance(NAU_Instance instance);
/* Check if an instance is not in the None mode */
extern int NAU_IsAuthEnabled(NAU_Instance instance);
/* Get NTP version recommended for better compatibility */
extern int NAU_GetSuggestedNtpVersion(NAU_Instance instance);
/* Perform operations necessary for NAU_GenerateRequestAuth() */
extern int NAU_PrepareRequestAuth(NAU_Instance instance);
/* Extend a request with data required by the authentication mode */
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
NTP_PacketInfo *info);
/* Verify that a request is authentic. If it is not authentic and a non-zero
kod code is returned, a KoD response should be sent back. */
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
/* Extend a response with data required by the authentication mode. This
function can be called only if the previous call of NAU_CheckRequestAuth()
was on the same request. */
extern int NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info,
NTP_Packet *response, NTP_PacketInfo *response_info,
NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr,
uint32_t kod);
/* Verify that a response is authentic */
extern int NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response,
NTP_PacketInfo *info);
/* Change an authentication-specific address (e.g. after replacing a source) */
extern void NAU_ChangeAddress(NAU_Instance instance, IPAddr *address);
/* Save authentication-specific data to speed up the next start */
extern void NAU_DumpData(NAU_Instance instance);
/* Provide a report about the current authentication state */
extern void NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report);
#endif

1266
ntp_core.c

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,12 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
@@ -53,8 +59,7 @@ extern void NCR_Initialise(void);
extern void NCR_Finalise(void);
/* Get a new instance for a server or peer */
extern NCR_Instance NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name);
extern NCR_Instance NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
/* Destroy an instance */
extern void NCR_DestroyInstance(NCR_Instance instance);
@@ -69,8 +74,7 @@ extern void NCR_ResetInstance(NCR_Instance inst);
extern void NCR_ResetPoll(NCR_Instance instance);
/* Change the remote address of an instance */
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr,
int ntp_only);
extern void NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr);
/* This routine is called when a new packet arrives off the network,
and it relates to a source we have an ongoing protocol exchange with */
@@ -116,7 +120,6 @@ extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
extern void NCR_ReportSource(NCR_Instance inst, RPT_SourceReport *report, struct timespec *now);
extern void NCR_GetAuthReport(NCR_Instance inst, RPT_AuthReport *report);
extern void NCR_GetNTPReport(NCR_Instance inst, RPT_NTPReport *report);
extern int NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all);
@@ -131,8 +134,6 @@ extern uint32_t NCR_GetLocalRefid(NCR_Instance inst);
extern int NCR_IsSyncPeer(NCR_Instance instance);
extern void NCR_DumpAuthData(NCR_Instance inst);
extern void NCR_AddBroadcastDestination(NTP_Remote_Address *addr, int interval);
extern void NCR_AddBroadcastDestination(IPAddr *addr, unsigned short port, int interval);
#endif /* GOT_NTP_CORE_H */

192
ntp_ext.c
View File

@@ -1,192 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019-2020
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Functions for adding and parsing NTPv4 extension fields
*/
#include "config.h"
#include "sysincl.h"
#include "ntp_ext.h"
struct ExtFieldHeader {
uint16_t type;
uint16_t length;
};
/* ================================================== */
static int
format_field(unsigned char *buffer, int buffer_length, int start,
int type, int body_length, int *length, void **body)
{
struct ExtFieldHeader *header;
if (buffer_length < 0 || start < 0 || buffer_length <= start ||
buffer_length - start < sizeof (*header) || start % 4 != 0)
return 0;
header = (struct ExtFieldHeader *)(buffer + start);
if (body_length < 0 || sizeof (*header) + body_length > 0xffff ||
start + sizeof (*header) + body_length > buffer_length || body_length % 4 != 0)
return 0;
header->type = htons(type);
header->length = htons(sizeof (*header) + body_length);
*length = sizeof (*header) + body_length;
*body = header + 1;
return 1;
}
/* ================================================== */
int
NEF_SetField(unsigned char *buffer, int buffer_length, int start,
int type, void *body, int body_length, int *length)
{
void *ef_body;
if (!format_field(buffer, buffer_length, start, type, body_length, length, &ef_body))
return 0;
memcpy(ef_body, body, body_length);
return 1;
}
/* ================================================== */
int
NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type, int body_length, void **body)
{
int ef_length, length = info->length;
if (length < NTP_HEADER_LENGTH || length >= sizeof (*packet) || length % 4 != 0)
return 0;
/* Only NTPv4 packets can have extension fields */
if (info->version != 4)
return 0;
if (!format_field((unsigned char *)packet, sizeof (*packet), length,
type, body_length, &ef_length, body))
return 0;
if (ef_length < NTP_MIN_EF_LENGTH)
return 0;
info->length += ef_length;
info->ext_fields++;
return 1;
}
/* ================================================== */
int
NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
int type, void *body, int body_length)
{
void *ef_body;
if (!NEF_AddBlankField(packet, info, type, body_length, &ef_body))
return 0;
memcpy(ef_body, body, body_length);
return 1;
}
/* ================================================== */
int
NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
int *length, int *type, void **body, int *body_length)
{
struct ExtFieldHeader *header;
int ef_length;
if (buffer_length < 0 || start < 0 || buffer_length <= start ||
buffer_length - start < sizeof (*header))
return 0;
header = (struct ExtFieldHeader *)(buffer + start);
assert(sizeof (*header) == 4);
ef_length = ntohs(header->length);
if (ef_length < (int)(sizeof (*header)) || start + ef_length > buffer_length ||
ef_length % 4 != 0)
return 0;
if (length)
*length = ef_length;
if (type)
*type = ntohs(header->type);
if (body)
*body = header + 1;
if (body_length)
*body_length = ef_length - sizeof (*header);
return 1;
}
/* ================================================== */
int
NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
int *length, int *type, void **body, int *body_length)
{
int ef_length;
if (packet_length <= NTP_HEADER_LENGTH || packet_length > sizeof (*packet) ||
packet_length <= start || packet_length % 4 != 0 ||
start < NTP_HEADER_LENGTH || start % 4 != 0)
return 0;
/* Only NTPv4 packets have extension fields */
if (NTP_LVM_TO_VERSION(packet->lvm) != 4)
return 0;
/* Check if the remaining data is a MAC. RFC 7822 specifies the maximum
length of a MAC in NTPv4 packets in order to enable deterministic
parsing. */
if (packet_length - start <= NTP_MAX_V4_MAC_LENGTH)
return 0;
if (!NEF_ParseSingleField((unsigned char *)packet, packet_length, start,
&ef_length, type, body, body_length))
return 0;
if (ef_length < NTP_MIN_EF_LENGTH)
return 0;
if (length)
*length = ef_length;
return 1;
}

View File

@@ -1,43 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
*
* 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 NTP extension fields
*/
#ifndef GOT_NTP_EXT_H
#define GOT_NTP_EXT_H
#include "ntp.h"
extern int NEF_SetField(unsigned char *buffer, int buffer_length, int start,
int type, void *body, int body_length, int *length);
extern int NEF_AddBlankField(NTP_Packet *packet, NTP_PacketInfo *info, int type,
int body_length, void **body);
extern int NEF_AddField(NTP_Packet *packet, NTP_PacketInfo *info,
int type, void *body, int body_length);
extern int NEF_ParseSingleField(unsigned char *buffer, int buffer_length, int start,
int *length, int *type, void **body, int *body_length);
extern int NEF_ParseField(NTP_Packet *packet, int packet_length, int start,
int *length, int *type, void **body, int *body_length);
#endif

844
ntp_io.c

File diff suppressed because it is too large Load Diff

View File

@@ -31,17 +31,13 @@
#include "ntp.h"
#include "addressing.h"
#include "socket.h"
/* Function to initialise the module. */
extern void NIO_Initialise(void);
extern void NIO_Initialise(int family);
/* Function to finalise the module */
extern void NIO_Finalise(void);
/* Function to check if HW timestamping is enabled on any interface */
extern int NIO_IsHwTsEnabled(void);
/* Function to obtain a socket for sending client packets */
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr);
@@ -57,15 +53,9 @@ extern void NIO_CloseServerSocket(int sock_fd);
/* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd);
/* Function to check if a server socket is currently open */
extern int NIO_IsServerSocketOpen(void);
/* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2023
* Copyright (C) Miroslav Lichvar 2016-2019
*
* 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
@@ -29,6 +29,7 @@
#include "sysincl.h"
#include <ifaddrs.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h>
#include <linux/net_tstamp.h>
#include <linux/sockios.h>
@@ -39,16 +40,22 @@
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
#include "ntp_sources.h"
#include "sched.h"
#include "socket.h"
#include "sys_linux.h"
#include "util.h"
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
struct Interface {
char name[IF_NAMESIZE];
int if_index;
@@ -60,22 +67,21 @@ struct Interface {
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
HCL_Instance clock;
int maxpoll;
SCH_TimeoutID poll_timeout_id;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 25
#define PHC_READINGS 10
/* Minimum and maximum interval between PHC readings */
/* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6
#define MAX_PHC_POLL 20
/* Maximum acceptable offset between SW/HW and daemon timestamp */
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
#define MAX_TS_DELAY 1.0
/* Array of Interfaces */
@@ -88,6 +94,19 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after
@@ -98,17 +117,13 @@ static int dummy_rxts_socket;
/* ================================================== */
static void poll_phc(struct Interface *iface, struct timespec *now);
/* ================================================== */
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
int sock_fd, if_index, minpoll, phc_fd, req_hwts_flags, rx_filter;
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i;
struct Interface *iface;
@@ -118,7 +133,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1;
}
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
@@ -127,13 +142,13 @@ add_interface(CNF_HwTsInterface *conf_iface)
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) {
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
@@ -144,7 +159,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
@@ -152,13 +167,13 @@ add_interface(CNF_HwTsInterface *conf_iface)
SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name);
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
@@ -182,14 +197,6 @@ add_interface(CNF_HwTsInterface *conf_iface)
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
#endif
case CNF_HWTS_RXFILTER_PTP:
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT))
rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
else if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_EVENT))
rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
else
rx_filter = HWTSTAMP_FILTER_NONE;
break;
default:
rx_filter = HWTSTAMP_FILTER_ALL;
break;
@@ -201,8 +208,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
req.ifr_data = (char *)&ts_config;
if (ioctl(sock_fd, SIOCSHWTSTAMP, &req)) {
LOG(errno == EPERM ? LOGS_ERR : LOGS_DEBUG,
"ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCSHWTSTAMP", strerror(errno));
/* Check the current timestamping configuration in case this interface
allows only reading of the configuration and it was already configured
@@ -213,12 +219,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
#endif
{
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 0;
}
}
SCK_CloseSocket(sock_fd);
close(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0)
@@ -237,18 +243,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(minpoll), conf_iface->precision);
iface->maxpoll = CLAMP(minpoll, conf_iface->maxpoll, MAX_PHC_POLL);
/* Do not schedule the first poll timeout here! The argument (interface) can
move until all interfaces are added. Wait for the first HW timestamp. */
iface->poll_timeout_id = 0;
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -293,7 +293,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req;
int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return;
@@ -306,11 +306,11 @@ update_interface_speed(struct Interface *iface)
if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
SCK_CloseSocket(sock_fd);
close(sock_fd);
return;
}
SCK_CloseSocket(sock_fd);
close(sock_fd);
link_speed = ethtool_cmd_speed(&cmd);
@@ -328,16 +328,17 @@ check_timestamping_option(int option)
{
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
return 0;
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, option)) {
SCK_CloseSocket(sock_fd);
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option);
close(sock_fd);
return 0;
}
SCK_CloseSocket(sock_fd);
close(sock_fd);
return 1;
}
#endif
@@ -349,15 +350,19 @@ open_dummy_socket(void)
{
int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
#ifdef FEAT_IPV6
&& (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
#endif
)
return INVALID_SOCK_FD;
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
SCK_CloseSocket(sock_fd);
close(sock_fd);
return INVALID_SOCK_FD;
}
UTI_FdSetCloexec(sock_fd);
return sock_fd;
}
@@ -413,6 +418,8 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD;
}
@@ -425,11 +432,10 @@ NIO_Linux_Finalise(void)
unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD)
SCK_CloseSocket(dummy_rxts_socket);
close(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
SCH_RemoveTimeout(iface->poll_timeout_id);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
@@ -439,14 +445,6 @@ NIO_Linux_Finalise(void)
/* ================================================== */
int
NIO_Linux_IsHwTsEnabled(void)
{
return ARR_GetSize(interfaces) > 0;
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
@@ -464,12 +462,14 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
if (client_only || permanent_ts_options)
flags |= ts_tx_flags;
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, val)) {
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
ts_flags = 0;
return 0;
}
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, flags)) {
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0;
return 0;
}
@@ -480,6 +480,73 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
static void
resume_socket(int sock_fd)
{
if (monitored_socket == sock_fd)
monitored_socket = INVALID_SOCK_FD;
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
return;
suspended_socket = INVALID_SOCK_FD;
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
resume_timeout_id ? "before" : "on", sock_fd);
if (resume_timeout_id) {
SCH_RemoveTimeout(resume_timeout_id);
resume_timeout_id = 0;
}
}
/* ================================================== */
static void
resume_timeout(void *arg)
{
resume_timeout_id = 0;
resume_socket(suspended_socket);
}
/* ================================================== */
static void
suspend_socket(int sock_fd)
{
resume_socket(suspended_socket);
suspended_socket = sock_fd;
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
}
/* ================================================== */
int
NIO_Linux_ProcessEvent(int sock_fd, int event)
{
if (sock_fd != monitored_socket)
return 0;
if (event == SCH_FILE_INPUT) {
suspend_socket(monitored_socket);
monitored_socket = INVALID_SOCK_FD;
/* Don't process the message yet */
return 1;
}
return 0;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
@@ -499,70 +566,26 @@ get_interface(int if_index)
/* ================================================== */
static void
poll_timeout(void *arg)
{
struct Interface *iface = arg;
struct timespec now;
iface->poll_timeout_id = 0;
SCH_GetLastEventTime(&now, NULL, NULL);
poll_phc(iface, &now);
}
/* ================================================== */
static void
poll_phc(struct Interface *iface, struct timespec *now)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
struct timespec phc_readings[PHC_READINGS][3];
double phc_err, local_err, interval;
int n_readings;
if (!HCL_NeedsNewSample(iface->clock, now))
return;
DEBUG_LOG("Polling PHC on %s%s",
iface->name, iface->poll_timeout_id != 0 ? " before timeout" : "");
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
&iface->phc_mode, PHC_READINGS, phc_readings);
/* Add timeout for the next poll in case no HW timestamp will be captured
between the minpoll and maxpoll. Separate reading of different PHCs to
avoid long intervals between handling I/O events. */
SCH_RemoveTimeout(iface->poll_timeout_id);
interval = UTI_Log2ToDouble(iface->maxpoll);
iface->poll_timeout_id = SCH_AddTimeoutInClass(interval, interval /
ARR_GetSize(interfaces) / 4, 0.1,
SCH_PhcPollClass, poll_timeout, iface);
if (n_readings <= 0)
return;
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, phc_err + local_err);
update_interface_speed(iface);
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
double rx_correction, ts_delay, local_err;
struct timespec ts;
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
double rx_correction, ts_delay, phc_err, local_err;
poll_phc(iface, &local_ts->ts);
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
}
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
@@ -602,28 +625,6 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
local_ts->source = NTP_TS_HARDWARE;
}
/* ================================================== */
static void
process_sw_timestamp(struct timespec *sw_ts, NTP_Local_Timestamp *local_ts)
{
double ts_delay, local_err;
struct timespec ts;
LCL_CookTime(sw_ts, &ts, &local_err);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_KERNEL;
}
/* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */
@@ -632,6 +633,7 @@ static int
extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
{
unsigned char *msg_start = msg;
union sockaddr_in46 addr;
remote_addr->ip_addr.family = IPADDR_UNSPEC;
remote_addr->port = 0;
@@ -654,21 +656,19 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
/* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4;
uint32_t addr;
if (len < ihl + 8 || msg[9] != 17)
return 0;
memcpy(&addr, msg + 16, sizeof (addr));
remote_addr->ip_addr.addr.in4 = ntohl(addr);
remote_addr->port = ntohs(*(uint16_t *)(msg + ihl + 2));
remote_addr->ip_addr.family = IPADDR_INET4;
memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t));
addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2);
addr.in4.sin_family = AF_INET;
len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) {
int eh_len, next_header = msg[6];
memcpy(&remote_addr->ip_addr.addr.in6, msg + 24, sizeof (remote_addr->ip_addr.addr.in6));
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16);
len -= 40, msg += 40;
/* Skip IPv6 extension headers if present */
@@ -700,14 +700,16 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
len -= eh_len, msg += eh_len;
}
remote_addr->port = ntohs(*(uint16_t *)(msg + 2));
remote_addr->ip_addr.family = IPADDR_INET6;
addr.in6.sin6_port = *(uint16_t *)(msg + 2);
addr.in6.sin6_family = AF_INET6;
len -= 8, msg += 8;
#endif
} else {
return 0;
}
UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
/* Move the message to fix alignment of its fields */
if (len > 0)
memmove(msg_start, msg, len);
@@ -718,33 +720,72 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
/* ================================================== */
int
NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event)
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length)
{
struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, ts_if_index, l2_length;
is_tx = event == SCH_FILE_EXCEPTION;
is_tx = hdr->msg_flags & MSG_ERRQUEUE;
iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
ts_if_index = message->timestamp.if_index;
if (ts_if_index == INVALID_IF_INDEX)
ts_if_index = message->if_index;
l2_length = message->timestamp.l2_length;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
struct scm_ts_pktinfo ts_pktinfo;
if (!UTI_IsZeroTimespec(&message->timestamp.hw)) {
iface = get_interface(ts_if_index);
if (iface) {
process_hw_timestamp(iface, &message->timestamp.hw, local_ts, !is_tx ? message->length : 0,
message->remote_addr.ip.ip_addr.family, l2_length);
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
ts_if_index = ts_pktinfo.if_index;
l2_length = ts_pktinfo.pkt_length;
DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
}
}
#endif
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
process_sw_timestamp(&message->timestamp.kernel, local_ts);
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(ts_if_index);
if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0,
remote_addr->ip_addr.family, l2_length);
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
(!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
}
}
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
DEBUG_LOG("Unknown extended error");
/* Drop the message */
return 1;
}
}
}
/* If the kernel is slow with enabling RX timestamping, open a dummy
@@ -762,19 +803,19 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
/* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */
l2_length = message->length;
message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
DEBUG_LOG("Extracted message for %s fd=%d len=%d",
UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, message->length);
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
local_addr->sock_fd, local_addr->if_index, local_ts->source);
/* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && message->length) {
if (message->remote_addr.ip.ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - message->length;
else if (message->remote_addr.ip.ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - message->length;
if (iface && length) {
if (remote_addr->ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - length;
else if (remote_addr->ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - length;
}
/* Drop the message if it has no timestamp or its processing failed */
@@ -783,28 +824,60 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1;
}
if (!NIO_UnwrapMessage(message, local_addr->sock_fd))
if (length < NTP_NORMAL_PACKET_LENGTH)
return 1;
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
NSR_ProcessTx(remote_addr, local_addr, local_ts,
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
return 1;
}
/* ================================================== */
void
NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
int
NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
if (!ts_flags)
return;
return cmsglen;
/* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response
without the HW timestamp of the request */
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
monitored_socket = sock_fd;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return;
return cmsglen;
message->timestamp.tx_flags = ts_tx_flags;
/* Add control message that will enable TX timestamping for this message.
Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
control messages. */
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg || cmsglen + CMSG_SPACE(sizeof (ts_tx_flags)) > msg->msg_controllen)
return cmsglen;
cmsg = (struct cmsghdr *)((char *)cmsg + cmsglen);
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
return cmsglen;
}
/* ================================================== */
void
NIO_Linux_NotifySocketClosing(int sock_fd)
{
resume_socket(sock_fd);
}

View File

@@ -27,19 +27,19 @@
#ifndef GOT_NTP_IO_LINUX_H
#define GOT_NTP_IO_LINUX_H
#include "socket.h"
extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_IsHwTsEnabled(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif

View File

@@ -34,7 +34,6 @@
#include "ntp_io.h"
#include "ntp_signd.h"
#include "sched.h"
#include "socket.h"
#include "util.h"
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
@@ -91,11 +90,19 @@ static ARR_Instance queue;
static unsigned int queue_head;
static unsigned int queue_tail;
#define INVALID_SOCK_FD (-6)
#define INVALID_SOCK_FD -1
/* Unix domain socket connected to ntp_signd */
static int sock_fd;
#define MIN_AUTH_DELAY 1.0e-5
#define MAX_AUTH_DELAY 1.0e-2
/* Average time needed for signing one packet. This is used to adjust the
transmit timestamp in NTP packets. The timestamp won't be very accurate as
the delay is variable, but it should be good enough for MS-SNTP clients. */
static double auth_delay;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
@@ -109,7 +116,7 @@ static void
close_socket(void)
{
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(sock_fd);
close(sock_fd);
sock_fd = INVALID_SOCK_FD;
/* Empty the queue */
@@ -121,24 +128,36 @@ close_socket(void)
static int
open_socket(void)
{
char path[PATH_MAX];
struct sockaddr_un s;
if (sock_fd != INVALID_SOCK_FD)
if (sock_fd >= 0)
return 1;
if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
DEBUG_LOG("signd socket path too long");
return 0;
}
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(sock_fd);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
DEBUG_LOG("signd socket path too long");
close_socket();
return 0;
}
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
close_socket();
return 0;
}
DEBUG_LOG("Connected to signd");
return 1;
}
@@ -175,6 +194,10 @@ process_response(SignInstance *inst)
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
ntohl(inst->response.length) + sizeof (inst->response.length) -
offsetof(SigndResponse, signed_packet), 0);
/* Update exponential moving average of the authentication delay */
delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
auth_delay += 0.1 * (delay - auth_delay);
}
/* ================================================== */
@@ -195,14 +218,16 @@ read_write_socket(int sock_fd, int event, void *anything)
if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
s = send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0);
if (s < 0) {
DEBUG_LOG("signd socket error: %s", strerror(errno));
close_socket();
return;
}
DEBUG_LOG("Sent %d bytes to signd", s);
inst->sent += s;
/* Try again later if the request is not complete yet */
@@ -221,14 +246,20 @@ read_write_socket(int sock_fd, int event, void *anything)
}
assert(inst->received < sizeof (inst->response));
s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
s = recv(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0);
if (s <= 0) {
if (s < 0)
DEBUG_LOG("signd socket error: %s", strerror(errno));
else
DEBUG_LOG("signd socket closed");
close_socket();
return;
}
DEBUG_LOG("Received %d bytes from signd", s);
inst->received += s;
if (inst->received < sizeof (inst->response.length))
@@ -262,6 +293,7 @@ void
NSD_Initialise()
{
sock_fd = INVALID_SOCK_FD;
auth_delay = MIN_AUTH_DELAY;
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
if (!enabled)
@@ -288,9 +320,15 @@ NSD_Finalise()
/* ================================================== */
extern int NSD_GetAuthDelay(uint32_t key_id)
{
return 1.0e9 * auth_delay;
}
/* ================================================== */
int
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
{
SignInstance *inst;
@@ -304,7 +342,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
return 0;
}
if (info->length != NTP_HEADER_LENGTH) {
if (length != NTP_NORMAL_PACKET_LENGTH) {
DEBUG_LOG("Invalid packet length");
return 0;
}
@@ -317,7 +355,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
inst->local_addr = *local_addr;
inst->sent = 0;
inst->received = 0;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length;
inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
/* The length field doesn't include itself */
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
@@ -327,7 +365,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
inst->request._pad = 0;
inst->request.key_id = htonl(key_id);
memcpy(&inst->request.packet_to_sign, packet, info->length);
memcpy(&inst->request.packet_to_sign, packet, length);
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())

View File

@@ -35,8 +35,10 @@ extern void NSD_Initialise(void);
/* Finalisation function */
extern void NSD_Finalise(void);
/* Function to get an estimate of delay due to signing */
extern int NSD_GetAuthDelay(uint32_t key_id);
/* Function to sign an NTP packet and send it */
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr);
extern int NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -44,21 +44,16 @@ typedef enum {
NSR_NoSuchSource, /* Remove - attempt to remove a source that is not known */
NSR_AlreadyInUse, /* AddSource - attempt to add a source that is already known */
NSR_TooManySources, /* AddSource - too many sources already present */
NSR_InvalidAF, /* AddSource - attempt to add a source with invalid address family */
NSR_InvalidName, /* AddSourceByName - attempt to add a source with invalid name */
NSR_UnresolvedName, /* AddSourceByName - name will be resolved later */
NSR_InvalidAF /* AddSource - attempt to add a source with invalid address family */
} NSR_Status;
/* Procedure to add a new server or peer source. */
extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParameters *params);
/* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the
name is an address, it is equivalent to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
intervals until it succeeds or fails with a non-temporary error. */
extern void NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, SourceParameters *params);
/* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */
@@ -77,10 +72,7 @@ extern void NSR_StartSources(void);
extern void NSR_AutoStartSources(void);
/* Procedure to remove a source */
extern NSR_Status NSR_RemoveSource(IPAddr *address);
/* Procedure to remove all sources matching a configuration ID */
extern void NSR_RemoveSourcesById(uint32_t conf_id);
extern NSR_Status NSR_RemoveSource(NTP_Remote_Address *remote_addr);
/* Procedure to remove all sources */
extern void NSR_RemoveAllSources(void);
@@ -91,18 +83,9 @@ extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void);
/* Procedure to update the address of a source. The update may be
postponed. */
extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
NTP_Remote_Address *new_addr);
/* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* Procedure to get the name of a source as it was specified (it may be
an IP address) */
extern char *NSR_GetName(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */
extern void NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length);
@@ -141,12 +124,8 @@ extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAd
extern void NSR_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int NSR_GetAuthReport(IPAddr *address, RPT_AuthReport *report);
extern int NSR_GetNTPReport(RPT_NTPReport *report);
extern void NSR_GetActivityReport(RPT_ActivityReport *report);
extern void NSR_DumpAuthData(void);
#endif /* GOT_NTP_SOURCES_H */

View File

@@ -1,81 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 NTS Key Establishment protocol
*/
#ifndef GOT_NTS_KE_H
#define GOT_NTS_KE_H
#include "siv.h"
#define NKE_PORT 4460
#define NKE_RECORD_CRITICAL_BIT (1U << 15)
#define NKE_RECORD_END_OF_MESSAGE 0
#define NKE_RECORD_NEXT_PROTOCOL 1
#define NKE_RECORD_ERROR 2
#define NKE_RECORD_WARNING 3
#define NKE_RECORD_AEAD_ALGORITHM 4
#define NKE_RECORD_COOKIE 5
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
#define NKE_NEXT_PROTOCOL_NTPV4 0
#define NKE_ERROR_UNRECOGNIZED_CRITICAL_RECORD 0
#define NKE_ERROR_BAD_REQUEST 1
#define NKE_ERROR_INTERNAL_SERVER_ERROR 2
#define NKE_ALPN_NAME "ntske/1"
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
#define NKE_MAX_MESSAGE_LENGTH 16384
#define NKE_MAX_RECORD_BODY_LENGTH 256
#define NKE_MAX_COOKIE_LENGTH 256
#define NKE_MAX_COOKIES 8
#define NKE_MAX_KEY_LENGTH SIV_MAX_KEY_LENGTH
#define NKE_RETRY_FACTOR2_CONNECT 4
#define NKE_RETRY_FACTOR2_TLS 10
#define NKE_MAX_RETRY_INTERVAL2 19
typedef struct {
int length;
unsigned char key[NKE_MAX_KEY_LENGTH];
} NKE_Key;
typedef struct {
SIV_Algorithm algorithm;
NKE_Key c2s;
NKE_Key s2c;
} NKE_Context;
typedef struct {
int length;
unsigned char cookie[NKE_MAX_COOKIE_LENGTH];
} NKE_Cookie;
#endif

View File

@@ -1,457 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021
*
* 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.
*
**********************************************************************
=======================================================================
NTS-KE client
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_client.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "nameserv_async.h"
#include "nts_ke_session.h"
#include "siv.h"
#include "socket.h"
#include "util.h"
#define CLIENT_TIMEOUT 16.0
struct NKC_Instance_Record {
char *name;
IPSockAddr address;
NKSN_Credentials credentials;
NKSN_Instance session;
int destroying;
int got_response;
int resolving_name;
NKE_Context context;
NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
IPSockAddr ntp_address;
};
/* ================================================== */
static NKSN_Credentials default_credentials = NULL;
static int default_credentials_refs = 0;
/* ================================================== */
static void
name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg)
{
NKC_Instance inst = arg;
int i;
inst->resolving_name = 0;
if (inst->destroying) {
Free(inst);
return;
}
if (status != DNS_Success || n_addrs < 1) {
LOG(LOGS_ERR, "Could not resolve NTP server %s from %s", inst->server_name, inst->name);
/* Force restart */
inst->got_response = 0;
return;
}
inst->ntp_address.ip_addr = ip_addrs[0];
/* Prefer an address in the same family as the NTS-KE server */
for (i = 0; i < n_addrs; i++) {
DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
if (ip_addrs[i].family == inst->address.ip_addr.family) {
inst->ntp_address.ip_addr = ip_addrs[i];
break;
}
}
}
/* ================================================== */
static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
uint16_t data[2];
int length;
NKSN_BeginMessage(session);
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0;
length = 0;
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
data[length++] = htons(AEAD_AES_128_GCM_SIV);
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0])))
return 0;
if (!NKSN_EndMessage(session))
return 0;
return 1;
}
/* ================================================== */
static int
process_response(NKC_Instance inst)
{
int next_protocol = -1, aead_algorithm = -1, error = 0;
int i, critical, type, length;
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH);
assert(sizeof (data) % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2);
inst->num_cookies = 0;
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
inst->ntp_address.port = 0;
inst->server_name[0] = '\0';
while (!error) {
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
break;
if (length > sizeof (data)) {
DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical);
if (critical)
error = 1;
continue;
}
switch (type) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
DEBUG_LOG("Unexpected NTS-KE next protocol");
error = 1;
break;
}
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1;
break;
}
aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm;
break;
case NKE_RECORD_ERROR:
if (length == 2)
DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
error = 1;
break;
case NKE_RECORD_WARNING:
if (length == 2)
DEBUG_LOG("NTS-KE warning %d", ntohs(data[0]));
error = 1;
break;
case NKE_RECORD_COOKIE:
DEBUG_LOG("Got cookie length=%d", length);
if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 ||
inst->num_cookies >= NKE_MAX_COOKIES) {
DEBUG_LOG("Unexpected length/cookie");
break;
}
assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
assert(NKE_MAX_COOKIES == sizeof (inst->cookies) /
sizeof (inst->cookies[inst->num_cookies]));
inst->cookies[inst->num_cookies].length = length;
memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
inst->num_cookies++;
break;
case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
if (length < 1 || length >= sizeof (inst->server_name)) {
DEBUG_LOG("Invalid server name");
error = 1;
break;
}
memcpy(inst->server_name, data, length);
inst->server_name[length] = '\0';
/* Make sure the name is printable and has no spaces */
for (i = 0; i < length && isgraph((unsigned char)inst->server_name[i]); i++)
;
if (i != length) {
DEBUG_LOG("Invalid server name");
error = 1;
break;
}
DEBUG_LOG("Negotiated server %s", inst->server_name);
break;
case NKE_RECORD_NTPV4_PORT_NEGOTIATION:
if (length != 2) {
DEBUG_LOG("Invalid port");
error = 1;
break;
}
inst->ntp_address.port = ntohs(data[0]);
DEBUG_LOG("Negotiated port %d", inst->ntp_address.port);
break;
default:
DEBUG_LOG("Unknown record type=%d length=%d critical=%d", type, length, critical);
if (critical)
error = 1;
}
}
DEBUG_LOG("NTS-KE response: error=%d next=%d aead=%d",
error, next_protocol, aead_algorithm);
if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm < 0)
return 0;
return 1;
}
/* ================================================== */
static int
handle_message(void *arg)
{
NKC_Instance inst = arg;
if (!process_response(inst)) {
LOG(LOGS_ERR, "Received invalid NTS-KE response from %s", inst->name);
return 0;
}
if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
&inst->context.c2s, &inst->context.s2c))
return 0;
if (inst->server_name[0] != '\0') {
if (inst->resolving_name)
return 0;
if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) {
int length = strlen(inst->server_name);
/* Add a trailing dot if not present to force the name to be
resolved as a fully qualified domain name */
if (length < 1 || length + 1 >= sizeof (inst->server_name))
return 0;
if (inst->server_name[length - 1] != '.') {
inst->server_name[length] = '.';
inst->server_name[length + 1] = '\0';
}
DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
inst->resolving_name = 1;
}
}
inst->got_response = 1;
return 1;
}
/* ================================================== */
NKC_Instance
NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set)
{
const char **trusted_certs;
uint32_t *certs_ids;
NKC_Instance inst;
int n_certs;
inst = MallocNew(struct NKC_Instance_Record);
inst->address = *address;
inst->name = Strdup(name);
inst->session = NKSN_CreateInstance(0, inst->name, handle_message, inst);
inst->resolving_name = 0;
inst->destroying = 0;
inst->got_response = 0;
n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids);
/* Share the credentials among clients using the default set of trusted
certificates, which likely contains most certificates */
if (cert_set == 0) {
if (!default_credentials)
default_credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
n_certs, cert_set);
inst->credentials = default_credentials;
if (default_credentials)
default_credentials_refs++;
} else {
inst->credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
n_certs, cert_set);
}
return inst;
}
/* ================================================== */
void
NKC_DestroyInstance(NKC_Instance inst)
{
NKSN_DestroyInstance(inst->session);
Free(inst->name);
if (inst->credentials) {
if (inst->credentials == default_credentials) {
default_credentials_refs--;
if (default_credentials_refs <= 0) {
NKSN_DestroyCertCredentials(default_credentials);
default_credentials = NULL;
}
} else {
NKSN_DestroyCertCredentials(inst->credentials);
}
}
/* If the asynchronous resolver is running, let the handler free
the instance later */
if (inst->resolving_name) {
inst->destroying = 1;
return;
}
Free(inst);
}
/* ================================================== */
int
NKC_Start(NKC_Instance inst)
{
IPSockAddr local_addr;
char label[512], *iface;
int sock_fd;
assert(!NKC_IsActive(inst));
inst->got_response = 0;
if (!inst->credentials) {
DEBUG_LOG("Missing client credentials");
return 0;
}
/* Don't try to connect if missing the algorithm which all servers
are required to support */
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) <= 0) {
LOG(LOGS_ERR, "Missing AES-SIV-CMAC-256");
return 0;
}
/* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0;
iface = CNF_GetBindAcquisitionInterface();
/* Make a label containing both the address and name of the server */
if (snprintf(label, sizeof (label), "%s (%s)",
UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
;
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, iface, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not connect to %s", label);
return 0;
}
/* Start an NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, label, inst->credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd);
return 0;
}
/* Send a request */
if (!prepare_request(inst)) {
DEBUG_LOG("Could not prepare NTS-KE request");
NKSN_StopSession(inst->session);
return 0;
}
return 1;
}
/* ================================================== */
int
NKC_IsActive(NKC_Instance inst)
{
return !NKSN_IsStopped(inst->session) || inst->resolving_name;
}
/* ================================================== */
int
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address)
{
int i;
if (!inst->got_response || inst->resolving_name)
return 0;
*context = inst->context;
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
cookies[i] = inst->cookies[i];
*num_cookies = i;
*ntp_address = inst->ntp_address;
return 1;
}
/* ================================================== */
int
NKC_GetRetryFactor(NKC_Instance inst)
{
return NKSN_GetRetryFactor(inst->session);
}

View File

@@ -1,56 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 NTS-KE client
*/
#ifndef GOT_NTS_KE_CLIENT_H
#define GOT_NTS_KE_CLIENT_H
#include "addressing.h"
#include "nts_ke.h"
typedef struct NKC_Instance_Record *NKC_Instance;
/* Create a client NTS-KE instance */
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set);
/* Destroy an instance */
extern void NKC_DestroyInstance(NKC_Instance inst);
/* Connect to the server, start an NTS-KE session, send an NTS-KE request, and
process the response (asynchronously) */
extern int NKC_Start(NKC_Instance inst);
/* Check if the client is still running */
extern int NKC_IsActive(NKC_Instance inst);
/* Get the NTS data if the session was successful */
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
IPSockAddr *ntp_address);
/* Get a factor to calculate retry interval (in log2 seconds) */
extern int NKC_GetRetryFactor(NKC_Instance inst);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,49 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 NTS-KE server
*/
#ifndef GOT_NTS_KE_SERVER_H
#define GOT_NTS_KE_SERVER_H
#include "nts_ke.h"
/* Init and fini functions */
extern void NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level);
extern void NKS_Initialise(void);
extern void NKS_Finalise(void);
/* Save the current server keys */
extern void NKS_DumpKeys(void);
/* Reload the keys */
extern void NKS_ReloadKeys(void);
/* Generate an NTS cookie with a given context */
extern int NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie);
/* Validate a cookie and decode the context */
extern int NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context);
#endif

View File

@@ -1,929 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020-2021
*
* 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.
*
**********************************************************************
=======================================================================
NTS-KE session used by server and client
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ke_session.h"
#include "conf.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "siv.h"
#include "socket.h"
#include "sched.h"
#include "util.h"
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#define INVALID_SOCK_FD (-8)
struct RecordHeader {
uint16_t type;
uint16_t body_length;
};
struct Message {
int length;
int sent;
int parsed;
int complete;
unsigned char data[NKE_MAX_MESSAGE_LENGTH];
};
typedef enum {
KE_WAIT_CONNECT,
KE_HANDSHAKE,
KE_SEND,
KE_RECEIVE,
KE_SHUTDOWN,
KE_STOPPED,
} KeState;
struct NKSN_Instance_Record {
int server;
char *server_name;
NKSN_MessageHandler handler;
void *handler_arg;
KeState state;
int sock_fd;
char *label;
gnutls_session_t tls_session;
SCH_TimeoutID timeout_id;
int retry_factor;
struct Message message;
int new_message;
};
/* ================================================== */
static gnutls_priority_t priority_cache;
static int credentials_counter = 0;
static int clock_updates = 0;
/* ================================================== */
static void
reset_message(struct Message *message)
{
message->length = 0;
message->sent = 0;
message->parsed = 0;
message->complete = 0;
}
/* ================================================== */
static int
add_record(struct Message *message, int critical, int type, const void *body, int body_length)
{
struct RecordHeader header;
assert(message->length <= sizeof (message->data));
if (body_length < 0 || body_length > 0xffff || type < 0 || type > 0x7fff ||
message->length + sizeof (header) + body_length > sizeof (message->data))
return 0;
header.type = htons(!!critical * NKE_RECORD_CRITICAL_BIT | type);
header.body_length = htons(body_length);
memcpy(&message->data[message->length], &header, sizeof (header));
message->length += sizeof (header);
if (body_length > 0) {
memcpy(&message->data[message->length], body, body_length);
message->length += body_length;
}
return 1;
}
/* ================================================== */
static void
reset_message_parsing(struct Message *message)
{
message->parsed = 0;
}
/* ================================================== */
static int
get_record(struct Message *message, int *critical, int *type, int *body_length,
void *body, int buffer_length)
{
struct RecordHeader header;
int blen, rlen;
if (message->length < message->parsed + sizeof (header) ||
buffer_length < 0)
return 0;
memcpy(&header, &message->data[message->parsed], sizeof (header));
blen = ntohs(header.body_length);
rlen = sizeof (header) + blen;
assert(blen >= 0 && rlen > 0);
if (message->length < message->parsed + rlen)
return 0;
if (critical)
*critical = !!(ntohs(header.type) & NKE_RECORD_CRITICAL_BIT);
if (type)
*type = ntohs(header.type) & ~NKE_RECORD_CRITICAL_BIT;
if (body)
memcpy(body, &message->data[message->parsed + sizeof (header)], MIN(buffer_length, blen));
if (body_length)
*body_length = blen;
message->parsed += rlen;
return 1;
}
/* ================================================== */
static int
check_message_format(struct Message *message, int eof)
{
int critical = 0, type = -1, length = -1, ends = 0;
reset_message_parsing(message);
message->complete = 0;
while (get_record(message, &critical, &type, &length, NULL, 0)) {
if (type == NKE_RECORD_END_OF_MESSAGE) {
if (!critical || length != 0 || ends > 0)
return 0;
ends++;
}
}
/* If the message cannot be fully parsed, but more data may be coming,
consider the format to be ok */
if (message->length == 0 || message->parsed < message->length)
return !eof;
if (type != NKE_RECORD_END_OF_MESSAGE)
return !eof;
message->complete = 1;
return 1;
}
/* ================================================== */
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)
{
if (inst->state == KE_STOPPED)
return;
inst->state = KE_STOPPED;
SCH_RemoveFileHandler(inst->sock_fd);
SCK_CloseSocket(inst->sock_fd);
inst->sock_fd = INVALID_SOCK_FD;
Free(inst->label);
inst->label = NULL;
gnutls_deinit(inst->tls_session);
inst->tls_session = NULL;
SCH_RemoveTimeout(inst->timeout_id);
inst->timeout_id = 0;
}
/* ================================================== */
static void
session_timeout(void *arg)
{
NKSN_Instance inst = arg;
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE session with %s timed out", inst->label);
inst->timeout_id = 0;
stop_session(inst);
}
/* ================================================== */
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)
{
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_INPUT, !output);
SCH_SetFileHandlerEvent(inst->sock_fd, SCH_FILE_OUTPUT, output);
}
/* ================================================== */
static void
change_state(NKSN_Instance inst, KeState state)
{
int output;
switch (state) {
case KE_HANDSHAKE:
output = !inst->server;
break;
case KE_WAIT_CONNECT:
case KE_SEND:
case KE_SHUTDOWN:
output = 1;
break;
case KE_RECEIVE:
output = 0;
break;
default:
assert(0);
}
set_input_output(inst, output);
inst->state = state;
}
/* ================================================== */
static int
handle_event(NKSN_Instance inst, int event)
{
struct Message *message = &inst->message;
int r;
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
switch (inst->state) {
case KE_WAIT_CONNECT:
/* Check if connect() succeeded */
if (event != SCH_FILE_OUTPUT)
return 0;
/* Get the socket error */
if (!SCK_GetIntOption(inst->sock_fd, SOL_SOCKET, SO_ERROR, &r))
r = EINVAL;
if (r != 0) {
LOG(LOGS_ERR, "Could not connect to %s : %s", inst->label, strerror(r));
stop_session(inst);
return 0;
}
DEBUG_LOG("Connected to %s", inst->label);
change_state(inst, KE_HANDSHAKE);
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);
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)
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;
case KE_SEND:
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);
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));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Sent %d bytes to %s", r, inst->label);
message->sent += r;
if (message->sent < message->length)
return 0;
/* Client will receive a response */
change_state(inst, inst->server ? KE_SHUTDOWN : KE_RECEIVE);
reset_message(&inst->message);
inst->new_message = 0;
return 0;
case KE_RECEIVE:
do {
if (message->length >= sizeof (message->data)) {
DEBUG_LOG("Message is too long");
stop_session(inst);
return 0;
}
r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
sizeof (message->data) - message->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));
stop_session(inst);
}
return 0;
}
DEBUG_LOG("Received %d bytes from %s", r, inst->label);
message->length += r;
} while (gnutls_record_check_pending(inst->tls_session) > 0);
if (!check_message_format(message, r == 0)) {
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
"Received invalid NTS-KE message from %s", inst->label);
stop_session(inst);
return 0;
}
/* Wait for more data if the message is not complete yet */
if (!message->complete)
return 0;
/* Server will send a response to the client */
change_state(inst, inst->server ? KE_SEND : KE_SHUTDOWN);
/* Return success to process the received message */
return 1;
case KE_SHUTDOWN:
r = gnutls_bye(inst->tls_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));
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);
stop_session(inst);
DEBUG_LOG("Shutdown completed");
return 0;
default:
assert(0);
return 0;
}
}
/* ================================================== */
static void
read_write_socket(int fd, int event, void *arg)
{
NKSN_Instance inst = arg;
if (!handle_event(inst, event))
return;
/* A valid message was received. Call the handler to process the message,
and prepare a response if it is a server. */
reset_message_parsing(&inst->message);
if (!(inst->handler)(inst->handler_arg)) {
stop_session(inst);
return;
}
}
/* ================================================== */
static time_t
get_time(time_t *t)
{
struct timespec now;
LCL_ReadCookedTime(&now, NULL);
if (t)
*t = now.tv_sec;
return now.tv_sec;
}
/* ================================================== */
static void
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type != LCL_ChangeUnknownStep && clock_updates < INT_MAX)
clock_updates++;
}
/* ================================================== */
static int gnutls_initialised = 0;
static int
init_gnutls(void)
{
int r;
if (gnutls_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();
return 0;
}
/* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time);
gnutls_initialised = 1;
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL);
return 1;
}
/* ================================================== */
static void
deinit_gnutls(void)
{
if (!gnutls_initialised || credentials_counter > 0)
return;
LCL_RemoveParameterChangeHandler(handle_step, NULL);
gnutls_priority_deinit(priority_cache);
gnutls_global_deinit();
gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
/* ================================================== */
static NKSN_Credentials
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;
if (!init_gnutls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)
goto error;
if (certs && keys) {
if (trusted_certs || trusted_certs_ids)
assert(0);
for (i = 0; i < n_certs_keys; i++) {
if (!UTI_CheckFilePermissions(keys[i], 0771))
;
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
} else {
if (certs || keys || n_certs_keys > 0)
assert(0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0)
goto error;
}
if (trusted_certs && trusted_certs_ids) {
for (i = 0; i < n_trusted_certs; i++) {
struct stat buf;
if (trusted_certs_ids[i] != trusted_cert_set)
continue;
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
else
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
}
}
}
credentials_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;
}
/* ================================================== */
NKSN_Credentials
NKSN_CreateServerCertCredentials(const char **certs, const char **keys, int n_certs_keys)
{
return create_credentials(certs, keys, n_certs_keys, NULL, NULL, 0, 0);
}
/* ================================================== */
NKSN_Credentials
NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
int n_certs_ids, uint32_t trusted_cert_set)
{
return create_credentials(NULL, NULL, 0, certs, ids, n_certs_ids, trusted_cert_set);
}
/* ================================================== */
void
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
{
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
credentials_counter--;
deinit_gnutls();
}
/* ================================================== */
NKSN_Instance
NKSN_CreateInstance(int server_mode, const char *server_name,
NKSN_MessageHandler handler, void *handler_arg)
{
NKSN_Instance inst;
inst = MallocNew(struct NKSN_Instance_Record);
inst->server = server_mode;
inst->server_name = server_name ? Strdup(server_name) : NULL;
inst->handler = handler;
inst->handler_arg = handler_arg;
/* Replace a NULL argument with the session itself */
if (!inst->handler_arg)
inst->handler_arg = inst;
inst->state = KE_STOPPED;
inst->sock_fd = INVALID_SOCK_FD;
inst->label = NULL;
inst->tls_session = NULL;
inst->timeout_id = 0;
inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
return inst;
}
/* ================================================== */
void
NKSN_DestroyInstance(NKSN_Instance inst)
{
stop_session(inst);
Free(inst->server_name);
Free(inst);
}
/* ================================================== */
int
NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
NKSN_Credentials credentials, double timeout)
{
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);
if (!inst->tls_session)
return 0;
inst->sock_fd = sock_fd;
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, inst);
inst->label = Strdup(label);
inst->timeout_id = SCH_AddTimeoutByDelay(timeout, session_timeout, inst);
inst->retry_factor = NKE_RETRY_FACTOR2_CONNECT;
reset_message(&inst->message);
inst->new_message = 0;
change_state(inst, inst->server ? KE_HANDSHAKE : KE_WAIT_CONNECT);
return 1;
}
/* ================================================== */
void
NKSN_BeginMessage(NKSN_Instance inst)
{
reset_message(&inst->message);
inst->new_message = 1;
}
/* ================================================== */
int
NKSN_AddRecord(NKSN_Instance inst, int critical, int type, const void *body, int body_length)
{
assert(inst->new_message && !inst->message.complete);
assert(type != NKE_RECORD_END_OF_MESSAGE);
return add_record(&inst->message, critical, type, body, body_length);
}
/* ================================================== */
int
NKSN_EndMessage(NKSN_Instance inst)
{
assert(!inst->message.complete);
/* Terminate the message */
if (!add_record(&inst->message, 1, NKE_RECORD_END_OF_MESSAGE, NULL, 0))
return 0;
inst->message.complete = 1;
return 1;
}
/* ================================================== */
int
NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length)
{
int type2;
assert(inst->message.complete);
if (body_length)
*body_length = 0;
if (!get_record(&inst->message, critical, &type2, body_length, body, buffer_length))
return 0;
/* Hide the end-of-message record */
if (type2 == NKE_RECORD_END_OF_MESSAGE)
return 0;
if (type)
*type = type2;
return 1;
}
/* ================================================== */
int
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
{
int length = SIV_GetKeyLength(siv);
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
DEBUG_LOG("Invalid algorithm");
return 0;
}
if (gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
length, (char *)c2s->key) < 0 ||
gnutls_prf_rfc5705(inst->tls_session,
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
length, (char *)s2c->key) < 0) {
DEBUG_LOG("Could not export key");
return 0;
}
c2s->length = length;
s2c->length = length;
return 1;
}
/* ================================================== */
int
NKSN_IsStopped(NKSN_Instance inst)
{
return inst->state == KE_STOPPED;
}
/* ================================================== */
void
NKSN_StopSession(NKSN_Instance inst)
{
stop_session(inst);
}
/* ================================================== */
int
NKSN_GetRetryFactor(NKSN_Instance inst)
{
return inst->retry_factor;
}

View File

@@ -1,93 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 NTS-KE session
*/
#ifndef GOT_NTS_KE_SESSION_H
#define GOT_NTS_KE_SESSION_H
#include "nts_ke.h"
#include "siv.h"
typedef struct NKSN_Credentials_Record *NKSN_Credentials;
typedef struct NKSN_Instance_Record *NKSN_Instance;
/* Handler for received NTS-KE messages. A zero return code stops
the session. */
typedef int (*NKSN_MessageHandler)(void *arg);
/* Get server or client credentials using a server certificate and key,
or certificates of trusted CAs. The credentials may be shared between
different clients or servers. */
extern NKSN_Credentials NKSN_CreateServerCertCredentials(const char **certs, const char **keys,
int n_certs_keys);
extern NKSN_Credentials NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
int n_certs_ids,
uint32_t trusted_cert_set);
/* Destroy the credentials */
extern void NKSN_DestroyCertCredentials(NKSN_Credentials credentials);
/* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name,
NKSN_MessageHandler handler, void *handler_arg);
/* Destroy an instance */
extern void NKSN_DestroyInstance(NKSN_Instance inst);
/* Start a new NTS-KE session */
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
NKSN_Credentials credentials, double timeout);
/* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */
extern void NKSN_BeginMessage(NKSN_Instance inst);
/* Add a record to the message */
extern int NKSN_AddRecord(NKSN_Instance inst, int critical, int type,
const void *body, int body_length);
/* Terminate the message */
extern int NKSN_EndMessage(NKSN_Instance inst);
/* Get the next record from the received message. This function should be
called from the message handler. */
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
void *body, int buffer_length);
/* Export NTS keys for a specified algorithm */
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
/* Check if the session has stopped */
extern int NKSN_IsStopped(NKSN_Instance inst);
/* Stop the session */
extern void NKSN_StopSession(NKSN_Instance inst);
/* Get a factor to calculate retry interval (in log2 seconds)
based on the session state or how it was terminated */
extern int NKSN_GetRetryFactor(NKSN_Instance inst);
#endif

View File

@@ -1,36 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 NTS-NTP protocol
*/
#ifndef GOT_NTS_NTP_H
#define GOT_NTS_NTP_H
#define NTP_KOD_NTS_NAK 0x4e54534e
#define NTS_MIN_UNIQ_ID_LENGTH 32
#define NTS_MIN_UNPADDED_NONCE_LENGTH 16
#define NTS_MAX_COOKIES 8
#endif

View File

@@ -1,187 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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.
*
**********************************************************************
=======================================================================
NTS Authenticator and Encrypted Extension Fields extension field
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_auth.h"
#include "logging.h"
#include "ntp_ext.h"
#include "nts_ntp.h"
#include "siv.h"
#include "util.h"
struct AuthHeader {
uint16_t nonce_length;
uint16_t ciphertext_length;
};
/* ================================================== */
static int
get_padding_length(int length)
{
return length % 4U ? 4 - length % 4U : 0;
}
/* ================================================== */
static int
get_padded_length(int length)
{
return length + get_padding_length(length);
}
/* ================================================== */
int
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length)
{
int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length;
int nonce_padding, ciphertext_padding, additional_padding;
unsigned char *ciphertext, *body;
struct AuthHeader *header;
assert(sizeof (*header) == 4);
if (max_nonce_length <= 0 || plaintext_length < 0) {
DEBUG_LOG("Invalid nonce/plaintext length");
return 0;
}
assoc_length = info->length;
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
nonce_length = MIN(max_nonce_length, max_siv_nonce_length);
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
nonce_padding = get_padding_length(nonce_length);
ciphertext_padding = get_padding_length(ciphertext_length);
min_ef_length = get_padded_length(min_ef_length);
auth_length = sizeof (*header) + nonce_length + nonce_padding +
ciphertext_length + ciphertext_padding;
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) -
nonce_length - nonce_padding, additional_padding);
auth_length += additional_padding;
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
(void **)&header)) {
DEBUG_LOG("Could not add EF");
return 0;
}
header->nonce_length = htons(nonce_length);
header->ciphertext_length = htons(ciphertext_length);
body = (unsigned char *)(header + 1);
ciphertext = body + nonce_length + nonce_padding;
if ((unsigned char *)header + auth_length !=
ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
assert(0);
memcpy(body, nonce, nonce_length);
memset(body + nonce_length, 0, nonce_padding);
if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed");
info->length = assoc_length;
info->ext_fields--;
return 0;
}
memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding);
return 1;
}
/* ================================================== */
int
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
unsigned char *plaintext, int buffer_length, int *plaintext_length)
{
int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length;
unsigned char *nonce, *ciphertext;
int ef_type, ef_body_length;
void *ef_body;
struct AuthHeader *header;
if (buffer_length < 0)
return 0;
if (!NEF_ParseField(packet, info->length, ef_start,
NULL, &ef_type, &ef_body, &ef_body_length))
return 0;
if (ef_type != NTP_EF_NTS_AUTH_AND_EEF || ef_body_length < sizeof (*header))
return 0;
header = ef_body;
nonce_length = ntohs(header->nonce_length);
ciphertext_length = ntohs(header->ciphertext_length);
if (get_padded_length(nonce_length) +
get_padded_length(ciphertext_length) > ef_body_length)
return 0;
nonce = (unsigned char *)(header + 1);
ciphertext = nonce + get_padded_length(nonce_length);
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
siv_tag_length = SIV_GetTagLength(siv);
if (nonce_length < 1 ||
ciphertext_length < siv_tag_length ||
ciphertext_length - siv_tag_length > buffer_length) {
DEBUG_LOG("Unexpected nonce/ciphertext length");
return 0;
}
if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) +
get_padded_length(ciphertext_length) > ef_body_length) {
DEBUG_LOG("Missing padding");
return 0;
}
*plaintext_length = ciphertext_length - siv_tag_length;
assert(*plaintext_length >= 0);
if (!SIV_Decrypt(siv, nonce, nonce_length, packet, ef_start,
ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
DEBUG_LOG("SIV decrypt failed");
return 0;
}
return 1;
}

View File

@@ -1,43 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 for NTS Authenticator and Encrypted Extension Fields
extension field
*/
#ifndef GOT_NTS_NTP_AUTH_H
#define GOT_NTS_NTP_AUTH_H
#include "ntp.h"
#include "siv.h"
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length);
extern int NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
int ef_start, unsigned char *plaintext, int buffer_length,
int *plaintext_length);
#endif

View File

@@ -1,717 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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.
*
**********************************************************************
=======================================================================
Client NTS-NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_client.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "ntp.h"
#include "ntp_ext.h"
#include "ntp_sources.h"
#include "nts_ke_client.h"
#include "nts_ntp.h"
#include "nts_ntp_auth.h"
#include "sched.h"
#include "siv.h"
#include "util.h"
/* Maximum length of all cookies to avoid IP fragmentation */
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
/* Retry interval for NTS-KE start (which doesn't generate network traffic) */
#define RETRY_INTERVAL_KE_START 2.0
/* Magic string of files containing keys and cookies */
#define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record {
/* Address of NTS-KE server */
IPSockAddr nts_address;
/* Hostname or IP address for certificate verification */
char *name;
/* ID of trusted certificates */
uint32_t cert_set;
/* Configured NTP port */
uint16_t default_ntp_port;
/* Address of NTP server (can be negotiated in NTS-KE) */
IPSockAddr ntp_address;
NKC_Instance nke;
SIV_Instance siv;
int nke_attempts;
double next_nke_attempt;
double last_nke_success;
NKE_Context context;
unsigned int context_id;
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int cookie_index;
int auth_ready;
int nak_response;
int ok_response;
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
};
/* ================================================== */
static void save_cookies(NNC_Instance inst);
static void load_cookies(NNC_Instance inst);
/* ================================================== */
static void
reset_instance(NNC_Instance inst)
{
if (inst->nke)
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0;
memset(&inst->context, 0, sizeof (inst->context));
inst->context_id = 0;
memset(inst->cookies, 0, sizeof (inst->cookies));
inst->num_cookies = 0;
inst->cookie_index = 0;
inst->auth_ready = 0;
inst->nak_response = 0;
inst->ok_response = 1;
memset(inst->nonce, 0, sizeof (inst->nonce));
memset(inst->uniq_id, 0, sizeof (inst->uniq_id));
}
/* ================================================== */
NNC_Instance
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, uint16_t ntp_port)
{
NNC_Instance inst;
inst = MallocNew(struct NNC_Instance_Record);
inst->nts_address = *nts_address;
inst->name = Strdup(name);
inst->cert_set = cert_set;
inst->default_ntp_port = ntp_port;
inst->ntp_address.ip_addr = nts_address->ip_addr;
inst->ntp_address.port = ntp_port;
inst->siv = NULL;
inst->nke = NULL;
reset_instance(inst);
/* Try to reload saved keys and cookies */
load_cookies(inst);
return inst;
}
/* ================================================== */
void
NNC_DestroyInstance(NNC_Instance inst)
{
save_cookies(inst);
reset_instance(inst);
Free(inst->name);
Free(inst);
}
/* ================================================== */
static int
check_cookies(NNC_Instance inst)
{
/* Force a new NTS-KE session if a NAK was received without a valid response,
or the keys encrypting the cookies need to be refreshed */
if (inst->num_cookies > 0 &&
((inst->nak_response && !inst->ok_response) ||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
inst->num_cookies = 0;
DEBUG_LOG("Dropped cookies");
}
return inst->num_cookies > 0;
}
/* ================================================== */
static int
set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
{
NTP_Remote_Address old_address, new_address;
old_address = inst->ntp_address;
new_address = *negotiated_address;
if (new_address.ip_addr.family == IPADDR_UNSPEC)
new_address.ip_addr = inst->nts_address.ip_addr;
if (new_address.port == 0)
new_address.port = inst->default_ntp_port;
if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
old_address.port == new_address.port)
/* Nothing to do */
return 1;
if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) {
LOG(LOGS_ERR, "Could not change %s to negotiated address %s",
UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr));
return 0;
}
inst->ntp_address = new_address;
return 1;
}
/* ================================================== */
static void
update_next_nke_attempt(NNC_Instance inst, int failed_start, double now)
{
int factor, interval;
if (failed_start) {
inst->next_nke_attempt = now + RETRY_INTERVAL_KE_START;
return;
}
if (!inst->nke)
return;
factor = NKC_GetRetryFactor(inst->nke);
interval = MIN(factor + inst->nke_attempts - 1, NKE_MAX_RETRY_INTERVAL2);
inst->next_nke_attempt = now + UTI_Log2ToDouble(interval);
}
/* ================================================== */
static int
get_cookies(NNC_Instance inst)
{
NTP_Remote_Address ntp_address;
int got_data, failed_start = 0;
double now;
assert(inst->num_cookies == 0);
now = SCH_GetLastEventMonoTime();
/* Create and start a new NTS-KE session if not already present */
if (!inst->nke) {
if (now < inst->next_nke_attempt) {
DEBUG_LOG("Limiting NTS-KE request rate (%f seconds)",
inst->next_nke_attempt - now);
return 0;
}
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
inst->nke_attempts++;
if (!NKC_Start(inst->nke))
failed_start = 1;
}
update_next_nke_attempt(inst, failed_start, now);
/* Wait until the session stops */
if (NKC_IsActive(inst->nke))
return 0;
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
/* Get the new keys, cookies and NTP address if the session was successful */
got_data = NKC_GetNtsData(inst->nke, &inst->context,
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
&ntp_address);
NKC_DestroyInstance(inst->nke);
inst->nke = NULL;
if (!got_data)
return 0;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
inst->context_id++;
/* Force a new session if the NTP address is used by another source, with
an expectation that it will eventually get a non-conflicting address */
if (!set_ntp_address(inst, &ntp_address)) {
inst->num_cookies = 0;
return 0;
}
inst->last_nke_success = now;
inst->cookie_index = 0;
return 1;
}
/* ================================================== */
int
NNC_PrepareForAuth(NNC_Instance inst)
{
inst->auth_ready = 0;
/* Prepare data for the next request and invalidate any responses to the
previous request */
UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
/* Get new cookies if there are not any, or they are no longer usable */
if (!check_cookies(inst)) {
if (!get_cookies(inst))
return 0;
}
inst->nak_response = 0;
if (!inst->siv)
inst->siv = SIV_CreateInstance(inst->context.algorithm);
if (!inst->siv ||
!SIV_SetKey(inst->siv, inst->context.c2s.key, inst->context.c2s.length)) {
DEBUG_LOG("Could not set SIV key");
return 0;
}
inst->auth_ready = 1;
return 1;
}
/* ================================================== */
int
NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info)
{
NKE_Cookie *cookie;
int i, req_cookies;
void *ef_body;
if (!inst->auth_ready)
return 0;
inst->auth_ready = 0;
if (inst->num_cookies <= 0 || !inst->siv)
return 0;
if (info->mode != MODE_CLIENT)
return 0;
cookie = &inst->cookies[inst->cookie_index];
inst->num_cookies--;
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies,
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
inst->uniq_id, sizeof (inst->uniq_id)))
return 0;
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
cookie->cookie, cookie->length))
return 0;
for (i = 0; i < req_cookies - 1; i++) {
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
cookie->length, &ef_body))
return 0;
memset(ef_body, 0, cookie->length);
}
if (!NNA_GenerateAuthEF(packet, info, inst->siv, inst->nonce, sizeof (inst->nonce),
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
return 0;
inst->ok_response = 0;
return 1;
}
/* ================================================== */
static int
parse_encrypted_efs(NNC_Instance inst, unsigned char *plaintext, int length)
{
int ef_length, parsed;
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed, &ef_length, NULL, NULL, NULL)) {
DEBUG_LOG("Could not parse encrypted EF");
return 0;
}
}
return 1;
}
/* ================================================== */
static int
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
{
int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved;
void *ef_body;
acceptable = saved = 0;
for (parsed = 0; parsed < length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
return 0;
if (ef_type != NTP_EF_NTS_COOKIE)
continue;
if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) {
DEBUG_LOG("Unexpected cookie length %d", ef_body_length);
continue;
}
acceptable++;
if (inst->num_cookies >= NTS_MAX_COOKIES)
continue;
index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
assert(index >= 0 && index < NTS_MAX_COOKIES);
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
inst->cookies[index].length = ef_body_length;
inst->num_cookies++;
saved++;
}
DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved);
return acceptable > 0;
}
/* ================================================== */
int
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info)
{
int ef_type, ef_body_length, ef_length, parsed, plaintext_length;
int has_valid_uniq_id = 0, has_valid_auth = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
void *ef_body;
if (info->ext_fields == 0 || info->mode != MODE_SERVER)
return 0;
/* Accept at most one response per request */
if (inst->ok_response || inst->auth_ready)
return 0;
if (!inst->siv ||
!SIV_SetKey(inst->siv, inst->context.s2c.key, inst->context.s2c.length)) {
DEBUG_LOG("Could not set SIV key");
return 0;
}
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
/* This is not expected as the packet already passed parsing */
return 0;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
if (ef_body_length != sizeof (inst->uniq_id) ||
memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) {
DEBUG_LOG("Invalid uniq id");
return 0;
}
has_valid_uniq_id = 1;
break;
case NTP_EF_NTS_COOKIE:
DEBUG_LOG("Unencrypted cookie");
break;
case NTP_EF_NTS_AUTH_AND_EEF:
if (parsed + ef_length != info->length) {
DEBUG_LOG("Auth not last EF");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, inst->siv, parsed,
plaintext, sizeof (plaintext), &plaintext_length))
return 0;
if (!parse_encrypted_efs(inst, plaintext, plaintext_length))
return 0;
has_valid_auth = 1;
break;
default:
break;
}
}
if (!has_valid_uniq_id || !has_valid_auth) {
if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM &&
ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) {
DEBUG_LOG("NTS NAK");
inst->nak_response = 1;
return 0;
}
DEBUG_LOG("Missing NTS EF");
return 0;
}
if (!extract_cookies(inst, plaintext, plaintext_length))
return 0;
inst->ok_response = 1;
/* At this point we know the client interoperates with the server. Allow a
new NTS-KE session to be started as soon as the cookies run out. */
inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0;
return 1;
}
/* ================================================== */
void
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
{
save_cookies(inst);
inst->nts_address.ip_addr = *address;
inst->ntp_address.ip_addr = *address;
reset_instance(inst);
DEBUG_LOG("NTS reset");
load_cookies(inst);
}
/* ================================================== */
static void
save_cookies(NNC_Instance inst)
{
char buf[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename;
struct timespec now;
double context_time;
FILE *f;
int i;
if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr))
return;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
filename = UTI_IPToString(&inst->nts_address.ip_addr);
f = UTI_OpenFile(dump_dir, filename, ".tmp", 'w', 0600);
if (!f)
return;
SCH_GetLastEventTime(&now, NULL, NULL);
context_time = inst->last_nke_success - SCH_GetLastEventMonoTime();
context_time += UTI_TimespecToDouble(&now);
if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
DUMP_IDENTIFIER, inst->name, context_time,
UTI_IPToString(&inst->ntp_address.ip_addr), inst->ntp_address.port,
inst->context_id, (int)inst->context.algorithm) < 0 ||
!UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
fprintf(f, "%s ", buf) < 0 ||
!UTI_BytesToHex(inst->context.c2s.key, inst->context.c2s.length, buf, sizeof (buf)) ||
fprintf(f, "%s\n", buf) < 0)
goto error;
for (i = 0; i < inst->num_cookies; i++) {
if (!UTI_BytesToHex(inst->cookies[i].cookie, inst->cookies[i].length, buf, sizeof (buf)) ||
fprintf(f, "%s\n", buf) < 0)
goto error;
}
fclose(f);
if (!UTI_RenameTempFile(dump_dir, filename, ".tmp", ".nts"))
;
return;
error:
DEBUG_LOG("Could not %s cookies for %s", "save", filename);
fclose(f);
if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
;
}
/* ================================================== */
#define MAX_WORDS 4
static void
load_cookies(NNC_Instance inst)
{
char line[2 * NKE_MAX_COOKIE_LENGTH + 2], *dump_dir, *filename, *words[MAX_WORDS];
unsigned int context_id;
int i, algorithm, port;
double context_time;
struct timespec now;
IPSockAddr ntp_addr;
FILE *f;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
return;
filename = UTI_IPToString(&inst->nts_address.ip_addr);
f = UTI_OpenFile(dump_dir, filename, ".nts", 'r', 0);
if (!f)
return;
/* Don't load this file again */
if (!UTI_RemoveFile(dump_dir, filename, ".nts"))
;
if (inst->siv)
SIV_DestroyInstance(inst->siv);
inst->siv = NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
strcmp(words[0], inst->name) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
!UTI_StringToIP(words[0], &ntp_addr.ip_addr) || sscanf(words[1], "%d", &port) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 4 ||
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
goto error;
inst->context.algorithm = algorithm;
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
sizeof (inst->context.s2c.key));
inst->context.c2s.length = UTI_HexToBytes(words[3], inst->context.c2s.key,
sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
inst->context.s2c.length <= 0 ||
inst->context.c2s.length != inst->context.s2c.length)
goto error;
for (i = 0; i < NTS_MAX_COOKIES && fgets(line, sizeof (line), f); i++) {
if (UTI_SplitString(line, words, MAX_WORDS) != 1)
goto error;
inst->cookies[i].length = UTI_HexToBytes(words[0], inst->cookies[i].cookie,
sizeof (inst->cookies[i].cookie));
if (inst->cookies[i].length == 0)
goto error;
}
inst->num_cookies = i;
ntp_addr.port = port;
if (!set_ntp_address(inst, &ntp_addr))
goto error;
SCH_GetLastEventTime(&now, NULL, NULL);
context_time -= UTI_TimespecToDouble(&now);
if (context_time > 0)
context_time = 0;
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
inst->context_id = context_id;
fclose(f);
DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return;
error:
DEBUG_LOG("Could not %s cookies for %s", "load", filename);
fclose(f);
memset(&inst->context, 0, sizeof (inst->context));
inst->num_cookies = 0;
}
/* ================================================== */
void
NNC_DumpData(NNC_Instance inst)
{
save_cookies(inst);
}
/* ================================================== */
void
NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report)
{
report->key_id = inst->context_id;
report->key_type = inst->context.algorithm;
report->key_length = 8 * inst->context.s2c.length;
report->ke_attempts = inst->nke_attempts;
if (report->key_length > 0)
report->last_ke_ago = SCH_GetLastEventMonoTime() - inst->last_nke_success;
else
report->last_ke_ago = -1;
report->cookies = inst->num_cookies;
report->cookie_length = inst->num_cookies > 0 ? inst->cookies[inst->cookie_index].length : 0;
report->nak = inst->nak_response;
}

View File

@@ -1,51 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 client NTS-NTP authentication
*/
#ifndef GOT_NTS_NTP_CLIENT_H
#define GOT_NTS_NTP_CLIENT_H
#include "addressing.h"
#include "ntp.h"
#include "reports.h"
typedef struct NNC_Instance_Record *NNC_Instance;
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
uint32_t cert_set, uint16_t ntp_port);
extern void NNC_DestroyInstance(NNC_Instance inst);
extern int NNC_PrepareForAuth(NNC_Instance inst);
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info);
extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
NTP_PacketInfo *info);
extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
extern void NNC_DumpData(NNC_Instance inst);
extern void NNC_GetReport(NNC_Instance inst, RPT_AuthReport *report);
#endif

View File

@@ -1,309 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020, 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Server NTS-NTP authentication
*/
#include "config.h"
#include "sysincl.h"
#include "nts_ntp_server.h"
#include "conf.h"
#include "logging.h"
#include "memory.h"
#include "ntp.h"
#include "ntp_ext.h"
#include "nts_ke_server.h"
#include "nts_ntp.h"
#include "nts_ntp_auth.h"
#include "siv.h"
#include "util.h"
#define MAX_SERVER_SIVS 2
struct NtsServer {
SIV_Instance sivs[MAX_SERVER_SIVS];
SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS];
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int siv_index;
NTP_int64 req_tx;
};
/* The server instance handling all requests */
struct NtsServer *server;
/* ================================================== */
void
NNS_Initialise(void)
{
const char **certs, **keys;
int i;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
server = NULL;
return;
}
server = Malloc(sizeof (struct NtsServer));
server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV;
assert(MAX_SERVER_SIVS == 2);
for (i = 0; i < 2; i++)
server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]);
/* AES-SIV-CMAC-256 is required on servers */
if (!server->sivs[0])
LOG_FATAL("Missing AES-SIV-CMAC-256");
}
/* ================================================== */
void
NNS_Finalise(void)
{
int i;
if (!server)
return;
for (i = 0; i < MAX_SERVER_SIVS; i++) {
if (server->sivs[i])
SIV_DestroyInstance(server->sivs[i]);
}
Free(server);
server = NULL;
}
/* ================================================== */
int
NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
{
int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0;
int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context;
NKE_Cookie cookie;
SIV_Instance siv;
void *ef_body;
*kod = 0;
if (!server)
return 0;
server->num_cookies = 0;
server->siv_index = -1;
server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
return 0;
requested_cookies = 0;
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
/* This is not expected as the packet already passed NAU_ParsePacket() */
return 0;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
has_uniq_id = 1;
break;
case NTP_EF_NTS_COOKIE:
if (has_cookie || ef_body_length > sizeof (cookie.cookie)) {
DEBUG_LOG("Unexpected cookie/length");
return 0;
}
cookie.length = ef_body_length;
memcpy(cookie.cookie, ef_body, ef_body_length);
has_cookie = 1;
/* Fall through */
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
requested_cookies++;
if (cookie_length >= 0 && cookie_length != ef_body_length) {
DEBUG_LOG("Invalid cookie/placeholder length");
return 0;
}
cookie_length = ef_body_length;
break;
case NTP_EF_NTS_AUTH_AND_EEF:
if (parsed + ef_length != info->length) {
DEBUG_LOG("Auth not last EF");
return 0;
}
auth_start = parsed;
has_auth = 1;
break;
default:
break;
}
}
if (!has_uniq_id || !has_cookie || !has_auth) {
DEBUG_LOG("Missing an NTS EF");
return 0;
}
if (!NKS_DecodeCookie(&cookie, &context)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
}
/* Find the SIV instance needed for authentication */
for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++)
;
if (i == MAX_SERVER_SIVS || !server->sivs[i]) {
DEBUG_LOG("Unexpected SIV");
return 0;
}
server->siv_index = i;
siv = server->sivs[i];
if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) {
DEBUG_LOG("Could not set C2S key");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
}
for (parsed = 0; parsed < plaintext_length; parsed += ef_length) {
if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) {
DEBUG_LOG("Could not parse encrypted EF");
return 0;
}
switch (ef_type) {
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
if (cookie_length != ef_body_length) {
DEBUG_LOG("Invalid cookie/placeholder length");
return 0;
}
requested_cookies++;
break;
default:
break;
}
}
if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) {
DEBUG_LOG("Could not set S2C key");
return 0;
}
/* Prepare data for NNS_GenerateResponseAuth() to minimise the time spent
there (when the TX timestamp is already set) */
UTI_GetRandomBytes(server->nonce, sizeof (server->nonce));
assert(sizeof (server->cookies) / sizeof (server->cookies[0]) == NTS_MAX_COOKIES);
for (i = 0; i < NTS_MAX_COOKIES && i < requested_cookies; i++)
if (!NKS_GenerateCookie(&context, &server->cookies[i]))
return 0;
server->num_cookies = i;
return 1;
}
/* ================================================== */
int
NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
NTP_Packet *response, NTP_PacketInfo *res_info,
uint32_t kod)
{
int i, ef_type, ef_body_length, ef_length, parsed;
void *ef_body;
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
int plaintext_length;
if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
return 0;
/* Make sure this is a response to the request from the last call
of NNS_CheckRequestAuth() */
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
assert(0);
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
/* This is not expected as the packet already passed parsing */
return 0;
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
/* Copy the ID from the request */
if (!NEF_AddField(response, res_info, ef_type, ef_body, ef_body_length))
return 0;
default:
break;
}
}
/* NTS NAK response does not have any other fields */
if (kod)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
NTP_EF_NTS_COOKIE, server->cookies[i].cookie,
server->cookies[i].length, &ef_length))
return 0;
plaintext_length += ef_length;
assert(plaintext_length <= sizeof (plaintext));
}
server->num_cookies = 0;
if (server->siv_index < 0)
return 0;
/* Generate an authenticator field which will make the length
of the response equal to the length of the request */
if (!NNA_GenerateAuthEF(response, res_info, server->sivs[server->siv_index],
server->nonce, sizeof (server->nonce),
plaintext, plaintext_length,
req_info->length - res_info->length))
return 0;
return 1;
}

View File

@@ -1,40 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
*
* 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 server NTS-NTP authentication
*/
#ifndef GOT_NTS_NTP_SERVER_H
#define GOT_NTS_NTP_SERVER_H
#include "ntp.h"
extern void NNS_Initialise(void);
extern void NNS_Finalise(void);
extern int NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod);
extern int NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
NTP_Packet *response, NTP_PacketInfo *res_info,
uint32_t kod);
#endif

View File

@@ -87,7 +87,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
{ 0, 0 }, /* DOFFSET - not supported */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET */
REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */
REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */
REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */
@@ -110,26 +110,16 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(smoothtime, null), /* SMOOTHTIME */
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
{ 0, 0 }, /* ADD_SERVER3 */
{ 0, 0 }, /* ADD_PEER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SOURCE */
REQ_LENGTH_ENTRY(ntp_source_name,
ntp_source_name), /* NTP_SOURCE_NAME */
REQ_LENGTH_ENTRY(null, null), /* RESET_SOURCES */
REQ_LENGTH_ENTRY(auth_data, auth_data), /* AUTH_DATA */
REQ_LENGTH_ENTRY(client_accesses_by_index,
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
};
static const uint16_t reply_lengths[] = {
@@ -147,18 +137,11 @@ static const uint16_t reply_lengths[] = {
0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
0, /* SERVER_STATS - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
};
/* ================================================== */

131
privops.c
View File

@@ -33,7 +33,6 @@
#include "nameserv.h"
#include "logging.h"
#include "privops.h"
#include "socket.h"
#include "util.h"
#define OP_ADJUSTTIME 1024
@@ -159,7 +158,7 @@ res_fatal(PrvResponse *res, const char *fmt, ...)
static int
send_response(int fd, const PrvResponse *res)
{
if (SCK_Send(fd, res, sizeof (*res), 0) != sizeof (*res))
if (send(fd, res, sizeof (*res), 0) != sizeof (*res))
return 0;
return 1;
@@ -171,23 +170,37 @@ send_response(int fd, const PrvResponse *res)
static int
receive_from_daemon(int fd, PrvRequest *req)
{
SCK_Message *message;
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov;
char cmsgbuf[256];
message = SCK_ReceiveMessage(fd, SCK_FLAG_MSG_DESCRIPTOR);
if (!message || message->length != sizeof (*req))
iov.iov_base = req;
iov.iov_len = sizeof (*req);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = (void *)cmsgbuf;
msg.msg_controllen = sizeof (cmsgbuf);
msg.msg_flags = MSG_WAITALL;
/* read the data */
if (recvmsg(fd, &msg, 0) != sizeof (*req))
return 0;
memcpy(req, message->data, sizeof (*req));
if (req->op == OP_BINDSOCKET) {
req->data.bind_socket.sock = message->descriptor;
/* extract transferred descriptor */
req->data.bind_socket.sock = -1;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
memcpy(&req->data.bind_socket.sock, CMSG_DATA(cmsg), sizeof (int));
}
/* return error if valid descriptor not found */
if (req->data.bind_socket.sock < 0)
return 0;
} else if (message->descriptor >= 0) {
SCK_CloseSocket(message->descriptor);
return 0;
}
return 1;
@@ -244,7 +257,8 @@ do_set_time(const ReqSetTime *req, PrvResponse *res)
static void
do_bind_socket(ReqBindSocket *req, PrvResponse *res)
{
IPSockAddr ip_saddr;
unsigned short port;
IPAddr ip;
int sock_fd;
struct sockaddr *sa;
socklen_t sa_len;
@@ -253,11 +267,10 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
sa_len = req->sa_len;
sock_fd = req->sock;
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort()) {
SCK_CloseSocket(sock_fd);
res_fatal(res, "Invalid port %d", ip_saddr.port);
UTI_SockaddrToIPAndPort(sa, &ip, &port);
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
close(sock_fd);
res_fatal(res, "Invalid port %d", port);
return;
}
@@ -266,7 +279,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
res->res_errno = errno;
/* sock is still open on daemon side, but we're done with it in the helper */
SCK_CloseSocket(sock_fd);
close(sock_fd);
}
#endif
@@ -360,7 +373,7 @@ helper_main(int fd)
send_response(fd, &res);
}
SCK_CloseSocket(fd);
close(fd);
exit(0);
}
@@ -373,7 +386,7 @@ receive_response(PrvResponse *res)
{
int resp_len;
resp_len = SCK_Receive(helper_fd, res, sizeof (*res), 0);
resp_len = recv(helper_fd, res, sizeof (*res), 0);
if (resp_len < 0)
LOG_FATAL("Could not read from helper : %s", strerror(errno));
if (resp_len != sizeof (*res))
@@ -396,22 +409,41 @@ receive_response(PrvResponse *res)
static void
send_request(PrvRequest *req)
{
SCK_Message message;
int flags;
struct msghdr msg;
struct iovec iov;
char cmsgbuf[256];
SCK_InitMessage(&message, SCK_ADDR_UNSPEC);
iov.iov_base = req;
iov.iov_len = sizeof (*req);
message.data = req;
message.length = sizeof (*req);
flags = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
if (req->op == OP_BINDSOCKET) {
/* send file descriptor as a control message */
message.descriptor = req->data.bind_socket.sock;
flags |= SCK_FLAG_MSG_DESCRIPTOR;
struct cmsghdr *cmsg;
int *ptr_send_fd;
msg.msg_control = cmsgbuf;
msg.msg_controllen = CMSG_SPACE(sizeof (int));
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof (int)));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof (int));
ptr_send_fd = (int *)CMSG_DATA(cmsg);
*ptr_send_fd = req->data.bind_socket.sock;
}
if (!SCK_SendMessage(helper_fd, &message, flags)) {
if (sendmsg(helper_fd, &msg, 0) < 0) {
/* don't try to send another request from exit() */
helper_fd = -1;
LOG_FATAL("Could not send to helper : %s", strerror(errno));
@@ -541,13 +573,13 @@ PRV_SetTime(const struct timeval *tp, const struct timezone *tzp)
int
PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
{
IPSockAddr ip_saddr;
PrvRequest req;
PrvResponse res;
IPAddr ip;
unsigned short port;
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort())
UTI_SockaddrToIPAndPort(address, &ip, &port);
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
assert(0);
if (!have_helper())
@@ -557,7 +589,6 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
req.op = OP_BINDSOCKET;
req.data.bind_socket.sock = sock;
req.data.bind_socket.sa_len = address_len;
assert(address_len <= sizeof (req.data.bind_socket.sa));
memcpy(&req.data.bind_socket.sa.u, address, address_len);
submit_request(&req, &res);
@@ -585,6 +616,7 @@ PRV_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
req.op = OP_NAME2IPADDRESS;
if (snprintf(req.data.name_to_ipaddress.name, sizeof (req.data.name_to_ipaddress.name),
"%s", name) >= sizeof (req.data.name_to_ipaddress.name)) {
DEBUG_LOG("Name too long");
return DNS_Failure;
}
@@ -638,14 +670,20 @@ void
PRV_StartHelper(void)
{
pid_t pid;
int fd, sock_fd1, sock_fd2;
int fd, sock_pair[2];
if (have_helper())
LOG_FATAL("Helper already running");
sock_fd1 = SCK_OpenUnixSocketPair(SCK_FLAG_BLOCK, &sock_fd2);
if (sock_fd1 < 0)
LOG_FATAL("Could not open socket pair");
if (
#ifdef SOCK_SEQPACKET
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock_pair) &&
#endif
socketpair(AF_UNIX, SOCK_DGRAM, 0, sock_pair))
LOG_FATAL("socketpair() failed : %s", strerror(errno));
UTI_FdSetCloexec(sock_pair[0]);
UTI_FdSetCloexec(sock_pair[1]);
pid = fork();
if (pid < 0)
@@ -653,26 +691,23 @@ PRV_StartHelper(void)
if (pid == 0) {
/* child process */
SCK_CloseSocket(sock_fd1);
close(sock_pair[0]);
/* close other descriptors inherited from the parent process, except
stdin, stdout, and stderr */
for (fd = STDERR_FILENO + 1; fd < 1024; fd++) {
if (fd != sock_fd2)
/* close other descriptors inherited from the parent process */
for (fd = 0; fd < 1024; fd++) {
if (fd != sock_pair[1])
close(fd);
}
UTI_ResetGetRandomFunctions();
/* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
helper_main(sock_fd2);
helper_main(sock_pair[1]);
} else {
/* parent process */
SCK_CloseSocket(sock_fd2);
helper_fd = sock_fd1;
close(sock_pair[1]);
helper_fd = sock_pair[0];
helper_pid = pid;
/* stop the helper even when not exiting cleanly from the main function */

69
ptp.h
View File

@@ -1,69 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
This is the header file for the Precision Time Protocol (PTP).
*/
#ifndef GOT_PTP_H
#define GOT_PTP_H
#include "sysincl.h"
#include "ntp.h"
#define PTP_VERSION 2
#define PTP_TYPE_DELAY_REQ 1
#define PTP_DOMAIN_NTP 123
#define PTP_FLAG_UNICAST (1 << (2 + 8))
#define PTP_TLV_NTP 0x2023
typedef struct {
uint8_t type;
uint8_t version;
uint16_t length;
uint8_t domain;
uint8_t min_sdoid;
uint16_t flags;
uint8_t correction[8];
uint8_t msg_specific[4];
uint8_t port_id[10];
uint16_t sequence_id;
uint8_t control;
int8_t interval;
} PTP_Header;
typedef struct {
uint16_t type;
uint16_t length;
} PTP_TlvHeader;
typedef struct {
PTP_Header header;
uint8_t origin_ts[10];
PTP_TlvHeader tlv_header;
NTP_Packet ntp_msg;
} PTP_NtpMessage;
#define PTP_NTP_PREFIX_LENGTH (int)offsetof(PTP_NtpMessage, ntp_msg)
#endif

View File

@@ -1,209 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Estimation of quantiles using the Frugal-2U streaming algorithm
(https://arxiv.org/pdf/1407.1121v1.pdf)
*/
#include "config.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
/* Maximum number of repeated estimates for stabilisation */
#define MAX_REPEAT 64
struct Quantile {
double est;
double step;
int sign;
};
struct QNT_Instance_Record {
struct Quantile *quants;
int n_quants;
int repeat;
int q;
int min_k;
double min_step;
int n_set;
};
/* ================================================== */
QNT_Instance
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
{
QNT_Instance inst;
long seed;
if (q < 2 || min_k > max_k || min_k < 1 || max_k >= q ||
repeat < 1 || repeat > MAX_REPEAT || min_step <= 0.0)
assert(0);
inst = MallocNew(struct QNT_Instance_Record);
inst->n_quants = (max_k - min_k + 1) * repeat;
inst->quants = MallocArray(struct Quantile, inst->n_quants);
inst->repeat = repeat;
inst->q = q;
inst->min_k = min_k;
inst->min_step = min_step;
QNT_Reset(inst);
/* Seed the random number generator, which will not be isolated from
other instances and other random() users */
UTI_GetRandomBytes(&seed, sizeof (seed));
srandom(seed);
return inst;
}
/* ================================================== */
void
QNT_DestroyInstance(QNT_Instance inst)
{
Free(inst->quants);
Free(inst);
}
/* ================================================== */
void
QNT_Reset(QNT_Instance inst)
{
int i;
inst->n_set = 0;
for (i = 0; i < inst->n_quants; i++) {
inst->quants[i].est = 0.0;
inst->quants[i].step = inst->min_step;
inst->quants[i].sign = 1;
}
}
/* ================================================== */
static void
insert_initial_value(QNT_Instance inst, double value)
{
int i, j, r = inst->repeat;
if (inst->n_set * r >= inst->n_quants)
assert(0);
/* Keep the initial estimates repeated and ordered */
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
}
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = value;
inst->n_set++;
/* Duplicate the largest value in unset quantiles */
for (i = inst->n_set * r; i < inst->n_quants; i++)
inst->quants[i].est = inst->quants[i - 1].est;
}
/* ================================================== */
static void
update_estimate(struct Quantile *quantile, double value, double p, double rand,
double min_step)
{
if (value > quantile->est && rand > (1.0 - p)) {
quantile->step += quantile->sign > 0 ? min_step : -min_step;
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est > value) {
quantile->step += value - quantile->est;
quantile->est = value;
}
if (quantile->sign < 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = 1;
} else if (value < quantile->est && rand > p) {
quantile->step += quantile->sign < 0 ? min_step : -min_step;
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est < value) {
quantile->step += quantile->est - value;
quantile->est = value;
}
if (quantile->sign > 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = -1;
}
}
/* ================================================== */
void
QNT_Accumulate(QNT_Instance inst, double value)
{
double p, rand;
int i;
/* Initialise the estimates with first received values */
if (inst->n_set * inst->repeat < inst->n_quants) {
insert_initial_value(inst, value);
return;
}
for (i = 0; i < inst->n_quants; i++) {
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
rand = (double)random() / ((1U << 31) - 1);
update_estimate(&inst->quants[i], value, p, rand, inst->min_step);
}
}
/* ================================================== */
int
QNT_GetMinK(QNT_Instance inst)
{
return inst->min_k;
}
/* ================================================== */
double
QNT_GetQuantile(QNT_Instance inst, int k)
{
double estimates[MAX_REPEAT];
int i;
if (k < inst->min_k || k - inst->min_k >= inst->n_quants)
assert(0);
for (i = 0; i < inst->repeat; i++)
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
return RGR_FindMedian(estimates, inst->repeat);
}

View File

@@ -1,41 +0,0 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Header file for estimation of quantiles.
*/
#ifndef GOT_QUANTILES_H
#define GOT_QUANTILES_H
typedef struct QNT_Instance_Record *QNT_Instance;
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step);
extern void QNT_DestroyInstance(QNT_Instance inst);
extern void QNT_Reset(QNT_Instance inst);
extern void QNT_Accumulate(QNT_Instance inst, double value);
extern int QNT_GetMinK(QNT_Instance inst);
extern double QNT_GetQuantile(QNT_Instance inst, int k);
#endif

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -40,9 +40,6 @@
#include "samplefilt.h"
#include "sched.h"
/* Maximum offset of locked reference as a fraction of the PPS interval */
#define PPS_LOCK_LIMIT 0.4
/* list of refclock drivers */
extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver;
@@ -55,6 +52,21 @@ struct FilterSample {
struct timespec sample_time;
};
struct MedianFilter {
int length;
int index;
int used;
int last;
int avg_var_n;
double avg_var;
double max_var;
struct FilterSample *samples;
int *selected;
double *x_data;
double *y_data;
double *w_data;
};
struct RCL_Instance_Record {
RefclockDriver *driver;
void *data;
@@ -64,7 +76,6 @@ struct RCL_Instance_Record {
int driver_polled;
int poll;
int leap_status;
int local;
int pps_forced;
int pps_rate;
int pps_active;
@@ -170,13 +181,12 @@ RCL_AddRefclock(RefclockParameters *params)
LOG_FATAL("refclock tai option requires leapsectz");
inst->data = NULL;
inst->driver_parameter = Strdup(params->driver_parameter);
inst->driver_parameter = params->driver_parameter;
inst->driver_parameter_length = 0;
inst->driver_poll = params->driver_poll;
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->local = params->local;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
@@ -218,13 +228,6 @@ RCL_AddRefclock(RefclockParameters *params)
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
}
if (inst->local) {
inst->pps_forced = 1;
inst->lock_ref = inst->ref_id;
inst->leap_status = LEAP_Unsynchronised;
inst->max_lock_age = MAX(inst->max_lock_age, 3);
}
if (inst->driver->poll) {
int max_samples;
@@ -250,21 +253,22 @@ RCL_AddRefclock(RefclockParameters *params)
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
NULL, params->min_samples, params->max_samples,
0.0, 0.0);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples, 0.0, 0.0);
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
params->driver_name, UTI_RefidToString(inst->ref_id),
inst->poll, inst->driver_poll, params->filter_length);
Free(params->driver_name);
return 1;
}
void
RCL_StartRefclocks(void)
{
unsigned int i, j, n, lock_index;
unsigned int i, j, n;
n = ARR_GetSize(refclocks);
@@ -274,31 +278,13 @@ RCL_StartRefclocks(void)
SRC_SetActive(inst->source);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
/* Replace lock refid with the refclock's index, or -1 if not valid */
lock_index = -1;
if (inst->lock_ref != 0) {
for (j = 0; j < n; j++) {
RCL_Instance inst2 = get_refclock(j);
if (inst->lock_ref != inst2->ref_id)
continue;
if (inst->driver->poll && inst2->driver->poll &&
(double)inst->max_lock_age / inst->pps_rate < UTI_Log2ToDouble(inst2->driver_poll))
LOG(LOGS_WARN, "%s maxlockage too small for %s",
UTI_RefidToString(inst->ref_id), UTI_RefidToString(inst2->ref_id));
lock_index = j;
break;
}
if (lock_index == -1 || (lock_index == i && !inst->local))
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
}
inst->lock_ref = lock_index;
if (inst->lock_ref) {
/* Replace lock refid with index to refclocks */
for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++)
;
inst->lock_ref = j < n ? j : -1;
} else
inst->lock_ref = -1;
}
}
@@ -429,29 +415,32 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
sample.root_delay = instance->delay;
sample.peer_dispersion = dispersion;
sample.root_dispersion = dispersion;
sample.leap = instance->leap_status;
/* Handle special case when PPS is used with the local reference */
if (instance->pps_active && instance->lock_ref == -1)
sample.stratum = pps_stratum(instance, &sample.time);
else
sample.stratum = instance->stratum;
return SPF_AccumulateSample(instance->filter, &sample);
}
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap)
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
double correction, dispersion, raw_offset, offset;
double correction, dispersion;
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));
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
return RCL_AddPulse(instance, sample_time, -offset);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
!valid_sample_time(instance, &cooked_time))
return 0;
@@ -466,24 +455,18 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
return 0;
}
/* Calculate offset = raw_offset - correction + instance->offset
in parts to avoid loss of precision if there are large differences */
offset = ref_time->tv_sec - sample_time->tv_sec -
(time_t)correction + (time_t)instance->offset;
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0;
}
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
if (!accumulate_sample(instance, &cooked_time,
offset - correction + instance->offset, dispersion))
return 0;
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
/* for logging purposes */
if (!instance->driver->poll)
@@ -562,40 +545,29 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
lock_refclock = get_refclock(instance->lock_ref);
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
if (instance->local) {
/* Make the first sample in order to lock to itself */
ref_sample.time = *cooked_time;
ref_sample.offset = offset;
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
ref_sample.root_delay = ref_sample.root_dispersion = 0;
} else {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
/* Restart the local mode */
if (instance->local) {
LOG(LOGS_WARN, "Local refclock lost lock");
SPF_DropSamples(instance->filter);
SRC_ResetInstance(instance->source);
}
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
return 0;
}
/* Align the offset to the reference sample */
shift = round((ref_sample.offset - offset) * rate) / rate;
if ((ref_sample.offset - offset) >= 0.0)
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
offset += shift;
if (fabs(ref_sample.offset - offset) +
ref_sample.root_dispersion + dispersion > PPS_LOCK_LIMIT / rate) {
ref_sample.root_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0;
@@ -711,57 +683,11 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
return 0;
}
static void
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
{
double offset_sd, freq_sd, skew, root_delay, root_disp;
SST_Stats stats = SRC_GetSourcestats(inst->source);
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
UTI_ZeroTimespec(ref);
return;
}
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
&skew, &root_delay, &root_disp);
}
static void
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
double prev_offset)
{
SST_Stats stats = SRC_GetSourcestats(inst->source);
double freq, dfreq, offset, doffset, elapsed;
struct timespec now, ref_time;
get_local_stats(inst, &ref_time, &freq, &offset);
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
return;
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
doffset = offset - elapsed * prev_freq - prev_offset;
if (!REF_AdjustReference(doffset, dfreq))
return;
LCL_ReadCookedTime(&now, NULL);
SST_SlewSamples(stats, &now, dfreq, doffset);
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
/* Keep the offset close to zero to not lose precision */
if (fabs(offset) >= 1.0) {
SST_CorrectOffset(stats, -round(offset));
SPF_CorrectOffset(inst->filter, -round(offset));
}
}
static void
poll_timeout(void *arg)
{
NTP_Sample sample;
int poll, stratum;
int poll;
RCL_Instance inst = (RCL_Instance)arg;
@@ -777,28 +703,10 @@ poll_timeout(void *arg)
inst->driver_polled = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) {
double local_freq, local_offset;
struct timespec local_ref_time;
/* Handle special case when PPS is used with the local reference */
if (inst->pps_active && inst->lock_ref == -1)
stratum = pps_stratum(inst, &sample.time);
else
stratum = inst->stratum;
if (inst->local) {
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
inst->leap_status = LEAP_Unsynchronised;
}
SRC_UpdateReachability(inst->source, 1);
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
if (inst->local)
follow_local(inst, &local_ref_time, local_freq, local_offset);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);

View File

@@ -37,7 +37,6 @@ typedef struct {
int driver_poll;
int poll;
int filter_length;
int local;
int pps_forced;
int pps_rate;
int min_samples;
@@ -75,8 +74,7 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
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_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013, 2017, 2023
* Copyright (C) Miroslav Lichvar 2013, 2017
*
* 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
@@ -33,9 +33,6 @@
#include "sysincl.h"
#include <sys/sysmacros.h>
#include "array.h"
#include "refclock.h"
#include "hwclock.h"
#include "local.h"
@@ -47,19 +44,14 @@
struct phc_instance {
int fd;
int dev_index;
int mode;
int nocrossts;
int extpps;
int pin;
int channel;
struct timespec last_extts;
HCL_Instance clock;
};
/* Array of RCL_Instance with enabled extpps */
static ARR_Instance extts_phcs = NULL;
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance)
@@ -67,7 +59,6 @@ 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;
struct stat st;
char *path, *s;
RCL_CheckDriverOptions(instance, options);
@@ -75,20 +66,16 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0)
if (phc_fd < 0) {
LOG_FATAL("Could not open PHC");
return 0;
}
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->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin");
@@ -96,18 +83,16 @@ static int phc_initialise(RCL_Instance instance)
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1))
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
if (!extts_phcs)
extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
ARR_AppendElement(extts_phcs, &instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
}
RCL_SetDriverData(instance, phc);
@@ -117,106 +102,68 @@ static int phc_initialise(RCL_Instance instance)
static void phc_finalise(RCL_Instance instance)
{
struct phc_instance *phc;
unsigned int i;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
ARR_RemoveElement(extts_phcs, i--);
}
if (ARR_GetSize(extts_phcs) == 0) {
ARR_DestroyInstance(extts_phcs);
extts_phcs = NULL;
}
HCL_DestroyInstance(phc->clock);
}
HCL_DestroyInstance(phc->clock);
close(phc->fd);
Free(phc);
}
static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
{
struct phc_instance *phc;
struct timespec local_ts;
double local_err;
phc = RCL_GetDriverData(instance);
if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
DEBUG_LOG("Ignoring duplicated PHC timestamp");
return;
}
phc->last_extts = *phc_ts;
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc1, *phc2;
struct timespec phc_ts;
unsigned int i;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
double local_err;
int channel;
if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
instance = anything;
phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
instance = anything;
phc1 = RCL_GetDriverData(instance);
/* The Linux kernel (as of 6.2) has one shared queue of timestamps for all
descriptors of the same PHC. Search for all refclocks that expect
the timestamp. */
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
phc2 = RCL_GetDriverData(instance);
if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
continue;
process_ext_pulse(instance, &phc_ts);
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return;
}
}
#define PHC_READINGS 25
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
}
static 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;
struct timespec phc_ts, sys_ts, local_ts;
double offset, phc_err, local_err;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
PHC_READINGS, readings);
if (n_readings < 1)
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &phc_err))
return 0;
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0;
}
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
if (phc->extpps)
return 0;
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
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, offset, LEAP_Normal);
}
RefclockDriver RCL_PHC_driver = {

View File

@@ -61,36 +61,49 @@ static int pps_initialise(RCL_Instance instance) {
edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
fd = open(path, O_RDWR);
if (fd < 0)
if (fd < 0) {
LOG_FATAL("Could not open %s : %s", path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0)
if (time_pps_create(fd, &handle) < 0) {
LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getcap(handle, &mode) < 0)
if (time_pps_getcap(handle, &mode) < 0) {
LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getparams(handle, &params) < 0)
if (time_pps_getparams(handle, &params) < 0) {
LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
return 0;
}
if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT))
if (!(mode & PPS_CAPTUREASSERT)) {
LOG_FATAL("CAPTUREASSERT not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR;
} else {
if (!(mode & PPS_CAPTURECLEAR))
if (!(mode & PPS_CAPTURECLEAR)) {
LOG_FATAL("CAPTURECLEAR not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTURECLEAR;
params.mode &= ~PPS_CAPTUREASSERT;
}
if (time_pps_setparams(handle, &params) < 0)
if (time_pps_setparams(handle, &params) < 0) {
LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
return 0;
}
pps = MallocNew(struct pps_instance);
pps->handle = handle;

View File

@@ -95,6 +95,7 @@ static int shm_poll(RCL_Instance instance)
{
struct timespec receive_ts, clock_ts;
struct shmTime t, *shm;
double offset;
shm = (struct shmTime *)RCL_GetDriverData(instance);
@@ -123,8 +124,9 @@ static int shm_poll(RCL_Instance instance)
UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts);
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
}
RefclockDriver RCL_SHM_driver = {

View File

@@ -33,7 +33,6 @@
#include "logging.h"
#include "util.h"
#include "sched.h"
#include "socket.h"
#define SOCK_MAGIC 0x534f434b
@@ -58,63 +57,23 @@ struct sock_sample {
int magic;
};
/* On 32-bit glibc-based systems enable conversion between timevals using
32-bit and 64-bit time_t to support SOCK clients compiled with different
time_t size than chrony */
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 34) && __TIMESIZE == 32
#define CONVERT_TIMEVAL 1
#if defined(_TIME_BITS) && _TIME_BITS == 64
typedef int32_t alt_time_t;
typedef int32_t alt_suseconds_t;
#else
typedef int64_t alt_time_t;
typedef int64_t alt_suseconds_t;
#endif
struct alt_timeval {
alt_time_t tv_sec;
alt_suseconds_t tv_usec;
};
#endif
#endif
static void read_sample(int sockfd, int event, void *anything)
{
char buf[sizeof (struct sock_sample) + 16];
struct timespec sys_ts, ref_ts;
struct sock_sample sample;
struct timespec ts;
RCL_Instance instance;
int s;
instance = (RCL_Instance)anything;
s = recv(sockfd, buf, sizeof (buf), 0);
s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 0) {
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return;
}
if (s == sizeof (sample)) {
memcpy(&sample, buf, sizeof (sample));
#ifdef CONVERT_TIMEVAL
} else if (s == sizeof (sample) - sizeof (struct timeval) + sizeof (struct alt_timeval)) {
struct alt_timeval atv;
memcpy(&atv, buf, sizeof (atv));
#ifndef HAVE_LONG_TIME_T
if (atv.tv_sec > INT32_MAX || atv.tv_sec < INT32_MIN ||
atv.tv_usec > INT32_MAX || atv.tv_usec < INT32_MIN) {
DEBUG_LOG("Could not convert 64-bit timeval");
return;
}
#endif
sample.tv.tv_sec = atv.tv_sec;
sample.tv.tv_usec = atv.tv_usec;
DEBUG_LOG("Converted %d-bit timeval", 8 * (int)sizeof (alt_time_t));
memcpy((char *)&sample + sizeof (struct timeval), buf + sizeof (struct alt_timeval),
sizeof (sample) - sizeof (struct timeval));
#endif
} else {
if (s != sizeof (sample)) {
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
return;
@@ -126,23 +85,19 @@ static void read_sample(int sockfd, int event, void *anything)
return;
}
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
if (sample.pulse) {
RCL_AddPulse(instance, &sys_ts, sample.offset);
RCL_AddPulse(instance, &ts, sample.offset);
} else {
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
}
}
static int sock_initialise(RCL_Instance instance)
{
struct sockaddr_un s;
int sockfd;
char *path;
@@ -150,9 +105,25 @@ static int sock_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance);
sockfd = SCK_OpenUnixDatagramSocket(NULL, path, 0);
if (sockfd < 0)
LOG_FATAL("Could not open socket %s", path);
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
LOG_FATAL("Path %s too long", path);
return 0;
}
sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sockfd < 0) {
LOG_FATAL("socket() failed");
return 0;
}
UTI_FdSetCloexec(sockfd);
unlink(path);
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
LOG_FATAL("bind(%s) failed : %s", path, strerror(errno));
return 0;
}
RCL_SetDriverData(instance, (void *)(long)sockfd);
SCH_AddFileHandler(sockfd, SCH_FILE_INPUT, read_sample, instance);
@@ -165,8 +136,7 @@ static void sock_finalise(RCL_Instance instance)
sockfd = (long)RCL_GetDriverData(instance);
SCH_RemoveFileHandler(sockfd);
SCK_RemoveSocket(sockfd);
SCK_CloseSocket(sockfd);
close(sockfd);
}
RefclockDriver RCL_SOCK_driver = {

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* Copyright (C) Miroslav Lichvar 2009-2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -42,18 +42,11 @@
/* The minimum allowed skew */
#define MIN_SKEW 1.0e-12
/* The update interval of the reference in the local reference mode */
#define LOCAL_REF_UPDATE_INTERVAL 64.0
/* Interval between updates of the drift file */
#define MAX_DRIFTFILE_AGE 3600.0
static int are_we_synchronised;
static int enable_local_stratum;
static int local_stratum;
static int local_orphan;
static double local_distance;
static struct timespec local_ref_time;
static NTP_Leap our_leap_status;
static int our_leap_sec;
static int our_tai_offset;
@@ -65,8 +58,6 @@ static double our_skew;
static double our_residual_freq;
static double our_root_delay;
static double our_root_dispersion;
static double our_offset_sd;
static double our_frequency_sd;
static double max_update_skew;
@@ -112,9 +103,6 @@ static void update_drift_file(double, double);
/* Leap second handling mode */
static REF_LeapMode leap_mode;
/* Time of UTC midnight of the upcoming or previous leap second */
static time_t leap_when;
/* Flag indicating the clock was recently corrected for leap second and it may
not have correct time yet (missing 23:59:60 in the UTC time scale) */
static int leap_in_progress;
@@ -146,13 +134,10 @@ static struct fb_drift *fb_drifts = NULL;
static int next_fb_drift;
static SCH_TimeoutID fb_drift_timeout_id;
/* Monotonic timestamp of the last reference update */
static double last_ref_update;
/* Timestamp of last reference update */
static struct timespec last_ref_update;
static double last_ref_update_interval;
static double last_ref_adjustment;
static int ref_adjustments;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
@@ -175,8 +160,9 @@ handle_slew(struct timespec *raw,
UTI_AdjustTimespec(&our_ref_time, cooked, &our_ref_time, &delta, dfreq, doffset);
if (change_type == LCL_ChangeUnknownStep) {
last_ref_update = 0.0;
REF_SetUnsynchronised();
UTI_ZeroTimespec(&last_ref_update);
} else if (last_ref_update.tv_sec) {
UTI_AdjustTimespec(&last_ref_update, cooked, &last_ref_update, &delta, dfreq, doffset);
}
/* When the clock was stepped, check if that doesn't change our leap status
@@ -208,14 +194,12 @@ REF_Initialise(void)
our_frequency_ppm = 0.0;
our_skew = 1.0; /* i.e. rather bad */
our_residual_freq = 0.0;
our_frequency_sd = 0.0;
our_offset_sd = 0.0;
drift_file_age = 0.0;
/* Now see if we can get the drift file opened */
drift_file = CNF_GetDriftFile();
if (drift_file) {
in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
in = fopen(drift_file, "r");
if (in) {
if (fscanf(in, "%lf%lf", &file_freq_ppm, &file_skew_ppm) == 2) {
/* We have read valid data */
@@ -250,9 +234,7 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
UTI_ZeroTimespec(&local_ref_time);
leap_when = 0;
leap_timeout_id = 0;
leap_in_progress = 0;
leap_mode = CNF_GetLeapSecMode();
@@ -287,10 +269,8 @@ REF_Initialise(void)
}
UTI_ZeroTimespec(&our_ref_time);
last_ref_update = 0.0;
UTI_ZeroTimespec(&last_ref_update);
last_ref_update_interval = 0.0;
last_ref_adjustment = 0.0;
ref_adjustments = 0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -309,8 +289,6 @@ REF_Finalise(void)
update_drift_file(LCL_ReadAbsoluteFrequency(), our_skew);
}
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
Free(fb_drifts);
initialised = 0;
@@ -353,20 +331,61 @@ REF_GetLeapMode(void)
static void
update_drift_file(double freq_ppm, double skew)
{
struct stat buf;
char *temp_drift_file;
FILE *out;
int r1, r2;
/* Create a temporary file with a '.tmp' extension. */
out = UTI_OpenFile(NULL, drift_file, ".tmp", 'w', 0644);
if (!out)
temp_drift_file = (char*) Malloc(strlen(drift_file)+8);
if(!temp_drift_file) {
return;
}
strcpy(temp_drift_file,drift_file);
strcat(temp_drift_file,".tmp");
out = fopen(temp_drift_file, "w");
if (!out) {
Free(temp_drift_file);
LOG(LOGS_WARN, "Could not open temporary driftfile %s.tmp for writing",
drift_file);
return;
}
/* Write the frequency and skew parameters in ppm */
fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
fclose(out);
r1 = fprintf(out, "%20.6f %20.6f\n", freq_ppm, 1.0e6 * skew);
r2 = fclose(out);
if (r1 < 0 || r2) {
Free(temp_drift_file);
LOG(LOGS_WARN, "Could not write to temporary driftfile %s.tmp",
drift_file);
return;
}
/* Rename the temporary file to the correct location */
if (!UTI_RenameTempFile(NULL, drift_file, ".tmp", NULL))
;
/* Clone the file attributes from the existing file if there is one. */
if (!stat(drift_file,&buf)) {
if (chown(temp_drift_file,buf.st_uid,buf.st_gid) ||
chmod(temp_drift_file,buf.st_mode & 0777)) {
LOG(LOGS_WARN, "Could not change ownership or permissions of temporary driftfile %s.tmp",
drift_file);
}
}
/* Rename the temporary file to the correct location (see rename(2) for details). */
if (rename(temp_drift_file,drift_file)) {
unlink(temp_drift_file);
Free(temp_drift_file);
LOG(LOGS_WARN, "Could not replace old driftfile %s with new one %s.tmp",
drift_file,drift_file);
return;
}
Free(temp_drift_file);
}
/* ================================================== */
@@ -432,16 +451,16 @@ fb_drift_timeout(void *arg)
/* ================================================== */
static void
schedule_fb_drift(void)
schedule_fb_drift(struct timespec *now)
{
int i, c, secs;
double unsynchronised, now;
double unsynchronised;
struct timespec when;
if (fb_drift_timeout_id)
return; /* already scheduled */
now = SCH_GetLastEventMonoTime();
unsynchronised = now - last_ref_update;
unsynchronised = UTI_DiffTimespecsToDouble(now, &last_ref_update);
for (c = secs = 0, i = fb_drift_min; i <= fb_drift_max; i++) {
secs = 1 << i;
@@ -463,7 +482,8 @@ schedule_fb_drift(void)
if (i <= fb_drift_max) {
next_fb_drift = i;
fb_drift_timeout_id = SCH_AddTimeoutByDelay(secs - unsynchronised, fb_drift_timeout, NULL);
UTI_AddDoubleToTimespec(now, secs - unsynchronised, &when);
fb_drift_timeout_id = SCH_AddTimeout(&when, fb_drift_timeout, NULL);
DEBUG_LOG("Fallback drift %d scheduled", i);
}
}
@@ -496,7 +516,8 @@ maybe_log_offset(double offset, time_t now)
abs_offset = fabs(offset);
if (abs_offset > log_change_threshold) {
LOG(LOGS_WARN, "System clock wrong by %.6f seconds", -offset);
LOG(LOGS_WARN, "System clock wrong by %.6f seconds, adjustment started",
-offset);
}
if (do_mail_change &&
@@ -562,7 +583,8 @@ is_offset_ok(double offset)
return 1;
}
if (fabs(offset) > max_offset) {
offset = fabs(offset);
if (offset > max_offset) {
LOG(LOGS_WARN,
"Adjustment of %.3f seconds exceeds the allowed maximum of %.3f seconds (%s) ",
-offset, max_offset, !max_offset_ignore ? "exiting" : "ignored");
@@ -729,12 +751,10 @@ set_leap_timeout(time_t now)
if (!our_leap_sec)
return;
leap_when = (now / (24 * 3600) + 1) * (24 * 3600);
/* Insert leap second at 0:00:00 UTC, delete at 23:59:59 UTC. If the clock
will be corrected by the system, timeout slightly sooner to be sure it
will happen before the system correction. */
when.tv_sec = leap_when;
when.tv_sec = (now / (24 * 3600) + 1) * (24 * 3600);
when.tv_nsec = 0;
if (our_leap_sec < 0)
when.tv_sec--;
@@ -778,7 +798,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
}
if ((leap_sec != our_leap_sec || tai_offset != our_tai_offset)
&& !REF_IsLeapSecondClose(NULL, 0.0)) {
&& !REF_IsLeapSecondClose()) {
our_leap_sec = leap_sec;
our_tai_offset = tai_offset;
@@ -817,20 +837,6 @@ get_root_dispersion(struct timespec *ts)
/* ================================================== */
static void
update_sync_status(struct timespec *now)
{
double elapsed;
elapsed = fabs(UTI_DiffTimespecsToDouble(now, &our_ref_time));
LCL_SetSyncStatus(are_we_synchronised,
our_offset_sd + elapsed * our_frequency_sd,
our_root_delay / 2.0 + get_root_dispersion(now));
}
/* ================================================== */
static void
write_log(struct timespec *now, int combined_sources, double freq,
double offset, double offset_sd, double uncorrected_offset,
@@ -953,39 +959,6 @@ get_clock_estimates(int manual,
/* ================================================== */
static void
fuzz_ref_time(struct timespec *ts)
{
uint32_t rnd;
/* Add a random value from interval [-1.0, 0.0] */
UTI_GetRandomBytes(&rnd, sizeof (rnd));
UTI_AddDoubleToTimespec(ts, -(double)rnd / (uint32_t)-1, ts);
}
/* ================================================== */
static double
get_correction_rate(double offset_sd, double update_interval)
{
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
return correction_time_ratio * 0.5 * offset_sd * update_interval;
}
/* ================================================== */
void
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
@@ -995,8 +968,9 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
{
double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency;
double elapsed, mono_now, update_interval, orig_root_distance;
double elapsed, update_interval, correction_rate, orig_root_distance;
struct timespec now, raw_now;
NTP_int64 ref_fuzz;
int manual;
assert(initialised);
@@ -1009,16 +983,17 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
manual = leap == LEAP_Unsynchronised;
mono_now = SCH_GetLastEventMonoTime();
LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
offset += elapsed * frequency;
offset_sd += elapsed * frequency_sd;
if (last_ref_update != 0.0) {
update_interval = mono_now - last_ref_update;
if (last_ref_update.tv_sec) {
update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
update_interval = MAX(update_interval, 0.0);
} else {
update_interval = 0.0;
}
@@ -1044,12 +1019,25 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
our_frequency_sd = frequency_sd;
our_offset_sd = offset_sd;
last_ref_update = mono_now;
last_ref_update = now;
last_ref_update_interval = update_interval;
last_offset = offset;
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
@@ -1061,9 +1049,9 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
}
/* Adjust the clock */
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
get_correction_rate(offset_sd, update_interval));
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
update_leap_status(leap, raw_now.tv_sec, 0);
maybe_log_offset(offset, raw_now.tv_sec);
if (step_offset != 0.0) {
@@ -1071,13 +1059,17 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
}
update_leap_status(leap, raw_now.tv_sec, 0);
update_sync_status(&now);
LCL_SetSyncStatus(are_we_synchronised, offset_sd,
root_delay / 2.0 + get_root_dispersion(&now));
/* Add a random error of up to one second to the reference time to make it
less useful when disclosed to NTP and cmdmon clients for estimating
receive timestamps in the interleaved symmetric NTP mode */
fuzz_ref_time(&our_ref_time);
UTI_GetNtp64Fuzz(&ref_fuzz, 0);
UTI_TimespecToNtp64(&our_ref_time, &ref_fuzz, &ref_fuzz);
UTI_Ntp64ToTimespec(&ref_fuzz, &our_ref_time);
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
our_ref_time.tv_sec--;
local_abs_frequency = LCL_ReadAbsoluteFrequency();
@@ -1087,7 +1079,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
if (drift_file) {
/* Update drift file at most once per hour */
drift_file_age += update_interval;
if (drift_file_age >= MAX_DRIFTFILE_AGE) {
if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
update_drift_file(local_abs_frequency, our_skew);
drift_file_age = 0.0;
}
@@ -1096,7 +1088,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
/* Update fallback drifts */
if (fb_drifts && are_we_synchronised) {
update_fb_drifts(local_abs_frequency, update_interval);
schedule_fb_drift();
schedule_fb_drift(&now);
}
/* Update the moving average of squares of offset, quickly on start */
@@ -1107,27 +1099,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
avg2_moving = 1;
avg2_offset = SQUARE(offset);
}
ref_adjustments = 0;
}
/* ================================================== */
int
REF_AdjustReference(double offset, double frequency)
{
double adj_corr_rate, ref_corr_rate, mono_now;
mono_now = SCH_GetLastEventMonoTime();
ref_adjustments++;
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
ref_adjustments;
last_ref_adjustment = mono_now;
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
MAX(adj_corr_rate, ref_corr_rate));
}
/* ================================================== */
@@ -1170,7 +1141,7 @@ REF_SetUnsynchronised(void)
UTI_AddDoubleToTimespec(&now_raw, uncorrected_offset, &now);
if (fb_drifts) {
schedule_fb_drift();
schedule_fb_drift(&now);
}
update_leap_status(LEAP_Unsynchronised, 0, 0);
@@ -1187,25 +1158,6 @@ REF_SetUnsynchronised(void)
/* ================================================== */
void
REF_UpdateLeapStatus(NTP_Leap leap)
{
struct timespec raw_now, now;
/* Wait for a full reference update if not already synchronised */
if (!are_we_synchronised)
return;
SCH_GetLastEventTime(&now, NULL, &raw_now);
update_leap_status(leap, raw_now.tv_sec, 0);
/* Update also the synchronisation status */
update_sync_status(&now);
}
/* ================================================== */
void
REF_GetReferenceParams
(
@@ -1219,7 +1171,7 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double dispersion, delta;
double dispersion;
assert(initialised);
@@ -1251,17 +1203,13 @@ REF_GetReferenceParams
*stratum = local_stratum;
*ref_id = NTP_REFID_LOCAL;
/* Keep the reference timestamp up to date. Adjust the timestamp to make
sure that the transmit timestamp cannot come before this (which might
fail a test of an NTP client). */
delta = UTI_DiffTimespecsToDouble(local_time, &local_ref_time);
if (delta > LOCAL_REF_UPDATE_INTERVAL || delta < 1.0) {
UTI_AddDoubleToTimespec(local_time, -1.0, &local_ref_time);
fuzz_ref_time(&local_ref_time);
}
*ref_time = local_ref_time;
/* Make the reference time be now less a second - this will
scarcely affect the client, but will ensure that the transmit
timestamp cannot come before this (which would cause test 7 to
fail in the client's read routine) if the local system clock's
read routine is broken in any way. */
*ref_time = *local_time;
--ref_time->tv_sec;
/* Not much else we can do for leap second bits - maybe need to
have a way for the administrator to feed leap bits in */
@@ -1329,7 +1277,6 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{
max_update_skew = new_max_update_skew * 1.0e-6;
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
}
/* ================================================== */
@@ -1339,7 +1286,6 @@ REF_ModifyMakestep(int limit, double threshold)
{
make_step_limit = limit;
make_step_threshold = threshold;
LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
}
/* ================================================== */
@@ -1351,7 +1297,6 @@ REF_EnableLocal(int stratum, double distance, int orphan)
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
/* ================================================== */
@@ -1360,32 +1305,28 @@ void
REF_DisableLocal(void)
{
enable_local_stratum = 0;
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
}
/* ================================================== */
#define LEAP_SECOND_CLOSE 5
static int
is_leap_close(time_t t)
{
return leap_when != 0 &&
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
}
/* ================================================== */
int REF_IsLeapSecondClose(struct timespec *ts, double offset)
int REF_IsLeapSecondClose(void)
{
struct timespec now, now_raw;
time_t t;
if (!our_leap_sec)
return 0;
SCH_GetLastEventTime(&now, NULL, &now_raw);
if (is_leap_close(now.tv_sec) || is_leap_close(now_raw.tv_sec))
t = now.tv_sec > 0 ? now.tv_sec : -now.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
if (ts && (is_leap_close(ts->tv_sec) || is_leap_close(ts->tv_sec + offset)))
t = now_raw.tv_sec > 0 ? now_raw.tv_sec : -now_raw.tv_sec;
if ((t + LEAP_SECOND_CLOSE) % (24 * 3600) < 2 * LEAP_SECOND_CLOSE)
return 1;
return 0;

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