| /* |
| 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.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, ¶ms, 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); |
| } |
| |
| |
| /* ================================================== */ |
| /* ================================================== */ |