#include "libbb.h"
#include <math.h>
#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
+#include <sys/resource.h> /* setpriority */
#include <sys/timex.h>
#ifndef IPTOS_LOWDELAY
# define IPTOS_LOWDELAY 0x10
} msg_t;
typedef struct {
- double d_recv_time;
double d_offset;
+ double d_recv_time;
double d_dispersion;
} datapoint_t;
typedef struct {
len_and_sockaddr *p_lsa;
char *p_dotted;
- /* when to send new query (if p_fd == -1)
- * or when receive times out (if p_fd >= 0): */
int p_fd;
int datapoint_idx;
uint32_t lastpkt_refid;
uint8_t lastpkt_status;
uint8_t lastpkt_stratum;
uint8_t reachable_bits;
+ /* when to send new query (if p_fd == -1)
+ * or when receive times out (if p_fd >= 0): */
double next_action_time;
double p_xmttime;
double lastpkt_recv_time;
llist_t *ntp_peers;
#if ENABLE_FEATURE_NTPD_SERVER
int listen_fd;
+# define G_listen_fd (G.listen_fd)
+#else
+# define G_listen_fd (-1)
#endif
unsigned verbose;
unsigned peer_cnt;
double last_update_offset; // c.last
double last_update_recv_time; // s.t
double discipline_jitter; // c.jitter
- double offset_to_jitter_ratio;
+ /* Since we only compare it with ints, can simplify code
+ * by not making this variable floating point:
+ */
+ unsigned offset_to_jitter_ratio;
//double cluster_offset; // s.offset
//double cluster_jitter; // s.jitter
#if !USING_KERNEL_PLL_LOOP
filter_datapoints(peer_t *p)
{
int i, idx;
+ double sum, wavg;
+ datapoint_t *fdp;
+
+#if 0
+/* Simulations have shown that use of *averaged* offset for p->filter_offset
+ * is in fact worse than simply using last received one: with large poll intervals
+ * (>= 2048) averaging code uses offset values which are outdated by hours,
+ * and time/frequency correction goes totally wrong when fed essentially bogus offsets.
+ */
int got_newest;
- double minoff, maxoff, wavg, sum, w;
+ double minoff, maxoff, w;
double x = x; /* for compiler */
double oldest_off = oldest_off;
double oldest_age = oldest_age;
double newest_off = newest_off;
double newest_age = newest_age;
- minoff = maxoff = p->filter_datapoint[0].d_offset;
+ fdp = p->filter_datapoint;
+
+ minoff = maxoff = fdp[0].d_offset;
for (i = 1; i < NUM_DATAPOINTS; i++) {
- if (minoff > p->filter_datapoint[i].d_offset)
- minoff = p->filter_datapoint[i].d_offset;
- if (maxoff < p->filter_datapoint[i].d_offset)
- maxoff = p->filter_datapoint[i].d_offset;
+ if (minoff > fdp[i].d_offset)
+ minoff = fdp[i].d_offset;
+ if (maxoff < fdp[i].d_offset)
+ maxoff = fdp[i].d_offset;
}
- idx = p->datapoint_idx; /* most recent datapoint */
+ idx = p->datapoint_idx; /* most recent datapoint's index */
/* Average offset:
* Drop two outliers and take weighted average of the rest:
* most_recent/2 + older1/4 + older2/8 ... + older5/32 + older6/32
VERB4 {
bb_error_msg("datapoint[%d]: off:%f disp:%f(%f) age:%f%s",
i,
- p->filter_datapoint[idx].d_offset,
- p->filter_datapoint[idx].d_dispersion, dispersion(&p->filter_datapoint[idx]),
- G.cur_time - p->filter_datapoint[idx].d_recv_time,
- (minoff == p->filter_datapoint[idx].d_offset || maxoff == p->filter_datapoint[idx].d_offset)
+ fdp[idx].d_offset,
+ fdp[idx].d_dispersion, dispersion(&fdp[idx]),
+ G.cur_time - fdp[idx].d_recv_time,
+ (minoff == fdp[idx].d_offset || maxoff == fdp[idx].d_offset)
? " (outlier by offset)" : ""
);
}
- sum += dispersion(&p->filter_datapoint[idx]) / (2 << i);
+ sum += dispersion(&fdp[idx]) / (2 << i);
- if (minoff == p->filter_datapoint[idx].d_offset) {
+ if (minoff == fdp[idx].d_offset) {
minoff -= 1; /* so that we don't match it ever again */
} else
- if (maxoff == p->filter_datapoint[idx].d_offset) {
+ if (maxoff == fdp[idx].d_offset) {
maxoff += 1;
} else {
- oldest_off = p->filter_datapoint[idx].d_offset;
- oldest_age = G.cur_time - p->filter_datapoint[idx].d_recv_time;
+ oldest_off = fdp[idx].d_offset;
+ oldest_age = G.cur_time - fdp[idx].d_recv_time;
if (!got_newest) {
got_newest = 1;
newest_off = oldest_off;
}
p->filter_offset = wavg;
+#else
+
+ fdp = p->filter_datapoint;
+ idx = p->datapoint_idx; /* most recent datapoint's index */
+
+ /* filter_offset: simply use the most recent value */
+ p->filter_offset = fdp[idx].d_offset;
+
+ /* n-1
+ * --- dispersion(i)
+ * filter_dispersion = \ -------------
+ * / (i+1)
+ * --- 2
+ * i=0
+ */
+ wavg = 0;
+ sum = 0;
+ for (i = 0; i < NUM_DATAPOINTS; i++) {
+ sum += dispersion(&fdp[idx]) / (2 << i);
+ wavg += fdp[idx].d_offset;
+ idx = (idx - 1) & (NUM_DATAPOINTS - 1);
+ }
+ wavg /= NUM_DATAPOINTS;
+ p->filter_dispersion = sum;
+#endif
+
/* +----- -----+ ^ 1/2
* | n-1 |
* | --- |
*/
sum = 0;
for (i = 0; i < NUM_DATAPOINTS; i++) {
- sum += SQUARE(wavg - p->filter_datapoint[i].d_offset);
+ sum += SQUARE(wavg - fdp[i].d_offset);
}
sum = SQRT(sum / NUM_DATAPOINTS);
p->filter_jitter = sum > G_precision_sec ? sum : G_precision_sec;
- VERB3 bb_error_msg("filter offset:%+f(corr:%e) disp:%f jitter:%f",
- p->filter_offset, x,
+ VERB3 bb_error_msg("filter offset:%+f disp:%f jitter:%f",
+ p->filter_offset,
p->filter_dispersion,
p->filter_jitter);
}
/* Correct various fields which contain time-relative values: */
+ /* Globals: */
+ G.cur_time += offset;
+ G.last_update_recv_time += offset;
+ G.last_script_run += offset;
+
/* p->lastpkt_recv_time, p->next_action_time and such: */
for (item = G.ntp_peers; item != NULL; item = item->link) {
peer_t *pp = (peer_t *) item->data;
//bb_error_msg("offset:%+f pp->next_action_time:%f -> %f",
// offset, pp->next_action_time, pp->next_action_time + offset);
pp->next_action_time += offset;
+ if (pp->p_fd >= 0) {
+ /* We wait for reply from this peer too.
+ * But due to step we are doing, reply's data is no longer
+ * useful (in fact, it'll be bogus). Stop waiting for it.
+ */
+ close(pp->p_fd);
+ pp->p_fd = -1;
+ set_next(pp, RETRY_INTERVAL);
+ }
}
- /* Globals: */
- G.cur_time += offset;
- G.last_update_recv_time += offset;
- G.last_script_run += offset;
}
return 1; /* "ok to increase poll interval" */
}
#endif
- offset = 0;
+ abs_offset = offset = 0;
set_new_values(STATE_SYNC, offset, recv_time);
} else { /* abs_offset <= STEP_THRESHOLD */
etemp = SQUARE(G.discipline_jitter);
dtemp = SQUARE(offset - G.last_update_offset);
G.discipline_jitter = SQRT(etemp + (dtemp - etemp) / AVG);
- if (G.discipline_jitter < G_precision_sec)
- G.discipline_jitter = G_precision_sec;
- G.offset_to_jitter_ratio = fabs(offset) / G.discipline_jitter;
- VERB3 bb_error_msg("discipline jitter=%f", G.discipline_jitter);
switch (G.discipline_state) {
case STATE_NSET:
}
}
+ if (G.discipline_jitter < G_precision_sec)
+ G.discipline_jitter = G_precision_sec;
+ G.offset_to_jitter_ratio = abs_offset / G.discipline_jitter;
+
G.reftime = G.cur_time;
G.ntp_status = p->lastpkt_status;
G.refid = p->lastpkt_refid;
memset(&tmx, 0, sizeof(tmx));
if (adjtimex(&tmx) < 0)
bb_perror_msg_and_die("adjtimex");
- VERB3 bb_error_msg("p adjtimex freq:%ld offset:%+ld constant:%ld status:0x%x",
- tmx.freq, tmx.offset, tmx.constant, tmx.status);
+ bb_error_msg("p adjtimex freq:%ld offset:%+ld status:0x%x tc:%ld",
+ tmx.freq, tmx.offset, tmx.status, tmx.constant);
}
memset(&tmx, 0, sizeof(tmx));
* To be on a safe side, let's do it only if offset is significantly
* larger than jitter.
*/
- if (tmx.constant > 0 && G.offset_to_jitter_ratio > TIMECONST_HACK_GATE)
+ if (tmx.constant > 0 && G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE)
tmx.constant--;
//tmx.esterror = (uint32_t)(clock_jitter * 1e6);
VERB3 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld status:0x%x",
rc, tmx.freq, tmx.offset, tmx.status);
G.kernel_freq_drift = tmx.freq / 65536;
- VERB2 bb_error_msg("update peer:%s, offset:%+f, jitter:%f, clock drift:%+.3f ppm, tc:%d",
+ VERB2 bb_error_msg("update from:%s offset:%+f jitter:%f clock drift:%+.3fppm tc:%d",
p->p_dotted, offset, G.discipline_jitter, (double)tmx.freq / 65536, (int)tmx.constant);
return 1; /* "ok to increase poll interval" */
) {
//TODO: always do this?
interval = retry_interval();
- goto set_next_and_close_sock;
+ goto set_next_and_ret;
}
xfunc_die();
}
if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
bb_error_msg("malformed packet received from %s", p->p_dotted);
- goto bail;
+ return;
}
if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl
|| msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl
) {
- goto bail;
+ /* Somebody else's packet */
+ return;
}
+ /* We do not expect any more packets from this peer for now.
+ * Closing the socket informs kernel about it.
+ * We open a new socket when we send a new query.
+ */
+ close(p->p_fd);
+ p->p_fd = -1;
+
if ((msg.m_status & LI_ALARM) == LI_ALARM
|| msg.m_stratum == 0
|| msg.m_stratum > NTP_MAXSTRATUM
// "DENY", "RSTR" - peer does not like us at all
// "RATE" - peer is overloaded, reduce polling freq
interval = poll_interval(0);
- bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval);
- goto set_next_and_close_sock;
+ bb_error_msg("reply from %s: peer is unsynced, next query in %us", p->p_dotted, interval);
+ goto set_next_and_ret;
}
// /* Verify valid root distance */
p->reachable_bits |= 1;
if ((MAX_VERBOSE && G.verbose) || (option_mask32 & OPT_w)) {
- bb_error_msg("reply from %s: reach 0x%02x offset %+f delay %f status 0x%02x strat %d refid 0x%08x rootdelay %f",
+ bb_error_msg("reply from %s: offset:%+f delay:%f status:0x%02x strat:%d refid:0x%08x rootdelay:%f reach:0x%02x",
p->p_dotted,
- p->reachable_bits,
datapoint->d_offset,
p->lastpkt_delay,
p->lastpkt_status,
p->lastpkt_stratum,
p->lastpkt_refid,
- p->lastpkt_rootdelay
+ p->lastpkt_rootdelay,
+ p->reachable_bits
/* not shown: m_ppoll, m_precision_exp, m_rootdisp,
* m_reftime, m_orgtime, m_rectime, m_xmttime
*/
* is increased, otherwise it is decreased. A bit of hysteresis
* helps calm the dance. Works best using burst mode.
*/
- VERB4 if (rc > 0) {
- bb_error_msg("offset:%+f POLLADJ_GATE*discipline_jitter:%f poll:%s",
- q->filter_offset, POLLADJ_GATE * G.discipline_jitter,
- fabs(q->filter_offset) < POLLADJ_GATE * G.discipline_jitter
- ? "grows" : "falls"
- );
- }
- if (rc > 0 && G.offset_to_jitter_ratio < POLLADJ_GATE) {
+ if (rc > 0 && G.offset_to_jitter_ratio <= POLLADJ_GATE) {
/* was += G.poll_exp but it is a bit
* too optimistic for my taste at high poll_exp's */
G.polladj_count += MINPOLL;
/* Decide when to send new query for this peer */
interval = poll_interval(0);
- set_next_and_close_sock:
+ set_next_and_ret:
set_next(p, interval);
- /* We do not expect any more packets from this peer for now.
- * Closing the socket informs kernel about it.
- * We open a new socket when we send a new query.
- */
- close(p->p_fd);
- p->p_fd = -1;
- bail:
- return;
}
#if ENABLE_FEATURE_NTPD_SERVER
uint8_t query_status;
l_fixedpt_t query_xmttime;
- to = get_sock_lsa(G.listen_fd);
+ to = get_sock_lsa(G_listen_fd);
from = xzalloc(to->len);
- size = recv_from_to(G.listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
+ size = recv_from_to(G_listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
char *addr;
if (size < 0) {
/* We reply from the local address packet was sent to,
* this makes to/from look swapped here: */
- do_sendto(G.listen_fd,
+ do_sendto(G_listen_fd,
/*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
&msg, size);
logmode = LOGMODE_NONE;
}
#if ENABLE_FEATURE_NTPD_SERVER
- G.listen_fd = -1;
+ G_listen_fd = -1;
if (opts & OPT_l) {
- G.listen_fd = create_and_bind_dgram_or_die(NULL, 123);
- socket_want_pktinfo(G.listen_fd);
- setsockopt(G.listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+ G_listen_fd = create_and_bind_dgram_or_die(NULL, 123);
+ socket_want_pktinfo(G_listen_fd);
+ setsockopt(G_listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
}
#endif
/* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
i = 0;
#if ENABLE_FEATURE_NTPD_SERVER
- if (G.listen_fd != -1) {
- pfd[0].fd = G.listen_fd;
+ if (G_listen_fd != -1) {
+ pfd[0].fd = G_listen_fd;
pfd[0].events = POLLIN;
i++;
}
/* Here we may block */
VERB2 {
- if (i > (ENABLE_FEATURE_NTPD_SERVER && G.listen_fd != -1)) {
+ if (i > (ENABLE_FEATURE_NTPD_SERVER && G_listen_fd != -1)) {
/* We wait for at least one reply.
* Poll for it, without wasting time for message.
* Since replies often come under 1 second, this also
if (--timeout <= 0)
goto did_poll;
}
- bb_error_msg("poll %us, sockets:%u, poll interval:%us", timeout, i, 1 << G.poll_exp);
+ bb_error_msg("poll:%us sockets:%u interval:%us", timeout, i, 1 << G.poll_exp);
}
nfds = poll(pfd, i, timeout * 1000);
did_poll: