initial, unchanged sources from openbsd 7.6
This commit is contained in:
commit
2f4aa1c412
19
Makefile
Normal file
19
Makefile
Normal file
@ -0,0 +1,19 @@
|
||||
# $OpenBSD: Makefile,v 1.16 2015/11/20 18:53:42 tedu Exp $
|
||||
|
||||
PROG= ntpd
|
||||
SRCS= ntpd.c log.c ntp.c ntp_msg.c parse.y config.c \
|
||||
server.c client.c sensors.c util.c ntp_dns.c \
|
||||
control.c constraint.c
|
||||
CFLAGS+= -Wall -I${.CURDIR}
|
||||
CFLAGS+= -fstack-protector-all
|
||||
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
|
||||
CFLAGS+= -Wmissing-declarations
|
||||
CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
|
||||
CFLAGS+= -Wsign-compare
|
||||
YFLAGS=
|
||||
LDADD+= -lm -lutil -ltls -lssl -lcrypto
|
||||
DPADD+= ${LIBUTIL} ${LIBCRYPTO} ${LIBSSL} ${LIBTLS}
|
||||
LINKS= ${BINDIR}/ntpd ${BINDIR}/ntpctl
|
||||
MAN= ntpd.8 ntpd.conf.5 ntpctl.8
|
||||
|
||||
.include <bsd.prog.mk>
|
513
client.c
Normal file
513
client.c
Normal file
@ -0,0 +1,513 @@
|
||||
/* $OpenBSD: client.c,v 1.118 2023/12/20 15:36:36 otto Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <md5.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
int client_update(struct ntp_peer *);
|
||||
int auto_cmp(const void *, const void *);
|
||||
void handle_auto(u_int8_t, double);
|
||||
void set_deadline(struct ntp_peer *, time_t);
|
||||
|
||||
void
|
||||
set_next(struct ntp_peer *p, time_t t)
|
||||
{
|
||||
p->next = getmonotime() + t;
|
||||
p->deadline = 0;
|
||||
p->poll = t;
|
||||
}
|
||||
|
||||
void
|
||||
set_deadline(struct ntp_peer *p, time_t t)
|
||||
{
|
||||
p->deadline = getmonotime() + t;
|
||||
p->next = 0;
|
||||
}
|
||||
|
||||
int
|
||||
client_peer_init(struct ntp_peer *p)
|
||||
{
|
||||
p->query.fd = -1;
|
||||
p->query.msg.status = MODE_CLIENT | (NTP_VERSION << 3);
|
||||
p->query.xmttime = 0;
|
||||
p->state = STATE_NONE;
|
||||
p->shift = 0;
|
||||
p->trustlevel = TRUSTLEVEL_PATHETIC;
|
||||
p->lasterror = 0;
|
||||
p->senderrors = 0;
|
||||
|
||||
return (client_addr_init(p));
|
||||
}
|
||||
|
||||
int
|
||||
client_addr_init(struct ntp_peer *p)
|
||||
{
|
||||
struct sockaddr_in *sa_in;
|
||||
struct sockaddr_in6 *sa_in6;
|
||||
struct ntp_addr *h;
|
||||
|
||||
for (h = p->addr; h != NULL; h = h->next) {
|
||||
switch (h->ss.ss_family) {
|
||||
case AF_INET:
|
||||
sa_in = (struct sockaddr_in *)&h->ss;
|
||||
if (ntohs(sa_in->sin_port) == 0)
|
||||
sa_in->sin_port = htons(123);
|
||||
p->state = STATE_DNS_DONE;
|
||||
break;
|
||||
case AF_INET6:
|
||||
sa_in6 = (struct sockaddr_in6 *)&h->ss;
|
||||
if (ntohs(sa_in6->sin6_port) == 0)
|
||||
sa_in6->sin6_port = htons(123);
|
||||
p->state = STATE_DNS_DONE;
|
||||
break;
|
||||
default:
|
||||
fatalx("king bula sez: wrong AF in client_addr_init");
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
p->query.fd = -1;
|
||||
set_next(p, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
client_nextaddr(struct ntp_peer *p)
|
||||
{
|
||||
if (p->query.fd != -1) {
|
||||
close(p->query.fd);
|
||||
p->query.fd = -1;
|
||||
}
|
||||
|
||||
if (p->state == STATE_DNS_INPROGRESS)
|
||||
return (-1);
|
||||
|
||||
if (p->addr_head.a == NULL) {
|
||||
priv_dns(IMSG_HOST_DNS, p->addr_head.name, p->id);
|
||||
p->state = STATE_DNS_INPROGRESS;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
p->shift = 0;
|
||||
p->trustlevel = TRUSTLEVEL_PATHETIC;
|
||||
|
||||
if (p->addr == NULL)
|
||||
p->addr = p->addr_head.a;
|
||||
else if ((p->addr = p->addr->next) == NULL)
|
||||
return (1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
client_query(struct ntp_peer *p)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (p->addr == NULL && client_nextaddr(p) == -1) {
|
||||
if (conf->settime)
|
||||
set_next(p, INTERVAL_AUIO_DNSFAIL);
|
||||
else
|
||||
set_next(p, MAXIMUM(SETTIME_TIMEOUT,
|
||||
scale_interval(INTERVAL_QUERY_AGGRESSIVE)));
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (conf->status.synced && p->addr->notauth) {
|
||||
peer_addr_head_clear(p);
|
||||
client_nextaddr(p);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (p->state < STATE_DNS_DONE || p->addr == NULL)
|
||||
return (-1);
|
||||
|
||||
if (p->query.fd == -1) {
|
||||
struct sockaddr *sa = (struct sockaddr *)&p->addr->ss;
|
||||
struct sockaddr *qa4 = (struct sockaddr *)&p->query_addr4;
|
||||
struct sockaddr *qa6 = (struct sockaddr *)&p->query_addr6;
|
||||
|
||||
if ((p->query.fd = socket(p->addr->ss.ss_family, SOCK_DGRAM,
|
||||
0)) == -1)
|
||||
fatal("client_query socket");
|
||||
|
||||
if (p->addr->ss.ss_family == qa4->sa_family) {
|
||||
if (bind(p->query.fd, qa4, SA_LEN(qa4)) == -1)
|
||||
fatal("couldn't bind to IPv4 query address: %s",
|
||||
log_sockaddr(qa4));
|
||||
} else if (p->addr->ss.ss_family == qa6->sa_family) {
|
||||
if (bind(p->query.fd, qa6, SA_LEN(qa6)) == -1)
|
||||
fatal("couldn't bind to IPv6 query address: %s",
|
||||
log_sockaddr(qa6));
|
||||
}
|
||||
|
||||
if (connect(p->query.fd, sa, SA_LEN(sa)) == -1) {
|
||||
if (errno == ECONNREFUSED || errno == ENETUNREACH ||
|
||||
errno == EHOSTUNREACH || errno == EADDRNOTAVAIL) {
|
||||
/* cycle through addresses, but do increase
|
||||
senderrors */
|
||||
client_nextaddr(p);
|
||||
if (p->addr == NULL)
|
||||
p->addr = p->addr_head.a;
|
||||
set_next(p, MAXIMUM(SETTIME_TIMEOUT,
|
||||
scale_interval(INTERVAL_QUERY_AGGRESSIVE)));
|
||||
p->senderrors++;
|
||||
return (-1);
|
||||
} else
|
||||
fatal("client_query connect");
|
||||
}
|
||||
val = IPTOS_LOWDELAY;
|
||||
if (p->addr->ss.ss_family == AF_INET && setsockopt(p->query.fd,
|
||||
IPPROTO_IP, IP_TOS, &val, sizeof(val)) == -1)
|
||||
log_warn("setsockopt IPTOS_LOWDELAY");
|
||||
val = 1;
|
||||
if (setsockopt(p->query.fd, SOL_SOCKET, SO_TIMESTAMP,
|
||||
&val, sizeof(val)) == -1)
|
||||
fatal("setsockopt SO_TIMESTAMP");
|
||||
}
|
||||
|
||||
/*
|
||||
* Send out a random 64-bit number as our transmit time. The NTP
|
||||
* server will copy said number into the originate field on the
|
||||
* response that it sends us. This is totally legal per the SNTP spec.
|
||||
*
|
||||
* The impact of this is two fold: we no longer send out the current
|
||||
* system time for the world to see (which may aid an attacker), and
|
||||
* it gives us a (not very secure) way of knowing that we're not
|
||||
* getting spoofed by an attacker that can't capture our traffic
|
||||
* but can spoof packets from the NTP server we're communicating with.
|
||||
*
|
||||
* Save the real transmit timestamp locally.
|
||||
*/
|
||||
|
||||
p->query.msg.xmttime.int_partl = arc4random();
|
||||
p->query.msg.xmttime.fractionl = arc4random();
|
||||
p->query.xmttime = gettime();
|
||||
|
||||
if (ntp_sendmsg(p->query.fd, NULL, &p->query.msg) == -1) {
|
||||
p->senderrors++;
|
||||
set_next(p, INTERVAL_QUERY_PATHETIC);
|
||||
p->trustlevel = TRUSTLEVEL_PATHETIC;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
p->senderrors = 0;
|
||||
p->state = STATE_QUERY_SENT;
|
||||
set_deadline(p, QUERYTIME_MAX);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
auto_cmp(const void *a, const void *b)
|
||||
{
|
||||
double at = *(const double *)a;
|
||||
double bt = *(const double *)b;
|
||||
return at < bt ? -1 : (at > bt ? 1 : 0);
|
||||
}
|
||||
|
||||
void
|
||||
handle_auto(uint8_t trusted, double offset)
|
||||
{
|
||||
static int count;
|
||||
static double v[AUTO_REPLIES];
|
||||
|
||||
/*
|
||||
* It happens the (constraint) resolves initially fail, don't give up
|
||||
* but see if we get validated replies later.
|
||||
*/
|
||||
if (!trusted && conf->constraint_median == 0)
|
||||
return;
|
||||
|
||||
if (offset < AUTO_THRESHOLD) {
|
||||
/* don't bother */
|
||||
priv_settime(0, "offset is negative or close enough");
|
||||
return;
|
||||
}
|
||||
/* collect some more */
|
||||
v[count++] = offset;
|
||||
if (count < AUTO_REPLIES)
|
||||
return;
|
||||
|
||||
/* we have enough */
|
||||
qsort(v, count, sizeof(double), auto_cmp);
|
||||
if (AUTO_REPLIES % 2 == 0)
|
||||
offset = (v[AUTO_REPLIES / 2 - 1] + v[AUTO_REPLIES / 2]) / 2;
|
||||
else
|
||||
offset = v[AUTO_REPLIES / 2];
|
||||
priv_settime(offset, "");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* -1: Not processed, not an NTP message (e.g. icmp induced ECONNREFUSED)
|
||||
* 0: Not prrocessed due to validation issues
|
||||
* 1: NTP message validated and processed
|
||||
*/
|
||||
int
|
||||
client_dispatch(struct ntp_peer *p, u_int8_t settime, u_int8_t automatic)
|
||||
{
|
||||
struct ntp_msg msg;
|
||||
struct msghdr somsg;
|
||||
struct iovec iov[1];
|
||||
struct timeval tv;
|
||||
char buf[NTP_MSGSIZE];
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
char buf[CMSG_SPACE(sizeof(tv))];
|
||||
} cmsgbuf;
|
||||
struct cmsghdr *cmsg;
|
||||
ssize_t size;
|
||||
double T1, T2, T3, T4, offset, delay;
|
||||
time_t interval;
|
||||
|
||||
memset(&somsg, 0, sizeof(somsg));
|
||||
iov[0].iov_base = buf;
|
||||
iov[0].iov_len = sizeof(buf);
|
||||
somsg.msg_iov = iov;
|
||||
somsg.msg_iovlen = 1;
|
||||
somsg.msg_control = cmsgbuf.buf;
|
||||
somsg.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
|
||||
if ((size = recvmsg(p->query.fd, &somsg, 0)) == -1) {
|
||||
if (errno == EHOSTUNREACH || errno == EHOSTDOWN ||
|
||||
errno == ENETUNREACH || errno == ENETDOWN ||
|
||||
errno == ECONNREFUSED || errno == EADDRNOTAVAIL ||
|
||||
errno == ENOPROTOOPT || errno == ENOENT) {
|
||||
client_log_error(p, "recvmsg", errno);
|
||||
set_next(p, error_interval());
|
||||
return (-1);
|
||||
} else
|
||||
fatal("recvfrom");
|
||||
}
|
||||
|
||||
if (somsg.msg_flags & MSG_TRUNC) {
|
||||
client_log_error(p, "recvmsg packet", EMSGSIZE);
|
||||
set_next(p, error_interval());
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (somsg.msg_flags & MSG_CTRUNC) {
|
||||
client_log_error(p, "recvmsg control data", E2BIG);
|
||||
set_next(p, error_interval());
|
||||
return (0);
|
||||
}
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&somsg); cmsg != NULL;
|
||||
cmsg = CMSG_NXTHDR(&somsg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_TIMESTAMP) {
|
||||
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
|
||||
T4 = gettime_from_timeval(&tv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmsg == NULL)
|
||||
fatal("SCM_TIMESTAMP");
|
||||
|
||||
ntp_getmsg((struct sockaddr *)&p->addr->ss, buf, size, &msg);
|
||||
|
||||
if (msg.orgtime.int_partl != p->query.msg.xmttime.int_partl ||
|
||||
msg.orgtime.fractionl != p->query.msg.xmttime.fractionl)
|
||||
return (0);
|
||||
|
||||
if ((msg.status & LI_ALARM) == LI_ALARM || msg.stratum == 0 ||
|
||||
msg.stratum > NTP_MAXSTRATUM) {
|
||||
char s[16];
|
||||
|
||||
if ((msg.status & LI_ALARM) == LI_ALARM) {
|
||||
strlcpy(s, "alarm", sizeof(s));
|
||||
} else if (msg.stratum == 0) {
|
||||
/* Kiss-o'-Death (KoD) packet */
|
||||
strlcpy(s, "KoD", sizeof(s));
|
||||
} else if (msg.stratum > NTP_MAXSTRATUM) {
|
||||
snprintf(s, sizeof(s), "stratum %d", msg.stratum);
|
||||
}
|
||||
interval = error_interval();
|
||||
set_next(p, interval);
|
||||
log_info("reply from %s: not synced (%s), next query %llds",
|
||||
log_ntp_addr(p->addr), s, (long long)interval);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* From RFC 2030 (with a correction to the delay math):
|
||||
*
|
||||
* Timestamp Name ID When Generated
|
||||
* ------------------------------------------------------------
|
||||
* Originate Timestamp T1 time request sent by client
|
||||
* Receive Timestamp T2 time request received by server
|
||||
* Transmit Timestamp T3 time reply sent by server
|
||||
* Destination Timestamp T4 time reply received by client
|
||||
*
|
||||
* The roundtrip delay d and local clock offset t are defined as
|
||||
*
|
||||
* d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2.
|
||||
*/
|
||||
|
||||
T1 = p->query.xmttime;
|
||||
T2 = lfp_to_d(msg.rectime);
|
||||
T3 = lfp_to_d(msg.xmttime);
|
||||
|
||||
/* Detect liars */
|
||||
if (!p->trusted && conf->constraint_median != 0 &&
|
||||
(constraint_check(T2) != 0 || constraint_check(T3) != 0)) {
|
||||
log_info("reply from %s: constraint check failed",
|
||||
log_ntp_addr(p->addr));
|
||||
set_next(p, error_interval());
|
||||
return (0);
|
||||
}
|
||||
|
||||
p->reply[p->shift].offset = ((T2 - T1) + (T3 - T4)) / 2 - getoffset();
|
||||
p->reply[p->shift].delay = (T4 - T1) - (T3 - T2);
|
||||
p->reply[p->shift].status.stratum = msg.stratum;
|
||||
if (p->reply[p->shift].delay < 0) {
|
||||
interval = error_interval();
|
||||
set_next(p, interval);
|
||||
log_info("reply from %s: negative delay %fs, "
|
||||
"next query %llds",
|
||||
log_ntp_addr(p->addr),
|
||||
p->reply[p->shift].delay, (long long)interval);
|
||||
return (0);
|
||||
}
|
||||
p->reply[p->shift].error = (T2 - T1) - (T3 - T4);
|
||||
p->reply[p->shift].rcvd = getmonotime();
|
||||
p->reply[p->shift].good = 1;
|
||||
|
||||
p->reply[p->shift].status.leap = (msg.status & LIMASK);
|
||||
p->reply[p->shift].status.precision = msg.precision;
|
||||
p->reply[p->shift].status.rootdelay = sfp_to_d(msg.rootdelay);
|
||||
p->reply[p->shift].status.rootdispersion = sfp_to_d(msg.dispersion);
|
||||
p->reply[p->shift].status.refid = msg.refid;
|
||||
p->reply[p->shift].status.reftime = lfp_to_d(msg.reftime);
|
||||
p->reply[p->shift].status.poll = msg.ppoll;
|
||||
|
||||
if (p->addr->ss.ss_family == AF_INET) {
|
||||
p->reply[p->shift].status.send_refid =
|
||||
((struct sockaddr_in *)&p->addr->ss)->sin_addr.s_addr;
|
||||
} else if (p->addr->ss.ss_family == AF_INET6) {
|
||||
MD5_CTX context;
|
||||
u_int8_t digest[MD5_DIGEST_LENGTH];
|
||||
|
||||
MD5Init(&context);
|
||||
MD5Update(&context, ((struct sockaddr_in6 *)&p->addr->ss)->
|
||||
sin6_addr.s6_addr, sizeof(struct in6_addr));
|
||||
MD5Final(digest, &context);
|
||||
memcpy((char *)&p->reply[p->shift].status.send_refid, digest,
|
||||
sizeof(u_int32_t));
|
||||
} else
|
||||
p->reply[p->shift].status.send_refid = msg.xmttime.fractionl;
|
||||
|
||||
p->state = STATE_REPLY_RECEIVED;
|
||||
|
||||
/* every received reply which we do not discard increases trust */
|
||||
if (p->trustlevel < TRUSTLEVEL_MAX) {
|
||||
if (p->trustlevel < TRUSTLEVEL_BADPEER &&
|
||||
p->trustlevel + 1 >= TRUSTLEVEL_BADPEER)
|
||||
log_info("peer %s now valid",
|
||||
log_ntp_addr(p->addr));
|
||||
p->trustlevel++;
|
||||
}
|
||||
|
||||
offset = p->reply[p->shift].offset;
|
||||
delay = p->reply[p->shift].delay;
|
||||
|
||||
client_update(p);
|
||||
if (settime) {
|
||||
if (automatic)
|
||||
handle_auto(p->trusted, p->reply[p->shift].offset);
|
||||
else
|
||||
priv_settime(p->reply[p->shift].offset, "");
|
||||
}
|
||||
|
||||
if (p->trustlevel < TRUSTLEVEL_PATHETIC)
|
||||
interval = scale_interval(INTERVAL_QUERY_PATHETIC);
|
||||
else if (p->trustlevel < TRUSTLEVEL_AGGRESSIVE)
|
||||
interval = (conf->settime && conf->automatic) ?
|
||||
INTERVAL_QUERY_ULTRA_VIOLENCE :
|
||||
scale_interval(INTERVAL_QUERY_AGGRESSIVE);
|
||||
else
|
||||
interval = scale_interval(INTERVAL_QUERY_NORMAL);
|
||||
|
||||
log_debug("reply from %s: offset %f delay %f, "
|
||||
"next query %llds", log_ntp_addr(p->addr),
|
||||
offset, delay, (long long)interval);
|
||||
|
||||
set_next(p, interval);
|
||||
|
||||
if (++p->shift >= OFFSET_ARRAY_SIZE)
|
||||
p->shift = 0;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
client_update(struct ntp_peer *p)
|
||||
{
|
||||
int shift, best = -1, good = 0;
|
||||
|
||||
/*
|
||||
* clock filter
|
||||
* find the offset which arrived with the lowest delay
|
||||
* use that as the peer update
|
||||
* invalidate it and all older ones
|
||||
*/
|
||||
|
||||
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++)
|
||||
if (p->reply[shift].good) {
|
||||
good++;
|
||||
if (best == -1 ||
|
||||
p->reply[shift].delay < p->reply[best].delay)
|
||||
best = shift;
|
||||
}
|
||||
|
||||
if (best == -1 || good < 8)
|
||||
return (-1);
|
||||
|
||||
p->update = p->reply[best];
|
||||
if (priv_adjtime() == 0) {
|
||||
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++)
|
||||
if (p->reply[shift].rcvd <= p->reply[best].rcvd)
|
||||
p->reply[shift].good = 0;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
client_log_error(struct ntp_peer *peer, const char *operation, int error)
|
||||
{
|
||||
const char *address;
|
||||
|
||||
address = log_ntp_addr(peer->addr);
|
||||
if (peer->lasterror == error) {
|
||||
log_debug("%s %s: %s", operation, address, strerror(error));
|
||||
return;
|
||||
}
|
||||
peer->lasterror = error;
|
||||
log_warn("%s %s", operation, address);
|
||||
}
|
186
config.c
Normal file
186
config.c
Normal file
@ -0,0 +1,186 @@
|
||||
/* $OpenBSD: config.c,v 1.33 2020/04/12 14:20:56 otto Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <resolv.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
struct ntp_addr *host_ip(const char *);
|
||||
int host_dns1(const char *, struct ntp_addr **, int);
|
||||
|
||||
static u_int32_t maxid = 0;
|
||||
static u_int32_t constraint_maxid = 0;
|
||||
int non_numeric;
|
||||
|
||||
void
|
||||
host(const char *s, struct ntp_addr **hn)
|
||||
{
|
||||
struct ntp_addr *h;
|
||||
|
||||
if (!strcmp(s, "*")) {
|
||||
if ((h = calloc(1, sizeof(*h))) == NULL)
|
||||
fatal(NULL);
|
||||
} else {
|
||||
if ((h = host_ip(s)) == NULL) {
|
||||
non_numeric = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*hn = h;
|
||||
}
|
||||
|
||||
struct ntp_addr *
|
||||
host_ip(const char *s)
|
||||
{
|
||||
struct addrinfo hints, *res;
|
||||
struct ntp_addr *h = NULL;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
if (getaddrinfo(s, "0", &hints, &res) == 0) {
|
||||
if (res->ai_family == AF_INET ||
|
||||
res->ai_family == AF_INET6) {
|
||||
if ((h = calloc(1, sizeof(*h))) == NULL)
|
||||
fatal(NULL);
|
||||
memcpy(&h->ss, res->ai_addr, res->ai_addrlen);
|
||||
}
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
void
|
||||
host_dns_free(struct ntp_addr *hn)
|
||||
{
|
||||
struct ntp_addr *h = hn, *tmp;
|
||||
while (h) {
|
||||
tmp = h;
|
||||
h = h->next;
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
host_dns1(const char *s, struct ntp_addr **hn, int notauth)
|
||||
{
|
||||
struct addrinfo hints, *res0, *res;
|
||||
int error, cnt = 0;
|
||||
struct ntp_addr *h, *hh = NULL;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
error = getaddrinfo(s, NULL, &hints, &res0);
|
||||
if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
|
||||
return (0);
|
||||
if (error) {
|
||||
log_warnx("could not parse \"%s\": %s", s,
|
||||
gai_strerror(error));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (res = res0; res && cnt < MAX_SERVERS_DNS; res = res->ai_next) {
|
||||
if (res->ai_family != AF_INET &&
|
||||
res->ai_family != AF_INET6)
|
||||
continue;
|
||||
if ((h = calloc(1, sizeof(*h))) == NULL)
|
||||
fatal(NULL);
|
||||
memcpy(&h->ss, res->ai_addr, res->ai_addrlen);
|
||||
h->notauth = notauth;
|
||||
|
||||
h->next = hh;
|
||||
hh = h;
|
||||
cnt++;
|
||||
}
|
||||
freeaddrinfo(res0);
|
||||
|
||||
*hn = hh;
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
int
|
||||
host_dns(const char *s, int synced, struct ntp_addr **hn)
|
||||
{
|
||||
int error, save_opts;
|
||||
|
||||
log_debug("trying to resolve %s", s);
|
||||
error = host_dns1(s, hn, 0);
|
||||
if (!synced && error <= 0) {
|
||||
log_debug("no luck, trying to resolve %s without checking", s);
|
||||
save_opts = _res.options;
|
||||
_res.options |= RES_USE_CD;
|
||||
error = host_dns1(s, hn, 1);
|
||||
_res.options = save_opts;
|
||||
}
|
||||
log_debug("resolve %s done: %d", s, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
struct ntp_peer *
|
||||
new_peer(void)
|
||||
{
|
||||
struct ntp_peer *p;
|
||||
|
||||
if ((p = calloc(1, sizeof(struct ntp_peer))) == NULL)
|
||||
fatal("new_peer calloc");
|
||||
p->id = ++maxid;
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
||||
struct ntp_conf_sensor *
|
||||
new_sensor(char *device)
|
||||
{
|
||||
struct ntp_conf_sensor *s;
|
||||
|
||||
if ((s = calloc(1, sizeof(struct ntp_conf_sensor))) == NULL)
|
||||
fatal("new_sensor calloc");
|
||||
if ((s->device = strdup(device)) == NULL)
|
||||
fatal("new_sensor strdup");
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
struct constraint *
|
||||
new_constraint(void)
|
||||
{
|
||||
struct constraint *p;
|
||||
|
||||
if ((p = calloc(1, sizeof(struct constraint))) == NULL)
|
||||
fatal("new_constraint calloc");
|
||||
p->id = ++constraint_maxid;
|
||||
p->fd = -1;
|
||||
|
||||
return (p);
|
||||
}
|
||||
|
1167
constraint.c
Normal file
1167
constraint.c
Normal file
File diff suppressed because it is too large
Load Diff
452
control.c
Normal file
452
control.c
Normal file
@ -0,0 +1,452 @@
|
||||
/* $OpenBSD: control.c,v 1.21 2024/04/23 13:34:51 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
#define CONTROL_BACKLOG 5
|
||||
|
||||
#define square(x) ((x) * (x))
|
||||
|
||||
int
|
||||
control_check(char *path)
|
||||
{
|
||||
struct sockaddr_un sun;
|
||||
int fd;
|
||||
|
||||
bzero(&sun, sizeof(sun));
|
||||
sun.sun_family = AF_UNIX;
|
||||
strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||
log_debug("control_check: socket check");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
|
||||
log_debug("control_check: socket in use");
|
||||
close(fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
control_init(char *path)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
int fd;
|
||||
mode_t old_umask;
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) {
|
||||
log_warn("control_init: socket");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
if (strlcpy(sa.sun_path, path, sizeof(sa.sun_path)) >=
|
||||
sizeof(sa.sun_path))
|
||||
errx(1, "ctl socket name too long");
|
||||
|
||||
if (unlink(path) == -1)
|
||||
if (errno != ENOENT) {
|
||||
log_warn("control_init: unlink %s", path);
|
||||
close(fd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
|
||||
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
|
||||
log_warn("control_init: bind: %s", path);
|
||||
close(fd);
|
||||
umask(old_umask);
|
||||
return (-1);
|
||||
}
|
||||
umask(old_umask);
|
||||
|
||||
if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
|
||||
log_warn("control_init: chmod");
|
||||
close(fd);
|
||||
(void)unlink(path);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
session_socket_nonblockmode(fd);
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
int
|
||||
control_listen(int fd)
|
||||
{
|
||||
if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
|
||||
log_warn("control_listen: listen");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
control_shutdown(int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int
|
||||
control_accept(int listenfd)
|
||||
{
|
||||
int connfd;
|
||||
socklen_t len;
|
||||
struct sockaddr_un sa;
|
||||
struct ctl_conn *ctl_conn;
|
||||
|
||||
len = sizeof(sa);
|
||||
if ((connfd = accept(listenfd,
|
||||
(struct sockaddr *)&sa, &len)) == -1) {
|
||||
if (errno != EWOULDBLOCK && errno != EINTR)
|
||||
log_warn("control_accept: accept");
|
||||
return (0);
|
||||
}
|
||||
|
||||
session_socket_nonblockmode(connfd);
|
||||
|
||||
if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
|
||||
log_warn("control_accept");
|
||||
close(connfd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
imsg_init(&ctl_conn->ibuf, connfd);
|
||||
|
||||
TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct ctl_conn *
|
||||
control_connbyfd(int fd)
|
||||
{
|
||||
struct ctl_conn *c;
|
||||
|
||||
TAILQ_FOREACH(c, &ctl_conns, entry) {
|
||||
if (c->ibuf.fd == fd)
|
||||
break;
|
||||
}
|
||||
|
||||
return (c);
|
||||
}
|
||||
|
||||
int
|
||||
control_close(int fd)
|
||||
{
|
||||
struct ctl_conn *c;
|
||||
|
||||
if ((c = control_connbyfd(fd)) == NULL) {
|
||||
log_warn("control_close: fd %d: not found", fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
msgbuf_clear(&c->ibuf.w);
|
||||
TAILQ_REMOVE(&ctl_conns, c, entry);
|
||||
|
||||
close(c->ibuf.fd);
|
||||
free(c);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
|
||||
{
|
||||
struct imsg imsg;
|
||||
struct ctl_conn *c;
|
||||
struct ntp_peer *p;
|
||||
struct ntp_sensor *s;
|
||||
struct ctl_show_status c_status;
|
||||
struct ctl_show_peer c_peer;
|
||||
struct ctl_show_sensor c_sensor;
|
||||
int cnt;
|
||||
ssize_t n;
|
||||
|
||||
if ((c = control_connbyfd(pfd->fd)) == NULL) {
|
||||
log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (pfd->revents & POLLOUT)
|
||||
if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
|
||||
*ctl_cnt -= control_close(pfd->fd);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (!(pfd->revents & POLLIN))
|
||||
return (0);
|
||||
|
||||
if (((n = imsg_read(&c->ibuf)) == -1 && errno != EAGAIN) || n == 0) {
|
||||
*ctl_cnt -= control_close(pfd->fd);
|
||||
return (1);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
|
||||
*ctl_cnt -= control_close(pfd->fd);
|
||||
return (1);
|
||||
}
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_CTL_SHOW_STATUS:
|
||||
build_show_status(&c_status);
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_STATUS, 0, 0, -1,
|
||||
&c_status, sizeof (c_status));
|
||||
break;
|
||||
case IMSG_CTL_SHOW_PEERS:
|
||||
cnt = 0;
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
build_show_peer(&c_peer, p);
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS,
|
||||
0, 0, -1, &c_peer, sizeof(c_peer));
|
||||
cnt++;
|
||||
}
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS_END,
|
||||
0, 0, -1, &cnt, sizeof(cnt));
|
||||
break;
|
||||
case IMSG_CTL_SHOW_SENSORS:
|
||||
cnt = 0;
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
build_show_sensor(&c_sensor, s);
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS,
|
||||
0, 0, -1, &c_sensor, sizeof(c_sensor));
|
||||
cnt++;
|
||||
}
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS_END,
|
||||
0, 0, -1, &cnt, sizeof(cnt));
|
||||
break;
|
||||
case IMSG_CTL_SHOW_ALL:
|
||||
build_show_status(&c_status);
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_STATUS, 0, 0, -1,
|
||||
&c_status, sizeof (c_status));
|
||||
|
||||
cnt = 0;
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
build_show_peer(&c_peer, p);
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS,
|
||||
0, 0, -1, &c_peer, sizeof(c_peer));
|
||||
cnt++;
|
||||
}
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_PEERS_END,
|
||||
0, 0, -1, &cnt, sizeof(cnt));
|
||||
|
||||
cnt = 0;
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
build_show_sensor(&c_sensor, s);
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS,
|
||||
0, 0, -1, &c_sensor, sizeof(c_sensor));
|
||||
cnt++;
|
||||
}
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_SENSORS_END,
|
||||
0, 0, -1, &cnt, sizeof(cnt));
|
||||
|
||||
imsg_compose(&c->ibuf, IMSG_CTL_SHOW_ALL_END,
|
||||
0, 0, -1, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
session_socket_nonblockmode(int fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1)
|
||||
fatal("fcntl F_GETFL");
|
||||
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if ((flags = fcntl(fd, F_SETFL, flags)) == -1)
|
||||
fatal("fcntl F_SETFL");
|
||||
}
|
||||
|
||||
void
|
||||
build_show_status(struct ctl_show_status *cs)
|
||||
{
|
||||
struct ntp_peer *p;
|
||||
struct ntp_sensor *s;
|
||||
|
||||
cs->peercnt = cs->valid_peers = 0;
|
||||
cs->sensorcnt = cs->valid_sensors = 0;
|
||||
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
cs->peercnt++;
|
||||
if (p->trustlevel >= TRUSTLEVEL_BADPEER)
|
||||
cs->valid_peers++;
|
||||
}
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
cs->sensorcnt++;
|
||||
if (s->update.good)
|
||||
cs->valid_sensors++;
|
||||
}
|
||||
|
||||
cs->synced = conf->status.synced;
|
||||
cs->stratum = conf->status.stratum;
|
||||
cs->clock_offset = getoffset() * 1000.0;
|
||||
cs->constraints = !TAILQ_EMPTY(&conf->constraints);
|
||||
cs->constraint_median = conf->constraint_median;
|
||||
cs->constraint_last = conf->constraint_last;
|
||||
cs->constraint_errors = conf->constraint_errors;
|
||||
}
|
||||
|
||||
void
|
||||
build_show_peer(struct ctl_show_peer *cp, struct ntp_peer *p)
|
||||
{
|
||||
const char *a = "not resolved";
|
||||
const char *pool = "", *addr_head_name = "";
|
||||
const char *auth = "";
|
||||
int shift, best = -1, validdelaycnt = 0, jittercnt = 0;
|
||||
time_t now;
|
||||
|
||||
now = getmonotime();
|
||||
|
||||
if (p->addr) {
|
||||
a = log_ntp_addr(p->addr);
|
||||
if (p->addr->notauth)
|
||||
auth = " (non-dnssec lookup)";
|
||||
}
|
||||
if (p->addr_head.pool)
|
||||
pool = "from pool ";
|
||||
|
||||
if (0 != strcmp(a, p->addr_head.name) || p->addr_head.pool)
|
||||
addr_head_name = p->addr_head.name;
|
||||
|
||||
snprintf(cp->peer_desc, sizeof(cp->peer_desc),
|
||||
"%s %s%s%s", a, pool, addr_head_name, auth);
|
||||
|
||||
cp->offset = cp->delay = 0.0;
|
||||
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++) {
|
||||
if (p->reply[shift].delay > 0.0) {
|
||||
cp->offset += p->reply[shift].offset;
|
||||
cp->delay += p->reply[shift].delay;
|
||||
|
||||
if (best == -1 ||
|
||||
p->reply[shift].delay < p->reply[best].delay)
|
||||
best = shift;
|
||||
|
||||
validdelaycnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (validdelaycnt > 1) {
|
||||
cp->offset /= validdelaycnt;
|
||||
cp->delay /= validdelaycnt;
|
||||
}
|
||||
|
||||
cp->jitter = 0.0;
|
||||
if (best != -1) {
|
||||
for (shift = 0; shift < OFFSET_ARRAY_SIZE; shift++) {
|
||||
if (p->reply[shift].delay > 0.0 && shift != best) {
|
||||
cp->jitter += square(p->reply[shift].delay -
|
||||
p->reply[best].delay);
|
||||
jittercnt++;
|
||||
}
|
||||
}
|
||||
if (jittercnt > 1)
|
||||
cp->jitter /= jittercnt;
|
||||
cp->jitter = sqrt(cp->jitter);
|
||||
}
|
||||
|
||||
if (p->shift == 0)
|
||||
shift = OFFSET_ARRAY_SIZE - 1;
|
||||
else
|
||||
shift = p->shift - 1;
|
||||
|
||||
if (conf->status.synced == 1 &&
|
||||
p->reply[shift].status.send_refid == conf->status.refid)
|
||||
cp->syncedto = 1;
|
||||
else
|
||||
cp->syncedto = 0;
|
||||
|
||||
/* milliseconds to reduce number of leading zeroes */
|
||||
cp->offset *= 1000.0;
|
||||
cp->delay *= 1000.0;
|
||||
cp->jitter *= 1000.0;
|
||||
|
||||
cp->weight = p->weight;
|
||||
cp->trustlevel = p->trustlevel;
|
||||
cp->stratum = p->reply[shift].status.stratum;
|
||||
cp->next = p->next - now < 0 ? 0 : p->next - now;
|
||||
cp->poll = p->poll;
|
||||
}
|
||||
|
||||
void
|
||||
build_show_sensor(struct ctl_show_sensor *cs, struct ntp_sensor *s)
|
||||
{
|
||||
time_t now;
|
||||
u_int8_t shift;
|
||||
u_int32_t refid;
|
||||
|
||||
now = getmonotime();
|
||||
|
||||
memcpy(&refid, SENSOR_DEFAULT_REFID, sizeof(refid));
|
||||
refid = refid == s->refid ? 0 : s->refid;
|
||||
|
||||
snprintf(cs->sensor_desc, sizeof(cs->sensor_desc),
|
||||
"%s %.4s", s->device, (char *)&refid);
|
||||
|
||||
if (s->shift == 0)
|
||||
shift = SENSOR_OFFSETS - 1;
|
||||
else
|
||||
shift = s->shift - 1;
|
||||
|
||||
if (conf->status.synced == 1 &&
|
||||
s->offsets[shift].status.send_refid == conf->status.refid)
|
||||
cs->syncedto = 1;
|
||||
else
|
||||
cs->syncedto = 0;
|
||||
|
||||
cs->weight = s->weight;
|
||||
cs->good = s->update.good;
|
||||
cs->stratum = s->offsets[shift].status.stratum;
|
||||
cs->next = s->next - now < 0 ? 0 : s->next - now;
|
||||
cs->poll = SENSOR_QUERY_INTERVAL;
|
||||
cs->offset = s->offsets[shift].offset * 1000.0;
|
||||
cs->correction = (double)s->correction / 1000.0;
|
||||
}
|
202
log.c
Normal file
202
log.c
Normal file
@ -0,0 +1,202 @@
|
||||
/* $OpenBSD: log.c,v 1.19 2019/07/03 05:04:19 otto Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include "log.h"
|
||||
|
||||
static int dest;
|
||||
static int verbose;
|
||||
const char *log_procname;
|
||||
|
||||
void
|
||||
log_init(int n_dest, int n_verbose, int facility)
|
||||
{
|
||||
extern char *__progname;
|
||||
|
||||
dest = n_dest;
|
||||
verbose = n_verbose;
|
||||
log_procinit(__progname);
|
||||
|
||||
if (dest & LOG_TO_SYSLOG)
|
||||
openlog(__progname, LOG_PID | LOG_NDELAY, facility);
|
||||
|
||||
tzset();
|
||||
}
|
||||
|
||||
void
|
||||
log_procinit(const char *procname)
|
||||
{
|
||||
if (procname != NULL)
|
||||
log_procname = procname;
|
||||
}
|
||||
|
||||
void
|
||||
log_setverbose(int v)
|
||||
{
|
||||
verbose = v;
|
||||
}
|
||||
|
||||
int
|
||||
log_getverbose(void)
|
||||
{
|
||||
return (verbose);
|
||||
}
|
||||
|
||||
void
|
||||
logit(int pri, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vlog(pri, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
vlog(int pri, const char *fmt, va_list ap)
|
||||
{
|
||||
char *nfmt;
|
||||
int saved_errno = errno;
|
||||
va_list ap2;
|
||||
|
||||
va_copy(ap2, ap);
|
||||
if (dest & LOG_TO_STDERR) {
|
||||
/* best effort in out of mem situations */
|
||||
if (asprintf(&nfmt, "%s\n", fmt) == -1) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
} else {
|
||||
vfprintf(stderr, nfmt, ap);
|
||||
free(nfmt);
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
if (dest & LOG_TO_SYSLOG)
|
||||
vsyslog(pri, fmt, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void
|
||||
log_warn(const char *emsg, ...)
|
||||
{
|
||||
char *nfmt;
|
||||
va_list ap;
|
||||
int saved_errno = errno;
|
||||
|
||||
/* best effort to even work in out of memory situations */
|
||||
if (emsg == NULL)
|
||||
logit(LOG_ERR, "%s", strerror(saved_errno));
|
||||
else {
|
||||
va_start(ap, emsg);
|
||||
|
||||
if (asprintf(&nfmt, "%s: %s", emsg,
|
||||
strerror(saved_errno)) == -1) {
|
||||
/* we tried it... */
|
||||
vlog(LOG_ERR, emsg, ap);
|
||||
logit(LOG_ERR, "%s", strerror(saved_errno));
|
||||
} else {
|
||||
vlog(LOG_ERR, nfmt, ap);
|
||||
free(nfmt);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
||||
void
|
||||
log_warnx(const char *emsg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, emsg);
|
||||
vlog(LOG_ERR, emsg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
log_info(const char *emsg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, emsg);
|
||||
vlog(LOG_INFO, emsg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
log_debug(const char *emsg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (verbose > 1) {
|
||||
va_start(ap, emsg);
|
||||
vlog(LOG_DEBUG, emsg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vfatalc(int code, const char *emsg, va_list ap)
|
||||
{
|
||||
static char s[BUFSIZ];
|
||||
const char *sep;
|
||||
|
||||
if (emsg != NULL) {
|
||||
(void)vsnprintf(s, sizeof(s), emsg, ap);
|
||||
sep = ": ";
|
||||
} else {
|
||||
s[0] = '\0';
|
||||
sep = "";
|
||||
}
|
||||
if (code)
|
||||
logit(LOG_CRIT, "%s: %s%s%s",
|
||||
log_procname, s, sep, strerror(code));
|
||||
else
|
||||
logit(LOG_CRIT, "%s%s%s", log_procname, sep, s);
|
||||
}
|
||||
|
||||
void
|
||||
fatal(const char *emsg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, emsg);
|
||||
vfatalc(errno, emsg, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
fatalx(const char *emsg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, emsg);
|
||||
vfatalc(0, emsg, ap);
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
48
log.h
Normal file
48
log.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* $OpenBSD: log.h,v 1.6 2021/12/13 18:28:40 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define LOG_TO_STDERR (1<<0)
|
||||
#define LOG_TO_SYSLOG (1<<1)
|
||||
|
||||
void log_init(int, int, int);
|
||||
void log_procinit(const char *);
|
||||
void log_setverbose(int);
|
||||
int log_getverbose(void);
|
||||
void log_warn(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
void log_warnx(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
void log_info(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
void log_debug(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
void logit(int, const char *, ...)
|
||||
__attribute__((__format__ (printf, 2, 3)));
|
||||
void vlog(int, const char *, va_list)
|
||||
__attribute__((__format__ (printf, 2, 0)));
|
||||
__dead void fatal(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
__dead void fatalx(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)));
|
||||
|
||||
#endif /* LOG_H */
|
907
ntp.c
Normal file
907
ntp.c
Normal file
@ -0,0 +1,907 @@
|
||||
/* $OpenBSD: ntp.c,v 1.174 2024/02/21 03:31:28 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
#define PFD_PIPE_MAIN 0
|
||||
#define PFD_PIPE_DNS 1
|
||||
#define PFD_SOCK_CTL 2
|
||||
#define PFD_MAX 3
|
||||
|
||||
volatile sig_atomic_t ntp_quit = 0;
|
||||
struct imsgbuf *ibuf_main;
|
||||
static struct imsgbuf *ibuf_dns;
|
||||
struct ntpd_conf *conf;
|
||||
struct ctl_conns ctl_conns;
|
||||
u_int peer_cnt;
|
||||
u_int sensors_cnt;
|
||||
extern u_int constraint_cnt;
|
||||
|
||||
void ntp_sighdlr(int);
|
||||
int ntp_dispatch_imsg(void);
|
||||
int ntp_dispatch_imsg_dns(void);
|
||||
void peer_add(struct ntp_peer *);
|
||||
void peer_remove(struct ntp_peer *);
|
||||
int inpool(struct sockaddr_storage *,
|
||||
struct sockaddr_storage[MAX_SERVERS_DNS], size_t);
|
||||
|
||||
void
|
||||
ntp_sighdlr(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
ntp_quit = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ntp_main(struct ntpd_conf *nconf, struct passwd *pw, int argc, char **argv)
|
||||
{
|
||||
int a, b, nfds, i, j, idx_peers, timeout;
|
||||
int nullfd, pipe_dns[2], idx_clients;
|
||||
int ctls;
|
||||
int fd_ctl;
|
||||
int clear_cdns;
|
||||
u_int pfd_elms = 0, idx2peer_elms = 0;
|
||||
u_int listener_cnt, new_cnt, sent_cnt, trial_cnt;
|
||||
u_int ctl_cnt;
|
||||
struct pollfd *pfd = NULL;
|
||||
struct servent *se;
|
||||
struct listen_addr *la;
|
||||
struct ntp_peer *p;
|
||||
struct ntp_peer **idx2peer = NULL;
|
||||
struct ntp_sensor *s, *next_s;
|
||||
struct constraint *cstr;
|
||||
struct timespec tp;
|
||||
struct stat stb;
|
||||
struct ctl_conn *cc;
|
||||
time_t nextaction, last_sensor_scan = 0, now;
|
||||
time_t last_action = 0, interval, last_cdns_reset = 0;
|
||||
void *newp;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
|
||||
pipe_dns) == -1)
|
||||
fatal("socketpair");
|
||||
|
||||
start_child(NTPDNS_PROC_NAME, pipe_dns[1], argc, argv);
|
||||
|
||||
log_init(nconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG, nconf->verbose,
|
||||
LOG_DAEMON);
|
||||
if (!nconf->debug && setsid() == -1)
|
||||
fatal("setsid");
|
||||
log_procinit("ntp");
|
||||
|
||||
if ((se = getservbyname("ntp", "udp")) == NULL)
|
||||
fatal("getservbyname");
|
||||
|
||||
/* Start control socket. */
|
||||
if ((fd_ctl = control_init(CTLSOCKET)) == -1)
|
||||
fatalx("control socket init failed");
|
||||
if (control_listen(fd_ctl) == -1)
|
||||
fatalx("control socket listen failed");
|
||||
if ((nullfd = open("/dev/null", O_RDWR)) == -1)
|
||||
fatal(NULL);
|
||||
|
||||
if (stat(pw->pw_dir, &stb) == -1) {
|
||||
fatal("privsep dir %s could not be opened", pw->pw_dir);
|
||||
}
|
||||
if (stb.st_uid != 0 || (stb.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
|
||||
fatalx("bad privsep dir %s permissions: %o",
|
||||
pw->pw_dir, stb.st_mode);
|
||||
}
|
||||
if (chroot(pw->pw_dir) == -1)
|
||||
fatal("chroot");
|
||||
if (chdir("/") == -1)
|
||||
fatal("chdir(\"/\")");
|
||||
|
||||
if (!nconf->debug) {
|
||||
dup2(nullfd, STDIN_FILENO);
|
||||
dup2(nullfd, STDOUT_FILENO);
|
||||
dup2(nullfd, STDERR_FILENO);
|
||||
}
|
||||
close(nullfd);
|
||||
|
||||
setproctitle("ntp engine");
|
||||
|
||||
conf = nconf;
|
||||
setup_listeners(se, conf, &listener_cnt);
|
||||
|
||||
if (setgroups(1, &pw->pw_gid) ||
|
||||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
|
||||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
|
||||
fatal("can't drop privileges");
|
||||
|
||||
endservent();
|
||||
|
||||
/* The ntp process will want to open NTP client sockets -> "inet" */
|
||||
if (pledge("stdio inet", NULL) == -1)
|
||||
err(1, "pledge");
|
||||
|
||||
signal(SIGTERM, ntp_sighdlr);
|
||||
signal(SIGINT, ntp_sighdlr);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL)
|
||||
fatal(NULL);
|
||||
imsg_init(ibuf_main, PARENT_SOCK_FILENO);
|
||||
if ((ibuf_dns = malloc(sizeof(struct imsgbuf))) == NULL)
|
||||
fatal(NULL);
|
||||
imsg_init(ibuf_dns, pipe_dns[0]);
|
||||
|
||||
constraint_cnt = 0;
|
||||
conf->constraint_median = 0;
|
||||
conf->constraint_last = getmonotime();
|
||||
TAILQ_FOREACH(cstr, &conf->constraints, entry)
|
||||
constraint_cnt += constraint_init(cstr);
|
||||
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry)
|
||||
client_peer_init(p);
|
||||
|
||||
memset(&conf->status, 0, sizeof(conf->status));
|
||||
|
||||
conf->freq.num = 0;
|
||||
conf->freq.samples = 0;
|
||||
conf->freq.x = 0.0;
|
||||
conf->freq.xx = 0.0;
|
||||
conf->freq.xy = 0.0;
|
||||
conf->freq.y = 0.0;
|
||||
conf->freq.overall_offset = 0.0;
|
||||
|
||||
conf->status.synced = 0;
|
||||
clock_getres(CLOCK_REALTIME, &tp);
|
||||
b = 1000000000 / tp.tv_nsec; /* convert to Hz */
|
||||
for (a = 0; b > 1; a--, b >>= 1)
|
||||
;
|
||||
conf->status.precision = a;
|
||||
conf->scale = 1;
|
||||
|
||||
TAILQ_INIT(&ctl_conns);
|
||||
sensor_init();
|
||||
|
||||
log_info("ntp engine ready");
|
||||
|
||||
ctl_cnt = 0;
|
||||
peer_cnt = 0;
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry)
|
||||
peer_cnt++;
|
||||
|
||||
while (ntp_quit == 0) {
|
||||
if (peer_cnt > idx2peer_elms) {
|
||||
if ((newp = reallocarray(idx2peer, peer_cnt,
|
||||
sizeof(*idx2peer))) == NULL) {
|
||||
/* panic for now */
|
||||
log_warn("could not resize idx2peer from %u -> "
|
||||
"%u entries", idx2peer_elms, peer_cnt);
|
||||
fatalx("exiting");
|
||||
}
|
||||
idx2peer = newp;
|
||||
idx2peer_elms = peer_cnt;
|
||||
}
|
||||
|
||||
new_cnt = PFD_MAX +
|
||||
peer_cnt + listener_cnt + ctl_cnt;
|
||||
if (new_cnt > pfd_elms) {
|
||||
if ((newp = reallocarray(pfd, new_cnt,
|
||||
sizeof(*pfd))) == NULL) {
|
||||
/* panic for now */
|
||||
log_warn("could not resize pfd from %u -> "
|
||||
"%u entries", pfd_elms, new_cnt);
|
||||
fatalx("exiting");
|
||||
}
|
||||
pfd = newp;
|
||||
pfd_elms = new_cnt;
|
||||
}
|
||||
|
||||
memset(pfd, 0, sizeof(*pfd) * pfd_elms);
|
||||
memset(idx2peer, 0, sizeof(*idx2peer) * idx2peer_elms);
|
||||
nextaction = getmonotime() + 900;
|
||||
pfd[PFD_PIPE_MAIN].fd = ibuf_main->fd;
|
||||
pfd[PFD_PIPE_MAIN].events = POLLIN;
|
||||
pfd[PFD_PIPE_DNS].fd = ibuf_dns->fd;
|
||||
pfd[PFD_PIPE_DNS].events = POLLIN;
|
||||
pfd[PFD_SOCK_CTL].fd = fd_ctl;
|
||||
pfd[PFD_SOCK_CTL].events = POLLIN;
|
||||
|
||||
i = PFD_MAX;
|
||||
TAILQ_FOREACH(la, &conf->listen_addrs, entry) {
|
||||
pfd[i].fd = la->fd;
|
||||
pfd[i].events = POLLIN;
|
||||
i++;
|
||||
}
|
||||
|
||||
idx_peers = i;
|
||||
sent_cnt = trial_cnt = 0;
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
if (!p->trusted && constraint_cnt &&
|
||||
conf->constraint_median == 0)
|
||||
continue;
|
||||
|
||||
if (p->next > 0 && p->next <= getmonotime()) {
|
||||
if (p->state > STATE_DNS_INPROGRESS)
|
||||
trial_cnt++;
|
||||
if (client_query(p) == 0)
|
||||
sent_cnt++;
|
||||
}
|
||||
if (p->deadline > 0 && p->deadline <= getmonotime()) {
|
||||
timeout = 300;
|
||||
log_debug("no reply from %s received in time, "
|
||||
"next query %ds", log_ntp_addr( p->addr),
|
||||
timeout);
|
||||
if (p->trustlevel >= TRUSTLEVEL_BADPEER &&
|
||||
(p->trustlevel /= 2) < TRUSTLEVEL_BADPEER)
|
||||
log_info("peer %s now invalid",
|
||||
log_ntp_addr(p->addr));
|
||||
if (client_nextaddr(p) == 1) {
|
||||
peer_addr_head_clear(p);
|
||||
client_nextaddr(p);
|
||||
}
|
||||
set_next(p, timeout);
|
||||
}
|
||||
if (p->senderrors > MAX_SEND_ERRORS) {
|
||||
log_debug("failed to send query to %s, "
|
||||
"next query %ds", log_ntp_addr(p->addr),
|
||||
INTERVAL_QUERY_PATHETIC);
|
||||
p->senderrors = 0;
|
||||
if (client_nextaddr(p) == 1) {
|
||||
peer_addr_head_clear(p);
|
||||
client_nextaddr(p);
|
||||
}
|
||||
set_next(p, INTERVAL_QUERY_PATHETIC);
|
||||
}
|
||||
if (p->next > 0 && p->next < nextaction)
|
||||
nextaction = p->next;
|
||||
if (p->deadline > 0 && p->deadline < nextaction)
|
||||
nextaction = p->deadline;
|
||||
|
||||
if (p->state == STATE_QUERY_SENT &&
|
||||
p->query.fd != -1) {
|
||||
pfd[i].fd = p->query.fd;
|
||||
pfd[i].events = POLLIN;
|
||||
idx2peer[i - idx_peers] = p;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
idx_clients = i;
|
||||
|
||||
if (!TAILQ_EMPTY(&conf->ntp_conf_sensors) &&
|
||||
(conf->trusted_sensors || constraint_cnt == 0 ||
|
||||
conf->constraint_median != 0)) {
|
||||
if (last_sensor_scan == 0 ||
|
||||
last_sensor_scan + SENSOR_SCAN_INTERVAL <= getmonotime()) {
|
||||
sensors_cnt = sensor_scan();
|
||||
last_sensor_scan = getmonotime();
|
||||
}
|
||||
if (sensors_cnt == 0 &&
|
||||
nextaction > last_sensor_scan + SENSOR_SCAN_INTERVAL)
|
||||
nextaction = last_sensor_scan + SENSOR_SCAN_INTERVAL;
|
||||
sensors_cnt = 0;
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
if (conf->settime && s->offsets[0].offset)
|
||||
priv_settime(s->offsets[0].offset, NULL);
|
||||
sensors_cnt++;
|
||||
if (s->next > 0 && s->next < nextaction)
|
||||
nextaction = s->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->settime &&
|
||||
((trial_cnt > 0 && sent_cnt == 0) ||
|
||||
(peer_cnt == 0 && sensors_cnt == 0)))
|
||||
priv_settime(0, "no valid peers configured");
|
||||
|
||||
clear_cdns = 1;
|
||||
TAILQ_FOREACH(cstr, &conf->constraints, entry) {
|
||||
constraint_query(cstr, conf->status.synced);
|
||||
if (cstr->state <= STATE_QUERY_SENT)
|
||||
clear_cdns = 0;
|
||||
}
|
||||
|
||||
if (ibuf_main->w.queued > 0)
|
||||
pfd[PFD_PIPE_MAIN].events |= POLLOUT;
|
||||
if (ibuf_dns->w.queued > 0)
|
||||
pfd[PFD_PIPE_DNS].events |= POLLOUT;
|
||||
|
||||
TAILQ_FOREACH(cc, &ctl_conns, entry) {
|
||||
pfd[i].fd = cc->ibuf.fd;
|
||||
pfd[i].events = POLLIN;
|
||||
if (cc->ibuf.w.queued > 0)
|
||||
pfd[i].events |= POLLOUT;
|
||||
i++;
|
||||
}
|
||||
ctls = i;
|
||||
|
||||
now = getmonotime();
|
||||
if (conf->constraint_median == 0 && clear_cdns &&
|
||||
now - last_cdns_reset > CONSTRAINT_SCAN_INTERVAL) {
|
||||
log_debug("Reset constraint info");
|
||||
constraint_reset();
|
||||
last_cdns_reset = now;
|
||||
nextaction = now + CONSTRAINT_RETRY_INTERVAL;
|
||||
}
|
||||
timeout = nextaction - now;
|
||||
if (timeout < 0)
|
||||
timeout = 0;
|
||||
|
||||
if ((nfds = poll(pfd, i, timeout ? timeout * 1000 : 1)) == -1)
|
||||
if (errno != EINTR) {
|
||||
log_warn("poll error");
|
||||
ntp_quit = 1;
|
||||
}
|
||||
|
||||
if (nfds > 0 && (pfd[PFD_PIPE_MAIN].revents & POLLOUT))
|
||||
if (msgbuf_write(&ibuf_main->w) <= 0 &&
|
||||
errno != EAGAIN) {
|
||||
log_warn("pipe write error (to parent)");
|
||||
ntp_quit = 1;
|
||||
}
|
||||
|
||||
if (nfds > 0 && pfd[PFD_PIPE_MAIN].revents & (POLLIN|POLLERR)) {
|
||||
nfds--;
|
||||
if (ntp_dispatch_imsg() == -1) {
|
||||
log_debug("pipe read error (from main)");
|
||||
ntp_quit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nfds > 0 && (pfd[PFD_PIPE_DNS].revents & POLLOUT))
|
||||
if (msgbuf_write(&ibuf_dns->w) <= 0 &&
|
||||
errno != EAGAIN) {
|
||||
log_warn("pipe write error (to dns engine)");
|
||||
ntp_quit = 1;
|
||||
}
|
||||
|
||||
if (nfds > 0 && pfd[PFD_PIPE_DNS].revents & (POLLIN|POLLERR)) {
|
||||
nfds--;
|
||||
if (ntp_dispatch_imsg_dns() == -1) {
|
||||
log_warn("pipe read error (from dns engine)");
|
||||
ntp_quit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nfds > 0 && pfd[PFD_SOCK_CTL].revents & (POLLIN|POLLERR)) {
|
||||
nfds--;
|
||||
ctl_cnt += control_accept(fd_ctl);
|
||||
}
|
||||
|
||||
for (j = PFD_MAX; nfds > 0 && j < idx_peers; j++)
|
||||
if (pfd[j].revents & (POLLIN|POLLERR)) {
|
||||
nfds--;
|
||||
if (server_dispatch(pfd[j].fd, conf) == -1) {
|
||||
log_warn("pipe write error (conf)");
|
||||
ntp_quit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (; nfds > 0 && j < idx_clients; j++) {
|
||||
if (pfd[j].revents & (POLLIN|POLLERR)) {
|
||||
struct ntp_peer *pp = idx2peer[j - idx_peers];
|
||||
|
||||
nfds--;
|
||||
switch (client_dispatch(pp, conf->settime,
|
||||
conf->automatic)) {
|
||||
case -1:
|
||||
log_debug("no reply from %s "
|
||||
"received", log_ntp_addr(pp->addr));
|
||||
if (pp->trustlevel >=
|
||||
TRUSTLEVEL_BADPEER &&
|
||||
(pp->trustlevel /= 2) <
|
||||
TRUSTLEVEL_BADPEER)
|
||||
log_info("peer %s now invalid",
|
||||
log_ntp_addr(pp->addr));
|
||||
break;
|
||||
case 0: /* invalid replies are ignored */
|
||||
break;
|
||||
case 1:
|
||||
last_action = now;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; nfds > 0 && j < ctls; j++) {
|
||||
nfds -= control_dispatch_msg(&pfd[j], &ctl_cnt);
|
||||
}
|
||||
|
||||
for (s = TAILQ_FIRST(&conf->ntp_sensors); s != NULL;
|
||||
s = next_s) {
|
||||
next_s = TAILQ_NEXT(s, entry);
|
||||
if (s->next <= now) {
|
||||
last_action = now;
|
||||
sensor_query(s);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute maximum of scale_interval(INTERVAL_QUERY_NORMAL),
|
||||
* if we did not process a time message for three times that
|
||||
* interval, stop advertising we're synced.
|
||||
*/
|
||||
interval = INTERVAL_QUERY_NORMAL * conf->scale;
|
||||
interval += SCALE_INTERVAL(interval) - 1;
|
||||
if (conf->status.synced && last_action + 3 * interval < now) {
|
||||
log_info("clock is now unsynced due to lack of replies");
|
||||
conf->status.synced = 0;
|
||||
conf->scale = 1;
|
||||
priv_dns(IMSG_UNSYNCED, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
msgbuf_write(&ibuf_main->w);
|
||||
msgbuf_clear(&ibuf_main->w);
|
||||
free(ibuf_main);
|
||||
msgbuf_write(&ibuf_dns->w);
|
||||
msgbuf_clear(&ibuf_dns->w);
|
||||
free(ibuf_dns);
|
||||
|
||||
log_info("ntp engine exiting");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
ntp_dispatch_imsg(void)
|
||||
{
|
||||
struct imsg imsg;
|
||||
int n;
|
||||
|
||||
if (((n = imsg_read(ibuf_main)) == -1 && errno != EAGAIN) || n == 0)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
if ((n = imsg_get(ibuf_main, &imsg)) == -1)
|
||||
return (-1);
|
||||
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_ADJTIME:
|
||||
memcpy(&n, imsg.data, sizeof(n));
|
||||
if (n == 1 && !conf->status.synced) {
|
||||
log_info("clock is now synced");
|
||||
conf->status.synced = 1;
|
||||
priv_dns(IMSG_SYNCED, NULL, 0);
|
||||
constraint_reset();
|
||||
} else if (n == 0 && conf->status.synced) {
|
||||
log_info("clock is now unsynced");
|
||||
conf->status.synced = 0;
|
||||
priv_dns(IMSG_UNSYNCED, NULL, 0);
|
||||
}
|
||||
break;
|
||||
case IMSG_CONSTRAINT_RESULT:
|
||||
constraint_msg_result(imsg.hdr.peerid,
|
||||
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
|
||||
break;
|
||||
case IMSG_CONSTRAINT_CLOSE:
|
||||
constraint_msg_close(imsg.hdr.peerid,
|
||||
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
inpool(struct sockaddr_storage *a,
|
||||
struct sockaddr_storage old[MAX_SERVERS_DNS], size_t n)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (a->ss_family != old[i].ss_family)
|
||||
continue;
|
||||
if (a->ss_family == AF_INET) {
|
||||
if (((struct sockaddr_in *)a)->sin_addr.s_addr ==
|
||||
((struct sockaddr_in *)&old[i])->sin_addr.s_addr)
|
||||
return 1;
|
||||
} else if (memcmp(&((struct sockaddr_in6 *)a)->sin6_addr,
|
||||
&((struct sockaddr_in6 *)&old[i])->sin6_addr,
|
||||
sizeof(struct in6_addr)) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ntp_dispatch_imsg_dns(void)
|
||||
{
|
||||
struct imsg imsg;
|
||||
struct sockaddr_storage existing[MAX_SERVERS_DNS];
|
||||
struct ntp_peer *peer, *npeer, *tmp;
|
||||
u_int16_t dlen;
|
||||
u_char *p;
|
||||
struct ntp_addr *h;
|
||||
size_t addrcount, peercount;
|
||||
int n;
|
||||
|
||||
if (((n = imsg_read(ibuf_dns)) == -1 && errno != EAGAIN) || n == 0)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
if ((n = imsg_get(ibuf_dns, &imsg)) == -1)
|
||||
return (-1);
|
||||
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_HOST_DNS:
|
||||
TAILQ_FOREACH(peer, &conf->ntp_peers, entry)
|
||||
if (peer->id == imsg.hdr.peerid)
|
||||
break;
|
||||
if (peer == NULL) {
|
||||
log_warnx("IMSG_HOST_DNS with invalid peerID");
|
||||
break;
|
||||
}
|
||||
if (peer->addr != NULL) {
|
||||
log_warnx("IMSG_HOST_DNS but addr != NULL!");
|
||||
break;
|
||||
}
|
||||
|
||||
if (peer->addr_head.pool) {
|
||||
n = 0;
|
||||
peercount = 0;
|
||||
|
||||
TAILQ_FOREACH_SAFE(npeer, &conf->ntp_peers,
|
||||
entry, tmp) {
|
||||
if (npeer->addr_head.pool !=
|
||||
peer->addr_head.pool)
|
||||
continue;
|
||||
peercount++;
|
||||
if (npeer->id == peer->id)
|
||||
continue;
|
||||
if (npeer->addr != NULL)
|
||||
existing[n++] = npeer->addr->ss;
|
||||
}
|
||||
}
|
||||
|
||||
dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
||||
if (dlen == 0) { /* no data -> temp error */
|
||||
log_debug("DNS lookup tempfail");
|
||||
peer->state = STATE_DNS_TEMPFAIL;
|
||||
if (conf->tmpfail++ == TRIES_AUTO_DNSFAIL)
|
||||
priv_settime(0, "of dns failures");
|
||||
break;
|
||||
}
|
||||
|
||||
p = (u_char *)imsg.data;
|
||||
addrcount = dlen / (sizeof(struct sockaddr_storage) +
|
||||
sizeof(int));
|
||||
|
||||
while (dlen >= sizeof(struct sockaddr_storage) +
|
||||
sizeof(int)) {
|
||||
if ((h = calloc(1, sizeof(struct ntp_addr))) ==
|
||||
NULL)
|
||||
fatal(NULL);
|
||||
memcpy(&h->ss, p, sizeof(h->ss));
|
||||
p += sizeof(h->ss);
|
||||
dlen -= sizeof(h->ss);
|
||||
memcpy(&h->notauth, p, sizeof(int));
|
||||
p += sizeof(int);
|
||||
dlen -= sizeof(int);
|
||||
if (peer->addr_head.pool) {
|
||||
if (peercount > addrcount) {
|
||||
free(h);
|
||||
continue;
|
||||
}
|
||||
if (inpool(&h->ss, existing,
|
||||
n)) {
|
||||
free(h);
|
||||
continue;
|
||||
}
|
||||
log_debug("Adding address %s to %s",
|
||||
log_ntp_addr(h), peer->addr_head.name);
|
||||
npeer = new_peer();
|
||||
npeer->weight = peer->weight;
|
||||
npeer->query_addr4 = peer->query_addr4;
|
||||
npeer->query_addr6 = peer->query_addr6;
|
||||
h->next = NULL;
|
||||
npeer->addr = h;
|
||||
npeer->addr_head.a = h;
|
||||
npeer->addr_head.name =
|
||||
peer->addr_head.name;
|
||||
npeer->addr_head.pool =
|
||||
peer->addr_head.pool;
|
||||
client_peer_init(npeer);
|
||||
npeer->state = STATE_DNS_DONE;
|
||||
peer_add(npeer);
|
||||
peercount++;
|
||||
} else {
|
||||
h->next = peer->addr;
|
||||
peer->addr = h;
|
||||
peer->addr_head.a = peer->addr;
|
||||
peer->state = STATE_DNS_DONE;
|
||||
}
|
||||
}
|
||||
if (dlen != 0)
|
||||
fatalx("IMSG_HOST_DNS: dlen != 0");
|
||||
if (peer->addr_head.pool)
|
||||
peer_remove(peer);
|
||||
else
|
||||
client_addr_init(peer);
|
||||
break;
|
||||
case IMSG_CONSTRAINT_DNS:
|
||||
constraint_msg_dns(imsg.hdr.peerid,
|
||||
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
|
||||
break;
|
||||
case IMSG_PROBE_ROOT:
|
||||
dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
|
||||
if (dlen != sizeof(int))
|
||||
fatalx("IMSG_PROBE_ROOT");
|
||||
memcpy(&n, imsg.data, sizeof(int));
|
||||
if (n < 0)
|
||||
priv_settime(0, "dns probe failed");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
peer_add(struct ntp_peer *p)
|
||||
{
|
||||
TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
|
||||
peer_cnt++;
|
||||
}
|
||||
|
||||
void
|
||||
peer_remove(struct ntp_peer *p)
|
||||
{
|
||||
TAILQ_REMOVE(&conf->ntp_peers, p, entry);
|
||||
free(p);
|
||||
peer_cnt--;
|
||||
}
|
||||
|
||||
void
|
||||
peer_addr_head_clear(struct ntp_peer *p)
|
||||
{
|
||||
host_dns_free(p->addr_head.a);
|
||||
p->addr_head.a = NULL;
|
||||
p->addr = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
priv_adjfreq(double offset)
|
||||
{
|
||||
double curtime, freq;
|
||||
|
||||
if (!conf->status.synced){
|
||||
conf->freq.samples = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
conf->freq.samples++;
|
||||
|
||||
if (conf->freq.samples <= 0)
|
||||
return;
|
||||
|
||||
conf->freq.overall_offset += offset;
|
||||
offset = conf->freq.overall_offset;
|
||||
|
||||
curtime = gettime_corrected();
|
||||
conf->freq.xy += offset * curtime;
|
||||
conf->freq.x += curtime;
|
||||
conf->freq.y += offset;
|
||||
conf->freq.xx += curtime * curtime;
|
||||
|
||||
if (conf->freq.samples % FREQUENCY_SAMPLES != 0)
|
||||
return;
|
||||
|
||||
freq =
|
||||
(conf->freq.xy - conf->freq.x * conf->freq.y / conf->freq.samples)
|
||||
/
|
||||
(conf->freq.xx - conf->freq.x * conf->freq.x / conf->freq.samples);
|
||||
|
||||
if (freq > MAX_FREQUENCY_ADJUST)
|
||||
freq = MAX_FREQUENCY_ADJUST;
|
||||
else if (freq < -MAX_FREQUENCY_ADJUST)
|
||||
freq = -MAX_FREQUENCY_ADJUST;
|
||||
|
||||
imsg_compose(ibuf_main, IMSG_ADJFREQ, 0, 0, -1, &freq, sizeof(freq));
|
||||
conf->filters |= FILTER_ADJFREQ;
|
||||
conf->freq.xy = 0.0;
|
||||
conf->freq.x = 0.0;
|
||||
conf->freq.y = 0.0;
|
||||
conf->freq.xx = 0.0;
|
||||
conf->freq.samples = 0;
|
||||
conf->freq.overall_offset = 0.0;
|
||||
conf->freq.num++;
|
||||
}
|
||||
|
||||
int
|
||||
priv_adjtime(void)
|
||||
{
|
||||
struct ntp_peer *p;
|
||||
struct ntp_sensor *s;
|
||||
int offset_cnt = 0, i = 0, j;
|
||||
struct ntp_offset **offsets;
|
||||
double offset_median;
|
||||
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
if (p->trustlevel < TRUSTLEVEL_BADPEER)
|
||||
continue;
|
||||
if (!p->update.good)
|
||||
return (1);
|
||||
offset_cnt += p->weight;
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
if (!s->update.good)
|
||||
continue;
|
||||
offset_cnt += s->weight;
|
||||
}
|
||||
|
||||
if (offset_cnt == 0)
|
||||
return (1);
|
||||
|
||||
if ((offsets = calloc(offset_cnt, sizeof(struct ntp_offset *))) == NULL)
|
||||
fatal("calloc priv_adjtime");
|
||||
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
if (p->trustlevel < TRUSTLEVEL_BADPEER)
|
||||
continue;
|
||||
for (j = 0; j < p->weight; j++)
|
||||
offsets[i++] = &p->update;
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
if (!s->update.good)
|
||||
continue;
|
||||
for (j = 0; j < s->weight; j++)
|
||||
offsets[i++] = &s->update;
|
||||
}
|
||||
|
||||
qsort(offsets, offset_cnt, sizeof(struct ntp_offset *), offset_compare);
|
||||
|
||||
i = offset_cnt / 2;
|
||||
if (offset_cnt % 2 == 0)
|
||||
if (offsets[i - 1]->delay < offsets[i]->delay)
|
||||
i -= 1;
|
||||
offset_median = offsets[i]->offset;
|
||||
conf->status.rootdelay = offsets[i]->delay;
|
||||
conf->status.stratum = offsets[i]->status.stratum;
|
||||
conf->status.leap = offsets[i]->status.leap;
|
||||
|
||||
imsg_compose(ibuf_main, IMSG_ADJTIME, 0, 0, -1,
|
||||
&offset_median, sizeof(offset_median));
|
||||
|
||||
priv_adjfreq(offset_median);
|
||||
|
||||
conf->status.reftime = gettime();
|
||||
conf->status.stratum++; /* one more than selected peer */
|
||||
if (conf->status.stratum > NTP_MAXSTRATUM)
|
||||
conf->status.stratum = NTP_MAXSTRATUM;
|
||||
update_scale(offset_median);
|
||||
|
||||
conf->status.refid = offsets[i]->status.send_refid;
|
||||
|
||||
free(offsets);
|
||||
|
||||
TAILQ_FOREACH(p, &conf->ntp_peers, entry) {
|
||||
for (i = 0; i < OFFSET_ARRAY_SIZE; i++)
|
||||
p->reply[i].offset -= offset_median;
|
||||
p->update.good = 0;
|
||||
}
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry) {
|
||||
for (i = 0; i < SENSOR_OFFSETS; i++)
|
||||
s->offsets[i].offset -= offset_median;
|
||||
s->update.offset -= offset_median;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
offset_compare(const void *aa, const void *bb)
|
||||
{
|
||||
const struct ntp_offset * const *a;
|
||||
const struct ntp_offset * const *b;
|
||||
|
||||
a = aa;
|
||||
b = bb;
|
||||
|
||||
if ((*a)->offset < (*b)->offset)
|
||||
return (-1);
|
||||
else if ((*a)->offset > (*b)->offset)
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
priv_settime(double offset, char *msg)
|
||||
{
|
||||
if (offset == 0)
|
||||
log_info("cancel settime because %s", msg);
|
||||
imsg_compose(ibuf_main, IMSG_SETTIME, 0, 0, -1,
|
||||
&offset, sizeof(offset));
|
||||
conf->settime = 0;
|
||||
}
|
||||
|
||||
void
|
||||
priv_dns(int cmd, char *name, u_int32_t peerid)
|
||||
{
|
||||
u_int16_t dlen = 0;
|
||||
|
||||
if (name != NULL)
|
||||
dlen = strlen(name) + 1;
|
||||
imsg_compose(ibuf_dns, cmd, peerid, 0, -1, name, dlen);
|
||||
}
|
||||
|
||||
void
|
||||
update_scale(double offset)
|
||||
{
|
||||
offset += getoffset();
|
||||
if (offset < 0)
|
||||
offset = -offset;
|
||||
|
||||
if (offset > QSCALE_OFF_MAX || !conf->status.synced ||
|
||||
conf->freq.num < 3)
|
||||
conf->scale = 1;
|
||||
else if (offset < QSCALE_OFF_MIN)
|
||||
conf->scale = QSCALE_OFF_MAX / QSCALE_OFF_MIN;
|
||||
else
|
||||
conf->scale = QSCALE_OFF_MAX / offset;
|
||||
}
|
||||
|
||||
time_t
|
||||
scale_interval(time_t requested)
|
||||
{
|
||||
time_t interval, r;
|
||||
|
||||
interval = requested * conf->scale;
|
||||
r = arc4random_uniform(SCALE_INTERVAL(interval));
|
||||
return (interval + r);
|
||||
}
|
||||
|
||||
time_t
|
||||
error_interval(void)
|
||||
{
|
||||
time_t interval, r;
|
||||
|
||||
interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN;
|
||||
r = arc4random_uniform(interval / 10);
|
||||
return (interval + r);
|
||||
}
|
163
ntp.h
Normal file
163
ntp.h
Normal file
@ -0,0 +1,163 @@
|
||||
/* $OpenBSD: ntp.h,v 1.15 2023/11/15 15:52:09 otto Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _NTP_H_
|
||||
#define _NTP_H_
|
||||
|
||||
/* Style borrowed from NTP ref/tcpdump and updated for SNTPv4 (RFC2030). */
|
||||
|
||||
/*
|
||||
* RFC Section 3
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Integer Part |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Fraction Part |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Integer Part | Fraction Part |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
struct l_fixedpt {
|
||||
u_int32_t int_partl;
|
||||
u_int32_t fractionl;
|
||||
};
|
||||
#define L_DENOMINATOR (UINT32_MAX + 1ULL)
|
||||
|
||||
struct s_fixedpt {
|
||||
u_int16_t int_parts;
|
||||
u_int16_t fractions;
|
||||
};
|
||||
#define S_DENOMINATOR (UINT16_MAX + 1)
|
||||
|
||||
/* RFC Section 4
|
||||
*
|
||||
* 0 1 2 3
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |LI | VN | Mode| Stratum | Poll | Precision |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Synchronizing Distance |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Synchronizing Dispersion |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Reference Clock Identifier |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | Reference Timestamp (64 bits) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | Originate Timestamp (64 bits) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | Receive Timestamp (64 bits) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | Transmit Timestamp (64 bits) |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Key Identifier (optional) (32) |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | |
|
||||
* | |
|
||||
* | Message Digest (optional) (128) |
|
||||
* | |
|
||||
* | |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
*/
|
||||
|
||||
#define NTP_DIGESTSIZE 16
|
||||
#define NTP_MSGSIZE_NOAUTH 48
|
||||
#define NTP_MSGSIZE (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE)
|
||||
|
||||
struct ntp_msg {
|
||||
u_int8_t status; /* status of local clock and leap info */
|
||||
u_int8_t stratum; /* Stratum level */
|
||||
u_int8_t ppoll; /* poll value */
|
||||
int8_t precision;
|
||||
struct s_fixedpt rootdelay;
|
||||
struct s_fixedpt dispersion;
|
||||
u_int32_t refid;
|
||||
struct l_fixedpt reftime;
|
||||
struct l_fixedpt orgtime;
|
||||
struct l_fixedpt rectime;
|
||||
struct l_fixedpt xmttime;
|
||||
} __packed;
|
||||
|
||||
struct ntp_query {
|
||||
int fd;
|
||||
struct ntp_msg msg;
|
||||
double xmttime;
|
||||
};
|
||||
|
||||
/*
|
||||
* Leap Second Codes (high order two bits)
|
||||
*/
|
||||
#define LI_NOWARNING (0 << 6) /* no warning */
|
||||
#define LI_PLUSSEC (1 << 6) /* add a second (61 seconds) */
|
||||
#define LI_MINUSSEC (2 << 6) /* minus a second (59 seconds) */
|
||||
#define LI_ALARM (3 << 6) /* alarm condition */
|
||||
|
||||
/*
|
||||
* Status Masks
|
||||
*/
|
||||
#define MODEMASK (7 << 0)
|
||||
#define VERSIONMASK (7 << 3)
|
||||
#define LIMASK (3 << 6)
|
||||
|
||||
/*
|
||||
* Mode values
|
||||
*/
|
||||
#define MODE_RES0 0 /* reserved */
|
||||
#define MODE_SYM_ACT 1 /* symmetric active */
|
||||
#define MODE_SYM_PAS 2 /* symmetric passive */
|
||||
#define MODE_CLIENT 3 /* client */
|
||||
#define MODE_SERVER 4 /* server */
|
||||
#define MODE_BROADCAST 5 /* broadcast */
|
||||
#define MODE_RES1 6 /* reserved for NTP control message */
|
||||
#define MODE_RES2 7 /* reserved for private use */
|
||||
|
||||
#define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
|
||||
|
||||
/*
|
||||
* The era we're in if we have no reason to assume otherwise.
|
||||
* If lfp_to_d() sees an offset <= INT32_MAX the era is is assumed to be
|
||||
* NTP_ERA + 1.
|
||||
* Once the actual year is well into era 1, (after 2036) define NTP_ERA to 1
|
||||
* and adapt (remove) the test in lfp_to_d().
|
||||
* Once more than half of era 1 has elapsed (after 2104), re-inroduce the test
|
||||
* to move to era 2 if offset <= INT32_MAX, repeat for each half era.
|
||||
*/
|
||||
#define NTP_ERA 0
|
||||
|
||||
#define SECS_IN_ERA (UINT32_MAX + 1ULL)
|
||||
|
||||
#define NTP_VERSION 4
|
||||
#define NTP_MAXSTRATUM 15
|
||||
|
||||
#endif /* _NTP_H_ */
|
251
ntp_dns.c
Normal file
251
ntp_dns.c
Normal file
@ -0,0 +1,251 @@
|
||||
/* $OpenBSD: ntp_dns.c,v 1.28 2023/04/19 12:58:16 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003-2008 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/nameser.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
volatile sig_atomic_t quit_dns = 0;
|
||||
static struct imsgbuf *ibuf_dns;
|
||||
extern int non_numeric;
|
||||
|
||||
void sighdlr_dns(int);
|
||||
int dns_dispatch_imsg(struct ntpd_conf *);
|
||||
int probe_root_ns(void);
|
||||
void probe_root(void);
|
||||
|
||||
void
|
||||
sighdlr_dns(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
quit_dns = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ntp_dns(struct ntpd_conf *nconf, struct passwd *pw)
|
||||
{
|
||||
struct pollfd pfd[1];
|
||||
int nfds, nullfd;
|
||||
|
||||
res_init();
|
||||
if (setpriority(PRIO_PROCESS, 0, 0) == -1)
|
||||
log_warn("could not set priority");
|
||||
|
||||
log_init(nconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG, nconf->verbose,
|
||||
LOG_DAEMON);
|
||||
if (!nconf->debug && setsid() == -1)
|
||||
fatal("setsid");
|
||||
log_procinit("dns");
|
||||
|
||||
if ((nullfd = open("/dev/null", O_RDWR)) == -1)
|
||||
fatal(NULL);
|
||||
|
||||
if (!nconf->debug) {
|
||||
dup2(nullfd, STDIN_FILENO);
|
||||
dup2(nullfd, STDOUT_FILENO);
|
||||
dup2(nullfd, STDERR_FILENO);
|
||||
}
|
||||
close(nullfd);
|
||||
|
||||
setproctitle("dns engine");
|
||||
|
||||
if (setgroups(1, &pw->pw_gid) ||
|
||||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
|
||||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
|
||||
fatal("can't drop privileges");
|
||||
|
||||
signal(SIGTERM, sighdlr_dns);
|
||||
signal(SIGINT, sighdlr_dns);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
|
||||
if ((ibuf_dns = malloc(sizeof(struct imsgbuf))) == NULL)
|
||||
fatal(NULL);
|
||||
imsg_init(ibuf_dns, PARENT_SOCK_FILENO);
|
||||
|
||||
if (pledge("stdio dns", NULL) == -1)
|
||||
err(1, "pledge");
|
||||
|
||||
if (non_numeric)
|
||||
probe_root();
|
||||
else
|
||||
log_debug("all addresses numeric, no dns probe");
|
||||
|
||||
while (quit_dns == 0) {
|
||||
pfd[0].fd = ibuf_dns->fd;
|
||||
pfd[0].events = POLLIN;
|
||||
if (ibuf_dns->w.queued)
|
||||
pfd[0].events |= POLLOUT;
|
||||
|
||||
if ((nfds = poll(pfd, 1, INFTIM)) == -1)
|
||||
if (errno != EINTR) {
|
||||
log_warn("poll error");
|
||||
quit_dns = 1;
|
||||
}
|
||||
|
||||
if (nfds > 0 && (pfd[0].revents & POLLOUT))
|
||||
if (msgbuf_write(&ibuf_dns->w) <= 0 &&
|
||||
errno != EAGAIN) {
|
||||
log_warn("pipe write error (to ntp engine)");
|
||||
quit_dns = 1;
|
||||
}
|
||||
|
||||
if (nfds > 0 && pfd[0].revents & POLLIN) {
|
||||
nfds--;
|
||||
if (dns_dispatch_imsg(nconf) == -1)
|
||||
quit_dns = 1;
|
||||
}
|
||||
}
|
||||
|
||||
msgbuf_clear(&ibuf_dns->w);
|
||||
free(ibuf_dns);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
dns_dispatch_imsg(struct ntpd_conf *nconf)
|
||||
{
|
||||
struct imsg imsg;
|
||||
int n, cnt;
|
||||
char *name;
|
||||
struct ntp_addr *h, *hn;
|
||||
struct ibuf *buf;
|
||||
const char *str;
|
||||
size_t len;
|
||||
|
||||
if (((n = imsg_read(ibuf_dns)) == -1 && errno != EAGAIN) || n == 0)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
if ((n = imsg_get(ibuf_dns, &imsg)) == -1)
|
||||
return (-1);
|
||||
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_HOST_DNS:
|
||||
case IMSG_CONSTRAINT_DNS:
|
||||
if (imsg.hdr.type == IMSG_HOST_DNS)
|
||||
str = "IMSG_HOST_DNS";
|
||||
else
|
||||
str = "IMSG_CONSTRAINT_DNS";
|
||||
name = imsg.data;
|
||||
if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
|
||||
fatalx("invalid %s received", str);
|
||||
len = imsg.hdr.len - 1 - IMSG_HEADER_SIZE;
|
||||
if (name[len] != '\0' ||
|
||||
strlen(name) != len)
|
||||
fatalx("invalid %s received", str);
|
||||
if ((cnt = host_dns(name, nconf->status.synced,
|
||||
&hn)) == -1)
|
||||
break;
|
||||
buf = imsg_create(ibuf_dns, imsg.hdr.type,
|
||||
imsg.hdr.peerid, 0,
|
||||
cnt * (sizeof(struct sockaddr_storage) + sizeof(int)));
|
||||
if (cnt > 0) {
|
||||
if (buf) {
|
||||
for (h = hn; h != NULL; h = h->next) {
|
||||
if (imsg_add(buf, &h->ss,
|
||||
sizeof(h->ss)) == -1) {
|
||||
buf = NULL;
|
||||
break;
|
||||
}
|
||||
if (imsg_add(buf, &h->notauth,
|
||||
sizeof(int)) == -1) {
|
||||
buf = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
host_dns_free(hn);
|
||||
hn = NULL;
|
||||
}
|
||||
if (buf)
|
||||
imsg_close(ibuf_dns, buf);
|
||||
break;
|
||||
case IMSG_SYNCED:
|
||||
nconf->status.synced = 1;
|
||||
break;
|
||||
case IMSG_UNSYNCED:
|
||||
nconf->status.synced = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
probe_root_ns(void)
|
||||
{
|
||||
int ret;
|
||||
int old_retrans, old_retry, old_options;
|
||||
unsigned char buf[4096];
|
||||
|
||||
old_retrans = _res.retrans;
|
||||
old_retry = _res.retry;
|
||||
old_options = _res.options;
|
||||
_res.retrans = 1;
|
||||
_res.retry = 1;
|
||||
_res.options |= RES_USE_CD;
|
||||
|
||||
ret = res_query(".", C_IN, T_NS, buf, sizeof(buf));
|
||||
|
||||
_res.retrans = old_retrans;
|
||||
_res.retry = old_retry;
|
||||
_res.options = old_options;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
probe_root(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = probe_root_ns();
|
||||
if (n < 0) {
|
||||
/* give programs like unwind a second chance */
|
||||
sleep(1);
|
||||
n = probe_root_ns();
|
||||
}
|
||||
if (imsg_compose(ibuf_dns, IMSG_PROBE_ROOT, 0, 0, -1, &n,
|
||||
sizeof(int)) == -1)
|
||||
fatalx("probe_root");
|
||||
}
|
71
ntp_msg.c
Normal file
71
ntp_msg.c
Normal file
@ -0,0 +1,71 @@
|
||||
/* $OpenBSD: ntp_msg.c,v 1.22 2016/09/03 11:52:06 reyk Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
int
|
||||
ntp_getmsg(struct sockaddr *sa, char *p, ssize_t len, struct ntp_msg *msg)
|
||||
{
|
||||
if (len != NTP_MSGSIZE_NOAUTH && len != NTP_MSGSIZE) {
|
||||
log_debug("malformed packet received from %s",
|
||||
log_sockaddr(sa));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(msg, p, sizeof(*msg));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ntp_sendmsg(int fd, struct sockaddr *sa, struct ntp_msg *msg)
|
||||
{
|
||||
socklen_t sa_len;
|
||||
ssize_t n;
|
||||
|
||||
if (sa != NULL)
|
||||
sa_len = SA_LEN(sa);
|
||||
else
|
||||
sa_len = 0;
|
||||
|
||||
n = sendto(fd, msg, sizeof(*msg), 0, sa, sa_len);
|
||||
if (n == -1) {
|
||||
if (errno == ENOBUFS || errno == EHOSTUNREACH ||
|
||||
errno == ENETDOWN || errno == EHOSTDOWN) {
|
||||
/* logging is futile */
|
||||
return (-1);
|
||||
}
|
||||
log_warn("sendto");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n != sizeof(*msg)) {
|
||||
log_warnx("ntp_sendmsg: only %zd of %zu bytes sent", n,
|
||||
sizeof(*msg));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
79
ntpctl.8
Normal file
79
ntpctl.8
Normal file
@ -0,0 +1,79 @@
|
||||
.\" $OpenBSD: ntpctl.8,v 1.9 2023/03/02 17:09:53 jmc Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
|
||||
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: March 2 2023 $
|
||||
.Dt NTPCTL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ntpctl
|
||||
.Nd control the NTP daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm ntpctl
|
||||
.Fl s Cm all | peers | Sensors | status
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
program displays information about the running
|
||||
.Xr ntpd 8
|
||||
daemon.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "-s modifierX"
|
||||
.It Fl s Cm all | peers | Sensors | status
|
||||
Used to display information about the running daemon.
|
||||
Keywords may be abbreviated.
|
||||
.Pp
|
||||
.Cm all
|
||||
shows all data available.
|
||||
.Pp
|
||||
.Cm peers
|
||||
shows the following information about each peer: weight, trustlevel,
|
||||
stratum, number of seconds until the next poll, polling interval
|
||||
in seconds, and offset, network delay and network jitter in milliseconds.
|
||||
When the system clock is synced to a peer, an asterisk
|
||||
is displayed to the left of the weight column for that peer.
|
||||
.Pp
|
||||
.Cm Sensors
|
||||
shows the following information about each sensor: weight, sensor "good"
|
||||
status, stratum, and offset and the configured correction in
|
||||
milliseconds.
|
||||
When the system clock is synced to a sensor, an asterisk
|
||||
is displayed to the left of the weight column for that sensor.
|
||||
.Pp
|
||||
.Cm status
|
||||
shows the status of peers and sensors, and whether the system clock is synced.
|
||||
When the system clock is synced, the stratum is displayed.
|
||||
When the system clock is not synced, the offset of the system clock,
|
||||
as reported by the
|
||||
.Xr adjtime 2
|
||||
system call, is displayed.
|
||||
When the median constraint is set, the offset to the local time is displayed.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/var/run/ntpd.sockXXX" -compact
|
||||
.It Pa /var/run/ntpd.sock
|
||||
Socket file for communication with
|
||||
.Xr ntpd 8 .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr adjtime 2 ,
|
||||
.Xr ntpd.conf 5 ,
|
||||
.Xr ntpd 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
program first appeared in
|
||||
.Ox 5.5 .
|
159
ntpd.8
Normal file
159
ntpd.8
Normal file
@ -0,0 +1,159 @@
|
||||
.\" $OpenBSD: ntpd.8,v 1.49 2023/03/02 17:09:53 jmc Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2003, 2004, 2006 Henning Brauer <henning@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
|
||||
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: March 2 2023 $
|
||||
.Dt NTPD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ntpd
|
||||
.Nd Network Time Protocol (NTP) daemon
|
||||
.Sh SYNOPSIS
|
||||
.Nm ntpd
|
||||
.Bk -words
|
||||
.Op Fl dnv
|
||||
.Op Fl f Ar file
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
daemon synchronizes the local clock to one or more remote NTP servers
|
||||
or local timedelta sensors.
|
||||
.Nm
|
||||
can also act as an NTP server itself,
|
||||
redistributing the local time.
|
||||
It implements the Simple Network Time Protocol version 4,
|
||||
as described in RFC 5905,
|
||||
and the Network Time Protocol version 3,
|
||||
as described in RFC 1305.
|
||||
Time can also be fetched from TLS HTTPS servers to reduce the
|
||||
impact of unauthenticated NTP
|
||||
man-in-the-middle attacks.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "-f fileXXX"
|
||||
.It Fl d
|
||||
Do not daemonize.
|
||||
If this option is specified,
|
||||
.Nm
|
||||
will run in the foreground and log to
|
||||
.Em stderr .
|
||||
.It Fl f Ar file
|
||||
Use
|
||||
.Ar file
|
||||
as the configuration file,
|
||||
instead of the default
|
||||
.Pa /etc/ntpd.conf .
|
||||
.It Fl n
|
||||
Configtest mode.
|
||||
Only check the configuration file for validity.
|
||||
.It Fl v
|
||||
This option allows
|
||||
.Nm
|
||||
to send DEBUG priority messages to syslog.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
uses the
|
||||
.Xr adjtime 2
|
||||
system call to correct the local system time without causing time jumps.
|
||||
Adjustments of 32ms and greater are logged using
|
||||
.Xr syslog 3 .
|
||||
The threshold value is chosen to avoid having local clock drift
|
||||
thrash the log files.
|
||||
Should
|
||||
.Nm
|
||||
be started with the
|
||||
.Fl d
|
||||
or
|
||||
.Fl v
|
||||
option, all calls to
|
||||
.Xr adjtime 2
|
||||
will be logged.
|
||||
.Pp
|
||||
At boot,
|
||||
.Nm
|
||||
will stay for a maximum of 15 seconds in the foreground and make efforts to
|
||||
verify and correct the time if constraints are configured and
|
||||
satisfied or if trusted servers or sensors return results,
|
||||
and if the clock is not being moved backwards.
|
||||
.Pp
|
||||
After the local clock is synchronized,
|
||||
.Nm
|
||||
adjusts the clock frequency using the
|
||||
.Xr adjfreq 2
|
||||
system call to compensate for systematic drift.
|
||||
.Pp
|
||||
.Nm
|
||||
is started at boot time by default via
|
||||
.Va ntpd_flags
|
||||
in
|
||||
.Pa /etc/rc.conf .
|
||||
See
|
||||
.Xr rc 8
|
||||
and
|
||||
.Xr rc.conf 8
|
||||
for more information on the boot process
|
||||
and enabling daemons.
|
||||
.Pp
|
||||
When
|
||||
.Nm
|
||||
starts up, it reads settings from its configuration file,
|
||||
typically
|
||||
.Xr ntpd.conf 5 ,
|
||||
and its initial clock drift from
|
||||
.Pa /var/db/ntpd.drift .
|
||||
Clock drift is periodically written to the drift file thereafter.
|
||||
.Sh FILES
|
||||
.Bl -tag -width "/var/db/ntpd.driftXXX" -compact
|
||||
.It Pa /etc/ntpd.conf
|
||||
Default configuration file.
|
||||
.It Pa /var/db/ntpd.drift
|
||||
Drift file.
|
||||
.It Pa /var/run/ntpd.sock
|
||||
Socket file for communication with
|
||||
.Xr ntpctl 8 .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr date 1 ,
|
||||
.Xr adjfreq 2 ,
|
||||
.Xr adjtime 2 ,
|
||||
.Xr ntpd.conf 5 ,
|
||||
.Xr ntpctl 8 ,
|
||||
.Xr rc 8 ,
|
||||
.Xr rc.conf 8 ,
|
||||
.Xr rdate 8
|
||||
.Sh STANDARDS
|
||||
.Rs
|
||||
.%A David L. Mills
|
||||
.%D March 1992
|
||||
.%R RFC 1305
|
||||
.%T Network Time Protocol (Version 3): Specification, Implementation and Analysis
|
||||
.Re
|
||||
.Pp
|
||||
.Rs
|
||||
.%A David L. Mills
|
||||
.%A Jim Martin
|
||||
.%A Jack Burbank
|
||||
.%A William Kasch
|
||||
.%D June 2010
|
||||
.%R RFC 5905
|
||||
.%T Network Time Protocol Version 4: Protocol and Algorithms Specification
|
||||
.Re
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
program first appeared in
|
||||
.Ox 3.6 .
|
924
ntpd.c
Normal file
924
ntpd.c
Normal file
@ -0,0 +1,924 @@
|
||||
/* $OpenBSD: ntpd.c,v 1.133 2024/05/21 05:00:48 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <tls.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
void sighdlr(int);
|
||||
__dead void usage(void);
|
||||
int auto_preconditions(const struct ntpd_conf *);
|
||||
int main(int, char *[]);
|
||||
void check_child(void);
|
||||
int dispatch_imsg(struct ntpd_conf *, int, char **);
|
||||
void reset_adjtime(void);
|
||||
int ntpd_adjtime(double);
|
||||
void ntpd_adjfreq(double, int);
|
||||
void ntpd_settime(double);
|
||||
void readfreq(void);
|
||||
int writefreq(double);
|
||||
void ctl_main(int, char*[]);
|
||||
const char *ctl_lookup_option(char *, const char **);
|
||||
void show_status_msg(struct imsg *);
|
||||
void show_peer_msg(struct imsg *, int);
|
||||
void show_sensor_msg(struct imsg *, int);
|
||||
|
||||
volatile sig_atomic_t quit = 0;
|
||||
volatile sig_atomic_t reconfig = 0;
|
||||
volatile sig_atomic_t sigchld = 0;
|
||||
struct imsgbuf *ibuf;
|
||||
int timeout = INFTIM;
|
||||
|
||||
extern u_int constraint_cnt;
|
||||
|
||||
const char *showopt;
|
||||
|
||||
static const char *ctl_showopt_list[] = {
|
||||
"peers", "Sensors", "status", "all", NULL
|
||||
};
|
||||
|
||||
void
|
||||
sighdlr(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
quit = 1;
|
||||
break;
|
||||
case SIGCHLD:
|
||||
sigchld = 1;
|
||||
break;
|
||||
case SIGHUP:
|
||||
reconfig = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__dead void
|
||||
usage(void)
|
||||
{
|
||||
extern char *__progname;
|
||||
|
||||
if (strcmp(__progname, "ntpctl") == 0)
|
||||
fprintf(stderr,
|
||||
"usage: ntpctl -s all | peers | Sensors | status\n");
|
||||
else
|
||||
fprintf(stderr, "usage: %s [-dnv] [-f file]\n",
|
||||
__progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int
|
||||
auto_preconditions(const struct ntpd_conf *cnf)
|
||||
{
|
||||
int mib[2] = { CTL_KERN, KERN_SECURELVL };
|
||||
int constraints, securelevel;
|
||||
size_t sz = sizeof(int);
|
||||
|
||||
if (sysctl(mib, 2, &securelevel, &sz, NULL, 0) == -1)
|
||||
err(1, "sysctl");
|
||||
constraints = !TAILQ_EMPTY(&cnf->constraints);
|
||||
return !cnf->settime && (constraints || cnf->trusted_peers ||
|
||||
conf->trusted_sensors) && securelevel == 0;
|
||||
}
|
||||
|
||||
#define POLL_MAX 8
|
||||
#define PFD_PIPE 0
|
||||
#define PFD_MAX 1
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct ntpd_conf lconf;
|
||||
struct pollfd *pfd = NULL;
|
||||
pid_t pid;
|
||||
const char *conffile;
|
||||
int ch, nfds, i, j;
|
||||
int pipe_chld[2];
|
||||
extern char *__progname;
|
||||
u_int pfd_elms = 0, new_cnt;
|
||||
struct constraint *cstr;
|
||||
struct passwd *pw;
|
||||
void *newp;
|
||||
int argc0 = argc, logdest;
|
||||
char **argv0 = argv;
|
||||
char *pname = NULL;
|
||||
time_t settime_deadline;
|
||||
int sopt = 0;
|
||||
|
||||
if (strcmp(__progname, "ntpctl") == 0) {
|
||||
ctl_main(argc, argv);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
conffile = CONFFILE;
|
||||
|
||||
memset(&lconf, 0, sizeof(lconf));
|
||||
|
||||
while ((ch = getopt(argc, argv, "df:nP:sSv")) != -1) {
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
lconf.debug = 1;
|
||||
break;
|
||||
case 'f':
|
||||
conffile = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
lconf.debug = 1;
|
||||
lconf.noaction = 1;
|
||||
break;
|
||||
case 'P':
|
||||
pname = optarg;
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
sopt = ch;
|
||||
break;
|
||||
case 'v':
|
||||
lconf.verbose++;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
/* log to stderr until daemonized */
|
||||
logdest = LOG_TO_STDERR;
|
||||
if (!lconf.debug)
|
||||
logdest |= LOG_TO_SYSLOG;
|
||||
|
||||
log_init(logdest, lconf.verbose, LOG_DAEMON);
|
||||
|
||||
if (sopt) {
|
||||
log_warnx("-%c option no longer works and will be removed soon.",
|
||||
sopt);
|
||||
log_warnx("Please reconfigure to use constraints or trusted servers.");
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc > 0)
|
||||
usage();
|
||||
|
||||
if (parse_config(conffile, &lconf))
|
||||
exit(1);
|
||||
|
||||
if (lconf.noaction) {
|
||||
fprintf(stderr, "configuration OK\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (geteuid())
|
||||
errx(1, "need root privileges");
|
||||
|
||||
if ((pw = getpwnam(NTPD_USER)) == NULL)
|
||||
errx(1, "unknown user %s", NTPD_USER);
|
||||
|
||||
lconf.automatic = auto_preconditions(&lconf);
|
||||
if (lconf.automatic)
|
||||
lconf.settime = 1;
|
||||
|
||||
if (pname != NULL) {
|
||||
/* Remove our proc arguments, so child doesn't need to. */
|
||||
if (sanitize_argv(&argc0, &argv0) == -1)
|
||||
fatalx("sanitize_argv");
|
||||
|
||||
if (strcmp(NTP_PROC_NAME, pname) == 0)
|
||||
ntp_main(&lconf, pw, argc0, argv0);
|
||||
else if (strcmp(NTPDNS_PROC_NAME, pname) == 0)
|
||||
ntp_dns(&lconf, pw);
|
||||
else if (strcmp(CONSTRAINT_PROC_NAME, pname) == 0)
|
||||
priv_constraint_child(pw->pw_dir, pw->pw_uid,
|
||||
pw->pw_gid);
|
||||
else
|
||||
fatalx("%s: invalid process name '%s'", __func__,
|
||||
pname);
|
||||
|
||||
fatalx("%s: process '%s' failed", __func__, pname);
|
||||
} else {
|
||||
if ((control_check(CTLSOCKET)) == -1)
|
||||
fatalx("ntpd already running");
|
||||
}
|
||||
|
||||
if (setpriority(PRIO_PROCESS, 0, -20) == -1)
|
||||
warn("can't set priority");
|
||||
reset_adjtime();
|
||||
|
||||
logdest = lconf.debug ? LOG_TO_STDERR : LOG_TO_SYSLOG;
|
||||
if (!lconf.settime) {
|
||||
log_init(logdest, lconf.verbose, LOG_DAEMON);
|
||||
if (!lconf.debug)
|
||||
if (daemon(1, 0))
|
||||
fatal("daemon");
|
||||
} else {
|
||||
settime_deadline = getmonotime();
|
||||
timeout = 100;
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC,
|
||||
pipe_chld) == -1)
|
||||
fatal("socketpair");
|
||||
|
||||
if (chdir("/") == -1)
|
||||
fatal("chdir(\"/\")");
|
||||
|
||||
signal(SIGCHLD, sighdlr);
|
||||
|
||||
/* fork child process */
|
||||
start_child(NTP_PROC_NAME, pipe_chld[1], argc0, argv0);
|
||||
|
||||
log_procinit("[priv]");
|
||||
readfreq();
|
||||
|
||||
signal(SIGTERM, sighdlr);
|
||||
signal(SIGINT, sighdlr);
|
||||
signal(SIGHUP, sighdlr);
|
||||
|
||||
constraint_purge();
|
||||
|
||||
if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL)
|
||||
fatal(NULL);
|
||||
imsg_init(ibuf, pipe_chld[0]);
|
||||
|
||||
constraint_cnt = 0;
|
||||
|
||||
/*
|
||||
* Constraint processes are forked with certificates in memory,
|
||||
* then privdrop into chroot before speaking to the outside world.
|
||||
*/
|
||||
if (unveil("/usr/sbin/ntpd", "x") == -1)
|
||||
err(1, "unveil /usr/sbin/ntpd");
|
||||
if (pledge("stdio settime proc exec", NULL) == -1)
|
||||
err(1, "pledge");
|
||||
|
||||
while (quit == 0) {
|
||||
new_cnt = PFD_MAX + constraint_cnt;
|
||||
if (new_cnt > pfd_elms) {
|
||||
if ((newp = reallocarray(pfd, new_cnt,
|
||||
sizeof(*pfd))) == NULL) {
|
||||
/* panic for now */
|
||||
log_warn("could not resize pfd from %u -> "
|
||||
"%u entries", pfd_elms, new_cnt);
|
||||
fatalx("exiting");
|
||||
}
|
||||
pfd = newp;
|
||||
pfd_elms = new_cnt;
|
||||
}
|
||||
|
||||
memset(pfd, 0, sizeof(*pfd) * pfd_elms);
|
||||
pfd[PFD_PIPE].fd = ibuf->fd;
|
||||
pfd[PFD_PIPE].events = POLLIN;
|
||||
if (ibuf->w.queued)
|
||||
pfd[PFD_PIPE].events |= POLLOUT;
|
||||
|
||||
i = PFD_MAX;
|
||||
TAILQ_FOREACH(cstr, &conf->constraints, entry) {
|
||||
pfd[i].fd = cstr->fd;
|
||||
pfd[i].events = POLLIN;
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((nfds = poll(pfd, i, timeout)) == -1)
|
||||
if (errno != EINTR) {
|
||||
log_warn("poll error");
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
if (nfds == 0 && lconf.settime &&
|
||||
getmonotime() > settime_deadline + SETTIME_TIMEOUT) {
|
||||
lconf.settime = 0;
|
||||
timeout = INFTIM;
|
||||
log_init(logdest, lconf.verbose, LOG_DAEMON);
|
||||
log_warnx("no reply received in time, skipping initial "
|
||||
"time setting");
|
||||
if (!lconf.debug)
|
||||
if (daemon(1, 0))
|
||||
fatal("daemon");
|
||||
}
|
||||
|
||||
if (nfds > 0 && (pfd[PFD_PIPE].revents & POLLOUT))
|
||||
if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) {
|
||||
log_warn("pipe write error (to child)");
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
if (nfds > 0 && pfd[PFD_PIPE].revents & POLLIN) {
|
||||
nfds--;
|
||||
if (dispatch_imsg(&lconf, argc0, argv0) == -1)
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
for (j = PFD_MAX; nfds > 0 && j < i; j++) {
|
||||
nfds -= priv_constraint_dispatch(&pfd[j]);
|
||||
}
|
||||
|
||||
if (sigchld) {
|
||||
check_child();
|
||||
sigchld = 0;
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
/* Close socket and start shutdown. */
|
||||
close(ibuf->fd);
|
||||
|
||||
do {
|
||||
if ((pid = wait(NULL)) == -1 &&
|
||||
errno != EINTR && errno != ECHILD)
|
||||
fatal("wait");
|
||||
} while (pid != -1 || (pid == -1 && errno == EINTR));
|
||||
|
||||
msgbuf_clear(&ibuf->w);
|
||||
free(ibuf);
|
||||
log_info("Terminating");
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
check_child(void)
|
||||
{
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
do {
|
||||
pid = waitpid(WAIT_ANY, &status, WNOHANG);
|
||||
if (pid <= 0)
|
||||
continue;
|
||||
|
||||
priv_constraint_check_child(pid, status);
|
||||
} while (pid > 0 || (pid == -1 && errno == EINTR));
|
||||
}
|
||||
|
||||
int
|
||||
dispatch_imsg(struct ntpd_conf *lconf, int argc, char **argv)
|
||||
{
|
||||
struct imsg imsg;
|
||||
int n;
|
||||
double d;
|
||||
|
||||
if (((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) || n == 0)
|
||||
return (-1);
|
||||
|
||||
for (;;) {
|
||||
if ((n = imsg_get(ibuf, &imsg)) == -1)
|
||||
return (-1);
|
||||
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_ADJTIME:
|
||||
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
|
||||
fatalx("invalid IMSG_ADJTIME received");
|
||||
memcpy(&d, imsg.data, sizeof(d));
|
||||
n = ntpd_adjtime(d);
|
||||
imsg_compose(ibuf, IMSG_ADJTIME, 0, 0, -1,
|
||||
&n, sizeof(n));
|
||||
break;
|
||||
case IMSG_ADJFREQ:
|
||||
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
|
||||
fatalx("invalid IMSG_ADJFREQ received");
|
||||
memcpy(&d, imsg.data, sizeof(d));
|
||||
ntpd_adjfreq(d, 1);
|
||||
break;
|
||||
case IMSG_SETTIME:
|
||||
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(d))
|
||||
fatalx("invalid IMSG_SETTIME received");
|
||||
if (!lconf->settime)
|
||||
break;
|
||||
log_init(lconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG,
|
||||
lconf->verbose, LOG_DAEMON);
|
||||
memcpy(&d, imsg.data, sizeof(d));
|
||||
ntpd_settime(d);
|
||||
/* daemonize now */
|
||||
if (!lconf->debug)
|
||||
if (daemon(1, 0))
|
||||
fatal("daemon");
|
||||
lconf->settime = 0;
|
||||
timeout = INFTIM;
|
||||
break;
|
||||
case IMSG_CONSTRAINT_QUERY:
|
||||
priv_constraint_msg(imsg.hdr.peerid,
|
||||
imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE,
|
||||
argc, argv);
|
||||
break;
|
||||
case IMSG_CONSTRAINT_KILL:
|
||||
priv_constraint_kill(imsg.hdr.peerid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
reset_adjtime(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
timerclear(&tv);
|
||||
if (adjtime(&tv, NULL) == -1)
|
||||
log_warn("reset adjtime failed");
|
||||
}
|
||||
|
||||
int
|
||||
ntpd_adjtime(double d)
|
||||
{
|
||||
struct timeval tv, olddelta;
|
||||
int synced = 0;
|
||||
static int firstadj = 1;
|
||||
|
||||
d += getoffset();
|
||||
if (d >= (double)LOG_NEGLIGIBLE_ADJTIME / 1000 ||
|
||||
d <= -1 * (double)LOG_NEGLIGIBLE_ADJTIME / 1000)
|
||||
log_info("adjusting local clock by %fs", d);
|
||||
else
|
||||
log_debug("adjusting local clock by %fs", d);
|
||||
d_to_tv(d, &tv);
|
||||
if (adjtime(&tv, &olddelta) == -1)
|
||||
log_warn("adjtime failed");
|
||||
else if (!firstadj && olddelta.tv_sec == 0 && olddelta.tv_usec == 0)
|
||||
synced = 1;
|
||||
firstadj = 0;
|
||||
return (synced);
|
||||
}
|
||||
|
||||
void
|
||||
ntpd_adjfreq(double relfreq, int wrlog)
|
||||
{
|
||||
int64_t curfreq;
|
||||
double ppmfreq;
|
||||
int r;
|
||||
|
||||
if (adjfreq(NULL, &curfreq) == -1) {
|
||||
log_warn("adjfreq failed");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* adjfreq's unit is ns/s shifted left 32; convert relfreq to
|
||||
* that unit before adding. We log values in part per million.
|
||||
*/
|
||||
curfreq += relfreq * 1e9 * (1LL << 32);
|
||||
r = writefreq(curfreq / 1e9 / (1LL << 32));
|
||||
ppmfreq = relfreq * 1e6;
|
||||
if (wrlog) {
|
||||
if (ppmfreq >= LOG_NEGLIGIBLE_ADJFREQ ||
|
||||
ppmfreq <= -LOG_NEGLIGIBLE_ADJFREQ)
|
||||
log_info("adjusting clock frequency by %f to %fppm%s",
|
||||
ppmfreq, curfreq / 1e3 / (1LL << 32),
|
||||
r ? "" : " (no drift file)");
|
||||
else
|
||||
log_debug("adjusting clock frequency by %f to %fppm%s",
|
||||
ppmfreq, curfreq / 1e3 / (1LL << 32),
|
||||
r ? "" : " (no drift file)");
|
||||
}
|
||||
|
||||
if (adjfreq(&curfreq, NULL) == -1)
|
||||
log_warn("adjfreq failed");
|
||||
}
|
||||
|
||||
void
|
||||
ntpd_settime(double d)
|
||||
{
|
||||
struct timeval tv, curtime;
|
||||
char buf[80];
|
||||
time_t tval;
|
||||
|
||||
if (d == 0)
|
||||
return;
|
||||
|
||||
if (gettimeofday(&curtime, NULL) == -1) {
|
||||
log_warn("gettimeofday");
|
||||
return;
|
||||
}
|
||||
d_to_tv(d, &tv);
|
||||
curtime.tv_usec += tv.tv_usec + 1000000;
|
||||
curtime.tv_sec += tv.tv_sec - 1 + (curtime.tv_usec / 1000000);
|
||||
curtime.tv_usec %= 1000000;
|
||||
|
||||
if (settimeofday(&curtime, NULL) == -1) {
|
||||
log_warn("settimeofday");
|
||||
return;
|
||||
}
|
||||
tval = curtime.tv_sec;
|
||||
strftime(buf, sizeof(buf), "%a %b %e %H:%M:%S %Z %Y",
|
||||
localtime(&tval));
|
||||
log_info("set local clock to %s (offset %fs)", buf, d);
|
||||
}
|
||||
|
||||
static FILE *freqfp;
|
||||
|
||||
void
|
||||
readfreq(void)
|
||||
{
|
||||
int64_t current;
|
||||
int fd;
|
||||
double d;
|
||||
|
||||
fd = open(DRIFTFILE, O_RDWR);
|
||||
if (fd == -1) {
|
||||
log_warnx("creating new %s", DRIFTFILE);
|
||||
current = 0;
|
||||
if (adjfreq(¤t, NULL) == -1)
|
||||
log_warn("adjfreq reset failed");
|
||||
freqfp = fopen(DRIFTFILE, "w");
|
||||
return;
|
||||
}
|
||||
|
||||
freqfp = fdopen(fd, "r+");
|
||||
|
||||
/* if we're adjusting frequency already, don't override */
|
||||
if (adjfreq(NULL, ¤t) == -1)
|
||||
log_warn("adjfreq failed");
|
||||
else if (current == 0 && freqfp) {
|
||||
if (fscanf(freqfp, "%lf", &d) == 1) {
|
||||
d /= 1e6; /* scale from ppm */
|
||||
ntpd_adjfreq(d, 0);
|
||||
} else
|
||||
log_warnx("%s is empty", DRIFTFILE);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
writefreq(double d)
|
||||
{
|
||||
int r;
|
||||
static int warnonce = 1;
|
||||
|
||||
if (freqfp == NULL)
|
||||
return 0;
|
||||
rewind(freqfp);
|
||||
r = fprintf(freqfp, "%.3f\n", d * 1e6); /* scale to ppm */
|
||||
if (r < 0 || fflush(freqfp) != 0) {
|
||||
if (warnonce) {
|
||||
log_warnx("can't write %s", DRIFTFILE);
|
||||
warnonce = 0;
|
||||
}
|
||||
clearerr(freqfp);
|
||||
return 0;
|
||||
}
|
||||
ftruncate(fileno(freqfp), ftello(freqfp));
|
||||
fsync(fileno(freqfp));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
ctl_main(int argc, char *argv[])
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
struct imsg imsg;
|
||||
struct imsgbuf *ibuf_ctl;
|
||||
int fd, n, done, ch, action;
|
||||
char *sockname;
|
||||
|
||||
sockname = CTLSOCKET;
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
while ((ch = getopt(argc, argv, "s:")) != -1) {
|
||||
switch (ch) {
|
||||
case 's':
|
||||
showopt = ctl_lookup_option(optarg, ctl_showopt_list);
|
||||
if (showopt == NULL) {
|
||||
warnx("Unknown show modifier '%s'", optarg);
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
|
||||
action = -1;
|
||||
if (showopt != NULL) {
|
||||
switch (*showopt) {
|
||||
case 'p':
|
||||
action = CTL_SHOW_PEERS;
|
||||
break;
|
||||
case 's':
|
||||
action = CTL_SHOW_STATUS;
|
||||
break;
|
||||
case 'S':
|
||||
action = CTL_SHOW_SENSORS;
|
||||
break;
|
||||
case 'a':
|
||||
action = CTL_SHOW_ALL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (action == -1)
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
||||
err(1, "ntpctl: socket");
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
if (strlcpy(sa.sun_path, sockname, sizeof(sa.sun_path)) >=
|
||||
sizeof(sa.sun_path))
|
||||
errx(1, "ctl socket name too long");
|
||||
if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
|
||||
err(1, "connect: %s", sockname);
|
||||
|
||||
if (pledge("stdio", NULL) == -1)
|
||||
err(1, "pledge");
|
||||
|
||||
if ((ibuf_ctl = malloc(sizeof(struct imsgbuf))) == NULL)
|
||||
err(1, NULL);
|
||||
imsg_init(ibuf_ctl, fd);
|
||||
|
||||
switch (action) {
|
||||
case CTL_SHOW_STATUS:
|
||||
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_STATUS,
|
||||
0, 0, -1, NULL, 0);
|
||||
break;
|
||||
case CTL_SHOW_PEERS:
|
||||
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_PEERS,
|
||||
0, 0, -1, NULL, 0);
|
||||
break;
|
||||
case CTL_SHOW_SENSORS:
|
||||
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_SENSORS,
|
||||
0, 0, -1, NULL, 0);
|
||||
break;
|
||||
case CTL_SHOW_ALL:
|
||||
imsg_compose(ibuf_ctl, IMSG_CTL_SHOW_ALL,
|
||||
0, 0, -1, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
errx(1, "invalid action");
|
||||
break; /* NOTREACHED */
|
||||
}
|
||||
|
||||
while (ibuf_ctl->w.queued)
|
||||
if (msgbuf_write(&ibuf_ctl->w) <= 0 && errno != EAGAIN)
|
||||
err(1, "ibuf_ctl: msgbuf_write error");
|
||||
|
||||
done = 0;
|
||||
while (!done) {
|
||||
if ((n = imsg_read(ibuf_ctl)) == -1 && errno != EAGAIN)
|
||||
err(1, "ibuf_ctl: imsg_read error");
|
||||
if (n == 0)
|
||||
errx(1, "ntpctl: pipe closed");
|
||||
|
||||
while (!done) {
|
||||
if ((n = imsg_get(ibuf_ctl, &imsg)) == -1)
|
||||
err(1, "ibuf_ctl: imsg_get error");
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
switch (action) {
|
||||
case CTL_SHOW_STATUS:
|
||||
show_status_msg(&imsg);
|
||||
done = 1;
|
||||
break;
|
||||
case CTL_SHOW_PEERS:
|
||||
show_peer_msg(&imsg, 0);
|
||||
if (imsg.hdr.type ==
|
||||
IMSG_CTL_SHOW_PEERS_END)
|
||||
done = 1;
|
||||
break;
|
||||
case CTL_SHOW_SENSORS:
|
||||
show_sensor_msg(&imsg, 0);
|
||||
if (imsg.hdr.type ==
|
||||
IMSG_CTL_SHOW_SENSORS_END)
|
||||
done = 1;
|
||||
break;
|
||||
case CTL_SHOW_ALL:
|
||||
switch (imsg.hdr.type) {
|
||||
case IMSG_CTL_SHOW_STATUS:
|
||||
show_status_msg(&imsg);
|
||||
break;
|
||||
case IMSG_CTL_SHOW_PEERS:
|
||||
show_peer_msg(&imsg, 1);
|
||||
break;
|
||||
case IMSG_CTL_SHOW_SENSORS:
|
||||
show_sensor_msg(&imsg, 1);
|
||||
break;
|
||||
case IMSG_CTL_SHOW_PEERS_END:
|
||||
case IMSG_CTL_SHOW_SENSORS_END:
|
||||
/* do nothing */
|
||||
break;
|
||||
case IMSG_CTL_SHOW_ALL_END:
|
||||
done=1;
|
||||
break;
|
||||
default:
|
||||
/* no action taken */
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* no action taken */
|
||||
break;
|
||||
}
|
||||
imsg_free(&imsg);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
free(ibuf_ctl);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
const char *
|
||||
ctl_lookup_option(char *cmd, const char **list)
|
||||
{
|
||||
const char *item = NULL;
|
||||
if (cmd != NULL && *cmd)
|
||||
for (; *list; list++)
|
||||
if (!strncmp(cmd, *list, strlen(cmd))) {
|
||||
if (item == NULL)
|
||||
item = *list;
|
||||
else
|
||||
errx(1, "%s is ambiguous", cmd);
|
||||
}
|
||||
return (item);
|
||||
}
|
||||
|
||||
void
|
||||
show_status_msg(struct imsg *imsg)
|
||||
{
|
||||
struct ctl_show_status *cstatus;
|
||||
double clock_offset;
|
||||
struct timeval tv;
|
||||
|
||||
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_status))
|
||||
fatalx("invalid IMSG_CTL_SHOW_STATUS received");
|
||||
|
||||
cstatus = (struct ctl_show_status *)imsg->data;
|
||||
|
||||
if (cstatus->peercnt > 0)
|
||||
printf("%d/%d peers valid, ",
|
||||
cstatus->valid_peers, cstatus->peercnt);
|
||||
|
||||
if (cstatus->sensorcnt > 0)
|
||||
printf("%d/%d sensors valid, ",
|
||||
cstatus->valid_sensors, cstatus->sensorcnt);
|
||||
|
||||
if (cstatus->constraint_median) {
|
||||
tv.tv_sec = cstatus->constraint_median +
|
||||
(getmonotime() - cstatus->constraint_last);
|
||||
tv.tv_usec = 0;
|
||||
d_to_tv(gettime_from_timeval(&tv) - gettime(), &tv);
|
||||
printf("constraint offset %llds", (long long)tv.tv_sec);
|
||||
if (cstatus->constraint_errors)
|
||||
printf(" (%d errors)",
|
||||
cstatus->constraint_errors);
|
||||
printf(", ");
|
||||
} else if (cstatus->constraints)
|
||||
printf("constraints configured but none available, ");
|
||||
|
||||
if (cstatus->peercnt + cstatus->sensorcnt == 0)
|
||||
printf("no peers and no sensors configured\n");
|
||||
|
||||
if (cstatus->synced == 1)
|
||||
printf("clock synced, stratum %u\n", cstatus->stratum);
|
||||
else {
|
||||
printf("clock unsynced");
|
||||
clock_offset = cstatus->clock_offset < 0 ?
|
||||
-1.0 * cstatus->clock_offset : cstatus->clock_offset;
|
||||
if (clock_offset > 5e-7)
|
||||
printf(", clock offset is %.3fms\n",
|
||||
cstatus->clock_offset);
|
||||
else
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
show_peer_msg(struct imsg *imsg, int calledfromshowall)
|
||||
{
|
||||
struct ctl_show_peer *cpeer;
|
||||
int cnt;
|
||||
char stratum[3];
|
||||
static int firsttime = 1;
|
||||
|
||||
if (imsg->hdr.type == IMSG_CTL_SHOW_PEERS_END) {
|
||||
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(cnt))
|
||||
fatalx("invalid IMSG_CTL_SHOW_PEERS_END received");
|
||||
memcpy(&cnt, imsg->data, sizeof(cnt));
|
||||
if (cnt == 0)
|
||||
printf("no peers configured\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_peer))
|
||||
fatalx("invalid IMSG_CTL_SHOW_PEERS received");
|
||||
|
||||
cpeer = (struct ctl_show_peer *)imsg->data;
|
||||
|
||||
if (strlen(cpeer->peer_desc) > MAX_DISPLAY_WIDTH - 1)
|
||||
fatalx("peer_desc is too long");
|
||||
|
||||
if (firsttime) {
|
||||
firsttime = 0;
|
||||
if (calledfromshowall)
|
||||
printf("\n");
|
||||
printf("peer\n wt tl st next poll "
|
||||
"offset delay jitter\n");
|
||||
}
|
||||
|
||||
if (cpeer->stratum > 0)
|
||||
snprintf(stratum, sizeof(stratum), "%2u", cpeer->stratum);
|
||||
else
|
||||
strlcpy(stratum, " -", sizeof (stratum));
|
||||
|
||||
printf("%s\n %1s %2u %2u %2s %4llds %4llds",
|
||||
cpeer->peer_desc, cpeer->syncedto == 1 ? "*" : " ",
|
||||
cpeer->weight, cpeer->trustlevel, stratum,
|
||||
(long long)cpeer->next, (long long)cpeer->poll);
|
||||
|
||||
if (cpeer->trustlevel >= TRUSTLEVEL_BADPEER)
|
||||
printf(" %12.3fms %9.3fms %8.3fms\n", cpeer->offset,
|
||||
cpeer->delay, cpeer->jitter);
|
||||
else
|
||||
printf(" ---- peer not valid ----\n");
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
show_sensor_msg(struct imsg *imsg, int calledfromshowall)
|
||||
{
|
||||
struct ctl_show_sensor *csensor;
|
||||
int cnt;
|
||||
static int firsttime = 1;
|
||||
|
||||
if (imsg->hdr.type == IMSG_CTL_SHOW_SENSORS_END) {
|
||||
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(cnt))
|
||||
fatalx("invalid IMSG_CTL_SHOW_SENSORS_END received");
|
||||
memcpy(&cnt, imsg->data, sizeof(cnt));
|
||||
if (cnt == 0)
|
||||
printf("no sensors configured\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_show_sensor))
|
||||
fatalx("invalid IMSG_CTL_SHOW_SENSORS received");
|
||||
|
||||
csensor = (struct ctl_show_sensor *)imsg->data;
|
||||
|
||||
if (strlen(csensor->sensor_desc) > MAX_DISPLAY_WIDTH - 1)
|
||||
fatalx("sensor_desc is too long");
|
||||
|
||||
if (firsttime) {
|
||||
firsttime = 0;
|
||||
if (calledfromshowall)
|
||||
printf("\n");
|
||||
printf("sensor\n wt gd st next poll "
|
||||
"offset correction\n");
|
||||
}
|
||||
|
||||
printf("%s\n %1s %2u %2u %2u %4llds %4llds",
|
||||
csensor->sensor_desc, csensor->syncedto == 1 ? "*" : " ",
|
||||
csensor->weight, csensor->good, csensor->stratum,
|
||||
(long long)csensor->next, (long long)csensor->poll);
|
||||
|
||||
if (csensor->good == 1)
|
||||
printf(" %11.3fms %9.3fms\n",
|
||||
csensor->offset, csensor->correction);
|
||||
else
|
||||
printf(" - sensor not valid -\n");
|
||||
|
||||
}
|
265
ntpd.conf.5
Normal file
265
ntpd.conf.5
Normal file
@ -0,0 +1,265 @@
|
||||
.\" $OpenBSD: ntpd.conf.5,v 1.49 2023/03/02 17:09:53 jmc Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
|
||||
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: March 2 2023 $
|
||||
.Dt NTPD.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ntpd.conf
|
||||
.Nd NTP daemon configuration file
|
||||
.Sh DESCRIPTION
|
||||
This manual page describes the format of the
|
||||
.Xr ntpd 8
|
||||
configuration file.
|
||||
.Pp
|
||||
.Nm
|
||||
has the following format:
|
||||
.Pp
|
||||
Empty lines and lines beginning with the
|
||||
.Sq #
|
||||
character are ignored.
|
||||
.Pp
|
||||
Keywords may be specified multiple times within the configuration file.
|
||||
The basic configuration options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Xo Ic listen on Ar address
|
||||
.Op Ic rtable Ar table-id
|
||||
.Xc
|
||||
Specify a local IP address or a hostname the
|
||||
.Xr ntpd 8
|
||||
daemon should listen on.
|
||||
If it appears multiple times,
|
||||
.Xr ntpd 8
|
||||
will listen on each given address.
|
||||
If
|
||||
.Sq *
|
||||
is given as an address,
|
||||
.Xr ntpd 8
|
||||
will listen on all local addresses using the specified routing table.
|
||||
.Xr ntpd 8
|
||||
does not listen on any address by default.
|
||||
The optional
|
||||
.Ic rtable
|
||||
keyword will specify which routing table to listen on.
|
||||
By default
|
||||
.Xr ntpd 8
|
||||
will listen using the current routing table.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
listen on *
|
||||
.Ed
|
||||
.Pp
|
||||
or
|
||||
.Bd -literal -offset indent
|
||||
listen on 127.0.0.1
|
||||
listen on ::1
|
||||
listen on 127.0.0.1 rtable 4
|
||||
.Ed
|
||||
.It Ic query from Ar sourceaddr
|
||||
Specify a local IP address the
|
||||
.Xr ntpd 8
|
||||
daemon should use for outgoing queries to subsequently specified servers,
|
||||
which is useful on machines with multiple interfaces.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
query from 192.0.2.1
|
||||
query from 2001:db8::1
|
||||
.Ed
|
||||
.It Xo Ic sensor Ar device
|
||||
.Op Ic correction Ar microseconds
|
||||
.Op Ic refid Ar ID-string
|
||||
.Op Ic stratum Ar stratum-value
|
||||
.Op Ic trusted
|
||||
.Op Ic weight Ar weight-value
|
||||
.Xc
|
||||
Specify a timedelta sensor device
|
||||
.Xr ntpd 8
|
||||
should use.
|
||||
The sensor can be specified multiple times:
|
||||
.Xr ntpd 8
|
||||
will use each given sensor that actually exists.
|
||||
Non-existent sensors are ignored.
|
||||
If
|
||||
.Sq *
|
||||
is given as device name,
|
||||
.Xr ntpd 8
|
||||
will use all timedelta sensors it finds.
|
||||
.Xr ntpd 8
|
||||
does not use any timedelta sensor by default.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
sensor *
|
||||
sensor nmea0
|
||||
.Ed
|
||||
.Pp
|
||||
A
|
||||
.Ic correction
|
||||
in microseconds can be given to compensate
|
||||
for the sensor's offset.
|
||||
The maximum correction is 127 seconds.
|
||||
For example, if a DCF77 receiver is lagging 70ms behind
|
||||
actual time:
|
||||
.Bd -literal -offset indent
|
||||
sensor udcf0 correction 70000
|
||||
.Ed
|
||||
.Pp
|
||||
A
|
||||
.Ic refid
|
||||
.Ar ID-string
|
||||
of up to 4 ASCII characters can be
|
||||
given to publish the sensor type to clients.
|
||||
RFC 2030 suggests some common reference identifiers, but new identifiers
|
||||
"can be contrived as appropriate."
|
||||
If an
|
||||
.Ar ID-string
|
||||
is not given,
|
||||
.Xr ntpd 8
|
||||
will use a generic reference ID.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
sensor nmea0 refid GPS
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Ic stratum
|
||||
keyword can be used to change the stratum value from the default of 1.
|
||||
.Pp
|
||||
The
|
||||
.Ic trusted
|
||||
keyword indicates the time learned is secure, trustworthy,
|
||||
and not vulnerable to man-in-the-middle attacks, so
|
||||
.Ic constraints
|
||||
validation is skipped.
|
||||
This is useful for boot-time correction in environments where
|
||||
.Ic constraints
|
||||
cannot be used.
|
||||
.Pp
|
||||
The
|
||||
.Ic weight
|
||||
keyword permits finer control over the relative importance
|
||||
of time sources (servers or sensor devices).
|
||||
Weights are specified in the range 1 to 10;
|
||||
if no weight is given,
|
||||
the default is 1.
|
||||
A server with a weight of 5, for example,
|
||||
will have five times more influence on time offset calculation
|
||||
than a server with a weight of 1.
|
||||
.It Xo Ic server Ar address
|
||||
.Op Ic trusted
|
||||
.Op Ic weight Ar weight-value
|
||||
.Xc
|
||||
Specify the IP address or the hostname of an NTP
|
||||
server to synchronize to.
|
||||
If it appears multiple times,
|
||||
.Xr ntpd 8
|
||||
will try to synchronize to all of the servers specified.
|
||||
If a hostname resolves to multiple IPv4 and/or IPv6 addresses,
|
||||
.Xr ntpd 8
|
||||
uses the first address.
|
||||
If it does not get a reply,
|
||||
.Xr ntpd 8
|
||||
retries with the next address and continues to do so until a working address
|
||||
is found.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
server 10.0.0.2 weight 5
|
||||
server ntp.example.org weight 1
|
||||
.Ed
|
||||
.Pp
|
||||
To provide redundancy, it is good practice to configure multiple servers.
|
||||
In general, best accuracy is obtained by using servers that have a low
|
||||
network latency.
|
||||
.It Xo Ic servers Ar address
|
||||
.Op Ic trusted
|
||||
.Op Ic weight Ar weight-value
|
||||
.Xc
|
||||
As with
|
||||
.Cm server ,
|
||||
specify the IP address or hostname of an NTP server to synchronize to.
|
||||
If it appears multiple times,
|
||||
.Xr ntpd 8
|
||||
will try to synchronize to all of the servers specified.
|
||||
Should the hostname resolve to multiple IP addresses,
|
||||
.Xr ntpd 8
|
||||
will try to synchronize to all of them.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
servers pool.ntp.org
|
||||
servers pool.ntp.org weight 5
|
||||
.Ed
|
||||
.El
|
||||
.Sh CONSTRAINTS
|
||||
.Xr ntpd 8
|
||||
can be configured to query the
|
||||
.Sq Date
|
||||
from trusted HTTPS servers via TLS.
|
||||
This time information is not used for precision but acts as an
|
||||
authenticated constraint,
|
||||
thereby reducing the impact of unauthenticated NTP
|
||||
man-in-the-middle attacks.
|
||||
Received NTP packets with time information falling outside of a range
|
||||
near the constraint will be discarded and such NTP servers
|
||||
will be marked as invalid.
|
||||
.Bl -tag -width Ds
|
||||
.It Ic constraint from Ar url [ip...]
|
||||
Specify the URL, IP address or the hostname of an HTTPS server to
|
||||
provide a constraint.
|
||||
If the url is followed by one or more addresses, the url and addresses will be
|
||||
tried until a working one is found.
|
||||
The url path and expected certificate name is always taken from the
|
||||
url specified.
|
||||
If
|
||||
.Ic constraint from
|
||||
is used more than once,
|
||||
.Xr ntpd 8
|
||||
will calculate a median constraint from all the servers specified.
|
||||
.Bd -literal -offset indent
|
||||
server ntp.example.org
|
||||
constraint from www.example.com
|
||||
constraint from "https://9.9.9.9" "2620:fe::9"
|
||||
.Ed
|
||||
.It Ic constraints from Ar url
|
||||
As with
|
||||
.Ic constraint from ,
|
||||
specify the URL, IP address or the hostname of an HTTPS server to
|
||||
provide a constraint.
|
||||
Should the hostname resolve to multiple IP addresses,
|
||||
.Xr ntpd 8
|
||||
will calculate a median constraint from all of them.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
servers pool.ntp.org
|
||||
constraints from "https://www.google.com/"
|
||||
.Ed
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width /etc/examples/ntpd.conf -compact
|
||||
.It Pa /etc/ntpd.conf
|
||||
Default
|
||||
.Xr ntpd 8
|
||||
configuration file.
|
||||
.It Pa /etc/examples/ntpd.conf
|
||||
Example configuration file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr ntpctl 8 ,
|
||||
.Xr ntpd 8 ,
|
||||
.Xr sysctl 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
file format first appeared in
|
||||
.Ox 3.6 .
|
436
ntpd.h
Normal file
436
ntpd.h
Normal file
@ -0,0 +1,436 @@
|
||||
/* $OpenBSD: ntpd.h,v 1.154 2024/05/21 05:00:48 jsg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2012 Mike Miller <mmiller@mgm51.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
#include <stdarg.h>
|
||||
#include <poll.h>
|
||||
#include <imsg.h>
|
||||
|
||||
#include "ntp.h"
|
||||
#include "log.h"
|
||||
|
||||
#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
#define NTPD_USER "_ntp"
|
||||
#define CONFFILE "/etc/ntpd.conf"
|
||||
#define DRIFTFILE "/var/db/ntpd.drift"
|
||||
#define CTLSOCKET "/var/run/ntpd.sock"
|
||||
|
||||
#define INTERVAL_QUERY_NORMAL 30 /* sync to peers every n secs */
|
||||
#define INTERVAL_QUERY_PATHETIC 60
|
||||
#define INTERVAL_QUERY_AGGRESSIVE 5
|
||||
#define INTERVAL_QUERY_ULTRA_VIOLENCE 1 /* used at startup for auto */
|
||||
|
||||
#define TRUSTLEVEL_BADPEER 6
|
||||
#define TRUSTLEVEL_PATHETIC 2
|
||||
#define TRUSTLEVEL_AGGRESSIVE 8
|
||||
#define TRUSTLEVEL_MAX 10
|
||||
|
||||
#define MAX_SERVERS_DNS 8
|
||||
|
||||
#define QSCALE_OFF_MIN 0.001
|
||||
#define QSCALE_OFF_MAX 0.050
|
||||
|
||||
#define QUERYTIME_MAX 15 /* single query might take n secs max */
|
||||
#define OFFSET_ARRAY_SIZE 8
|
||||
#define SENSOR_OFFSETS 6
|
||||
#define SETTIME_TIMEOUT 15 /* max seconds to wait with -s */
|
||||
#define LOG_NEGLIGIBLE_ADJTIME 32 /* negligible drift to not log (ms) */
|
||||
#define LOG_NEGLIGIBLE_ADJFREQ 0.05 /* negligible rate to not log (ppm) */
|
||||
#define FREQUENCY_SAMPLES 8 /* samples for est. of permanent drift */
|
||||
#define MAX_FREQUENCY_ADJUST 128e-5 /* max correction per iteration */
|
||||
#define MAX_SEND_ERRORS 3 /* max send errors before reconnect */
|
||||
#define MAX_DISPLAY_WIDTH 80 /* max chars in ctl_show report line */
|
||||
|
||||
#define FILTER_ADJFREQ 0x01 /* set after doing adjfreq */
|
||||
#define AUTO_REPLIES 4 /* # of ntp replies we want for auto */
|
||||
#define AUTO_THRESHOLD 60 /* dont bother auto setting < this */
|
||||
#define INTERVAL_AUIO_DNSFAIL 1 /* DNS tmpfail interval for auto */
|
||||
#define TRIES_AUTO_DNSFAIL 4 /* DNS tmpfail quick retries */
|
||||
|
||||
|
||||
#define SENSOR_DATA_MAXAGE (15*60)
|
||||
#define SENSOR_QUERY_INTERVAL 15
|
||||
#define SENSOR_QUERY_INTERVAL_SETTIME (SETTIME_TIMEOUT/3)
|
||||
#define SENSOR_SCAN_INTERVAL (1*60)
|
||||
#define SENSOR_DEFAULT_REFID "HARD"
|
||||
|
||||
#define CONSTRAINT_ERROR_MARGIN (4)
|
||||
#define CONSTRAINT_RETRY_INTERVAL (15)
|
||||
#define CONSTRAINT_SCAN_INTERVAL (15*60)
|
||||
#define CONSTRAINT_SCAN_TIMEOUT (10)
|
||||
#define CONSTRAINT_MARGIN (2.0*60)
|
||||
#define CONSTRAINT_PORT "443" /* HTTPS port */
|
||||
#define CONSTRAINT_MAXHEADERLENGTH 8192
|
||||
#define CONSTRAINT_PASSFD (STDERR_FILENO + 1)
|
||||
|
||||
#define PARENT_SOCK_FILENO CONSTRAINT_PASSFD
|
||||
|
||||
#define NTP_PROC_NAME "ntp_main"
|
||||
#define NTPDNS_PROC_NAME "ntp_dns"
|
||||
#define CONSTRAINT_PROC_NAME "constraint"
|
||||
|
||||
enum client_state {
|
||||
STATE_NONE,
|
||||
STATE_DNS_INPROGRESS,
|
||||
STATE_DNS_TEMPFAIL,
|
||||
STATE_DNS_DONE,
|
||||
STATE_QUERY_SENT,
|
||||
STATE_REPLY_RECEIVED,
|
||||
STATE_TIMEOUT,
|
||||
STATE_INVALID
|
||||
};
|
||||
|
||||
struct listen_addr {
|
||||
TAILQ_ENTRY(listen_addr) entry;
|
||||
struct sockaddr_storage sa;
|
||||
int fd;
|
||||
int rtable;
|
||||
};
|
||||
|
||||
struct ntp_addr {
|
||||
struct ntp_addr *next;
|
||||
struct sockaddr_storage ss;
|
||||
int notauth;
|
||||
};
|
||||
|
||||
struct ntp_addr_wrap {
|
||||
char *name;
|
||||
char *path;
|
||||
struct ntp_addr *a;
|
||||
u_int8_t pool;
|
||||
};
|
||||
|
||||
struct ntp_addr_msg {
|
||||
struct ntp_addr a;
|
||||
size_t namelen;
|
||||
size_t pathlen;
|
||||
u_int8_t synced;
|
||||
};
|
||||
|
||||
struct ntp_status {
|
||||
double rootdelay;
|
||||
double rootdispersion;
|
||||
double reftime;
|
||||
u_int32_t refid;
|
||||
u_int32_t send_refid;
|
||||
u_int8_t synced;
|
||||
u_int8_t leap;
|
||||
int8_t precision;
|
||||
u_int8_t poll;
|
||||
u_int8_t stratum;
|
||||
};
|
||||
|
||||
struct ntp_offset {
|
||||
struct ntp_status status;
|
||||
double offset;
|
||||
double delay;
|
||||
double error;
|
||||
time_t rcvd;
|
||||
u_int8_t good;
|
||||
};
|
||||
|
||||
struct ntp_peer {
|
||||
TAILQ_ENTRY(ntp_peer) entry;
|
||||
struct ntp_addr_wrap addr_head;
|
||||
struct ntp_query query;
|
||||
struct ntp_addr *addr;
|
||||
struct ntp_offset reply[OFFSET_ARRAY_SIZE];
|
||||
struct ntp_offset update;
|
||||
struct sockaddr_in query_addr4;
|
||||
struct sockaddr_in6 query_addr6;
|
||||
enum client_state state;
|
||||
time_t next;
|
||||
time_t deadline;
|
||||
time_t poll;
|
||||
u_int32_t id;
|
||||
u_int8_t shift;
|
||||
u_int8_t trustlevel;
|
||||
u_int8_t weight;
|
||||
u_int8_t trusted;
|
||||
int lasterror;
|
||||
int senderrors;
|
||||
};
|
||||
|
||||
struct ntp_sensor {
|
||||
TAILQ_ENTRY(ntp_sensor) entry;
|
||||
struct ntp_offset offsets[SENSOR_OFFSETS];
|
||||
struct ntp_offset update;
|
||||
time_t next;
|
||||
time_t last;
|
||||
char *device;
|
||||
u_int32_t refid;
|
||||
int sensordevid;
|
||||
int correction;
|
||||
u_int8_t stratum;
|
||||
u_int8_t weight;
|
||||
u_int8_t shift;
|
||||
u_int8_t trusted;
|
||||
};
|
||||
|
||||
struct constraint {
|
||||
TAILQ_ENTRY(constraint) entry;
|
||||
struct ntp_addr_wrap addr_head;
|
||||
struct ntp_addr *addr;
|
||||
int senderrors;
|
||||
enum client_state state;
|
||||
u_int32_t id;
|
||||
int fd;
|
||||
pid_t pid;
|
||||
struct imsgbuf ibuf;
|
||||
time_t last;
|
||||
time_t constraint;
|
||||
int dnstries;
|
||||
};
|
||||
|
||||
struct ntp_conf_sensor {
|
||||
TAILQ_ENTRY(ntp_conf_sensor) entry;
|
||||
char *device;
|
||||
char *refstr;
|
||||
int correction;
|
||||
u_int8_t stratum;
|
||||
u_int8_t weight;
|
||||
u_int8_t trusted;
|
||||
};
|
||||
|
||||
struct ntp_freq {
|
||||
double overall_offset;
|
||||
double x, y;
|
||||
double xx, xy;
|
||||
int samples;
|
||||
u_int num;
|
||||
};
|
||||
|
||||
struct ntpd_conf {
|
||||
TAILQ_HEAD(listen_addrs, listen_addr) listen_addrs;
|
||||
TAILQ_HEAD(ntp_peers, ntp_peer) ntp_peers;
|
||||
TAILQ_HEAD(ntp_sensors, ntp_sensor) ntp_sensors;
|
||||
TAILQ_HEAD(ntp_conf_sensors, ntp_conf_sensor) ntp_conf_sensors;
|
||||
TAILQ_HEAD(constraints, constraint) constraints;
|
||||
struct ntp_status status;
|
||||
struct ntp_freq freq;
|
||||
struct sockaddr_in query_addr4;
|
||||
struct sockaddr_in6 query_addr6;
|
||||
u_int32_t scale;
|
||||
int debug;
|
||||
int verbose;
|
||||
u_int8_t listen_all;
|
||||
u_int8_t settime;
|
||||
u_int8_t automatic;
|
||||
u_int8_t noaction;
|
||||
u_int8_t filters;
|
||||
u_int8_t trusted_peers;
|
||||
u_int8_t trusted_sensors;
|
||||
time_t constraint_last;
|
||||
time_t constraint_median;
|
||||
u_int constraint_errors;
|
||||
u_int8_t *ca;
|
||||
size_t ca_len;
|
||||
int tmpfail;
|
||||
};
|
||||
|
||||
struct ctl_show_status {
|
||||
time_t constraint_median;
|
||||
time_t constraint_last;
|
||||
double clock_offset;
|
||||
u_int peercnt;
|
||||
u_int sensorcnt;
|
||||
u_int valid_peers;
|
||||
u_int valid_sensors;
|
||||
u_int constraint_errors;
|
||||
u_int8_t synced;
|
||||
u_int8_t stratum;
|
||||
u_int8_t constraints;
|
||||
};
|
||||
|
||||
struct ctl_show_peer {
|
||||
char peer_desc[MAX_DISPLAY_WIDTH];
|
||||
u_int8_t syncedto;
|
||||
u_int8_t weight;
|
||||
u_int8_t trustlevel;
|
||||
u_int8_t stratum;
|
||||
time_t next;
|
||||
time_t poll;
|
||||
double offset;
|
||||
double delay;
|
||||
double jitter;
|
||||
};
|
||||
|
||||
struct ctl_show_sensor {
|
||||
char sensor_desc[MAX_DISPLAY_WIDTH];
|
||||
u_int8_t syncedto;
|
||||
u_int8_t weight;
|
||||
u_int8_t good;
|
||||
u_int8_t stratum;
|
||||
time_t next;
|
||||
time_t poll;
|
||||
double offset;
|
||||
double correction;
|
||||
};
|
||||
|
||||
struct ctl_conn {
|
||||
TAILQ_ENTRY(ctl_conn) entry;
|
||||
struct imsgbuf ibuf;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(ctl_conns, ctl_conn) ;
|
||||
|
||||
enum imsg_type {
|
||||
IMSG_NONE,
|
||||
IMSG_ADJTIME,
|
||||
IMSG_ADJFREQ,
|
||||
IMSG_SETTIME,
|
||||
IMSG_HOST_DNS,
|
||||
IMSG_CONSTRAINT_DNS,
|
||||
IMSG_CONSTRAINT_QUERY,
|
||||
IMSG_CONSTRAINT_RESULT,
|
||||
IMSG_CONSTRAINT_CLOSE,
|
||||
IMSG_CONSTRAINT_KILL,
|
||||
IMSG_CTL_SHOW_STATUS,
|
||||
IMSG_CTL_SHOW_PEERS,
|
||||
IMSG_CTL_SHOW_PEERS_END,
|
||||
IMSG_CTL_SHOW_SENSORS,
|
||||
IMSG_CTL_SHOW_SENSORS_END,
|
||||
IMSG_CTL_SHOW_ALL,
|
||||
IMSG_CTL_SHOW_ALL_END,
|
||||
IMSG_SYNCED,
|
||||
IMSG_UNSYNCED,
|
||||
IMSG_PROBE_ROOT
|
||||
};
|
||||
|
||||
enum ctl_actions {
|
||||
CTL_SHOW_STATUS,
|
||||
CTL_SHOW_PEERS,
|
||||
CTL_SHOW_SENSORS,
|
||||
CTL_SHOW_ALL
|
||||
};
|
||||
|
||||
/* prototypes */
|
||||
|
||||
/* ntp.c */
|
||||
void ntp_main(struct ntpd_conf *, struct passwd *, int, char **);
|
||||
void peer_addr_head_clear(struct ntp_peer *);
|
||||
int priv_adjtime(void);
|
||||
void priv_settime(double, char *);
|
||||
void priv_dns(int, char *, u_int32_t);
|
||||
int offset_compare(const void *, const void *);
|
||||
void update_scale(double);
|
||||
time_t scale_interval(time_t);
|
||||
time_t error_interval(void);
|
||||
extern struct ntpd_conf *conf;
|
||||
extern struct ctl_conns ctl_conns;
|
||||
|
||||
#define SCALE_INTERVAL(x) MAXIMUM(5, (x) / 10)
|
||||
|
||||
/* parse.y */
|
||||
int parse_config(const char *, struct ntpd_conf *);
|
||||
|
||||
/* config.c */
|
||||
void host(const char *, struct ntp_addr **);
|
||||
int host_dns(const char *, int, struct ntp_addr **);
|
||||
void host_dns_free(struct ntp_addr *);
|
||||
struct ntp_peer *new_peer(void);
|
||||
struct ntp_conf_sensor *new_sensor(char *);
|
||||
struct constraint *new_constraint(void);
|
||||
|
||||
/* ntp_msg.c */
|
||||
int ntp_getmsg(struct sockaddr *, char *, ssize_t, struct ntp_msg *);
|
||||
int ntp_sendmsg(int, struct sockaddr *, struct ntp_msg *);
|
||||
|
||||
/* server.c */
|
||||
int setup_listeners(struct servent *, struct ntpd_conf *, u_int *);
|
||||
int server_dispatch(int, struct ntpd_conf *);
|
||||
|
||||
/* client.c */
|
||||
int client_peer_init(struct ntp_peer *);
|
||||
int client_addr_init(struct ntp_peer *);
|
||||
int client_nextaddr(struct ntp_peer *);
|
||||
int client_query(struct ntp_peer *);
|
||||
int client_dispatch(struct ntp_peer *, u_int8_t, u_int8_t);
|
||||
void client_log_error(struct ntp_peer *, const char *, int);
|
||||
void set_next(struct ntp_peer *, time_t);
|
||||
|
||||
/* constraint.c */
|
||||
void constraint_add(struct constraint *);
|
||||
void constraint_remove(struct constraint *);
|
||||
void constraint_purge(void);
|
||||
void constraint_reset(void);
|
||||
int constraint_init(struct constraint *);
|
||||
int constraint_query(struct constraint *, int);
|
||||
int constraint_check(double);
|
||||
void constraint_msg_dns(u_int32_t, u_int8_t *, size_t);
|
||||
void constraint_msg_result(u_int32_t, u_int8_t *, size_t);
|
||||
void constraint_msg_close(u_int32_t, u_int8_t *, size_t);
|
||||
void priv_constraint_msg(u_int32_t, u_int8_t *, size_t, int, char **);
|
||||
void priv_constraint_child(const char *, uid_t, gid_t);
|
||||
void priv_constraint_kill(u_int32_t);
|
||||
int priv_constraint_dispatch(struct pollfd *);
|
||||
void priv_constraint_check_child(pid_t, int);
|
||||
char *get_string(u_int8_t *, size_t);
|
||||
|
||||
/* util.c */
|
||||
double gettime_corrected(void);
|
||||
double gettime_from_timeval(struct timeval *);
|
||||
double getoffset(void);
|
||||
double gettime(void);
|
||||
time_t getmonotime(void);
|
||||
void d_to_tv(double, struct timeval *);
|
||||
double lfp_to_d(struct l_fixedpt);
|
||||
struct l_fixedpt d_to_lfp(double);
|
||||
double sfp_to_d(struct s_fixedpt);
|
||||
struct s_fixedpt d_to_sfp(double);
|
||||
char *print_rtable(int);
|
||||
const char *log_sockaddr(struct sockaddr *);
|
||||
const char *log_ntp_addr(struct ntp_addr *);
|
||||
pid_t start_child(char *, int, int, char **);
|
||||
int sanitize_argv(int *, char ***);
|
||||
|
||||
/* sensors.c */
|
||||
void sensor_init(void);
|
||||
int sensor_scan(void);
|
||||
void sensor_query(struct ntp_sensor *);
|
||||
|
||||
/* ntp_dns.c */
|
||||
void ntp_dns(struct ntpd_conf *, struct passwd *);
|
||||
|
||||
/* control.c */
|
||||
int control_check(char *);
|
||||
int control_init(char *);
|
||||
int control_listen(int);
|
||||
void control_shutdown(int);
|
||||
int control_accept(int);
|
||||
struct ctl_conn *control_connbyfd(int);
|
||||
int control_close(int);
|
||||
int control_dispatch_msg(struct pollfd *, u_int *);
|
||||
void session_socket_nonblockmode(int);
|
||||
void build_show_status(struct ctl_show_status *);
|
||||
void build_show_peer(struct ctl_show_peer *,
|
||||
struct ntp_peer *);
|
||||
void build_show_sensor(struct ctl_show_sensor *,
|
||||
struct ntp_sensor *);
|
||||
|
841
parse.y
Normal file
841
parse.y
Normal file
@ -0,0 +1,841 @@
|
||||
/* $OpenBSD: parse.y,v 1.78 2021/10/15 15:01:28 naddy Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2001 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
|
||||
* Copyright (c) 2001 Theo de Raadt. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
%{
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
|
||||
static struct file {
|
||||
TAILQ_ENTRY(file) entry;
|
||||
FILE *stream;
|
||||
char *name;
|
||||
int lineno;
|
||||
int errors;
|
||||
} *file, *topfile;
|
||||
struct file *pushfile(const char *);
|
||||
int popfile(void);
|
||||
int yyparse(void);
|
||||
int yylex(void);
|
||||
int yyerror(const char *, ...)
|
||||
__attribute__((__format__ (printf, 1, 2)))
|
||||
__attribute__((__nonnull__ (1)));
|
||||
int kw_cmp(const void *, const void *);
|
||||
int lookup(char *);
|
||||
int lgetc(int);
|
||||
int lungetc(int);
|
||||
int findeol(void);
|
||||
|
||||
struct sockaddr_in query_addr4;
|
||||
struct sockaddr_in6 query_addr6;
|
||||
int poolseqnum;
|
||||
|
||||
struct opts {
|
||||
int weight;
|
||||
int correction;
|
||||
int stratum;
|
||||
int rtable;
|
||||
int trusted;
|
||||
char *refstr;
|
||||
} opts;
|
||||
void opts_default(void);
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
int64_t number;
|
||||
char *string;
|
||||
struct ntp_addr_wrap *addr;
|
||||
struct opts opts;
|
||||
} v;
|
||||
int lineno;
|
||||
} YYSTYPE;
|
||||
|
||||
%}
|
||||
|
||||
%token LISTEN ON CONSTRAINT CONSTRAINTS FROM QUERY TRUSTED
|
||||
%token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT
|
||||
%token ERROR
|
||||
%token <v.string> STRING
|
||||
%token <v.number> NUMBER
|
||||
%type <v.addr> address url urllist
|
||||
%type <v.opts> listen_opts listen_opts_l listen_opt
|
||||
%type <v.opts> server_opts server_opts_l server_opt
|
||||
%type <v.opts> sensor_opts sensor_opts_l sensor_opt
|
||||
%type <v.opts> correction
|
||||
%type <v.opts> rtable
|
||||
%type <v.opts> refid
|
||||
%type <v.opts> stratum
|
||||
%type <v.opts> weight
|
||||
%type <v.opts> trusted
|
||||
%%
|
||||
|
||||
grammar : /* empty */
|
||||
| grammar '\n'
|
||||
| grammar main '\n'
|
||||
| grammar error '\n' { file->errors++; }
|
||||
;
|
||||
|
||||
main : LISTEN ON address listen_opts {
|
||||
struct listen_addr *la;
|
||||
struct ntp_addr *h, *next;
|
||||
|
||||
if ((h = $3->a) == NULL &&
|
||||
(host_dns($3->name, 0, &h) == -1 || !h)) {
|
||||
yyerror("could not resolve \"%s\"", $3->name);
|
||||
free($3->name);
|
||||
free($3);
|
||||
YYERROR;
|
||||
}
|
||||
|
||||
for (; h != NULL; h = next) {
|
||||
next = h->next;
|
||||
la = calloc(1, sizeof(struct listen_addr));
|
||||
if (la == NULL)
|
||||
fatal("listen on calloc");
|
||||
la->fd = -1;
|
||||
la->rtable = $4.rtable;
|
||||
memcpy(&la->sa, &h->ss,
|
||||
sizeof(struct sockaddr_storage));
|
||||
TAILQ_INSERT_TAIL(&conf->listen_addrs, la,
|
||||
entry);
|
||||
free(h);
|
||||
}
|
||||
free($3->name);
|
||||
free($3);
|
||||
}
|
||||
| QUERY FROM STRING {
|
||||
struct sockaddr_in sin4;
|
||||
struct sockaddr_in6 sin6;
|
||||
|
||||
memset(&sin4, 0, sizeof(sin4));
|
||||
sin4.sin_family = AF_INET;
|
||||
sin4.sin_len = sizeof(struct sockaddr_in);
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
|
||||
if (inet_pton(AF_INET, $3, &sin4.sin_addr) == 1)
|
||||
memcpy(&query_addr4, &sin4, sin4.sin_len);
|
||||
else if (inet_pton(AF_INET6, $3, &sin6.sin6_addr) == 1)
|
||||
memcpy(&query_addr6, &sin6, sin6.sin6_len);
|
||||
else {
|
||||
yyerror("invalid IPv4 or IPv6 address: %s\n",
|
||||
$3);
|
||||
free($3);
|
||||
YYERROR;
|
||||
}
|
||||
|
||||
free($3);
|
||||
}
|
||||
| SERVERS address server_opts {
|
||||
struct ntp_peer *p;
|
||||
struct ntp_addr *h, *next;
|
||||
|
||||
h = $2->a;
|
||||
do {
|
||||
if (h != NULL) {
|
||||
next = h->next;
|
||||
if (h->ss.ss_family != AF_INET &&
|
||||
h->ss.ss_family != AF_INET6) {
|
||||
yyerror("IPv4 or IPv6 address "
|
||||
"or hostname expected");
|
||||
free(h);
|
||||
free($2->name);
|
||||
free($2);
|
||||
YYERROR;
|
||||
}
|
||||
h->next = NULL;
|
||||
} else
|
||||
next = NULL;
|
||||
|
||||
p = new_peer();
|
||||
p->weight = $3.weight;
|
||||
p->trusted = $3.trusted;
|
||||
conf->trusted_peers = conf->trusted_peers ||
|
||||
$3.trusted;
|
||||
p->query_addr4 = query_addr4;
|
||||
p->query_addr6 = query_addr6;
|
||||
p->addr = h;
|
||||
p->addr_head.a = h;
|
||||
p->addr_head.pool = ++poolseqnum;
|
||||
p->addr_head.name = strdup($2->name);
|
||||
if (p->addr_head.name == NULL)
|
||||
fatal(NULL);
|
||||
if (p->addr != NULL)
|
||||
p->state = STATE_DNS_DONE;
|
||||
TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
|
||||
h = next;
|
||||
} while (h != NULL);
|
||||
|
||||
free($2->name);
|
||||
free($2);
|
||||
}
|
||||
| SERVER address server_opts {
|
||||
struct ntp_peer *p;
|
||||
struct ntp_addr *h, *next;
|
||||
|
||||
p = new_peer();
|
||||
for (h = $2->a; h != NULL; h = next) {
|
||||
next = h->next;
|
||||
if (h->ss.ss_family != AF_INET &&
|
||||
h->ss.ss_family != AF_INET6) {
|
||||
yyerror("IPv4 or IPv6 address "
|
||||
"or hostname expected");
|
||||
free(h);
|
||||
free(p);
|
||||
free($2->name);
|
||||
free($2);
|
||||
YYERROR;
|
||||
}
|
||||
h->next = p->addr;
|
||||
p->addr = h;
|
||||
}
|
||||
|
||||
p->weight = $3.weight;
|
||||
p->trusted = $3.trusted;
|
||||
conf->trusted_peers = conf->trusted_peers ||
|
||||
$3.trusted;
|
||||
p->query_addr4 = query_addr4;
|
||||
p->query_addr6 = query_addr6;
|
||||
p->addr_head.a = p->addr;
|
||||
p->addr_head.pool = 0;
|
||||
p->addr_head.name = strdup($2->name);
|
||||
if (p->addr_head.name == NULL)
|
||||
fatal(NULL);
|
||||
if (p->addr != NULL)
|
||||
p->state = STATE_DNS_DONE;
|
||||
TAILQ_INSERT_TAIL(&conf->ntp_peers, p, entry);
|
||||
free($2->name);
|
||||
free($2);
|
||||
}
|
||||
| CONSTRAINTS FROM url {
|
||||
struct constraint *p;
|
||||
struct ntp_addr *h, *next;
|
||||
|
||||
h = $3->a;
|
||||
do {
|
||||
if (h != NULL) {
|
||||
next = h->next;
|
||||
if (h->ss.ss_family != AF_INET &&
|
||||
h->ss.ss_family != AF_INET6) {
|
||||
yyerror("IPv4 or IPv6 address "
|
||||
"or hostname expected");
|
||||
free(h);
|
||||
free($3->name);
|
||||
free($3->path);
|
||||
free($3);
|
||||
YYERROR;
|
||||
}
|
||||
h->next = NULL;
|
||||
} else
|
||||
next = NULL;
|
||||
|
||||
p = new_constraint();
|
||||
p->addr = h;
|
||||
p->addr_head.a = h;
|
||||
p->addr_head.pool = ++poolseqnum;
|
||||
p->addr_head.name = strdup($3->name);
|
||||
p->addr_head.path = strdup($3->path);
|
||||
if (p->addr_head.name == NULL ||
|
||||
p->addr_head.path == NULL)
|
||||
fatal(NULL);
|
||||
if (p->addr != NULL)
|
||||
p->state = STATE_DNS_DONE;
|
||||
constraint_add(p);
|
||||
h = next;
|
||||
} while (h != NULL);
|
||||
|
||||
free($3->name);
|
||||
free($3);
|
||||
}
|
||||
| CONSTRAINT FROM urllist {
|
||||
struct constraint *p;
|
||||
struct ntp_addr *h, *next;
|
||||
|
||||
p = new_constraint();
|
||||
for (h = $3->a; h != NULL; h = next) {
|
||||
next = h->next;
|
||||
if (h->ss.ss_family != AF_INET &&
|
||||
h->ss.ss_family != AF_INET6) {
|
||||
yyerror("IPv4 or IPv6 address "
|
||||
"or hostname expected");
|
||||
free(h);
|
||||
free(p);
|
||||
free($3->name);
|
||||
free($3->path);
|
||||
free($3);
|
||||
YYERROR;
|
||||
}
|
||||
h->next = p->addr;
|
||||
p->addr = h;
|
||||
}
|
||||
|
||||
p->addr_head.a = p->addr;
|
||||
p->addr_head.pool = 0;
|
||||
p->addr_head.name = strdup($3->name);
|
||||
p->addr_head.path = strdup($3->path);
|
||||
if (p->addr_head.name == NULL ||
|
||||
p->addr_head.path == NULL)
|
||||
fatal(NULL);
|
||||
if (p->addr != NULL)
|
||||
p->state = STATE_DNS_DONE;
|
||||
constraint_add(p);
|
||||
free($3->name);
|
||||
free($3);
|
||||
}
|
||||
| SENSOR STRING sensor_opts {
|
||||
struct ntp_conf_sensor *s;
|
||||
|
||||
s = new_sensor($2);
|
||||
s->weight = $3.weight;
|
||||
s->correction = $3.correction;
|
||||
s->refstr = $3.refstr;
|
||||
s->stratum = $3.stratum;
|
||||
s->trusted = $3.trusted;
|
||||
conf->trusted_sensors = conf->trusted_sensors ||
|
||||
$3.trusted;
|
||||
free($2);
|
||||
TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry);
|
||||
}
|
||||
;
|
||||
|
||||
address : STRING {
|
||||
if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
|
||||
NULL)
|
||||
fatal(NULL);
|
||||
host($1, &$$->a);
|
||||
$$->name = $1;
|
||||
}
|
||||
;
|
||||
|
||||
urllist : urllist address {
|
||||
struct ntp_addr *p, *q = NULL;
|
||||
struct in_addr ina;
|
||||
struct in6_addr in6a;
|
||||
|
||||
if (inet_pton(AF_INET, $2->name, &ina) != 1 &&
|
||||
inet_pton(AF_INET6, $2->name, &in6a) != 1) {
|
||||
yyerror("url can only be followed by IP "
|
||||
"addresses");
|
||||
free($2->name);
|
||||
free($2);
|
||||
YYERROR;
|
||||
}
|
||||
p = $2->a;
|
||||
while (p != NULL) {
|
||||
q = p;
|
||||
p = p->next;
|
||||
}
|
||||
if (q != NULL) {
|
||||
q->next = $1->a;
|
||||
$1->a = $2->a;
|
||||
free($2);
|
||||
}
|
||||
$$ = $1;
|
||||
}
|
||||
| url {
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
url : STRING {
|
||||
char *hname, *path;
|
||||
|
||||
if (($$ = calloc(1, sizeof(struct ntp_addr_wrap))) ==
|
||||
NULL)
|
||||
fatal("calloc");
|
||||
|
||||
if (strncmp("https://", $1,
|
||||
strlen("https://")) != 0) {
|
||||
host($1, &$$->a);
|
||||
$$->name = $1;
|
||||
} else {
|
||||
hname = $1 + strlen("https://");
|
||||
|
||||
path = hname + strcspn(hname, "/\\");
|
||||
if (*path != '\0') {
|
||||
if (($$->path = strdup(path)) == NULL)
|
||||
fatal("strdup");
|
||||
*path = '\0';
|
||||
}
|
||||
host(hname, &$$->a);
|
||||
if (($$->name = strdup(hname)) == NULL)
|
||||
fatal("strdup");
|
||||
}
|
||||
if ($$->path == NULL &&
|
||||
($$->path = strdup("/")) == NULL)
|
||||
fatal("strdup");
|
||||
}
|
||||
;
|
||||
|
||||
listen_opts : { opts_default(); }
|
||||
listen_opts_l
|
||||
{ $$ = opts; }
|
||||
| { opts_default(); $$ = opts; }
|
||||
;
|
||||
listen_opts_l : listen_opts_l listen_opt
|
||||
| listen_opt
|
||||
;
|
||||
listen_opt : rtable
|
||||
;
|
||||
|
||||
server_opts : { opts_default(); }
|
||||
server_opts_l
|
||||
{ $$ = opts; }
|
||||
| { opts_default(); $$ = opts; }
|
||||
;
|
||||
server_opts_l : server_opts_l server_opt
|
||||
| server_opt
|
||||
;
|
||||
server_opt : weight
|
||||
| trusted
|
||||
;
|
||||
|
||||
sensor_opts : { opts_default(); }
|
||||
sensor_opts_l
|
||||
{ $$ = opts; }
|
||||
| { opts_default(); $$ = opts; }
|
||||
;
|
||||
sensor_opts_l : sensor_opts_l sensor_opt
|
||||
| sensor_opt
|
||||
;
|
||||
sensor_opt : correction
|
||||
| refid
|
||||
| stratum
|
||||
| weight
|
||||
| trusted
|
||||
;
|
||||
|
||||
correction : CORRECTION NUMBER {
|
||||
if ($2 < -127000000 || $2 > 127000000) {
|
||||
yyerror("correction must be between "
|
||||
"-127000000 and 127000000 microseconds");
|
||||
YYERROR;
|
||||
}
|
||||
opts.correction = $2;
|
||||
}
|
||||
;
|
||||
|
||||
refid : REFID STRING {
|
||||
size_t l = strlen($2);
|
||||
|
||||
if (l < 1 || l > 4) {
|
||||
yyerror("refid must be 1 to 4 characters");
|
||||
free($2);
|
||||
YYERROR;
|
||||
}
|
||||
opts.refstr = $2;
|
||||
}
|
||||
;
|
||||
|
||||
stratum : STRATUM NUMBER {
|
||||
if ($2 < 1 || $2 > 15) {
|
||||
yyerror("stratum must be between "
|
||||
"1 and 15");
|
||||
YYERROR;
|
||||
}
|
||||
opts.stratum = $2;
|
||||
}
|
||||
;
|
||||
|
||||
weight : WEIGHT NUMBER {
|
||||
if ($2 < 1 || $2 > 10) {
|
||||
yyerror("weight must be between 1 and 10");
|
||||
YYERROR;
|
||||
}
|
||||
opts.weight = $2;
|
||||
}
|
||||
rtable : RTABLE NUMBER {
|
||||
if ($2 < 0 || $2 > RT_TABLEID_MAX) {
|
||||
yyerror("rtable must be between 1"
|
||||
" and RT_TABLEID_MAX");
|
||||
YYERROR;
|
||||
}
|
||||
opts.rtable = $2;
|
||||
}
|
||||
;
|
||||
|
||||
trusted : TRUSTED {
|
||||
opts.trusted = 1;
|
||||
}
|
||||
|
||||
%%
|
||||
|
||||
void
|
||||
opts_default(void)
|
||||
{
|
||||
memset(&opts, 0, sizeof opts);
|
||||
opts.weight = 1;
|
||||
opts.stratum = 1;
|
||||
}
|
||||
|
||||
struct keywords {
|
||||
const char *k_name;
|
||||
int k_val;
|
||||
};
|
||||
|
||||
int
|
||||
yyerror(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
|
||||
file->errors++;
|
||||
va_start(ap, fmt);
|
||||
if (vasprintf(&msg, fmt, ap) == -1)
|
||||
fatalx("yyerror vasprintf");
|
||||
va_end(ap);
|
||||
log_warnx("%s:%d: %s", file->name, yylval.lineno, msg);
|
||||
free(msg);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kw_cmp(const void *k, const void *e)
|
||||
{
|
||||
return (strcmp(k, ((const struct keywords *)e)->k_name));
|
||||
}
|
||||
|
||||
int
|
||||
lookup(char *s)
|
||||
{
|
||||
/* this has to be sorted always */
|
||||
static const struct keywords keywords[] = {
|
||||
{ "constraint", CONSTRAINT},
|
||||
{ "constraints", CONSTRAINTS},
|
||||
{ "correction", CORRECTION},
|
||||
{ "from", FROM},
|
||||
{ "listen", LISTEN},
|
||||
{ "on", ON},
|
||||
{ "query", QUERY},
|
||||
{ "refid", REFID},
|
||||
{ "rtable", RTABLE},
|
||||
{ "sensor", SENSOR},
|
||||
{ "server", SERVER},
|
||||
{ "servers", SERVERS},
|
||||
{ "stratum", STRATUM},
|
||||
{ "trusted", TRUSTED},
|
||||
{ "weight", WEIGHT}
|
||||
};
|
||||
const struct keywords *p;
|
||||
|
||||
p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
|
||||
sizeof(keywords[0]), kw_cmp);
|
||||
|
||||
if (p)
|
||||
return (p->k_val);
|
||||
else
|
||||
return (STRING);
|
||||
}
|
||||
|
||||
#define MAXPUSHBACK 128
|
||||
|
||||
char *parsebuf;
|
||||
int parseindex;
|
||||
char pushback_buffer[MAXPUSHBACK];
|
||||
int pushback_index = 0;
|
||||
|
||||
int
|
||||
lgetc(int quotec)
|
||||
{
|
||||
int c, next;
|
||||
|
||||
if (parsebuf) {
|
||||
/* Read character from the parsebuffer instead of input. */
|
||||
if (parseindex >= 0) {
|
||||
c = (unsigned char)parsebuf[parseindex++];
|
||||
if (c != '\0')
|
||||
return (c);
|
||||
parsebuf = NULL;
|
||||
} else
|
||||
parseindex++;
|
||||
}
|
||||
|
||||
if (pushback_index)
|
||||
return ((unsigned char)pushback_buffer[--pushback_index]);
|
||||
|
||||
if (quotec) {
|
||||
if ((c = getc(file->stream)) == EOF) {
|
||||
yyerror("reached end of file while parsing "
|
||||
"quoted string");
|
||||
if (file == topfile || popfile() == EOF)
|
||||
return (EOF);
|
||||
return (quotec);
|
||||
}
|
||||
return (c);
|
||||
}
|
||||
|
||||
while ((c = getc(file->stream)) == '\\') {
|
||||
next = getc(file->stream);
|
||||
if (next != '\n') {
|
||||
c = next;
|
||||
break;
|
||||
}
|
||||
yylval.lineno = file->lineno;
|
||||
file->lineno++;
|
||||
}
|
||||
|
||||
while (c == EOF) {
|
||||
if (file == topfile || popfile() == EOF)
|
||||
return (EOF);
|
||||
c = getc(file->stream);
|
||||
}
|
||||
return (c);
|
||||
}
|
||||
|
||||
int
|
||||
lungetc(int c)
|
||||
{
|
||||
if (c == EOF)
|
||||
return (EOF);
|
||||
if (parsebuf) {
|
||||
parseindex--;
|
||||
if (parseindex >= 0)
|
||||
return (c);
|
||||
}
|
||||
if (pushback_index + 1 >= MAXPUSHBACK)
|
||||
return (EOF);
|
||||
pushback_buffer[pushback_index++] = c;
|
||||
return (c);
|
||||
}
|
||||
|
||||
int
|
||||
findeol(void)
|
||||
{
|
||||
int c;
|
||||
|
||||
parsebuf = NULL;
|
||||
|
||||
/* skip to either EOF or the first real EOL */
|
||||
while (1) {
|
||||
if (pushback_index)
|
||||
c = (unsigned char)pushback_buffer[--pushback_index];
|
||||
else
|
||||
c = lgetc(0);
|
||||
if (c == '\n') {
|
||||
file->lineno++;
|
||||
break;
|
||||
}
|
||||
if (c == EOF)
|
||||
break;
|
||||
}
|
||||
return (ERROR);
|
||||
}
|
||||
|
||||
int
|
||||
yylex(void)
|
||||
{
|
||||
char buf[8096];
|
||||
char *p;
|
||||
int quotec, next, c;
|
||||
int token;
|
||||
|
||||
p = buf;
|
||||
while ((c = lgetc(0)) == ' ' || c == '\t')
|
||||
; /* nothing */
|
||||
|
||||
yylval.lineno = file->lineno;
|
||||
if (c == '#')
|
||||
while ((c = lgetc(0)) != '\n' && c != EOF)
|
||||
; /* nothing */
|
||||
|
||||
switch (c) {
|
||||
case '\'':
|
||||
case '"':
|
||||
quotec = c;
|
||||
while (1) {
|
||||
if ((c = lgetc(quotec)) == EOF)
|
||||
return (0);
|
||||
if (c == '\n') {
|
||||
file->lineno++;
|
||||
continue;
|
||||
} else if (c == '\\') {
|
||||
if ((next = lgetc(quotec)) == EOF)
|
||||
return (0);
|
||||
if (next == quotec || next == ' ' ||
|
||||
next == '\t')
|
||||
c = next;
|
||||
else if (next == '\n') {
|
||||
file->lineno++;
|
||||
continue;
|
||||
} else
|
||||
lungetc(next);
|
||||
} else if (c == quotec) {
|
||||
*p = '\0';
|
||||
break;
|
||||
} else if (c == '\0') {
|
||||
yyerror("syntax error");
|
||||
return (findeol());
|
||||
}
|
||||
if (p + 1 >= buf + sizeof(buf) - 1) {
|
||||
yyerror("string too long");
|
||||
return (findeol());
|
||||
}
|
||||
*p++ = c;
|
||||
}
|
||||
yylval.v.string = strdup(buf);
|
||||
if (yylval.v.string == NULL)
|
||||
fatal("yylex: strdup");
|
||||
return (STRING);
|
||||
}
|
||||
|
||||
#define allowed_to_end_number(x) \
|
||||
(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
|
||||
|
||||
if (c == '-' || isdigit(c)) {
|
||||
do {
|
||||
*p++ = c;
|
||||
if ((size_t)(p-buf) >= sizeof(buf)) {
|
||||
yyerror("string too long");
|
||||
return (findeol());
|
||||
}
|
||||
} while ((c = lgetc(0)) != EOF && isdigit(c));
|
||||
lungetc(c);
|
||||
if (p == buf + 1 && buf[0] == '-')
|
||||
goto nodigits;
|
||||
if (c == EOF || allowed_to_end_number(c)) {
|
||||
const char *errstr = NULL;
|
||||
|
||||
*p = '\0';
|
||||
yylval.v.number = strtonum(buf, LLONG_MIN,
|
||||
LLONG_MAX, &errstr);
|
||||
if (errstr) {
|
||||
yyerror("\"%s\" invalid number: %s",
|
||||
buf, errstr);
|
||||
return (findeol());
|
||||
}
|
||||
return (NUMBER);
|
||||
} else {
|
||||
nodigits:
|
||||
while (p > buf + 1)
|
||||
lungetc((unsigned char)*--p);
|
||||
c = (unsigned char)*--p;
|
||||
if (c == '-')
|
||||
return (c);
|
||||
}
|
||||
}
|
||||
|
||||
#define allowed_in_string(x) \
|
||||
(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
|
||||
x != '{' && x != '}' && x != '<' && x != '>' && \
|
||||
x != '!' && x != '=' && x != '/' && x != '#' && \
|
||||
x != ','))
|
||||
|
||||
if (isalnum(c) || c == ':' || c == '_' || c == '*') {
|
||||
do {
|
||||
*p++ = c;
|
||||
if ((size_t)(p-buf) >= sizeof(buf)) {
|
||||
yyerror("string too long");
|
||||
return (findeol());
|
||||
}
|
||||
} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
|
||||
lungetc(c);
|
||||
*p = '\0';
|
||||
if ((token = lookup(buf)) == STRING)
|
||||
if ((yylval.v.string = strdup(buf)) == NULL)
|
||||
fatal("yylex: strdup");
|
||||
return (token);
|
||||
}
|
||||
if (c == '\n') {
|
||||
yylval.lineno = file->lineno;
|
||||
file->lineno++;
|
||||
}
|
||||
if (c == EOF)
|
||||
return (0);
|
||||
return (c);
|
||||
}
|
||||
|
||||
struct file *
|
||||
pushfile(const char *name)
|
||||
{
|
||||
struct file *nfile;
|
||||
|
||||
if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
|
||||
log_warn("%s", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
if ((nfile->name = strdup(name)) == NULL) {
|
||||
log_warn("%s", __func__);
|
||||
free(nfile);
|
||||
return (NULL);
|
||||
}
|
||||
if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
|
||||
log_warn("%s: %s", __func__, nfile->name);
|
||||
free(nfile->name);
|
||||
free(nfile);
|
||||
return (NULL);
|
||||
}
|
||||
nfile->lineno = 1;
|
||||
TAILQ_INSERT_TAIL(&files, nfile, entry);
|
||||
return (nfile);
|
||||
}
|
||||
|
||||
int
|
||||
popfile(void)
|
||||
{
|
||||
struct file *prev;
|
||||
|
||||
if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
|
||||
prev->errors += file->errors;
|
||||
|
||||
TAILQ_REMOVE(&files, file, entry);
|
||||
fclose(file->stream);
|
||||
free(file->name);
|
||||
free(file);
|
||||
file = prev;
|
||||
return (file ? 0 : EOF);
|
||||
}
|
||||
|
||||
int
|
||||
parse_config(const char *filename, struct ntpd_conf *xconf)
|
||||
{
|
||||
int errors = 0;
|
||||
|
||||
conf = xconf;
|
||||
TAILQ_INIT(&conf->listen_addrs);
|
||||
TAILQ_INIT(&conf->ntp_peers);
|
||||
TAILQ_INIT(&conf->ntp_conf_sensors);
|
||||
TAILQ_INIT(&conf->constraints);
|
||||
|
||||
if ((file = pushfile(filename)) == NULL) {
|
||||
return (-1);
|
||||
}
|
||||
topfile = file;
|
||||
|
||||
yyparse();
|
||||
errors = file->errors;
|
||||
popfile();
|
||||
|
||||
return (errors ? -1 : 0);
|
||||
}
|
265
sensors.c
Normal file
265
sensors.c
Normal file
@ -0,0 +1,265 @@
|
||||
/* $OpenBSD: sensors.c,v 1.54 2019/11/11 06:32:52 otto Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/sensors.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/device.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
#define MAXDEVNAMLEN 16
|
||||
|
||||
int sensor_probe(int, char *, struct sensor *);
|
||||
void sensor_add(int, char *);
|
||||
void sensor_remove(struct ntp_sensor *);
|
||||
void sensor_update(struct ntp_sensor *);
|
||||
|
||||
void
|
||||
sensor_init(void)
|
||||
{
|
||||
TAILQ_INIT(&conf->ntp_sensors);
|
||||
}
|
||||
|
||||
int
|
||||
sensor_scan(void)
|
||||
{
|
||||
int i, n, err;
|
||||
char d[MAXDEVNAMLEN];
|
||||
struct sensor s;
|
||||
|
||||
n = 0;
|
||||
for (i = 0; ; i++)
|
||||
if ((err = sensor_probe(i, d, &s))) {
|
||||
if (err == 0)
|
||||
continue;
|
||||
if (err == -1) /* no further sensors */
|
||||
break;
|
||||
sensor_add(i, d);
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* 1 = time sensor!
|
||||
* 0 = sensor exists... but is not a time sensor
|
||||
* -1: no sensor here, and no further sensors after this
|
||||
*/
|
||||
int
|
||||
sensor_probe(int devid, char *dxname, struct sensor *sensor)
|
||||
{
|
||||
int mib[5];
|
||||
size_t slen, sdlen;
|
||||
struct sensordev sensordev;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_SENSORS;
|
||||
mib[2] = devid;
|
||||
mib[3] = SENSOR_TIMEDELTA;
|
||||
mib[4] = 0;
|
||||
|
||||
sdlen = sizeof(sensordev);
|
||||
if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
|
||||
if (errno == ENXIO)
|
||||
return (0);
|
||||
if (errno == ENOENT)
|
||||
return (-1);
|
||||
log_warn("sensor_probe sysctl");
|
||||
}
|
||||
|
||||
if (sensordev.maxnumt[SENSOR_TIMEDELTA] == 0)
|
||||
return (0);
|
||||
|
||||
strlcpy(dxname, sensordev.xname, MAXDEVNAMLEN);
|
||||
|
||||
slen = sizeof(*sensor);
|
||||
if (sysctl(mib, 5, sensor, &slen, NULL, 0) == -1) {
|
||||
if (errno != ENOENT)
|
||||
log_warn("sensor_probe sysctl");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
sensor_add(int sensordev, char *dxname)
|
||||
{
|
||||
struct ntp_sensor *s;
|
||||
struct ntp_conf_sensor *cs;
|
||||
|
||||
/* check whether it is already there */
|
||||
TAILQ_FOREACH(s, &conf->ntp_sensors, entry)
|
||||
if (!strcmp(s->device, dxname))
|
||||
return;
|
||||
|
||||
/* check whether it is requested in the config file */
|
||||
for (cs = TAILQ_FIRST(&conf->ntp_conf_sensors); cs != NULL &&
|
||||
strcmp(cs->device, dxname) && strcmp(cs->device, "*");
|
||||
cs = TAILQ_NEXT(cs, entry))
|
||||
; /* nothing */
|
||||
if (cs == NULL)
|
||||
return;
|
||||
|
||||
if ((s = calloc(1, sizeof(*s))) == NULL)
|
||||
fatal("sensor_add calloc");
|
||||
|
||||
s->next = getmonotime();
|
||||
s->weight = cs->weight;
|
||||
s->correction = cs->correction;
|
||||
s->stratum = cs->stratum - 1;
|
||||
s->trusted = cs->trusted;
|
||||
if ((s->device = strdup(dxname)) == NULL)
|
||||
fatal("sensor_add strdup");
|
||||
s->sensordevid = sensordev;
|
||||
|
||||
if (cs->refstr == NULL)
|
||||
memcpy(&s->refid, SENSOR_DEFAULT_REFID, sizeof(s->refid));
|
||||
else {
|
||||
s->refid = 0;
|
||||
strncpy((char *)&s->refid, cs->refstr, sizeof(s->refid));
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&conf->ntp_sensors, s, entry);
|
||||
|
||||
log_debug("sensor %s added (weight %d, correction %.6f, refstr %.4u, "
|
||||
"stratum %d)", s->device, s->weight, s->correction / 1e6,
|
||||
s->refid, s->stratum);
|
||||
}
|
||||
|
||||
void
|
||||
sensor_remove(struct ntp_sensor *s)
|
||||
{
|
||||
TAILQ_REMOVE(&conf->ntp_sensors, s, entry);
|
||||
free(s->device);
|
||||
free(s);
|
||||
}
|
||||
|
||||
void
|
||||
sensor_query(struct ntp_sensor *s)
|
||||
{
|
||||
char dxname[MAXDEVNAMLEN];
|
||||
struct sensor sensor;
|
||||
double sens_time;
|
||||
|
||||
if (conf->settime)
|
||||
s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME;
|
||||
else
|
||||
s->next = getmonotime() + SENSOR_QUERY_INTERVAL;
|
||||
|
||||
/* rcvd is walltime here, monotime in client.c. not used elsewhere */
|
||||
if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE)
|
||||
s->update.good = 0;
|
||||
|
||||
if (!sensor_probe(s->sensordevid, dxname, &sensor)) {
|
||||
sensor_remove(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sensor.flags & SENSOR_FINVALID ||
|
||||
sensor.status != SENSOR_S_OK)
|
||||
return;
|
||||
|
||||
if (strcmp(dxname, s->device)) {
|
||||
sensor_remove(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sensor.tv.tv_sec == s->last) /* already seen */
|
||||
return;
|
||||
|
||||
s->last = sensor.tv.tv_sec;
|
||||
|
||||
if (!s->trusted && !TAILQ_EMPTY(&conf->constraints)) {
|
||||
if (conf->constraint_median == 0) {
|
||||
return;
|
||||
}
|
||||
sens_time = gettime() + (sensor.value / -1e9) +
|
||||
(s->correction / 1e6);
|
||||
if (constraint_check(sens_time) != 0) {
|
||||
log_info("sensor %s: constraint check failed", s->device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* TD = device time
|
||||
* TS = system time
|
||||
* sensor.value = TS - TD in ns
|
||||
* if value is positive, system time is ahead
|
||||
*/
|
||||
s->offsets[s->shift].offset = (sensor.value / -1e9) - getoffset() +
|
||||
(s->correction / 1e6);
|
||||
s->offsets[s->shift].rcvd = sensor.tv.tv_sec;
|
||||
s->offsets[s->shift].good = 1;
|
||||
|
||||
s->offsets[s->shift].status.send_refid = s->refid;
|
||||
/* stratum increased when sent out */
|
||||
s->offsets[s->shift].status.stratum = s->stratum;
|
||||
s->offsets[s->shift].status.rootdelay = 0;
|
||||
s->offsets[s->shift].status.rootdispersion = 0;
|
||||
s->offsets[s->shift].status.reftime = sensor.tv.tv_sec;
|
||||
s->offsets[s->shift].status.synced = 1;
|
||||
|
||||
log_debug("sensor %s: offset %f", s->device,
|
||||
s->offsets[s->shift].offset);
|
||||
|
||||
if (++s->shift >= SENSOR_OFFSETS) {
|
||||
s->shift = 0;
|
||||
sensor_update(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
sensor_update(struct ntp_sensor *s)
|
||||
{
|
||||
struct ntp_offset **offsets;
|
||||
int i;
|
||||
|
||||
if ((offsets = calloc(SENSOR_OFFSETS, sizeof(struct ntp_offset *))) ==
|
||||
NULL)
|
||||
fatal("calloc sensor_update");
|
||||
|
||||
for (i = 0; i < SENSOR_OFFSETS; i++)
|
||||
offsets[i] = &s->offsets[i];
|
||||
|
||||
qsort(offsets, SENSOR_OFFSETS, sizeof(struct ntp_offset *),
|
||||
offset_compare);
|
||||
|
||||
i = SENSOR_OFFSETS / 2;
|
||||
memcpy(&s->update, offsets[i], sizeof(s->update));
|
||||
if (SENSOR_OFFSETS % 2 == 0) {
|
||||
s->update.offset =
|
||||
(offsets[i - 1]->offset + offsets[i]->offset) / 2;
|
||||
}
|
||||
free(offsets);
|
||||
|
||||
log_debug("sensor update %s: offset %f", s->device, s->update.offset);
|
||||
priv_adjtime();
|
||||
}
|
203
server.c
Normal file
203
server.c
Normal file
@ -0,0 +1,203 @@
|
||||
/* $OpenBSD: server.c,v 1.44 2016/09/03 11:52:06 reyk Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
* Copyright (c) 2004 Alexander Guy <alexander@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <errno.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
int
|
||||
setup_listeners(struct servent *se, struct ntpd_conf *lconf, u_int *cnt)
|
||||
{
|
||||
struct listen_addr *la, *nla, *lap;
|
||||
struct ifaddrs *ifa, *ifap;
|
||||
struct sockaddr *sa;
|
||||
struct if_data *ifd;
|
||||
u_int8_t *a6;
|
||||
size_t sa6len = sizeof(struct in6_addr);
|
||||
u_int new_cnt = 0;
|
||||
int tos = IPTOS_LOWDELAY, rdomain = 0;
|
||||
|
||||
TAILQ_FOREACH(lap, &lconf->listen_addrs, entry) {
|
||||
switch (lap->sa.ss_family) {
|
||||
case AF_UNSPEC:
|
||||
if (getifaddrs(&ifa) == -1)
|
||||
fatal("getifaddrs");
|
||||
|
||||
for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
|
||||
sa = ifap->ifa_addr;
|
||||
if (sa == NULL || SA_LEN(sa) == 0)
|
||||
continue;
|
||||
if (sa->sa_family == AF_LINK) {
|
||||
ifd = ifap->ifa_data;
|
||||
rdomain = ifd->ifi_rdomain;
|
||||
}
|
||||
if (sa->sa_family != AF_INET &&
|
||||
sa->sa_family != AF_INET6)
|
||||
continue;
|
||||
if (lap->rtable != -1 && rdomain != lap->rtable)
|
||||
continue;
|
||||
|
||||
if (sa->sa_family == AF_INET &&
|
||||
((struct sockaddr_in *)sa)->sin_addr.s_addr ==
|
||||
INADDR_ANY)
|
||||
continue;
|
||||
|
||||
if (sa->sa_family == AF_INET6) {
|
||||
a6 = ((struct sockaddr_in6 *)sa)->
|
||||
sin6_addr.s6_addr;
|
||||
if (memcmp(a6, &in6addr_any, sa6len) == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((la = calloc(1, sizeof(struct listen_addr))) ==
|
||||
NULL)
|
||||
fatal("setup_listeners calloc");
|
||||
|
||||
memcpy(&la->sa, sa, SA_LEN(sa));
|
||||
la->rtable = rdomain;
|
||||
|
||||
TAILQ_INSERT_TAIL(&lconf->listen_addrs, la, entry);
|
||||
}
|
||||
|
||||
freeifaddrs(ifa);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (la = TAILQ_FIRST(&lconf->listen_addrs); la; ) {
|
||||
switch (la->sa.ss_family) {
|
||||
case AF_INET:
|
||||
if (((struct sockaddr_in *)&la->sa)->sin_port == 0)
|
||||
((struct sockaddr_in *)&la->sa)->sin_port =
|
||||
se->s_port;
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (((struct sockaddr_in6 *)&la->sa)->sin6_port == 0)
|
||||
((struct sockaddr_in6 *)&la->sa)->sin6_port =
|
||||
se->s_port;
|
||||
break;
|
||||
case AF_UNSPEC:
|
||||
nla = TAILQ_NEXT(la, entry);
|
||||
TAILQ_REMOVE(&lconf->listen_addrs, la, entry);
|
||||
free(la);
|
||||
la = nla;
|
||||
continue;
|
||||
default:
|
||||
fatalx("king bula sez: af borked");
|
||||
}
|
||||
|
||||
log_info("listening on %s %s",
|
||||
log_sockaddr((struct sockaddr *)&la->sa),
|
||||
print_rtable(la->rtable));
|
||||
|
||||
if ((la->fd = socket(la->sa.ss_family, SOCK_DGRAM, 0)) == -1)
|
||||
fatal("socket");
|
||||
|
||||
if (la->sa.ss_family == AF_INET && setsockopt(la->fd,
|
||||
IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1)
|
||||
log_warn("setsockopt IPTOS_LOWDELAY");
|
||||
|
||||
if (la->rtable != -1 &&
|
||||
setsockopt(la->fd, SOL_SOCKET, SO_RTABLE, &la->rtable,
|
||||
sizeof(la->rtable)) == -1)
|
||||
fatal("setup_listeners setsockopt SO_RTABLE");
|
||||
|
||||
if (bind(la->fd, (struct sockaddr *)&la->sa,
|
||||
SA_LEN((struct sockaddr *)&la->sa)) == -1) {
|
||||
log_warn("bind on %s failed, skipping",
|
||||
log_sockaddr((struct sockaddr *)&la->sa));
|
||||
close(la->fd);
|
||||
nla = TAILQ_NEXT(la, entry);
|
||||
TAILQ_REMOVE(&lconf->listen_addrs, la, entry);
|
||||
free(la);
|
||||
la = nla;
|
||||
continue;
|
||||
}
|
||||
new_cnt++;
|
||||
la = TAILQ_NEXT(la, entry);
|
||||
}
|
||||
|
||||
*cnt = new_cnt;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
server_dispatch(int fd, struct ntpd_conf *lconf)
|
||||
{
|
||||
ssize_t size;
|
||||
double rectime;
|
||||
struct sockaddr_storage fsa;
|
||||
socklen_t fsa_len;
|
||||
struct ntp_msg query, reply;
|
||||
char buf[NTP_MSGSIZE];
|
||||
|
||||
fsa_len = sizeof(fsa);
|
||||
if ((size = recvfrom(fd, &buf, sizeof(buf), 0,
|
||||
(struct sockaddr *)&fsa, &fsa_len)) == -1) {
|
||||
if (errno == EHOSTUNREACH || errno == EHOSTDOWN ||
|
||||
errno == ENETUNREACH || errno == ENETDOWN) {
|
||||
log_warn("recvfrom %s",
|
||||
log_sockaddr((struct sockaddr *)&fsa));
|
||||
return (0);
|
||||
} else
|
||||
fatal("recvfrom");
|
||||
}
|
||||
|
||||
rectime = gettime_corrected();
|
||||
|
||||
if (ntp_getmsg((struct sockaddr *)&fsa, buf, size, &query) == -1)
|
||||
return (0);
|
||||
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
if (lconf->status.synced)
|
||||
reply.status = lconf->status.leap;
|
||||
else
|
||||
reply.status = LI_ALARM;
|
||||
reply.status |= (query.status & VERSIONMASK);
|
||||
if ((query.status & MODEMASK) == MODE_CLIENT)
|
||||
reply.status |= MODE_SERVER;
|
||||
else if ((query.status & MODEMASK) == MODE_SYM_ACT)
|
||||
reply.status |= MODE_SYM_PAS;
|
||||
else /* ignore packets of different type (e.g. bcast) */
|
||||
return (0);
|
||||
|
||||
reply.stratum = lconf->status.stratum;
|
||||
reply.ppoll = query.ppoll;
|
||||
reply.precision = lconf->status.precision;
|
||||
reply.rectime = d_to_lfp(rectime);
|
||||
reply.reftime = d_to_lfp(lconf->status.reftime);
|
||||
reply.xmttime = d_to_lfp(gettime_corrected());
|
||||
reply.orgtime = query.xmttime;
|
||||
reply.rootdelay = d_to_sfp(lconf->status.rootdelay);
|
||||
reply.refid = lconf->status.refid;
|
||||
|
||||
ntp_sendmsg(fd, (struct sockaddr *)&fsa, &reply);
|
||||
return (0);
|
||||
}
|
253
util.c
Normal file
253
util.c
Normal file
@ -0,0 +1,253 @@
|
||||
/* $OpenBSD: util.c,v 1.28 2023/12/20 15:36:36 otto Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ntpd.h"
|
||||
|
||||
double
|
||||
gettime_corrected(void)
|
||||
{
|
||||
return (gettime() + getoffset());
|
||||
}
|
||||
|
||||
double
|
||||
getoffset(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
if (adjtime(NULL, &tv) == -1)
|
||||
return (0.0);
|
||||
return (tv.tv_sec + 1.0e-6 * tv.tv_usec);
|
||||
}
|
||||
|
||||
double
|
||||
gettime(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
if (gettimeofday(&tv, NULL) == -1)
|
||||
fatal("gettimeofday");
|
||||
|
||||
return (gettime_from_timeval(&tv));
|
||||
}
|
||||
|
||||
double
|
||||
gettime_from_timeval(struct timeval *tv)
|
||||
{
|
||||
/*
|
||||
* Account for overflow on OSes that have a 32-bit time_t.
|
||||
*/
|
||||
return ((uint64_t)tv->tv_sec + JAN_1970 + 1.0e-6 * tv->tv_usec);
|
||||
}
|
||||
|
||||
time_t
|
||||
getmonotime(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
|
||||
fatal("clock_gettime");
|
||||
|
||||
return (ts.tv_sec);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
d_to_tv(double d, struct timeval *tv)
|
||||
{
|
||||
tv->tv_sec = d;
|
||||
tv->tv_usec = (d - tv->tv_sec) * 1000000;
|
||||
while (tv->tv_usec < 0) {
|
||||
tv->tv_usec += 1000000;
|
||||
tv->tv_sec -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
lfp_to_d(struct l_fixedpt lfp)
|
||||
{
|
||||
double base, ret;
|
||||
|
||||
lfp.int_partl = ntohl(lfp.int_partl);
|
||||
lfp.fractionl = ntohl(lfp.fractionl);
|
||||
|
||||
/* see comment in ntp.h */
|
||||
base = NTP_ERA;
|
||||
if (lfp.int_partl <= INT32_MAX)
|
||||
base++;
|
||||
ret = base * SECS_IN_ERA;
|
||||
ret += (double)(lfp.int_partl) + ((double)lfp.fractionl / L_DENOMINATOR);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
struct l_fixedpt
|
||||
d_to_lfp(double d)
|
||||
{
|
||||
struct l_fixedpt lfp;
|
||||
|
||||
while (d > SECS_IN_ERA)
|
||||
d -= SECS_IN_ERA;
|
||||
lfp.int_partl = htonl((u_int32_t)d);
|
||||
lfp.fractionl = htonl((u_int32_t)((d - (u_int32_t)d) * L_DENOMINATOR));
|
||||
|
||||
return (lfp);
|
||||
}
|
||||
|
||||
double
|
||||
sfp_to_d(struct s_fixedpt sfp)
|
||||
{
|
||||
double ret;
|
||||
|
||||
sfp.int_parts = ntohs(sfp.int_parts);
|
||||
sfp.fractions = ntohs(sfp.fractions);
|
||||
|
||||
ret = (double)(sfp.int_parts) + ((double)sfp.fractions / S_DENOMINATOR);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
struct s_fixedpt
|
||||
d_to_sfp(double d)
|
||||
{
|
||||
struct s_fixedpt sfp;
|
||||
|
||||
sfp.int_parts = htons((u_int16_t)d);
|
||||
sfp.fractions = htons((u_int16_t)((d - (u_int16_t)d) * S_DENOMINATOR));
|
||||
|
||||
return (sfp);
|
||||
}
|
||||
|
||||
char *
|
||||
print_rtable(int r)
|
||||
{
|
||||
static char b[11];
|
||||
|
||||
b[0] = 0;
|
||||
if (r > 0)
|
||||
snprintf(b, sizeof(b), "rtable %d", r);
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
const char *
|
||||
log_sockaddr(struct sockaddr *sa)
|
||||
{
|
||||
static char buf[NI_MAXHOST];
|
||||
|
||||
if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0,
|
||||
NI_NUMERICHOST))
|
||||
return ("(unknown)");
|
||||
else
|
||||
return (buf);
|
||||
}
|
||||
|
||||
const char *
|
||||
log_ntp_addr(struct ntp_addr *addr)
|
||||
{
|
||||
if (addr == NULL)
|
||||
return ("(unknown)");
|
||||
return log_sockaddr((struct sockaddr *)&addr->ss);
|
||||
}
|
||||
|
||||
pid_t
|
||||
start_child(char *pname, int cfd, int argc, char **argv)
|
||||
{
|
||||
char **nargv;
|
||||
int nargc, i;
|
||||
pid_t pid;
|
||||
|
||||
/* Prepare the child process new argv. */
|
||||
nargv = calloc(argc + 3, sizeof(char *));
|
||||
if (nargv == NULL)
|
||||
fatal("%s: calloc", __func__);
|
||||
|
||||
/* Copy the program name first. */
|
||||
nargc = 0;
|
||||
nargv[nargc++] = argv[0];
|
||||
|
||||
/* Set the process name and copy the original args. */
|
||||
nargv[nargc++] = "-P";
|
||||
nargv[nargc++] = pname;
|
||||
for (i = 1; i < argc; i++)
|
||||
nargv[nargc++] = argv[i];
|
||||
|
||||
nargv[nargc] = NULL;
|
||||
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
fatal("%s: fork", __func__);
|
||||
break;
|
||||
case 0:
|
||||
/* Prepare the parent socket and execute. */
|
||||
if (cfd != PARENT_SOCK_FILENO) {
|
||||
if (dup2(cfd, PARENT_SOCK_FILENO) == -1)
|
||||
fatal("dup2");
|
||||
} else if (fcntl(cfd, F_SETFD, 0) == -1)
|
||||
fatal("fcntl");
|
||||
|
||||
execvp(argv[0], nargv);
|
||||
fatal("%s: execvp", __func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Close child's socket end. */
|
||||
close(cfd);
|
||||
break;
|
||||
}
|
||||
|
||||
free(nargv);
|
||||
return (pid);
|
||||
}
|
||||
|
||||
int
|
||||
sanitize_argv(int *argc, char ***argv)
|
||||
{
|
||||
char **nargv;
|
||||
int nargc;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* We need at least three arguments:
|
||||
* Example: '/usr/sbin/ntpd' '-P' 'foobar'.
|
||||
*/
|
||||
if (*argc < 3)
|
||||
return (-1);
|
||||
|
||||
*argc -= 2;
|
||||
|
||||
/* Allocate new arguments vector and copy pointers. */
|
||||
nargv = calloc((*argc) + 1, sizeof(char *));
|
||||
if (nargv == NULL)
|
||||
return (-1);
|
||||
|
||||
nargc = 0;
|
||||
nargv[nargc++] = (*argv)[0];
|
||||
for (i = 1; i < *argc; i++)
|
||||
nargv[nargc++] = (*argv)[i + 2];
|
||||
|
||||
nargv[nargc] = NULL;
|
||||
*argv = nargv;
|
||||
return (0);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user