chrony/cmdmon.c
Miroslav Lichvar 070b4f69d0 ntp: add maxdelayquant option
Add a new test for maximum delay using a long-term estimate of a
p-quantile of the peer delay. If enabled, it replaces the
maxdelaydevratio test. It's main advantage is that it is not sensitive
to outliers corrupting the minimum delay.

As it can take a large number of samples for the estimate to reach the
expected value and adapt to a new value after a network change, the
option is recommended only for local networks with very short polling
intervals.
2022-07-21 16:05:48 +02:00

1813 lines
56 KiB
C

/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-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.
*
**********************************************************************
=======================================================================
Command and monitoring module in the main program
*/
#include "config.h"
#include "sysincl.h"
#include "cmdmon.h"
#include "candm.h"
#include "sched.h"
#include "util.h"
#include "logging.h"
#include "keys.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "smooth.h"
#include "socket.h"
#include "sources.h"
#include "sourcestats.h"
#include "reference.h"
#include "manual.h"
#include "memory.h"
#include "nts_ke_server.h"
#include "local.h"
#include "addrfilt.h"
#include "conf.h"
#include "rtc.h"
#include "pktlength.h"
#include "clientlog.h"
#include "refclock.h"
/* ================================================== */
#define INVALID_SOCK_FD (-5)
/* File descriptors for command and monitoring sockets */
static int sock_fdu;
static int sock_fd4;
static int sock_fd6;
/* Flag indicating the IPv4 socket is bound to an address */
static int bound_sock_fd4;
/* Flag indicating whether this module has been initialised or not */
static int initialised = 0;
/* ================================================== */
/* Array of permission levels for command types */
static const char permissions[] = {
PERMIT_OPEN, /* NULL */
PERMIT_AUTH, /* ONLINE */
PERMIT_AUTH, /* OFFLINE */
PERMIT_AUTH, /* BURST */
PERMIT_AUTH, /* MODIFY_MINPOLL */
PERMIT_AUTH, /* MODIFY_MAXPOLL */
PERMIT_AUTH, /* DUMP */
PERMIT_AUTH, /* MODIFY_MAXDELAY */
PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */
PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */
PERMIT_OPEN, /* LOGON */
PERMIT_AUTH, /* SETTIME */
PERMIT_AUTH, /* LOCAL */
PERMIT_AUTH, /* MANUAL */
PERMIT_OPEN, /* N_SOURCES */
PERMIT_OPEN, /* SOURCE_DATA */
PERMIT_AUTH, /* REKEY */
PERMIT_AUTH, /* ALLOW */
PERMIT_AUTH, /* ALLOWALL */
PERMIT_AUTH, /* DENY */
PERMIT_AUTH, /* DENYALL */
PERMIT_AUTH, /* CMDALLOW */
PERMIT_AUTH, /* CMDALLOWALL */
PERMIT_AUTH, /* CMDDENY */
PERMIT_AUTH, /* CMDDENYALL */
PERMIT_AUTH, /* ACCHECK */
PERMIT_AUTH, /* CMDACCHECK */
PERMIT_AUTH, /* ADD_SERVER */
PERMIT_AUTH, /* ADD_PEER */
PERMIT_AUTH, /* DEL_SOURCE */
PERMIT_AUTH, /* WRITERTC */
PERMIT_AUTH, /* DFREQ */
PERMIT_AUTH, /* DOFFSET */
PERMIT_OPEN, /* TRACKING */
PERMIT_OPEN, /* SOURCESTATS */
PERMIT_OPEN, /* RTCREPORT */
PERMIT_AUTH, /* TRIMRTC */
PERMIT_AUTH, /* CYCLELOGS */
PERMIT_AUTH, /* SUBNETS_ACCESSED */
PERMIT_AUTH, /* CLIENT_ACCESSES (by subnet) */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX */
PERMIT_OPEN, /* MANUAL_LIST */
PERMIT_AUTH, /* MANUAL_DELETE */
PERMIT_AUTH, /* MAKESTEP */
PERMIT_OPEN, /* ACTIVITY */
PERMIT_AUTH, /* MODIFY_MINSTRATUM */
PERMIT_AUTH, /* MODIFY_POLLTARGET */
PERMIT_AUTH, /* MODIFY_MAXDELAYDEVRATIO */
PERMIT_AUTH, /* RESELECT */
PERMIT_AUTH, /* RESELECTDISTANCE */
PERMIT_AUTH, /* MODIFY_MAKESTEP */
PERMIT_OPEN, /* SMOOTHING */
PERMIT_AUTH, /* SMOOTHTIME */
PERMIT_AUTH, /* REFRESH */
PERMIT_AUTH, /* SERVER_STATS */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
PERMIT_AUTH, /* LOCAL2 */
PERMIT_AUTH, /* NTP_DATA */
PERMIT_AUTH, /* ADD_SERVER2 */
PERMIT_AUTH, /* ADD_PEER2 */
PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
PERMIT_AUTH, /* ONOFFLINE */
PERMIT_AUTH, /* ADD_SOURCE */
PERMIT_OPEN, /* NTP_SOURCE_NAME */
PERMIT_AUTH, /* RESET_SOURCES */
PERMIT_AUTH, /* AUTH_DATA */
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
};
/* ================================================== */
/* This authorisation table is used for checking whether particular
machines are allowed to make command and monitoring requests. */
static ADF_AuthTable access_auth_table;
/* ================================================== */
/* Forward prototypes */
static void read_from_cmd_socket(int sock_fd, int event, void *anything);
/* ================================================== */
static int
open_socket(int family)
{
const char *local_path, *iface;
IPSockAddr local_addr;
int sock_fd, port;
switch (family) {
case IPADDR_INET4:
case IPADDR_INET6:
port = CNF_GetCommandPort();
if (port == 0 || !SCK_IsIpFamilyEnabled(family))
return INVALID_SOCK_FD;
CNF_GetBindCommandAddress(family, &local_addr.ip_addr);
local_addr.port = port;
iface = CNF_GetBindCommandInterface();
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, iface, SCK_FLAG_RX_DEST_ADDR);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open command socket on %s",
UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;
}
if (family == IPADDR_INET4)
bound_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
break;
case IPADDR_UNSPEC:
local_path = CNF_GetBindCommandPath();
sock_fd = SCK_OpenUnixDatagramSocket(NULL, local_path, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open command socket on %s", local_path);
return INVALID_SOCK_FD;
}
break;
default:
assert(0);
}
/* Register handler for read events on the socket */
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_from_cmd_socket, NULL);
return sock_fd;
}
/* ================================================== */
static void
do_size_checks(void)
{
int i, request_length, padding_length, reply_length;
CMD_Request request;
CMD_Reply reply;
assert(offsetof(CMD_Request, data) == 20);
assert(offsetof(CMD_Reply, data) == 28);
for (i = 0; i < N_REQUEST_TYPES; i++) {
request.version = PROTO_VERSION_NUMBER;
request.command = htons(i);
request_length = PKL_CommandLength(&request);
padding_length = PKL_CommandPaddingLength(&request);
if (padding_length > MAX_PADDING_LENGTH || padding_length > request_length ||
request_length > sizeof (CMD_Request) ||
(request_length && request_length < offsetof(CMD_Request, data)))
assert(0);
}
for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i);
reply.status = STT_SUCCESS;
reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply))
assert(0);
}
}
/* ================================================== */
void
CAM_Initialise(void)
{
assert(!initialised);
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
do_size_checks();
initialised = 1;
bound_sock_fd4 = 0;
sock_fdu = INVALID_SOCK_FD;
sock_fd4 = open_socket(IPADDR_INET4);
sock_fd6 = open_socket(IPADDR_INET6);
access_auth_table = ADF_CreateTable();
}
/* ================================================== */
void
CAM_Finalise(void)
{
if (sock_fdu != INVALID_SOCK_FD) {
SCH_RemoveFileHandler(sock_fdu);
SCK_RemoveSocket(sock_fdu);
SCK_CloseSocket(sock_fdu);
sock_fdu = INVALID_SOCK_FD;
}
if (sock_fd4 != INVALID_SOCK_FD) {
SCH_RemoveFileHandler(sock_fd4);
SCK_CloseSocket(sock_fd4);
sock_fd4 = INVALID_SOCK_FD;
}
if (sock_fd6 != INVALID_SOCK_FD) {
SCH_RemoveFileHandler(sock_fd6);
SCK_CloseSocket(sock_fd6);
sock_fd6 = INVALID_SOCK_FD;
}
ADF_DestroyTable(access_auth_table);
initialised = 0;
}
/* ================================================== */
void
CAM_OpenUnixSocket(void)
{
/* This is separated from CAM_Initialise() as it needs to be called when
the process has already dropped the root privileges */
if (CNF_GetBindCommandPath())
sock_fdu = open_socket(IPADDR_UNSPEC);
}
/* ================================================== */
static void
transmit_reply(int sock_fd, int request_length, SCK_Message *message)
{
message->length = PKL_ReplyLength((CMD_Reply *)message->data);
if (request_length < message->length) {
DEBUG_LOG("Response longer than request req_len=%d res_len=%d",
request_length, message->length);
return;
}
/* Don't require responses to non-link-local addresses to use the same
interface */
if (message->addr_type == SCK_ADDR_IP &&
!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
message->if_index = INVALID_IF_INDEX;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
(sock_fd != sock_fd4 || bound_sock_fd4))
message->local_addr.ip.family = IPADDR_UNSPEC;
#endif
if (!SCK_SendMessage(sock_fd, message, 0))
return;
}
/* ================================================== */
static void
handle_dump(CMD_Request *rx_message, CMD_Reply *tx_message)
{
SRC_DumpSources();
NSR_DumpAuthData();
NKS_DumpKeys();
}
/* ================================================== */
static void
handle_online(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.online.address, &address);
if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.offline.address, &address);
if (!NSR_SetConnectivity(&mask, &address, SRC_OFFLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_onoffline(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
address.family = mask.family = IPADDR_UNSPEC;
if (!NSR_SetConnectivity(&mask, &address, SRC_MAYBE_ONLINE))
;
}
/* ================================================== */
static void
handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
UTI_IPNetworkToHost(&rx_message->data.burst.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.burst.address, &address);
if (!NSR_InitiateSampleBurst(ntohl(rx_message->data.burst.n_good_samples),
ntohl(rx_message->data.burst.n_total_samples),
&mask, &address))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_minpoll(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address);
if (!NSR_ModifyMinpoll(&address,
ntohl(rx_message->data.modify_minpoll.new_minpoll)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_maxpoll(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address);
if (!NSR_ModifyMaxpoll(&address,
ntohl(rx_message->data.modify_minpoll.new_minpoll)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_maxdelay(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_maxdelay.address, &address);
if (!NSR_ModifyMaxdelay(&address,
UTI_FloatNetworkToHost(rx_message->data.modify_maxdelay.new_max_delay)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_maxdelayratio(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_maxdelayratio.address, &address);
if (!NSR_ModifyMaxdelayratio(&address,
UTI_FloatNetworkToHost(rx_message->data.modify_maxdelayratio.new_max_delay_ratio)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_maxdelaydevratio(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_maxdelaydevratio.address, &address);
if (!NSR_ModifyMaxdelaydevratio(&address,
UTI_FloatNetworkToHost(rx_message->data.modify_maxdelaydevratio.new_max_delay_dev_ratio)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_minstratum(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_minpoll.address, &address);
if (!NSR_ModifyMinstratum(&address,
ntohl(rx_message->data.modify_minstratum.new_min_stratum)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_polltarget(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address;
UTI_IPNetworkToHost(&rx_message->data.modify_polltarget.address, &address);
if (!NSR_ModifyPolltarget(&address,
ntohl(rx_message->data.modify_polltarget.new_poll_target)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_maxupdateskew(CMD_Request *rx_message, CMD_Reply *tx_message)
{
REF_ModifyMaxupdateskew(UTI_FloatNetworkToHost(rx_message->data.modify_maxupdateskew.new_max_update_skew));
}
/* ================================================== */
static void
handle_modify_makestep(CMD_Request *rx_message, CMD_Reply *tx_message)
{
REF_ModifyMakestep(ntohl(rx_message->data.modify_makestep.limit),
UTI_FloatNetworkToHost(rx_message->data.modify_makestep.threshold));
}
/* ================================================== */
static void
handle_settime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timespec ts;
double offset, dfreq_ppm, new_afreq_ppm;
UTI_TimespecNetworkToHost(&rx_message->data.settime.ts, &ts);
if (!MNL_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED);
} else if (MNL_AcceptTimestamp(&ts, &offset, &dfreq_ppm, &new_afreq_ppm)) {
tx_message->reply = htons(RPY_MANUAL_TIMESTAMP2);
tx_message->data.manual_timestamp.offset = UTI_FloatHostToNetwork(offset);
tx_message->data.manual_timestamp.dfreq_ppm = UTI_FloatHostToNetwork(dfreq_ppm);
tx_message->data.manual_timestamp.new_afreq_ppm = UTI_FloatHostToNetwork(new_afreq_ppm);
} else {
tx_message->status = htons(STT_FAILED);
}
}
/* ================================================== */
static void
handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
{
if (ntohl(rx_message->data.local.on_off)) {
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan));
} else {
REF_DisableLocal();
}
}
/* ================================================== */
static void
handle_manual(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int option;
option = ntohl(rx_message->data.manual.option);
switch (option) {
case 0:
MNL_Disable();
break;
case 1:
MNL_Enable();
break;
case 2:
MNL_Reset();
break;
default:
tx_message->status = htons(STT_INVALID);
break;
}
}
/* ================================================== */
static void
handle_n_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int n_sources;
n_sources = SRC_ReadNumberOfSources();
tx_message->reply = htons(RPY_N_SOURCES);
tx_message->data.n_sources.n_sources = htonl(n_sources);
}
/* ================================================== */
static void
handle_source_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SourceReport report;
struct timespec now_corr;
/* Get data */
SCH_GetLastEventTime(&now_corr, NULL, NULL);
if (SRC_ReportSource(ntohl(rx_message->data.source_data.index), &report, &now_corr)) {
switch (SRC_GetType(ntohl(rx_message->data.source_data.index))) {
case SRC_NTP:
NSR_ReportSource(&report, &now_corr);
break;
case SRC_REFCLOCK:
RCL_ReportSource(&report, &now_corr);
break;
}
tx_message->reply = htons(RPY_SOURCE_DATA);
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.source_data.ip_addr);
tx_message->data.source_data.stratum = htons(report.stratum);
tx_message->data.source_data.poll = htons(report.poll);
switch (report.state) {
case RPT_NONSELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_NONSELECTABLE);
break;
case RPT_FALSETICKER:
tx_message->data.source_data.state = htons(RPY_SD_ST_FALSETICKER);
break;
case RPT_JITTERY:
tx_message->data.source_data.state = htons(RPY_SD_ST_JITTERY);
break;
case RPT_SELECTABLE:
tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTABLE);
break;
case RPT_UNSELECTED:
tx_message->data.source_data.state = htons(RPY_SD_ST_UNSELECTED);
break;
case RPT_SELECTED:
tx_message->data.source_data.state = htons(RPY_SD_ST_SELECTED);
break;
}
switch (report.mode) {
case RPT_NTP_CLIENT:
tx_message->data.source_data.mode = htons(RPY_SD_MD_CLIENT);
break;
case RPT_NTP_PEER:
tx_message->data.source_data.mode = htons(RPY_SD_MD_PEER);
break;
case RPT_LOCAL_REFERENCE:
tx_message->data.source_data.mode = htons(RPY_SD_MD_REF);
break;
}
tx_message->data.source_data.flags = htons(0);
tx_message->data.source_data.reachability = htons(report.reachability);
tx_message->data.source_data.since_sample = htonl(report.latest_meas_ago);
tx_message->data.source_data.orig_latest_meas = UTI_FloatHostToNetwork(report.orig_latest_meas);
tx_message->data.source_data.latest_meas = UTI_FloatHostToNetwork(report.latest_meas);
tx_message->data.source_data.latest_meas_err = UTI_FloatHostToNetwork(report.latest_meas_err);
} else {
tx_message->status = htons(STT_NOSUCHSOURCE);
}
}
/* ================================================== */
static void
handle_rekey(CMD_Request *rx_message, CMD_Reply *tx_message)
{
KEY_Reload();
NKS_ReloadKeys();
}
/* ================================================== */
static void
handle_allowdeny(CMD_Request *rx_message, CMD_Reply *tx_message, int allow, int all)
{
IPAddr ip;
int subnet_bits;
UTI_IPNetworkToHost(&rx_message->data.allow_deny.ip, &ip);
subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
if (!NCR_AddAccessRestriction(&ip, subnet_bits, allow, all))
tx_message->status = htons(STT_BADSUBNET);
}
/* ================================================== */
static void
handle_cmdallowdeny(CMD_Request *rx_message, CMD_Reply *tx_message, int allow, int all)
{
IPAddr ip;
int subnet_bits;
UTI_IPNetworkToHost(&rx_message->data.allow_deny.ip, &ip);
subnet_bits = ntohl(rx_message->data.allow_deny.subnet_bits);
if (!CAM_AddAccessRestriction(&ip, subnet_bits, allow, all))
tx_message->status = htons(STT_BADSUBNET);
}
/* ================================================== */
static void
handle_accheck(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr ip;
UTI_IPNetworkToHost(&rx_message->data.ac_check.ip, &ip);
if (NCR_CheckAccessRestriction(&ip)) {
tx_message->status = htons(STT_ACCESSALLOWED);
} else {
tx_message->status = htons(STT_ACCESSDENIED);
}
}
/* ================================================== */
static void
handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr ip;
UTI_IPNetworkToHost(&rx_message->data.ac_check.ip, &ip);
if (CAM_CheckAccessRestriction(&ip)) {
tx_message->status = htons(STT_ACCESSALLOWED);
} else {
tx_message->status = htons(STT_ACCESSDENIED);
}
}
/* ================================================== */
static void
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Source_Type type;
SourceParameters params;
NSR_Status status;
char *name;
int pool, port;
switch (ntohl(rx_message->data.ntp_source.type)) {
case REQ_ADDSRC_SERVER:
type = NTP_SERVER;
pool = 0;
break;
case REQ_ADDSRC_PEER:
type = NTP_PEER;
pool = 0;
break;
case REQ_ADDSRC_POOL:
type = NTP_SERVER;
pool = 1;
break;
default:
tx_message->status = htons(STT_INVALID);
return;
}
name = (char *)rx_message->data.ntp_source.name;
/* Make sure the name is terminated */
if (name[sizeof (rx_message->data.ntp_source.name) - 1] != '\0') {
tx_message->status = htons(STT_INVALIDNAME);
return;
}
port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
params.presend_minpoll = ntohl(rx_message->data.ntp_source.presend_minpoll);
params.min_stratum = ntohl(rx_message->data.ntp_source.min_stratum);
params.poll_target = ntohl(rx_message->data.ntp_source.poll_target);
params.version = ntohl(rx_message->data.ntp_source.version);
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
params.cert_set = ntohl(rx_message->data.ntp_source.cert_set);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.max_delay_dev_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
params.max_delay_quant =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields =
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) {
case NSR_Success:
break;
case NSR_UnresolvedName:
/* Try to resolve the name now */
NSR_ResolveSources();
break;
case NSR_AlreadyInUse:
tx_message->status = htons(STT_SOURCEALREADYKNOWN);
break;
case NSR_TooManySources:
tx_message->status = htons(STT_TOOMANYSOURCES);
break;
case NSR_InvalidName:
tx_message->status = htons(STT_INVALIDNAME);
break;
case NSR_InvalidAF:
case NSR_NoSuchSource:
assert(0);
break;
}
}
/* ================================================== */
static void
handle_del_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NSR_Status status;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.del_source.ip_addr, &ip_addr);
status = NSR_RemoveSource(&ip_addr);
switch (status) {
case NSR_Success:
break;
case NSR_NoSuchSource:
tx_message->status = htons(STT_NOSUCHSOURCE);
break;
case NSR_TooManySources:
case NSR_AlreadyInUse:
case NSR_InvalidAF:
case NSR_InvalidName:
case NSR_UnresolvedName:
assert(0);
break;
}
}
/* ================================================== */
static void
handle_writertc(CMD_Request *rx_message, CMD_Reply *tx_message)
{
switch (RTC_WriteParameters()) {
case RTC_ST_OK:
break;
case RTC_ST_NODRV:
tx_message->status = htons(STT_NORTC);
break;
case RTC_ST_BADFILE:
tx_message->status = htons(STT_BADRTCFILE);
break;
}
}
/* ================================================== */
static void
handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
{
double dfreq;
dfreq = UTI_FloatNetworkToHost(rx_message->data.dfreq.dfreq);
LCL_AccumulateDeltaFrequency(dfreq * 1.0e-6);
LOG(LOGS_INFO, "Accumulated delta freq of %.3fppm", dfreq);
}
/* ================================================== */
static void
handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
{
double doffset;
doffset = UTI_FloatNetworkToHost(rx_message->data.doffset.doffset);
if (!LCL_AccumulateOffset(doffset, 0.0)) {
tx_message->status = htons(STT_FAILED);
} else {
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
}
}
/* ================================================== */
static void
handle_tracking(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_TrackingReport rpt;
REF_GetTrackingReport(&rpt);
tx_message->reply = htons(RPY_TRACKING);
tx_message->data.tracking.ref_id = htonl(rpt.ref_id);
UTI_IPHostToNetwork(&rpt.ip_addr, &tx_message->data.tracking.ip_addr);
tx_message->data.tracking.stratum = htons(rpt.stratum);
tx_message->data.tracking.leap_status = htons(rpt.leap_status);
UTI_TimespecHostToNetwork(&rpt.ref_time, &tx_message->data.tracking.ref_time);
tx_message->data.tracking.current_correction = UTI_FloatHostToNetwork(rpt.current_correction);
tx_message->data.tracking.last_offset = UTI_FloatHostToNetwork(rpt.last_offset);
tx_message->data.tracking.rms_offset = UTI_FloatHostToNetwork(rpt.rms_offset);
tx_message->data.tracking.freq_ppm = UTI_FloatHostToNetwork(rpt.freq_ppm);
tx_message->data.tracking.resid_freq_ppm = UTI_FloatHostToNetwork(rpt.resid_freq_ppm);
tx_message->data.tracking.skew_ppm = UTI_FloatHostToNetwork(rpt.skew_ppm);
tx_message->data.tracking.root_delay = UTI_FloatHostToNetwork(rpt.root_delay);
tx_message->data.tracking.root_dispersion = UTI_FloatHostToNetwork(rpt.root_dispersion);
tx_message->data.tracking.last_update_interval = UTI_FloatHostToNetwork(rpt.last_update_interval);
}
/* ================================================== */
static void
handle_smoothing(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SmoothingReport report;
struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
if (!SMT_GetSmoothingReport(&report, &now)) {
tx_message->status = htons(STT_NOTENABLED);
return;
}
tx_message->reply = htons(RPY_SMOOTHING);
tx_message->data.smoothing.flags = htonl((report.active ? RPY_SMT_FLAG_ACTIVE : 0) |
(report.leap_only ? RPY_SMT_FLAG_LEAPONLY : 0));
tx_message->data.smoothing.offset = UTI_FloatHostToNetwork(report.offset);
tx_message->data.smoothing.freq_ppm = UTI_FloatHostToNetwork(report.freq_ppm);
tx_message->data.smoothing.wander_ppm = UTI_FloatHostToNetwork(report.wander_ppm);
tx_message->data.smoothing.last_update_ago = UTI_FloatHostToNetwork(report.last_update_ago);
tx_message->data.smoothing.remaining_time = UTI_FloatHostToNetwork(report.remaining_time);
}
/* ================================================== */
static void
handle_smoothtime(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timespec now;
int option;
if (!SMT_IsEnabled()) {
tx_message->status = htons(STT_NOTENABLED);
return;
}
option = ntohl(rx_message->data.smoothtime.option);
SCH_GetLastEventTime(&now, NULL, NULL);
switch (option) {
case REQ_SMOOTHTIME_RESET:
SMT_Reset(&now);
break;
case REQ_SMOOTHTIME_ACTIVATE:
SMT_Activate(&now);
break;
default:
tx_message->status = htons(STT_INVALID);
break;
}
}
/* ================================================== */
static void
handle_sourcestats(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int status;
RPT_SourcestatsReport report;
struct timespec now_corr;
SCH_GetLastEventTime(&now_corr, NULL, NULL);
status = SRC_ReportSourcestats(ntohl(rx_message->data.sourcestats.index),
&report, &now_corr);
if (status) {
tx_message->reply = htons(RPY_SOURCESTATS);
tx_message->data.sourcestats.ref_id = htonl(report.ref_id);
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.sourcestats.ip_addr);
tx_message->data.sourcestats.n_samples = htonl(report.n_samples);
tx_message->data.sourcestats.n_runs = htonl(report.n_runs);
tx_message->data.sourcestats.span_seconds = htonl(report.span_seconds);
tx_message->data.sourcestats.resid_freq_ppm = UTI_FloatHostToNetwork(report.resid_freq_ppm);
tx_message->data.sourcestats.skew_ppm = UTI_FloatHostToNetwork(report.skew_ppm);
tx_message->data.sourcestats.sd = UTI_FloatHostToNetwork(report.sd);
tx_message->data.sourcestats.est_offset = UTI_FloatHostToNetwork(report.est_offset);
tx_message->data.sourcestats.est_offset_err = UTI_FloatHostToNetwork(report.est_offset_err);
} else {
tx_message->status = htons(STT_NOSUCHSOURCE);
}
}
/* ================================================== */
static void
handle_rtcreport(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int status;
RPT_RTC_Report report;
status = RTC_GetReport(&report);
if (status) {
tx_message->reply = htons(RPY_RTC);
UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.rtc.ref_time);
tx_message->data.rtc.n_samples = htons(report.n_samples);
tx_message->data.rtc.n_runs = htons(report.n_runs);
tx_message->data.rtc.span_seconds = htonl(report.span_seconds);
tx_message->data.rtc.rtc_seconds_fast = UTI_FloatHostToNetwork(report.rtc_seconds_fast);
tx_message->data.rtc.rtc_gain_rate_ppm = UTI_FloatHostToNetwork(report.rtc_gain_rate_ppm);
} else {
tx_message->status = htons(STT_NORTC);
}
}
/* ================================================== */
static void
handle_trimrtc(CMD_Request *rx_message, CMD_Reply *tx_message)
{
if (!RTC_Trim())
tx_message->status = htons(STT_NORTC);
}
/* ================================================== */
static void
handle_cyclelogs(CMD_Request *rx_message, CMD_Reply *tx_message)
{
LOG_CycleLogFiles();
}
/* ================================================== */
static void
handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_ClientAccessByIndex_Report report;
RPY_ClientAccesses_Client *client;
int n_indices;
uint32_t i, j, req_first_index, req_n_clients, req_min_hits, req_reset;
struct timespec now;
SCH_GetLastEventTime(&now, NULL, NULL);
req_first_index = ntohl(rx_message->data.client_accesses_by_index.first_index);
req_n_clients = ntohl(rx_message->data.client_accesses_by_index.n_clients);
if (req_n_clients > MAX_CLIENT_ACCESSES)
req_n_clients = MAX_CLIENT_ACCESSES;
req_min_hits = ntohl(rx_message->data.client_accesses_by_index.min_hits);
req_reset = ntohl(rx_message->data.client_accesses_by_index.reset);
n_indices = CLG_GetNumberOfIndices();
if (n_indices < 0) {
tx_message->status = htons(STT_INACTIVE);
return;
}
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX3);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, req_reset, req_min_hits, &report, &now))
continue;
client = &tx_message->data.client_accesses_by_index.clients[j++];
UTI_IPHostToNetwork(&report.ip_addr, &client->ip);
client->ntp_hits = htonl(report.ntp_hits);
client->nke_hits = htonl(report.nke_hits);
client->cmd_hits = htonl(report.cmd_hits);
client->ntp_drops = htonl(report.ntp_drops);
client->nke_drops = htonl(report.nke_drops);
client->cmd_drops = htonl(report.cmd_drops);
client->ntp_interval = report.ntp_interval;
client->nke_interval = report.nke_interval;
client->cmd_interval = report.cmd_interval;
client->ntp_timeout_interval = report.ntp_timeout_interval;
client->last_ntp_hit_ago = htonl(report.last_ntp_hit_ago);
client->last_nke_hit_ago = htonl(report.last_nke_hit_ago);
client->last_cmd_hit_ago = htonl(report.last_cmd_hit_ago);
}
tx_message->data.client_accesses_by_index.next_index = htonl(i);
tx_message->data.client_accesses_by_index.n_clients = htonl(j);
}
/* ================================================== */
static void
handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int n_samples;
int i;
RPY_ManualListSample *sample;
RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES];
tx_message->reply = htons(RPY_MANUAL_LIST2);
MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i];
UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
sample->slewed_offset = UTI_FloatHostToNetwork(report[i].slewed_offset);
sample->orig_offset = UTI_FloatHostToNetwork(report[i].orig_offset);
sample->residual = UTI_FloatHostToNetwork(report[i].residual);
}
}
/* ================================================== */
static void
handle_manual_delete(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int index;
index = ntohl(rx_message->data.manual_delete.index);
if (!MNL_DeleteSample(index))
tx_message->status = htons(STT_BADSAMPLE);
}
/* ================================================== */
static void
handle_make_step(CMD_Request *rx_message, CMD_Reply *tx_message)
{
if (!LCL_MakeStep())
tx_message->status = htons(STT_FAILED);
}
/* ================================================== */
static void
handle_activity(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_ActivityReport report;
NSR_GetActivityReport(&report);
tx_message->data.activity.online = htonl(report.online);
tx_message->data.activity.offline = htonl(report.offline);
tx_message->data.activity.burst_online = htonl(report.burst_online);
tx_message->data.activity.burst_offline = htonl(report.burst_offline);
tx_message->data.activity.unresolved = htonl(report.unresolved);
tx_message->reply = htons(RPY_ACTIVITY);
}
/* ================================================== */
static void
handle_reselect_distance(CMD_Request *rx_message, CMD_Reply *tx_message)
{
double dist;
dist = UTI_FloatNetworkToHost(rx_message->data.reselect_distance.distance);
SRC_SetReselectDistance(dist);
}
/* ================================================== */
static void
handle_reselect(CMD_Request *rx_message, CMD_Reply *tx_message)
{
SRC_ReselectSource();
}
/* ================================================== */
static void
handle_refresh(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NSR_RefreshAddresses();
}
/* ================================================== */
static void
handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS3);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
}
/* ================================================== */
static void
handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_NTPReport report;
UTI_IPNetworkToHost(&rx_message->data.ntp_data.ip_addr, &report.remote_addr);
if (!NSR_GetNTPReport(&report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_NTP_DATA);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
tx_message->data.ntp_data.leap = report.leap;
tx_message->data.ntp_data.version = report.version;
tx_message->data.ntp_data.mode = report.mode;
tx_message->data.ntp_data.stratum = report.stratum;
tx_message->data.ntp_data.poll = report.poll;
tx_message->data.ntp_data.precision = report.precision;
tx_message->data.ntp_data.root_delay = UTI_FloatHostToNetwork(report.root_delay);
tx_message->data.ntp_data.root_dispersion = UTI_FloatHostToNetwork(report.root_dispersion);
tx_message->data.ntp_data.ref_id = htonl(report.ref_id);
UTI_TimespecHostToNetwork(&report.ref_time, &tx_message->data.ntp_data.ref_time);
tx_message->data.ntp_data.offset = UTI_FloatHostToNetwork(report.offset);
tx_message->data.ntp_data.peer_delay = UTI_FloatHostToNetwork(report.peer_delay);
tx_message->data.ntp_data.peer_dispersion = UTI_FloatHostToNetwork(report.peer_dispersion);
tx_message->data.ntp_data.response_time = UTI_FloatHostToNetwork(report.response_time);
tx_message->data.ntp_data.jitter_asymmetry = UTI_FloatHostToNetwork(report.jitter_asymmetry);
tx_message->data.ntp_data.flags = htons((report.tests & RPY_NTP_FLAGS_TESTS) |
(report.interleaved ? RPY_NTP_FLAG_INTERLEAVED : 0) |
(report.authenticated ? RPY_NTP_FLAG_AUTHENTICATED : 0));
tx_message->data.ntp_data.tx_tss_char = report.tx_tss_char;
tx_message->data.ntp_data.rx_tss_char = report.rx_tss_char;
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
/* ================================================== */
static void
handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message)
{
LOG(LOGS_INFO, "Received shutdown command");
SCH_QuitProgram();
}
/* ================================================== */
static void
handle_ntp_source_name(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr addr;
char *name;
UTI_IPNetworkToHost(&rx_message->data.ntp_source_name.ip_addr, &addr);
name = NSR_GetName(&addr);
if (!name) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_NTP_SOURCE_NAME);
/* Avoid compiler warning */
if (strlen(name) >= sizeof (tx_message->data.ntp_source_name.name))
memcpy(tx_message->data.ntp_source_name.name, name,
sizeof (tx_message->data.ntp_source_name.name));
else
strncpy((char *)tx_message->data.ntp_source_name.name, name,
sizeof (tx_message->data.ntp_source_name.name));
}
/* ================================================== */
static void
handle_reload_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
CNF_ReloadSources();
}
/* ================================================== */
static void
handle_reset_sources(CMD_Request *rx_message, CMD_Reply *tx_message)
{
struct timespec cooked_now, now;
SRC_ResetSources();
SCH_GetLastEventTime(&cooked_now, NULL, &now);
LCL_NotifyExternalTimeStep(&now, &cooked_now, 0.0, 0.0);
}
/* ================================================== */
static void
handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_AuthReport report;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.auth_data.ip_addr, &ip_addr);
if (!NSR_GetAuthReport(&ip_addr, &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_AUTH_DATA);
switch (report.mode) {
case NTP_AUTH_NONE:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NONE);
break;
case NTP_AUTH_SYMMETRIC:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_SYMMETRIC);
break;
case NTP_AUTH_NTS:
tx_message->data.auth_data.mode = htons(RPY_AD_MD_NTS);
break;
default:
break;
}
tx_message->data.auth_data.key_type = htons(report.key_type);
tx_message->data.auth_data.key_id = htonl(report.key_id);
tx_message->data.auth_data.key_length = htons(report.key_length);
tx_message->data.auth_data.ke_attempts = htons(report.ke_attempts);
tx_message->data.auth_data.last_ke_ago = htonl(report.last_ke_ago);
tx_message->data.auth_data.cookies = htons(report.cookies);
tx_message->data.auth_data.cookie_length = htons(report.cookie_length);
tx_message->data.auth_data.nak = htons(report.nak);
}
/* ================================================== */
static uint16_t
convert_select_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
(options & SRC_SELECT_TRUST ? RPY_SD_OPTION_TRUST : 0) |
(options & SRC_SELECT_REQUIRE ? RPY_SD_OPTION_REQUIRE : 0);
}
/* ================================================== */
static void
handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
{
RPT_SelectReport report;
if (!SRC_GetSelectReport(ntohl(rx_message->data.select_data.index), &report)) {
tx_message->status = htons(STT_NOSUCHSOURCE);
return;
}
tx_message->reply = htons(RPY_SELECT_DATA);
tx_message->data.select_data.ref_id = htonl(report.ref_id);
UTI_IPHostToNetwork(&report.ip_addr, &tx_message->data.select_data.ip_addr);
tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
/* ================================================== */
/* Read a packet and process it */
static void
read_from_cmd_socket(int sock_fd, int event, void *anything)
{
SCK_Message *sck_message;
CMD_Request rx_message;
CMD_Reply tx_message;
IPAddr loopback_addr, remote_ip;
int read_length, expected_length;
int localhost, allowed, log_index;
uint16_t rx_command;
struct timespec now, cooked_now;
sck_message = SCK_ReceiveMessage(sock_fd, 0);
if (!sck_message)
return;
read_length = sck_message->length;
/* Get current time cheaply */
SCH_GetLastEventTime(&cooked_now, NULL, &now);
/* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
or an authorised address */
switch (sck_message->addr_type) {
case SCK_ADDR_IP:
assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
remote_ip = sck_message->remote_addr.ip.ip_addr;
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
if (!localhost && !ADF_IsAllowed(access_auth_table, &remote_ip)) {
DEBUG_LOG("Unauthorised host %s",
UTI_IPSockAddrToString(&sck_message->remote_addr.ip));
return;
}
assert(remote_ip.family != IPADDR_UNSPEC);
break;
case SCK_ADDR_UNIX:
assert(sock_fd == sock_fdu);
remote_ip.family = IPADDR_UNSPEC;
localhost = 1;
break;
default:
DEBUG_LOG("Unexpected address type");
return;
}
if (read_length < offsetof(CMD_Request, data) ||
read_length < offsetof(CMD_Reply, data) ||
read_length > sizeof (CMD_Request)) {
/* We don't know how to process anything like this or an error reply
would be larger than the request */
DEBUG_LOG("Unexpected length");
return;
}
memcpy(&rx_message, sck_message->data, read_length);
if (rx_message.pkt_type != PKT_TYPE_CMD_REQUEST ||
rx_message.res1 != 0 ||
rx_message.res2 != 0) {
DEBUG_LOG("Command packet dropped");
return;
}
log_index = CLG_LogServiceAccess(CLG_CMDMON, &remote_ip, &cooked_now);
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command);
memset(&tx_message, 0, sizeof (tx_message));
sck_message->data = &tx_message;
sck_message->length = 0;
tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
tx_message.command = rx_message.command;
tx_message.reply = htons(RPY_NULL);
tx_message.status = htons(STT_SUCCESS);
tx_message.sequence = rx_message.sequence;
if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG("Command packet has invalid version (%d != %d)",
rx_message.version, PROTO_VERSION_NUMBER);
if (rx_message.version >= PROTO_VERSION_MISMATCH_COMPAT_SERVER) {
tx_message.status = htons(STT_BADPKTVERSION);
transmit_reply(sock_fd, read_length, sck_message);
}
return;
}
if (rx_command >= N_REQUEST_TYPES ||
expected_length < (int)offsetof(CMD_Request, data)) {
DEBUG_LOG("Command packet has invalid command %d", rx_command);
tx_message.status = htons(STT_INVALID);
transmit_reply(sock_fd, read_length, sck_message);
return;
}
if (read_length < expected_length) {
DEBUG_LOG("Command packet is too short (%d < %d)", read_length,
expected_length);
tx_message.status = htons(STT_BADPKTLENGTH);
transmit_reply(sock_fd, read_length, sck_message);
return;
}
/* OK, we have a valid message. Now dispatch on message type and process it. */
if (rx_command >= N_REQUEST_TYPES) {
/* This should be already handled */
assert(0);
} else {
/* Check level of authority required to issue the command. All commands
from the Unix domain socket (which is accessible only by the root and
chrony user/group) are allowed. */
if (remote_ip.family == IPADDR_UNSPEC) {
assert(sock_fd == sock_fdu);
allowed = 1;
} else {
switch (permissions[rx_command]) {
case PERMIT_AUTH:
allowed = 0;
break;
case PERMIT_LOCAL:
allowed = localhost;
break;
case PERMIT_OPEN:
allowed = 1;
break;
default:
assert(0);
allowed = 0;
}
}
if (allowed) {
switch(rx_command) {
case REQ_NULL:
/* Do nothing */
break;
case REQ_DUMP:
handle_dump(&rx_message, &tx_message);
break;
case REQ_ONLINE:
handle_online(&rx_message, &tx_message);
break;
case REQ_OFFLINE:
handle_offline(&rx_message, &tx_message);
break;
case REQ_BURST:
handle_burst(&rx_message, &tx_message);
break;
case REQ_MODIFY_MINPOLL:
handle_modify_minpoll(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXPOLL:
handle_modify_maxpoll(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXDELAY:
handle_modify_maxdelay(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXDELAYRATIO:
handle_modify_maxdelayratio(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXDELAYDEVRATIO:
handle_modify_maxdelaydevratio(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAXUPDATESKEW:
handle_modify_maxupdateskew(&rx_message, &tx_message);
break;
case REQ_MODIFY_MAKESTEP:
handle_modify_makestep(&rx_message, &tx_message);
break;
case REQ_LOGON:
/* Authentication is no longer supported, log-on always fails */
tx_message.status = htons(STT_FAILED);
break;
case REQ_SETTIME:
handle_settime(&rx_message, &tx_message);
break;
case REQ_LOCAL2:
handle_local(&rx_message, &tx_message);
break;
case REQ_MANUAL:
handle_manual(&rx_message, &tx_message);
break;
case REQ_N_SOURCES:
handle_n_sources(&rx_message, &tx_message);
break;
case REQ_SOURCE_DATA:
handle_source_data(&rx_message, &tx_message);
break;
case REQ_REKEY:
handle_rekey(&rx_message, &tx_message);
break;
case REQ_ALLOW:
handle_allowdeny(&rx_message, &tx_message, 1, 0);
break;
case REQ_ALLOWALL:
handle_allowdeny(&rx_message, &tx_message, 1, 1);
break;
case REQ_DENY:
handle_allowdeny(&rx_message, &tx_message, 0, 0);
break;
case REQ_DENYALL:
handle_allowdeny(&rx_message, &tx_message, 0, 1);
break;
case REQ_CMDALLOW:
handle_cmdallowdeny(&rx_message, &tx_message, 1, 0);
break;
case REQ_CMDALLOWALL:
handle_cmdallowdeny(&rx_message, &tx_message, 1, 1);
break;
case REQ_CMDDENY:
handle_cmdallowdeny(&rx_message, &tx_message, 0, 0);
break;
case REQ_CMDDENYALL:
handle_cmdallowdeny(&rx_message, &tx_message, 0, 1);
break;
case REQ_ACCHECK:
handle_accheck(&rx_message, &tx_message);
break;
case REQ_CMDACCHECK:
handle_cmdaccheck(&rx_message, &tx_message);
break;
case REQ_ADD_SOURCE:
handle_add_source(&rx_message, &tx_message);
break;
case REQ_DEL_SOURCE:
handle_del_source(&rx_message, &tx_message);
break;
case REQ_WRITERTC:
handle_writertc(&rx_message, &tx_message);
break;
case REQ_DFREQ:
handle_dfreq(&rx_message, &tx_message);
break;
case REQ_DOFFSET2:
handle_doffset(&rx_message, &tx_message);
break;
case REQ_TRACKING:
handle_tracking(&rx_message, &tx_message);
break;
case REQ_SMOOTHING:
handle_smoothing(&rx_message, &tx_message);
break;
case REQ_SMOOTHTIME:
handle_smoothtime(&rx_message, &tx_message);
break;
case REQ_SOURCESTATS:
handle_sourcestats(&rx_message, &tx_message);
break;
case REQ_RTCREPORT:
handle_rtcreport(&rx_message, &tx_message);
break;
case REQ_TRIMRTC:
handle_trimrtc(&rx_message, &tx_message);
break;
case REQ_CYCLELOGS:
handle_cyclelogs(&rx_message, &tx_message);
break;
case REQ_CLIENT_ACCESSES_BY_INDEX3:
handle_client_accesses_by_index(&rx_message, &tx_message);
break;
case REQ_MANUAL_LIST:
handle_manual_list(&rx_message, &tx_message);
break;
case REQ_MANUAL_DELETE:
handle_manual_delete(&rx_message, &tx_message);
break;
case REQ_MAKESTEP:
handle_make_step(&rx_message, &tx_message);
break;
case REQ_ACTIVITY:
handle_activity(&rx_message, &tx_message);
break;
case REQ_RESELECTDISTANCE:
handle_reselect_distance(&rx_message, &tx_message);
break;
case REQ_RESELECT:
handle_reselect(&rx_message, &tx_message);
break;
case REQ_MODIFY_MINSTRATUM:
handle_modify_minstratum(&rx_message, &tx_message);
break;
case REQ_MODIFY_POLLTARGET:
handle_modify_polltarget(&rx_message, &tx_message);
break;
case REQ_REFRESH:
handle_refresh(&rx_message, &tx_message);
break;
case REQ_SERVER_STATS:
handle_server_stats(&rx_message, &tx_message);
break;
case REQ_NTP_DATA:
handle_ntp_data(&rx_message, &tx_message);
break;
case REQ_SHUTDOWN:
handle_shutdown(&rx_message, &tx_message);
break;
case REQ_ONOFFLINE:
handle_onoffline(&rx_message, &tx_message);
break;
case REQ_NTP_SOURCE_NAME:
handle_ntp_source_name(&rx_message, &tx_message);
break;
case REQ_RESET_SOURCES:
handle_reset_sources(&rx_message, &tx_message);
break;
case REQ_AUTH_DATA:
handle_auth_data(&rx_message, &tx_message);
break;
case REQ_SELECT_DATA:
handle_select_data(&rx_message, &tx_message);
break;
case REQ_RELOAD_SOURCES:
handle_reload_sources(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}
} else {
tx_message.status = htons(STT_UNAUTH);
}
}
/* Transmit the response */
transmit_reply(sock_fd, read_length, sck_message);
}
/* ================================================== */
int
CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
{
ADF_Status status;
if (allow) {
if (all) {
status = ADF_AllowAll(access_auth_table, ip_addr, subnet_bits);
} else {
status = ADF_Allow(access_auth_table, ip_addr, subnet_bits);
}
} else {
if (all) {
status = ADF_DenyAll(access_auth_table, ip_addr, subnet_bits);
} else {
status = ADF_Deny(access_auth_table, ip_addr, subnet_bits);
}
}
if (status == ADF_BADSUBNET) {
return 0;
} else if (status == ADF_SUCCESS) {
return 1;
} else {
return 0;
}
}
/* ================================================== */
int
CAM_CheckAccessRestriction(IPAddr *ip_addr)
{
return ADF_IsAllowed(access_auth_table, ip_addr);
}
/* ================================================== */
/* ================================================== */