X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Fntpd.c;h=5b92db6f66a822d3cfff9a2d24e8f59dd257d5f8;hb=11181335f9a97feddb30da4d09f9cd3739b8badd;hp=b0bfe440f28e4fdca8277206494c1b69b0fee21c;hpb=132b044f4b09cd9334718b3f58dbb498ca63f82e;p=oweals%2Fbusybox.git diff --git a/networking/ntpd.c b/networking/ntpd.c index b0bfe440f..5b92db6f6 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -46,6 +46,7 @@ #include "libbb.h" #include #include /* For IPTOS_LOWDELAY definition */ +#include /* setpriority */ #include #ifndef IPTOS_LOWDELAY # define IPTOS_LOWDELAY 0x10 @@ -212,22 +213,22 @@ typedef struct { } 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; @@ -276,6 +277,9 @@ struct globals { 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; @@ -340,7 +344,10 @@ struct globals { 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 @@ -514,23 +521,34 @@ static void 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 @@ -552,24 +570,24 @@ filter_datapoints(peer_t *p) 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; @@ -602,6 +620,32 @@ filter_datapoints(peer_t *p) } 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 | * | --- | @@ -615,13 +659,13 @@ filter_datapoints(peer_t *p) */ 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); } @@ -852,6 +896,11 @@ step_time(double offset) /* 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; @@ -859,11 +908,16 @@ step_time(double offset) //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; } @@ -1339,7 +1393,7 @@ update_local_clock(peer_t *p) 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 */ @@ -1356,10 +1410,6 @@ update_local_clock(peer_t *p) 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: @@ -1436,6 +1486,10 @@ update_local_clock(peer_t *p) } } + 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; @@ -1473,8 +1527,8 @@ update_local_clock(peer_t *p) 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)); @@ -1506,7 +1560,7 @@ update_local_clock(peer_t *p) * 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); @@ -1520,7 +1574,7 @@ update_local_clock(peer_t *p) 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" */ @@ -1580,22 +1634,30 @@ recv_and_process_peer_pkt(peer_t *p) ) { //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 @@ -1604,8 +1666,8 @@ recv_and_process_peer_pkt(peer_t *p) // "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 */ @@ -1664,15 +1726,15 @@ recv_and_process_peer_pkt(peer_t *p) 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 */ @@ -1705,14 +1767,7 @@ recv_and_process_peer_pkt(peer_t *p) * 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; @@ -1758,16 +1813,8 @@ recv_and_process_peer_pkt(peer_t *p) /* 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 @@ -1782,10 +1829,10 @@ recv_and_process_client_pkt(void /*int fd*/) 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) { @@ -1828,7 +1875,7 @@ recv_and_process_client_pkt(void /*int fd*/) /* 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); @@ -1967,11 +2014,11 @@ static NOINLINE void ntp_init(char **argv) 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 */ @@ -2045,8 +2092,8 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv) 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++; } @@ -2092,7 +2139,7 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv) /* 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 @@ -2104,7 +2151,7 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv) 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: