From: Pekka Helenius Date: Tue, 04 Aug 2020 01:52:15 +0300 Subject: Unhardcode majority of configuration settings, update manual --- a/src/sensors.c 2020-07-31 23:58:46.000000000 +0300 +++ b/src/sensors.c 2020-08-03 23:12:53.431633678 +0300 @@ -145,7 +145,7 @@ sensor_add(int sensordev, char *dxname) s->sensordevid = sensordev; if (cs->refstr == NULL) - memcpy(&s->refid, SENSOR_DEFAULT_REFID, sizeof(s->refid)); + memcpy(&s->refid, conf->sensor_default_refid, sizeof(s->refid)); else { s->refid = 0; strncpy((char *)&s->refid, cs->refstr, sizeof(s->refid)); @@ -174,12 +174,12 @@ sensor_query(struct ntp_sensor *s) double sens_time; if (conf->settime) - s->next = getmonotime() + SENSOR_QUERY_INTERVAL_SETTIME; + s->next = getmonotime() + conf->sensor_query_interval_settime; else - s->next = getmonotime() + SENSOR_QUERY_INTERVAL; + s->next = getmonotime() + conf->sensor_query_interval; /* rcvd is walltime here, monotime in client.c. not used elsewhere */ - if (s->update.rcvd < time(NULL) - SENSOR_DATA_MAXAGE) + if (s->update.rcvd < time(NULL) - conf->sensor_data_maxage) s->update.good = 0; if (!sensor_probe(s->sensordevid, dxname, &sensor)) { --- a/src/ntpd.c 2020-08-03 23:29:45.150837701 +0300 +++ b/src/ntpd.c 2020-08-03 23:48:03.062564686 +0300 @@ -58,10 +58,10 @@ void ntpd_adjfreq(double, int); void ntpd_settime(double); void readfreq(void); int writefreq(double); -void ctl_main(int, char*[]); +void ctl_main(int, char*[], const struct ntpd_conf *); const char *ctl_lookup_option(char *, const char **); void show_status_msg(struct imsg *); -void show_peer_msg(struct imsg *, int); +void show_peer_msg(struct imsg *, int, const struct ntpd_conf *); void show_sensor_msg(struct imsg *, int); void update_time_sync_status(int); @@ -135,7 +135,7 @@ auto_preconditions(const struct ntpd_con #endif constraints = !TAILQ_EMPTY(&cnf->constraints); return !cnf->settime && (constraints || cnf->trusted_peers || - conf->trusted_sensors) && securelevel == 0; + cnf->trusted_sensors) && securelevel == 0; } #define POLL_MAX 8 @@ -171,15 +171,16 @@ main(int argc, char *argv[]) __progname = get_progname(argv[0]); - if (strcmp(__progname, "ntpctl") == 0) { - ctl_main(argc, argv); - /* NOTREACHED */ - } - conffile = CONFFILE; memset(&lconf, 0, sizeof(lconf)); + if (strcmp(__progname, "ntpctl") == 0) { + parse_config(conffile, &lconf); + ctl_main(argc, argv, &lconf); + /* NOTREACHED */ + } + #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ saved_argv = calloc(argc + 1, sizeof(*saved_argv)); @@ -253,8 +254,8 @@ main(int argc, char *argv[]) if (geteuid()) errx(1, "main process: need root privileges"); - if ((pw = getpwnam(NTPD_USER)) == NULL) - errx(1, "main process: unknown user %s", NTPD_USER); + if ((pw = getpwnam(conf->ntpd_user)) == NULL) + errx(1, "main process: unknown user %s", conf->ntpd_user); lconf.automatic = auto_preconditions(&lconf); if (lconf.automatic) @@ -277,7 +278,7 @@ main(int argc, char *argv[]) fatalx("main process: process '%s' failed (%s)", pname, __func__); } else { - if ((control_check(CTLSOCKET)) == -1) + if ((control_check(conf->ctlsocket)) == -1) fatalx("OpenNTPD is already running"); } @@ -295,7 +296,7 @@ main(int argc, char *argv[]) } } else { settime_deadline = getmonotime(); - timeout = 100; + timeout = conf->settime_timeout; } if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, PF_UNSPEC, @@ -311,6 +312,10 @@ main(int argc, char *argv[]) start_child(NTP_PROC_NAME, pipe_chld[1], argc0, argv0); log_procinit("[priv]"); + + if (lconf.debug) + print_conf(&lconf); + readfreq(); signal(SIGTERM, sighdlr); @@ -370,7 +375,7 @@ main(int argc, char *argv[]) } if (nfds == 0 && lconf.settime && - getmonotime() > settime_deadline + SETTIME_TIMEOUT) { + getmonotime() > settime_deadline + conf->settime_timeout) { lconf.settime = 0; timeout = INFTIM; log_init(logdest, lconf.verbose, LOG_DAEMON); @@ -520,7 +525,7 @@ ntpd_adjtime(double d) { int synced = 0; static int firstadj = 1; - double threshold = (double)LOG_NEGLIGIBLE_ADJTIME / 1000; + double threshold = (double)conf->log_negligible_adjtime / 1000; d += getoffset(); if (d >= threshold || d <= -1 * threshold) @@ -581,8 +586,8 @@ ntpd_adjfreq(double relfreq, int wrlog) r = writefreq(curfreq / 1e9 / (1LL << 32)); ppmfreq = relfreq * 1e6; if (wrlog) { - if (ppmfreq >= LOG_NEGLIGIBLE_ADJFREQ || - ppmfreq <= -LOG_NEGLIGIBLE_ADJFREQ) + if (ppmfreq >= conf->log_negligible_adjfreq || + ppmfreq <= -(conf->log_negligible_adjfreq)) log_info("main process: adjusting clock frequency by %f to %f ppm%s", ppmfreq, curfreq / 1e3 / (1LL << 32), r ? "" : " (no drift file)"); @@ -634,13 +639,13 @@ readfreq(void) int fd; double d; - fd = open(DRIFTFILE, O_RDWR); + fd = open(conf->driftfile, O_RDWR); if (fd == -1) { - log_warnx("main process: creating new drift file %s", DRIFTFILE); + log_warnx("main process: creating new drift file %s", conf->driftfile); current = 0; if (adjfreq(¤t, NULL) == -1) log_warn("main process: frequency reset failed"); - freqfp = fopen(DRIFTFILE, "w"); + freqfp = fopen(conf->driftfile, "w"); return; } @@ -654,7 +659,7 @@ readfreq(void) d /= 1e6; /* scale from ppm */ ntpd_adjfreq(d, 0); } else - log_warnx("main process: drift file %s is empty", DRIFTFILE); + log_warnx("main process: drift file %s is empty", conf->driftfile); } } @@ -671,7 +676,7 @@ writefreq(double d) r = fprintf(freqfp, "%.3f\n", d * 1e6); /* scale to ppm */ if (r < 0 || fflush(freqfp) != 0) { if (warnonce) { - log_warnx("main process: can't write drift file %s", DRIFTFILE); + log_warnx("main process: can't write drift file %s", conf->driftfile); warnonce = 0; } clearerr(freqfp); @@ -679,13 +684,13 @@ writefreq(double d) } off = ftello(freqfp); if (off == -1 || ftruncate(fileno(freqfp), off) == -1) - log_warnx("main process: can't truncate drift file %s", DRIFTFILE); + log_warnx("main process: can't truncate drift file %s", conf->driftfile); fsync(fileno(freqfp)); return 1; } void -ctl_main(int argc, char *argv[]) +ctl_main(int argc, char *argv[], const struct ntpd_conf *cconf) { struct sockaddr_un sa; struct imsg imsg; @@ -693,7 +698,7 @@ ctl_main(int argc, char *argv[]) int fd, n, done, ch, action; char *sockname; - sockname = CTLSOCKET; + sockname = cconf->ctlsocket; if (argc < 2) { usage(); @@ -741,6 +746,7 @@ ctl_main(int argc, char *argv[]) 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, "ntpctl: control socket name is too long"); @@ -799,7 +805,7 @@ ctl_main(int argc, char *argv[]) done = 1; break; case CTL_SHOW_PEERS: - show_peer_msg(&imsg, 0); + show_peer_msg(&imsg, 0, cconf); if (imsg.hdr.type == IMSG_CTL_SHOW_PEERS_END) done = 1; @@ -816,7 +822,7 @@ ctl_main(int argc, char *argv[]) show_status_msg(&imsg); break; case IMSG_CTL_SHOW_PEERS: - show_peer_msg(&imsg, 1); + show_peer_msg(&imsg, 1, cconf); break; case IMSG_CTL_SHOW_SENSORS: show_sensor_msg(&imsg, 1); @@ -911,7 +917,7 @@ show_status_msg(struct imsg *imsg) } void -show_peer_msg(struct imsg *imsg, int calledfromshowall) +show_peer_msg(struct imsg *imsg, int calledfromshowall, const struct ntpd_conf *pconf) { struct ctl_show_peer *cpeer; int cnt; @@ -953,7 +959,7 @@ show_peer_msg(struct imsg *imsg, int cal cpeer->weight, cpeer->trustlevel, stratum, (long long)cpeer->next, (long long)cpeer->poll); - if (cpeer->trustlevel >= TRUSTLEVEL_BADPEER) + if (cpeer->trustlevel >= pconf->trustlevel_badpeer) printf(" %12.3fms %9.3fms %8.3fms\n", cpeer->offset, cpeer->delay, cpeer->jitter); else --- a/src/ntp.c 2020-08-01 15:22:42.000000000 +0300 +++ b/src/ntp.c 2020-08-03 23:08:32.397143675 +0300 @@ -55,7 +55,7 @@ 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); + struct sockaddr_storage[conf->max_servers_dns], size_t); void ntp_sighdlr(int sig) @@ -258,12 +258,12 @@ ntp_main(struct ntpd_conf *nconf, struct sent_cnt++; } if (p->deadline > 0 && p->deadline <= getmonotime()) { - timeout = 300; + timeout = conf->interval_query_timeout; log_debug("NTP client: NTP peer %s - no reply received in time, " "next query in %ds", log_sockaddr( (struct sockaddr *)&p->addr->ss), timeout); - if (p->trustlevel >= TRUSTLEVEL_BADPEER && - (p->trustlevel /= 2) < TRUSTLEVEL_BADPEER) + if (p->trustlevel >= conf->trustlevel_badpeer && + (p->trustlevel /= 2) < conf->trustlevel_badpeer) log_info("NTP client: NTP peer %s is invalid now", log_sockaddr( (struct sockaddr *)&p->addr->ss)); @@ -273,17 +273,17 @@ ntp_main(struct ntpd_conf *nconf, struct } set_next(p, timeout); } - if (p->senderrors > MAX_SEND_ERRORS) { + if (p->senderrors > conf->max_send_errors) { log_debug("NTP client: NTP peer %s - failed to send query, " "next query in %ds", log_sockaddr( (struct sockaddr *)&p->addr->ss), - INTERVAL_QUERY_PATHETIC); + conf->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); + set_next(p, conf->interval_query_pathetic); } if (p->next > 0 && p->next < nextaction) nextaction = p->next; @@ -304,13 +304,13 @@ ntp_main(struct ntpd_conf *nconf, struct (conf->trusted_sensors || constraint_cnt == 0 || conf->constraint_median != 0)) { if (last_sensor_scan == 0 || - last_sensor_scan + SENSOR_SCAN_INTERVAL <= getmonotime()) { + last_sensor_scan + conf->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; + nextaction > last_sensor_scan + conf->sensor_scan_interval) + nextaction = last_sensor_scan + conf->sensor_scan_interval; sensors_cnt = 0; TAILQ_FOREACH(s, &conf->ntp_sensors, entry) { if (conf->settime && s->offsets[0].offset) @@ -482,7 +482,7 @@ ntp_dispatch_imsg(void) int inpool(struct sockaddr_storage *a, - struct sockaddr_storage old[MAX_SERVERS_DNS], size_t n) + struct sockaddr_storage old[conf->max_servers_dns], size_t n) { size_t i; @@ -506,7 +506,7 @@ int ntp_dispatch_imsg_dns(void) { struct imsg imsg; - struct sockaddr_storage existing[MAX_SERVERS_DNS]; + struct sockaddr_storage existing[conf->max_servers_dns]; struct ntp_peer *peer, *npeer, *tmp; u_int16_t dlen; u_char *p; @@ -558,7 +558,7 @@ ntp_dispatch_imsg_dns(void) if (dlen == 0) { /* no data -> temp error */ log_warnx("DNS lookup temporary failed"); peer->state = STATE_DNS_TEMPFAIL; - if (conf->tmpfail++ == TRIES_AUTO_DNSFAIL) + if (conf->tmpfail++ == conf->tries_auto_dnsfail) priv_settime(0, "of DNS failures"); break; } @@ -690,7 +690,7 @@ priv_adjfreq(double offset) conf->freq.y += offset; conf->freq.xx += curtime * curtime; - if (conf->freq.samples % FREQUENCY_SAMPLES != 0) + if (conf->freq.samples % conf->frequency_samples != 0) return; freq = @@ -698,10 +698,10 @@ priv_adjfreq(double offset) / (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; + if (freq > conf->max_frequency_adjust) + freq = conf->max_frequency_adjust; + else if (freq < -(conf->max_frequency_adjust)) + freq = -(conf->max_frequency_adjust); imsg_compose(ibuf_main, IMSG_ADJFREQ, 0, 0, -1, &freq, sizeof(freq)); conf->filters |= FILTER_ADJFREQ; @@ -724,7 +724,7 @@ priv_adjtime(void) double offset_median; TAILQ_FOREACH(p, &conf->ntp_peers, entry) { - if (p->trustlevel < TRUSTLEVEL_BADPEER) + if (p->trustlevel < conf->trustlevel_badpeer) continue; if (!p->update.good) return (1); @@ -744,7 +744,7 @@ priv_adjtime(void) fatal("main process: can't allocate memory for time adjustment"); TAILQ_FOREACH(p, &conf->ntp_peers, entry) { - if (p->trustlevel < TRUSTLEVEL_BADPEER) + if (p->trustlevel < conf->trustlevel_badpeer) continue; for (j = 0; j < p->weight; j++) offsets[i++] = &p->update; @@ -841,13 +841,13 @@ update_scale(double offset) if (offset < 0) offset = -offset; - if (offset > QSCALE_OFF_MAX || !conf->status.synced || + if (offset > conf->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 if (offset < conf->qscale_off_min) + conf->scale = conf->qscale_off_max / conf->qscale_off_min; else - conf->scale = QSCALE_OFF_MAX / offset; + conf->scale = conf->qscale_off_max / offset; } time_t @@ -865,7 +865,7 @@ error_interval(void) { time_t interval, r; - interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN; + interval = conf->interval_query_pathetic * conf->qscale_off_max / conf->qscale_off_min; r = arc4random_uniform(interval / 10); return (interval + r); } --- a/src/control.c 2020-07-31 23:23:56.000000000 +0300 +++ b/src/control.c 2020-08-03 23:06:05.136249122 +0300 @@ -317,7 +317,7 @@ build_show_status(struct ctl_show_status TAILQ_FOREACH(p, &conf->ntp_peers, entry) { cs->peercnt++; - if (p->trustlevel >= TRUSTLEVEL_BADPEER) + if (p->trustlevel >= conf->trustlevel_badpeer) cs->valid_peers++; } TAILQ_FOREACH(s, &conf->ntp_sensors, entry) { @@ -424,7 +424,7 @@ build_show_sensor(struct ctl_show_sensor now = getmonotime(); - memcpy(&refid, SENSOR_DEFAULT_REFID, sizeof(refid)); + memcpy(&refid, conf->sensor_default_refid, sizeof(refid)); refid = refid == s->refid ? 0 : s->refid; snprintf(cs->sensor_desc, sizeof(cs->sensor_desc), @@ -445,7 +445,7 @@ build_show_sensor(struct ctl_show_sensor 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->poll = conf->sensor_query_interval; cs->offset = s->offsets[shift].offset * 1000.0; cs->correction = (double)s->correction / 1000.0; } --- a/src/constraint.c 2020-08-02 01:57:36.430286127 +0300 +++ b/src/constraint.c 2020-08-02 01:57:57.020286149 +0300 @@ -165,8 +165,8 @@ constraint_query(struct constraint *cstr /* Proceed and query the time */ break; case STATE_DNS_TEMPFAIL: - if (now > cstr->last + (cstr->dnstries >= TRIES_AUTO_DNSFAIL ? - CONSTRAINT_RETRY_INTERVAL : INTERVAL_AUIO_DNSFAIL)) { + if (now > cstr->last + (cstr->dnstries >= conf->tries_auto_dnsfail ? + conf->constraint_retry_interval : conf->interval_auto_dnsfail)) { cstr->dnstries++; /* Retry resolving the address */ constraint_init(cstr); @@ -174,7 +174,7 @@ constraint_query(struct constraint *cstr } return (-1); case STATE_QUERY_SENT: - if (cstr->last + CONSTRAINT_SCAN_TIMEOUT > now) { + if (cstr->last + conf->constraint_scan_timeout > now) { /* The caller should expect a reply */ return (0); } @@ -186,7 +186,7 @@ constraint_query(struct constraint *cstr cstr->state = STATE_TIMEOUT; return (-1); case STATE_INVALID: - if (cstr->last + CONSTRAINT_SCAN_INTERVAL > now) { + if (cstr->last + conf->constraint_scan_interval > now) { /* Nothing to do */ return (-1); } @@ -745,7 +745,7 @@ constraint_msg_close(u_int32_t id, u_int log_debug("constraint %s: no reply" " received in time, next query in %ds", log_sockaddr((struct sockaddr *) - &cstr->addr->ss), CONSTRAINT_SCAN_INTERVAL); + &cstr->addr->ss), conf->constraint_scan_interval); cnt = 0; TAILQ_FOREACH(tmp, &conf->constraints, entry) @@ -920,9 +920,9 @@ constraint_check(double val) tv.tv_usec = 0; diff = fabs(val - gettime_from_timeval(&tv)); - if (diff > CONSTRAINT_MARGIN) { + if (diff > conf->constraint_margin) { if (conf->constraint_errors++ > - (CONSTRAINT_ERROR_MARGIN * peer_cnt)) { + (conf->constraint_error_margin * peer_cnt)) { constraint_reset(); } @@ -999,7 +999,7 @@ int httpsdate_request(struct httpsdate *httpsdate, struct timeval *when) { char timebuf1[32], timebuf2[32]; - size_t outlen = 0, maxlength = CONSTRAINT_MAXHEADERLENGTH, len; + size_t outlen = 0, maxlength = conf->constraint_maxheaderlength, len; char *line, *p, *buf; time_t httptime, notbefore, notafter; struct tm *tm; --- a/src/config.c 2020-08-01 01:02:14.000000000 +0300 +++ b/src/config.c 2020-08-01 11:35:05.758097319 +0300 @@ -115,7 +115,7 @@ host_dns1(const char *s, struct ntp_addr return (-1); } - for (res = res0; res && cnt < MAX_SERVERS_DNS; res = res->ai_next) { + for (res = res0; res && cnt < conf->max_servers_dns; res = res->ai_next) { if (res->ai_family != AF_INET && res->ai_family != AF_INET6) continue; --- a/src/client.c 2020-08-02 02:04:55.666953258 +0300 +++ b/src/client.c 2020-08-03 23:12:14.368300303 +0300 @@ -57,7 +57,7 @@ client_peer_init(struct ntp_peer *p) p->query->msg.status = MODE_CLIENT | (NTP_VERSION << 3); p->state = STATE_NONE; p->shift = 0; - p->trustlevel = TRUSTLEVEL_PATHETIC; + p->trustlevel = conf->trustlevel_pathetic; p->lasterror = 0; p->senderrors = 0; @@ -120,7 +120,7 @@ client_nextaddr(struct ntp_peer *p) } p->shift = 0; - p->trustlevel = TRUSTLEVEL_PATHETIC; + p->trustlevel = conf->trustlevel_pathetic; if (p->addr == NULL) { p->addr = p->addr_head.a; @@ -148,10 +148,10 @@ client_query(struct ntp_peer *p) if (p->addr == NULL && client_nextaddr(p) == -1) { if (conf->settime) - set_next(p, INTERVAL_AUIO_DNSFAIL); + set_next(p, conf->interval_auto_dnsfail); else - set_next(p, MAXIMUM(SETTIME_TIMEOUT, - scale_interval(INTERVAL_QUERY_AGGRESSIVE))); + set_next(p, MAXIMUM(conf->settime_timeout, + scale_interval(conf->interval_query_aggressive))); return (0); } @@ -200,8 +200,8 @@ client_query(struct ntp_peer *p) client_nextaddr(p); if (p->addr == NULL) p->addr = p->addr_head.a; - set_next(p, MAXIMUM(SETTIME_TIMEOUT, - scale_interval(INTERVAL_QUERY_AGGRESSIVE))); + set_next(p, MAXIMUM(conf->settime_timeout, + scale_interval(conf->interval_query_aggressive))); p->senderrors++; return (-1); } else @@ -239,14 +239,14 @@ client_query(struct ntp_peer *p) if (ntp_sendmsg(p->query->fd, NULL, &p->query->msg) == -1) { p->senderrors++; - set_next(p, INTERVAL_QUERY_PATHETIC); - p->trustlevel = TRUSTLEVEL_PATHETIC; + set_next(p, conf->interval_query_pathetic); + p->trustlevel = conf->trustlevel_pathetic; return (-1); } p->senderrors = 0; p->state = STATE_QUERY_SENT; - set_deadline(p, QUERYTIME_MAX); + set_deadline(p, conf->querytime_max); return (0); } @@ -263,7 +263,7 @@ void handle_auto(uint8_t trusted, double offset) { static int count; - static double v[AUTO_REPLIES]; + double v[conf->auto_replies]; /* * It happens the (constraint) resolves initially fail, don't give up @@ -272,7 +272,7 @@ handle_auto(uint8_t trusted, double offs if (!trusted && conf->constraint_median == 0) return; - if (offset < AUTO_THRESHOLD) { + if (offset < conf->auto_threshold) { /* don't bother */ priv_settime(0, "NTP client: NTP peer offset is negative or close enough"); return; @@ -281,13 +281,13 @@ handle_auto(uint8_t trusted, double offs 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; + if (conf->auto_replies % 2 == 0) + offset = (v[conf->auto_replies / 2 - 1] + v[conf->auto_replies / 2]) / 2; else - offset = v[AUTO_REPLIES / 2]; + offset = v[conf->auto_replies / 2]; priv_settime(offset, ""); } @@ -451,22 +451,22 @@ client_dispatch(struct ntp_peer *p, u_in } else p->reply[p->shift].status.send_refid = msg.xmttime.fractionl; - if (p->trustlevel < TRUSTLEVEL_PATHETIC) - interval = scale_interval(INTERVAL_QUERY_PATHETIC); - else if (p->trustlevel < TRUSTLEVEL_AGGRESSIVE) + if (p->trustlevel < conf->trustlevel_pathetic) + interval = scale_interval(conf->interval_query_pathetic); + else if (p->trustlevel < conf->trustlevel_aggressive) interval = (conf->settime && conf->automatic) ? - INTERVAL_QUERY_ULTRA_VIOLENCE : - scale_interval(INTERVAL_QUERY_AGGRESSIVE); + conf->interval_query_ultra_violence : + scale_interval(conf->interval_query_aggressive); else - interval = scale_interval(INTERVAL_QUERY_NORMAL); + interval = scale_interval(conf->interval_query_normal); set_next(p, interval); 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) + if (p->trustlevel < conf->trustlevel_max) { + if (p->trustlevel < conf->trustlevel_badpeer && + p->trustlevel + 1 >= conf->trustlevel_badpeer) log_info("NTP client: NTP peer %s is valid now", log_sockaddr((struct sockaddr *)&p->addr->ss)); p->trustlevel++; --- a/src/ntpd.h 2020-08-01 01:27:06.000000000 +0300 +++ b/src/ntpd.h 2020-08-03 23:10:00.839597442 +0300 @@ -56,6 +56,8 @@ #define INTERVAL_QUERY_AGGRESSIVE 5 #define INTERVAL_QUERY_ULTRA_VIOLENCE 1 /* used at startup for auto */ +#define INTERVAL_QUERY_TIMEOUT 300 + #define TRUSTLEVEL_BADPEER 6 #define TRUSTLEVEL_PATHETIC 2 #define TRUSTLEVEL_AGGRESSIVE 8 @@ -69,7 +71,7 @@ #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 SETTIME_TIMEOUT 100 /* max seconds to wait when settime == 1 */ #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 */ @@ -80,7 +82,7 @@ #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 INTERVAL_AUTO_DNSFAIL 1 /* DNS tmpfail interval for auto */ #define TRIES_AUTO_DNSFAIL 4 /* DNS tmpfail quick retries */ @@ -268,6 +270,59 @@ struct ntpd_conf { size_t ca_len; int tmpfail; char *pid_file; + + char *ntpd_user; + char *driftfile; + char *ctlsocket; + + int interval_query_normal; + int interval_query_pathetic; + int interval_query_aggressive; + int interval_query_ultra_violence; + + int interval_query_timeout; + + int trustlevel_badpeer; + int trustlevel_pathetic; + int trustlevel_aggressive; + int trustlevel_max; + + int max_servers_dns; + + double qscale_off_min; + double qscale_off_max; + + int querytime_max; + int settime_timeout; + + int log_negligible_adjtime; + double log_negligible_adjfreq; + + int frequency_samples; + double max_frequency_adjust; + + int max_send_errors; + + u_int8_t filter_adjfreq; + + int auto_replies; + int auto_threshold; + int interval_auto_dnsfail; + int tries_auto_dnsfail; + + int sensor_query_interval_settime; + int sensor_data_maxage; + int sensor_query_interval; + int sensor_scan_interval; + char *sensor_default_refid; + + double constraint_error_margin; + int constraint_retry_interval; + int constraint_scan_interval; + int constraint_scan_timeout; + double constraint_margin; + + int constraint_maxheaderlength; }; struct ctl_show_status { @@ -363,6 +418,7 @@ extern struct ctl_conns ctl_conns; /* parse.y */ int parse_config(const char *, struct ntpd_conf *); +void print_conf(struct ntpd_conf *); /* config.c */ void host(const char *, struct ntp_addr **); --- a/src/parse.y 2020-08-01 01:51:28.000000000 +0300 +++ b/src/parse.y 2020-08-03 23:11:12.796264187 +0300 @@ -52,7 +52,6 @@ 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); @@ -70,12 +69,15 @@ struct opts { int trusted; char *refstr; int port; + int pos_num; + double pos_decimal; } opts; void opts_default(void); typedef struct { union { int64_t number; + double decimal; char *string; struct ntp_addr_wrap *addr; struct opts opts; @@ -89,8 +91,64 @@ typedef struct { %token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT %token ERROR %token PORT + +%token _NTPD_USER +%token _DRIFTFILE +%token _CTLSOCKET + +%token _INTERVAL_QUERY_NORMAL +%token _INTERVAL_QUERY_PATHETIC +%token _INTERVAL_QUERY_AGGRESSIVE +%token _INTERVAL_QUERY_ULTRA_VIOLENCE + +%token _INTERVAL_QUERY_TIMEOUT + +%token _TRUSTLEVEL_BADPEER +%token _TRUSTLEVEL_PATHETIC +%token _TRUSTLEVEL_AGGRESSIVE +%token _TRUSTLEVEL_MAX + +%token _MAX_SERVERS_DNS + +%token _QSCALE_OFF_MIN +%token _QSCALE_OFF_MAX + +%token _QUERYTIME_MAX +%token _SETTIME_TIMEOUT + +%token _LOG_NEGLIGIBLE_ADJTIME +%token _LOG_NEGLIGIBLE_ADJFREQ + +%token _FREQUENCY_SAMPLES +%token _MAX_FREQUENCY_ADJUST + +%token _MAX_SEND_ERRORS + +%token _FILTER_ADJFREQ + +%token _AUTO_REPLIES +%token _AUTO_THRESHOLD + +%token _INTERVAL_AUTO_DNSFAIL +%token _TRIES_AUTO_DNSFAIL + +%token _SENSOR_DATA_MAXAGE +%token _SENSOR_QUERY_INTERVAL +%token _SENSOR_SCAN_INTERVAL + +%token _SENSOR_DEFAULT_REFID + +%token _CONSTRAINT_ERROR_MARGIN +%token _CONSTRAINT_RETRY_INTERVAL +%token _CONSTRAINT_SCAN_INTERVAL +%token _CONSTRAINT_SCAN_TIMEOUT +%token _CONSTRAINT_MARGIN + +%token _CONSTRAINT_MAXHEADERLENGTH + %token STRING %token NUMBER +%token NUMBER_DOUBLE %type address url urllist %type listen_opts listen_opts_l listen_opt %type server_opts server_opts_l server_opt @@ -103,6 +161,9 @@ typedef struct { %type weight %type trusted %type port + +%type pos_num +%type pos_decimal %% grammar : /* empty */ @@ -385,6 +446,161 @@ main : LISTEN ON address listen_opts { free($2); TAILQ_INSERT_TAIL(&conf->ntp_conf_sensors, s, entry); } + + | _NTPD_USER STRING { + conf->ntpd_user = $2; + } + | _DRIFTFILE STRING { + conf->driftfile = $2; + } + | _CTLSOCKET STRING { + conf->ctlsocket = $2; + } + + | _INTERVAL_QUERY_NORMAL pos_num { + conf->interval_query_normal = $2.pos_num; + } + | _INTERVAL_QUERY_PATHETIC pos_num { + conf->interval_query_pathetic = $2.pos_num; + } + | _INTERVAL_QUERY_AGGRESSIVE pos_num { + conf->interval_query_aggressive = $2.pos_num; + } + | _INTERVAL_QUERY_ULTRA_VIOLENCE pos_num { + conf->interval_query_ultra_violence = $2.pos_num; + } + + | _INTERVAL_QUERY_TIMEOUT pos_num { + conf->interval_query_timeout = $2.pos_num; + } + + | _TRUSTLEVEL_BADPEER pos_num { + conf->trustlevel_badpeer = $2.pos_num; + } + | _TRUSTLEVEL_PATHETIC pos_num { + conf->trustlevel_pathetic = $2.pos_num; + } + | _TRUSTLEVEL_AGGRESSIVE pos_num { + conf->trustlevel_aggressive = $2.pos_num; + } + | _TRUSTLEVEL_MAX pos_num { + conf->trustlevel_max = $2.pos_num; + } + + | _MAX_SERVERS_DNS pos_num { + conf->max_servers_dns = $2.pos_num; + } + + | _QSCALE_OFF_MIN pos_decimal { + conf->qscale_off_min = $2.pos_decimal; + } + | _QSCALE_OFF_MAX pos_decimal { + conf->qscale_off_max = $2.pos_decimal; + } + + | _QUERYTIME_MAX pos_num { + conf->querytime_max = $2.pos_num; + } + | _SETTIME_TIMEOUT pos_num { + conf->settime_timeout = $2.pos_num; + } + + | _LOG_NEGLIGIBLE_ADJTIME pos_num { + conf->log_negligible_adjtime = $2.pos_num; + } + | _LOG_NEGLIGIBLE_ADJFREQ pos_decimal { + conf->log_negligible_adjfreq = $2.pos_decimal; + } + + | _FREQUENCY_SAMPLES pos_num { + conf->frequency_samples = $2.pos_num; + } + | _MAX_FREQUENCY_ADJUST pos_decimal { + conf->max_frequency_adjust = $2.pos_decimal; + } + + | _MAX_SEND_ERRORS pos_num { + conf->max_send_errors = $2.pos_num; + } + + | _AUTO_REPLIES pos_num { + conf->auto_replies = $2.pos_num; + } + | _AUTO_THRESHOLD pos_num { + conf->auto_threshold = $2.pos_num; + } + | _INTERVAL_AUTO_DNSFAIL pos_num { + conf->interval_auto_dnsfail = $2.pos_num; + } + | _TRIES_AUTO_DNSFAIL pos_num { + conf->tries_auto_dnsfail = $2.pos_num; + } + + | _FILTER_ADJFREQ STRING { + u_int8_t val; + + if (strcmp("true", $2) == 0) { + val = 0x01; + } else if (strcmp("false", $2) == 0) { + val = 0x00; + } else { + yyerror("option filter_adjfreq expects either 'true' or 'false'"); + YYERROR; + } + + conf->filter_adjfreq = val; + } + + | _SENSOR_DATA_MAXAGE pos_num { + conf->sensor_data_maxage = $2.pos_num; + } + | _SENSOR_QUERY_INTERVAL pos_num { + conf->sensor_query_interval = $2.pos_num; + } + | _SENSOR_SCAN_INTERVAL pos_num { + conf->sensor_scan_interval = $2.pos_num; + } + + | _SENSOR_DEFAULT_REFID STRING { + conf->sensor_default_refid = $2; + } + + | _CONSTRAINT_ERROR_MARGIN pos_num { + conf->constraint_error_margin = $2.pos_num; + } + | _CONSTRAINT_RETRY_INTERVAL pos_num { + conf->constraint_retry_interval = $2.pos_num; + } + | _CONSTRAINT_SCAN_INTERVAL pos_num { + conf->constraint_scan_interval = $2.pos_num; + } + | _CONSTRAINT_SCAN_TIMEOUT pos_num { + conf->constraint_scan_timeout = $2.pos_num; + } + | _CONSTRAINT_MARGIN pos_num { + conf->constraint_margin = (double)$2.pos_num; + } + | _CONSTRAINT_MAXHEADERLENGTH pos_num { + conf->constraint_maxheaderlength = $2.pos_num; + } + ; + +pos_num : NUMBER { + if ($1 < 0) { + yyerror("must be a positive number"); + YYERROR; + } + $$.pos_num = $1; + } + ; + +pos_decimal : NUMBER_DOUBLE { + if ($1 < 0) { + yyerror("must be a positive decimal number"); + YYERROR; + } + $$.pos_decimal = $1; + } ; address : STRING { @@ -587,8 +803,12 @@ opts_default(void) struct keywords { const char *k_name; int k_val; + const char *k_times; + int k_seen; }; +struct keywords *lookup(char *); + int yyerror(const char *fmt, ...) { @@ -611,37 +831,80 @@ kw_cmp(const void *k, const void *e) return (strcmp(k, ((const struct keywords *)e)->k_name)); } -int +struct keywords * 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}, - { "port", PORT}, - { "query", QUERY}, - { "refid", REFID}, - { "rtable", RTABLE}, - { "sensor", SENSOR}, - { "server", SERVER}, - { "servers", SERVERS}, - { "stratum", STRATUM}, - { "trusted", TRUSTED}, - { "weight", WEIGHT} + /* NOTE: this has to be sorted always! */ + // NOTE: Because dynamic k_seen has been added, const definition is removed + // from this data structure. If you want to keep const, already set k_seen + // value can't be changed from its initial value. + static struct keywords keywords[] = { + { "auto_replies", _AUTO_REPLIES, "single" }, + { "auto_threshold", _AUTO_THRESHOLD, "single" }, + { "constraint", CONSTRAINT, "multiple" }, + { "constraint_error_margin", _CONSTRAINT_ERROR_MARGIN, "single" }, + { "constraint_margin", _CONSTRAINT_MARGIN, "single" }, + { "constraint_maxheaderlength", _CONSTRAINT_MAXHEADERLENGTH, "single" }, + { "constraint_retry_interval", _CONSTRAINT_RETRY_INTERVAL, "single" }, + { "constraint_scan_interval", _CONSTRAINT_SCAN_INTERVAL, "single" }, + { "constraint_scan_timeout", _CONSTRAINT_SCAN_TIMEOUT, "single" }, + { "constraints", CONSTRAINTS, "multiple" }, + { "correction", CORRECTION, "multiple" }, + { "ctlsocket", _CTLSOCKET, "single" }, + { "driftfile", _DRIFTFILE, "single" }, + { "filter_adjfreq", _FILTER_ADJFREQ, "single" }, + { "frequency_samples", _FREQUENCY_SAMPLES, "single" }, + { "from", FROM, "multiple" }, + { "interval_auto_dnsfail", _INTERVAL_AUTO_DNSFAIL, "single" }, + { "interval_query_aggressive", _INTERVAL_QUERY_AGGRESSIVE, "single" }, + { "interval_query_normal", _INTERVAL_QUERY_NORMAL, "single" }, + { "interval_query_pathetic", _INTERVAL_QUERY_PATHETIC, "single" }, + { "interval_query_timeout", _INTERVAL_QUERY_TIMEOUT, "single" }, + { "interval_query_ultra_violence", _INTERVAL_QUERY_ULTRA_VIOLENCE, "single" }, + { "listen", LISTEN, "multiple" }, + { "log_negligible_adjfreq", _LOG_NEGLIGIBLE_ADJFREQ, "single" }, + { "log_negligible_adjtime", _LOG_NEGLIGIBLE_ADJTIME, "single" }, + { "max_frequency_adjust", _MAX_FREQUENCY_ADJUST, "single" }, + { "max_send_errors", _MAX_SEND_ERRORS, "single" }, + { "max_servers_dns", _MAX_SERVERS_DNS, "single" }, + { "ntpd_user", _NTPD_USER, "single" }, + { "on", ON, "multiple" }, + { "port", PORT, "multiple" }, + { "qscale_off_min", _QSCALE_OFF_MIN, "single" }, + { "qscale_off_max", _QSCALE_OFF_MAX, "single" }, + { "query", QUERY, "multiple" }, + { "querytime_max", _QUERYTIME_MAX, "single" }, + { "refid", REFID, "multiple" }, + { "rtable", RTABLE, "multiple" }, + { "sensor", SENSOR, "multiple" }, + { "sensor_data_maxage", _SENSOR_DATA_MAXAGE, "single" }, + { "sensor_default_refid", _SENSOR_DEFAULT_REFID, "single" }, + { "sensor_query_interval", _SENSOR_QUERY_INTERVAL, "single" }, + { "sensor_scan_interval", _SENSOR_SCAN_INTERVAL, "single" }, + { "server", SERVER, "multiple" }, + { "servers", SERVERS, "multiple" }, + { "settime_timeout", _SETTIME_TIMEOUT, "single" }, + { "stratum", STRATUM, "multiple" }, + { "tries_auto_dnsfail", _TRIES_AUTO_DNSFAIL, "single" }, + { "trusted", TRUSTED, "multiple" }, + { "trustlevel_aggressive", _TRUSTLEVEL_AGGRESSIVE, "single" }, + { "trustlevel_badpeer", _TRUSTLEVEL_BADPEER, "single" }, + { "trustlevel_max", _TRUSTLEVEL_MAX, "single" }, + { "trustlevel_pathetic", _TRUSTLEVEL_PATHETIC, "single" }, + { "weight", WEIGHT, "multiple" }, }; - const struct keywords *p; + struct keywords *p; + // Compare supplied character buffer and keywords[0] p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), sizeof(keywords[0]), kw_cmp); - if (p) - return (p->k_val); - else - return (STRING); + /* + * When having non-keyword (i.e. invalid keyword) value, + * we return NULL. Therefore, return value must separately + * be checked if it is used anywhere. + */ + return (p); } #define MAXPUSHBACK 128 @@ -743,8 +1006,9 @@ yylex(void) { u_char buf[8096]; u_char *p; + char *derr; int quotec, next, c; - int token; + struct keywords *token; p = buf; while ((c = lgetc(0)) == ' ' || c == '\t') @@ -805,7 +1069,7 @@ yylex(void) yyerror("string is too long"); return (findeol()); } - } while ((c = lgetc(0)) != EOF && isdigit(c)); + } while ((c = lgetc(0)) != EOF && (isdigit(c) || c == '.')); lungetc(c); if (p == buf + 1 && buf[0] == '-') goto nodigits; @@ -816,10 +1080,23 @@ yylex(void) yylval.v.number = strtonum(buf, LLONG_MIN, LLONG_MAX, &errstr); if (errstr) { + + // Fall back. Check if it is actually a decimal number + yylval.v.decimal = strtod(buf, &derr); + if (*derr != 0) { + // Fall back. Assume it is actually a string (e.g. IP address) + yylval.v.string = strdup(buf); + return (STRING); + // If not a string, syntax error is returned in further checks + } + return (NUMBER_DOUBLE); + /* yyerror("\"%s\" invalid number: %s", buf, errstr); return (findeol()); + */ } + return (NUMBER); } else { nodigits: @@ -847,11 +1124,47 @@ nodigits: } 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("can't allocate memory for buffered string"); - return (token); + + /* + * When having *non-null* value for token, we have a validated + * keyword argument (integer) in place. In other cases, + * we assume integer value of STRING, generated dynamically + * by Bison yyParser. This STRING integer value is used for + * further argument checks where ever STRING keyworded + * arguments are used. + * Furthermore, we limit count of arguments which are meant + * to be defined only once by a user. This is defined by + * values of token->k_seen and token->k_times individually + * for each supplied argument. + */ + if ((token = lookup(buf)) != NULL) { + + if (!token->k_seen) + token->k_seen = 0; + + if (strcmp("multiple", token->k_times) == 0) { + return (token->k_val); + + } else if ((strcmp("single", token->k_times) == 0) && + token->k_seen == 0) { + token->k_seen = 1; + return (token->k_val); + + } else { + yyerror("option %s is already set", token->k_name); + return (c); + } + + } else { + if ((yylval.v.string = strdup(buf)) == NULL) { + fatal("can't duplicate memory for buffered string"); + } else { + return STRING; + } + } + } + if (c == '\n') { yylval.lineno = file->lineno; file->lineno++; @@ -902,12 +1215,193 @@ popfile(void) return (file ? 0 : EOF); } +void +checkvalues(const double a, char *aa, const double b, char *bb) +{ + if (a <= b) + fatalx("error in configuration values: %s can't be greater or equal than %s", bb, aa); +} + +// NTP Configuration defaults +void +init_conf(struct ntpd_conf *conf) +{ + conf->ntpd_user = NTPD_USER; // _ntp; + + conf->driftfile = DRIFTFILE; // /var/db/ntpd.drift; + conf->ctlsocket = CTLSOCKET; // /var/run/ntpd.sock; + + conf->interval_query_normal = INTERVAL_QUERY_NORMAL; // 30; + conf->interval_query_pathetic = INTERVAL_QUERY_PATHETIC; // 60; + conf->interval_query_aggressive = INTERVAL_QUERY_AGGRESSIVE; // 5; + conf->interval_query_ultra_violence = INTERVAL_QUERY_ULTRA_VIOLENCE; // 1; + + conf->interval_query_timeout = INTERVAL_QUERY_TIMEOUT; // 300; + + conf->trustlevel_badpeer = TRUSTLEVEL_BADPEER; // 6; + conf->trustlevel_pathetic = TRUSTLEVEL_PATHETIC; // 2; + conf->trustlevel_aggressive = TRUSTLEVEL_AGGRESSIVE; // 8; + conf->trustlevel_max = TRUSTLEVEL_MAX; // 10; + + /* maximum number of servers from DNS query */ + conf->max_servers_dns = MAX_SERVERS_DNS; // 8; + + conf->qscale_off_min = QSCALE_OFF_MIN; // 0.001; + conf->qscale_off_max = QSCALE_OFF_MAX; // 0.050; + + /* single query might take n secs max */ + conf->querytime_max = QUERYTIME_MAX; // 15; + /* max seconds to wait with -s */ + conf->settime_timeout = SETTIME_TIMEOUT; // 100; + + /* negligible drift to not log (ms) */ + conf->log_negligible_adjtime = LOG_NEGLIGIBLE_ADJTIME; // 32; + + /* negligible rate to not log (ppm) */ + conf->log_negligible_adjfreq = LOG_NEGLIGIBLE_ADJFREQ; // 0.05; + + /* samples for est. of permanent drift */ + conf->frequency_samples = FREQUENCY_SAMPLES; // 8; + + /* max correction per iteration */ + conf->max_frequency_adjust = MAX_FREQUENCY_ADJUST; // 128e-5; + + /* max send errors before reconnect */ + conf->max_send_errors = MAX_SEND_ERRORS; // 3; + + /* set after doing adjfreq */ + conf->filter_adjfreq = FILTER_ADJFREQ; // 0x01; + + /* # of ntp replies we want for auto */ + conf->auto_replies = AUTO_REPLIES; // 4; + + /* dont bother auto setting < this */ + conf->auto_threshold = AUTO_THRESHOLD; // 60; + + /* DNS tmpfail interval for auto */ + conf->interval_auto_dnsfail = INTERVAL_AUTO_DNSFAIL; // 1; + + /* DNS tmpfail quick retries */ + conf->tries_auto_dnsfail = TRIES_AUTO_DNSFAIL; // 4; + + conf->sensor_data_maxage = SENSOR_DATA_MAXAGE; // 15*60; + conf->sensor_query_interval = SENSOR_QUERY_INTERVAL; // 15; + conf->sensor_scan_interval = SENSOR_SCAN_INTERVAL; // 1*60; + + conf->sensor_default_refid = SENSOR_DEFAULT_REFID; // "HARD"; + + conf->constraint_error_margin = CONSTRAINT_ERROR_MARGIN; // 4; + conf->constraint_retry_interval = CONSTRAINT_RETRY_INTERVAL; // 15; + conf->constraint_scan_interval = CONSTRAINT_SCAN_INTERVAL; // 15*60; + conf->constraint_scan_timeout = CONSTRAINT_SCAN_TIMEOUT; // 10; + conf->constraint_margin = CONSTRAINT_MARGIN; // 2.0*60; + + conf->constraint_maxheaderlength = CONSTRAINT_MAXHEADERLENGTH; // 8192; +} + +void +print_conf(struct ntpd_conf *lconf) +{ + char* boolean[5]; + struct constraint *cstr; + struct ntp_conf_sensor *sens; + struct ntp_peer *peer; + + fprintf(stdout, "\n"); + fprintf(stdout, "Current configuration:\n\n"); + fprintf(stdout, "NTPd user: %s\n", conf->ntpd_user); + fprintf(stdout, "Drift file: %s\n", conf->driftfile); + fprintf(stdout, "CTL socket file: %s\n", conf->ctlsocket); + fprintf(stdout, "\n"); + fprintf(stdout, "Query interval (normal): %d seconds\n", conf->interval_query_normal); + fprintf(stdout, "Query interval (pathetic): %d seconds\n", conf->interval_query_pathetic); + fprintf(stdout, "Query interval (aggressive): %d seconds\n", conf->interval_query_aggressive); + fprintf(stdout, "Query interval (ultra violent): %d seconds\n", conf->interval_query_ultra_violence); + fprintf(stdout, "\n"); + fprintf(stdout, "Query interval after reply timeout: %d seconds\n", conf->interval_query_timeout); + fprintf(stdout, "\n"); + fprintf(stdout, "Trust level (bad peer): %d\n", conf->trustlevel_badpeer); + fprintf(stdout, "Trust level (pathetic): %d\n", conf->trustlevel_pathetic); + fprintf(stdout, "Trust level (aggressive): %d\n", conf->trustlevel_aggressive); + fprintf(stdout, "Trust level (maximum): %d\n", conf->trustlevel_max); + fprintf(stdout, "\n"); + fprintf(stdout, "Query time (maximum): %d seconds\n", conf->querytime_max); + fprintf(stdout, "Start up timeout in auto mode: %d seconds\n", conf->settime_timeout); + fprintf(stdout, "\n"); + fprintf(stdout, "Maximum number of retrievable servers from a DNS query: %d\n", conf->max_servers_dns); + fprintf(stdout, "\n"); + fprintf(stdout, "Time adjustment minimum scale: %.4f seconds\n", conf->qscale_off_min); + fprintf(stdout, "Time adjustment maximum scale: %.4f seconds\n", conf->qscale_off_max); + fprintf(stdout, "\n"); + fprintf(stdout, "Neglible drift time to not log: %d milliseconds\n", conf->log_negligible_adjtime); + fprintf(stdout, "Neglible frequency rate to not log: %f ppm\n", conf->log_negligible_adjfreq); + fprintf(stdout, "\n"); + fprintf(stdout, "Frequency samples for estimation of permanent drift: %d\n", conf->frequency_samples); + fprintf(stdout, "Maximum frequency correction per iteration: %f\n", conf->max_frequency_adjust); + fprintf(stdout, "\n"); + fprintf(stdout, "Maximum send errors before reconnection: %d\n", conf->max_send_errors); + fprintf(stdout, "\n"); + + if ((conf->filter_adjfreq) == 0x01) + *boolean = "true"; + else if ((conf->filter_adjfreq) == 0x00) + *boolean = "false"; + + fprintf(stdout, "Filter frequency adjustment after maximum frequency correction: %s\n", *boolean); + fprintf(stdout, "\n"); + fprintf(stdout, "NTP replies for auto mode: %d seconds\n", conf->auto_replies); + fprintf(stdout, "Threshold count for auto mode: %d\n", conf->auto_threshold); + fprintf(stdout, "DNS failure interval for auto mode: %d\n", conf->interval_auto_dnsfail); + fprintf(stdout, "DNS retries on failure in auto mode: %d\n", conf->tries_auto_dnsfail); + fprintf(stdout, "\n"); + fprintf(stdout, "Sensor query interval: %d seconds\n", conf->sensor_query_interval); + fprintf(stdout, "Sensor data maximum age: %d seconds\n", conf->sensor_data_maxage); + fprintf(stdout, "Sensor scan interval: %d seconds\n", conf->sensor_scan_interval); + fprintf(stdout, "\n"); + fprintf(stdout, "Sensor default reference ID string: %s\n", conf->sensor_default_refid); + fprintf(stdout, "\n"); + fprintf(stdout, "Constraint error margin: %.2f seconds\n", conf->constraint_error_margin); + fprintf(stdout, "Constraint default margin: %.2f seconds\n", conf->constraint_margin); + fprintf(stdout, "\n"); + fprintf(stdout, "Constraint retry interval: %d seconds\n", conf->constraint_retry_interval); + fprintf(stdout, "Constraint scan interval: %d seconds\n", conf->constraint_scan_interval); + fprintf(stdout, "Constraint scan timeout: %d seconds\n", conf->constraint_scan_timeout); + fprintf(stdout, "Constraint maximum HTTP header length: %d bytes\n", conf->constraint_maxheaderlength); + fprintf(stdout, "\n"); + + TAILQ_FOREACH(sens, &conf->ntp_conf_sensors, entry) { + fprintf(stdout, "Configuration: sensor %s, refid %s stratum %d weight %d\n", + sens->device, + sens->refstr, + sens->stratum, + sens->weight + ); + } + + TAILQ_FOREACH(peer, &conf->ntp_peers, entry) { + fprintf(stdout, "Configuration: NTP server %s on remote UDP port %d\n", + peer->addr_head.name, + peer->addr_head.port + ); + } + + TAILQ_FOREACH(cstr, &conf->constraints, entry) { + fprintf(stdout, "Configuration: HTTPS constraint server %s on remote TCP port %d\n", + cstr->addr_head.name, + cstr->addr_head.port + ); + } + fprintf(stdout, "\n"); +} + int parse_config(const char *filename, struct ntpd_conf *xconf) { int errors = 0; conf = xconf; + init_conf(conf); + TAILQ_INIT(&conf->listen_addrs); TAILQ_INIT(&conf->ntp_peers); TAILQ_INIT(&conf->ntp_conf_sensors); @@ -922,5 +1416,18 @@ parse_config(const char *filename, struc errors = file->errors; popfile(); + // Rough checks for conflicting values + checkvalues(conf->qscale_off_max, "qscale_off_max", + conf->qscale_off_min, "qscale_off_min"); + + checkvalues(conf->trustlevel_max, "trustlevel_max", + conf->trustlevel_aggressive, "trustlevel_aggressive"); + + checkvalues(conf->trustlevel_aggressive, "trustlevel_aggressive", + conf->trustlevel_badpeer, "trustlevel_badpeer"); + + checkvalues(conf->trustlevel_badpeer, "trustlevel_badpeer", + conf->trustlevel_pathetic, "trustlevel_pathetic"); + return (errors ? -1 : 0); } --- a/src/ntpd.conf.5 2020-08-01 01:22:25.000000000 +0300 +++ b/src/ntpd.conf.5 2020-08-03 23:07:12.770476926 +0300 @@ -14,7 +14,7 @@ .\" 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: August 01 2020 $ +.Dd $Mdocdate: August 02 2020 $ .Dt NTPD.CONF 5 .Os .Sh NAME @@ -31,8 +31,10 @@ has the following format: Empty lines and lines beginning with the .Sq # character are ignored. -.Pp -Keywords may be specified multiple times within the configuration file. +.Ed +.El +.Sh BASIC KEYWORDS +Basic 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 @@ -282,12 +284,505 @@ constraints from "https://www.google.com constraints from "https://duckduckgo.com/" port 443 .Ed .El +.Sh ADVANCED KEYWORDS +Some +.Xr ntpd 8 +default configuration values can be overridden, thus offering better +adaption to system policy and flexibility for system administrators. Advanced +keywords may be specified only once within the configuration file. The +following values can be changed from the highlighted defaults: +.Bl -tag -width Ds + +.It Ic auto_replies Ar number +During OpenNTPD initialization, all NTP peers get automatic time offset value, +if pre-conditions for automatic interval adjustment are being met. The +conditions are as follows: OpenNTPD configuration has constraints, +.Ic trusted +NTP peers or +.Ic trusted +sensors and current internally defined +process security level is 0. In this case, initial time offset value +is set to 1 which, in return, triggers automatic offset calculation. +.Pp +In the automatic offset calculation, a +.Ic trusted +NTP peer offset values are being counted for each peer. For each peer an independent +pool size is determined by +.Ic auto_replies +value, ignoring the last value. For instance, with +.Ic auto_replies +value 4, first 3 NTP peer offset values are considered for a single NTP peer, +and a median offset value of these collected 3 offset values is calculated +and used for time adjustment. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +4 +.El +.Ed +.It Ic auto_threshold Ar number +In OpenNTPD initial automatic time offset calculation, three conditions +are being considered for NTP peers: is a NTP peer +.Ic trusted +and current overall constraint-based median offset not 0, and whether an initial NTP +peer time offset exceeds value of +.Ic auto_threshold +\&. If these conditions are met, then +.Ic auto_threshold +value may be considered. If NTP peer current time offset value is less than +.Ic auto_threshold +, then the system time offset value is considered to be already OK, and +OpenNTPD stops calculating automatic offset value from further NTP +peer queries. In this case, median offset value is not calculated. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +60 +.El +.Ed +.It Ic interval_auto_dnsfail Ar seconds +In automatic NTP peer offset calculation mode (during OpenNTPD initialization), +if NTP peer IP address is still unresolved (unknown), the next query is +attempted in +.Ic interval_auto_dnsfail +seconds. Applies to unresolved constraint IP addresses, as well. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +1 +.El +.Ed +.It Ic tries_auto_dnsfail Ar number +Maximum number of attempts to resolve a constraint IP address(es) with a +DNS query before falling back from +.Ic constraint_retry_interval +to +.Ic interval_auto_dnsfail +\& in constraint initialization. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +4 +.El +.Ed +.It Ic constraint_error_margin Ar number +Accepted number of errors during constraint process. If error count exceeds +this value multiplied by calculated peer count, constraint connection will +be reseted and a new constraint is retrieved. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +4 +.El +.Ed +.It Ic constraint_margin Ar seconds +Acceptable time difference between retrieved HTTP header time value and +calculated time value in seconds. HTTP header time values exceeding this +margin value will be ignored. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +120 +.El +.Ed +.It Ic constraint_maxheaderlength Ar length +Maximum allowed HTTP header length of constraint HTTPS server reply to +be fetched in bytes. If the value is exceeded during processing, +nothing is returned and constraint check fails. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +8192 +.El +.Ed +.It Ic constraint_scan_interval Ar seconds +Constraint HTTPS servers scan interval in seconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +900 +.El +.Ed +.It Ic constraint_scan_timeout Ar seconds +Maximum connection establishment time to a constraint HTTPS server in seconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +10 +.El +.Ed +.It Ic ctlsocket Ar path-to-file +.Xr ntpd 8 +socket file path. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +/var/run/ntpd.sock +.El +.Ed +.It Ic driftfile Ar path-to-file +.Xr ntpd 8 +drift file path. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +/var/db/ntpd.drift +.El +.Ed +.It Ic filter_adjfreq Ar true | false +Whether to reset frequency filters after frequency adjustment. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +true +.El +.Ed +.It Ic frequency_samples Ar number +Number of frequency samples for estimating permanent drift value. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +8 +.El +.Ed +.It Ic trustlevel_pathetic Ar number +Initial trust level for a new, timed out or erroneous remote NTP +server. Every received and non-discarded reply increases trust +for the server. The trust level is used for setting used +.Ic interval_query_* +value for the server and keeping track of valid remote NTP servers. +.Pp +A server having this trust level uses remote NTP query interval +value +.Ic interval_query_aggressive +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +2 +.El +.Ed +.It Ic trustlevel_badpeer Ar number +If a replying remote NTP server has trust level one number +less than this value, the server gets trusted. In this case, +the server can achieve maximum trust level +.Ic trustlevel_max +\&. This trust level is preceded by trust level +.Ic trustlevel_pathetic +and followed by trust level +.Ic trustlevel_aggressive +\&. +.Pp +A NTP server having trust level value +.Ic trustlevel_badpeer +, or value greater than +.Ic trustlevel_pathetic +but less than +.Ic trustlevel_aggressive +uses remote NTP query interval value +.Ic interval_query_aggressive +\&. +.Pp +In a case of NTP server reply time out, if the server has at +least trust level value +.Ic trustlevel_badpeer +and the trust level value divided by 2 is less than the +.Ic trustlevel_badpeer +value, the server will be invalidated and falls back to +initial trust level +.Ic trustlevel_pathetic +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +6 +.El +.Ed +.It Ic trustlevel_aggressive Ar number +Aggressive trust level is preceded by trust level +.Ic trustlevel_badpeer +and followed by trust level +.Ic trustlevel_max +\&. If a remote NTP server current trust level is at least value +.Ic trustlevel_pathetic +but less than this value, used remote NTP query interval is +determined by value +.Ic interval_query_aggressive +\&. A server with exact trust level +.Ic trustlevel_aggressive +uses query interval +.Ic interval_query_normal +(see +.Ic trustlevel_max +below). +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +8 +.El +.Ed +.It Ic trustlevel_max Ar number +Maximum trust level follows trust level +.Ic trustlevel_aggressive +\&. This is the maximum trust level which a remote +NTP server can achieve. A server having at least trust level of +.Ic trustlevel_aggressive +uses remote NTP query interval value +.Ic interval_query_normal +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +10 +.El +.Ed +.It Ic interval_query_pathetic Ar seconds +Remote NTP server query interval in seconds for servers with +a trust level value less than +.Ic trustlevel_pathetic +\&. Practically never used. +This value is not the final query interval value but used +in a combination with a dynamic offset scale value, determined by +.Ic qscale_off_min +and +.Ic qscale_off_max +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +60 +.El +.Ed +.It Ic interval_query_ultra_violence Ar seconds +Remote NTP server query interval in seconds for servers +with a trust level value greater than +.Ic trustlevel_pathetic +but less than +.Ic trustlevel_aggressive +in a case where a NTP peer does not still have large enough +pool of already queried offset time values for its offset +time median calculation (checked against value +.Ic auto replies +) or is not +.Ic trusted +, interval value +.Ic interval_query_ultra_violence +may be triggered. Applies only to NTP offset calculation +automatic mode. +.Pp +In most cases, +.Ic interval_query_aggressive +is used instead. Dynamic offset scale value factors +.Ic qscale_off_min +and +.Ic qscale_off_max +are ignored. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +1 +.El +.Ed +.It Ic interval_query_aggressive Ar seconds +Remote NTP server query interval in seconds for servers +with a trust level value greater than +.Ic trustlevel_pathetic +but less than +.Ic trustlevel_aggressive +\&. Applies only, if automatic NTP peer query state is finished. +This value is not the final query interval value but used +in a combination with a dynamic offset scale value, determined by +.Ic qscale_off_min +and +.Ic qscale_off_max +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +5 +.El +.Ed +.It Ic interval_query_normal Ar seconds +Remote NTP server query interval in seconds for servers with +a trust level value between +.Ic trustlevel_aggressive +and +.Ic trustlevel_max +\&. This value is not the final query interval value but used +in a combination with a dynamic offset scale value, determined by +.Ic qscale_off_min +and +.Ic qscale_off_max +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +30 +.El +.Ed +.It Ic interval_query_timeout Ar seconds +Retry time in seconds after failed connection attempt to a remote NTP server. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +300 +.El +.Ed +.It Ic log_negligible_adjfreq Ar ppm +Negligible frequency rate to not log in PPM. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +0.05 +.El +.Ed +.It Ic log_negligible_adjtime Ar milliseconds +Negligible drift time to not log in milliseconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +32 +.El +.Ed +.It Ic max_frequency_adjust Ar decimal +Maximum allowed frequency correction per iteration. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +0.0128 +.El +.Ed +.It Ic max_send_errors Ar number +Maximum number of errors tolerated before reconnecting to a remote NTP server. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +3 +.El +.Ed +.It Ic max_servers_dns Ar number +Maximum number of remote NTP server IP addresses fetched per DNS query. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +8 +.El +.Ed +.It Ic ntpd_user Ar user name +.Xr ntpd 8 +process user name. Group name and working directory are internally fetched by +.Xr getpwnam 3 +\&. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +ntp +.El +.Ed +.It Ic qscale_off_min Ar decimal +Minimum scale value used for dynamically adjusting NTP server query +interval time. If median NTP server & sensor offset value is lower +than this value, then this value is used for scale calculation as +minimum value. Otherwise, the offset value is used as minimum +value. The offset value is a combined median value, based on all +NTP server & sensor offset values. +.Pp +The determined frequency scale is +.Ic qscale_off_max +/ { +.Ic qscale_off_min +OR median offset } +\&. +.Pp +In the end, the calculated scale value is multiplied one of +.Ic interval_query_* +values (pathetic, aggressive, normal) +on a client side, and ultimately used for dynamic adjustment of +client-side NTP server query interval time for +.Xr ntpd 8 +process. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +0.001 +.El +.Ed +.It Ic qscale_off_max Ar decimal +Maximum scale value used for dynamically adjusting NTP server query +interval time. This value is used either with a median NTP server & +sensor offset value, described in +.Ic qscale_off_min +section, or directly with the value +.Ic qscale_off_min +\&. The more detailed description about further use of this value is above +and in +.Ic interval_query_* +sections. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +0.050 +.El +.Ed +.It Ic querytime_max Ar seconds +Maximum time reserved for a single NTP server query in seconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +15 +.El +.Ed +.It Ic sensor_data_maxage Ar seconds +Sensor data maximum valid age in seconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +900 +.El +.Ed +.It Ic sensor_default_refid Ar string +Sensor default reference ID string. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +"HARD" +.El +.Ed +.It Ic sensor_query_interval Ar seconds +Sensor query interval in seconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +15 +.El +.Ed +.It Ic sensor_scan_interval Ar seconds +Scan interval for new sensors in seconds. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +60 +.El +.Ed +.It Ic settime_timeout Ar seconds +Maximum time to wait for a constraint to reply during OpenNTPD initial automatic mode. +.Bd -literal -offset indent +.Bl -tag -width "Default:" -compact +.It Default: +100 +.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 /var/db/ntpd.drift +Default drift file. +.It Pa /var/run/ntpd.sock +Default socket file for communication with +.Xr ntpctl 8 . .It Pa /etc/examples/ntpd.conf Example configuration file. .El --- a/src/client.c 2020-08-03 23:12:14.368300303 +0300 +++ b/src/client.c 2020-08-03 23:09:41.036264088 +0300 @@ -279,7 +279,7 @@ handle_auto(uint8_t trusted, double offs } /* collect some more */ v[count++] = offset; - if (count < AUTO_REPLIES) + if (count < conf->auto_replies) return; /* we have enough */