From 9b20adca4b2b59c791b4b5579857e4f028a969ed Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 17 Jan 2010 02:51:33 +0100 Subject: [PATCH] ntpd: add anti-clock-hopping code function old new delta select_and_cluster 837 950 +113 update_local_clock 759 767 +8 root_distance 61 - -61 Signed-off-by: Denys Vlasenko --- networking/ntpd.c | 78 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/networking/ntpd.c b/networking/ntpd.c index b2cd0a3c0..aca79c95a 100644 --- a/networking/ntpd.c +++ b/networking/ntpd.c @@ -306,13 +306,15 @@ struct globals { uint8_t poll_exp; // s.poll int polladj_count; // c.count long kernel_freq_drift; + peer_t *last_update_peer; double last_update_offset; // c.last double last_update_recv_time; // s.t double discipline_jitter; // c.jitter -//TODO: add s.jitter - grep for it here and see clock_combine() in doc + //double cluster_offset; // s.offset + //double cluster_jitter; // s.jitter #if !USING_KERNEL_PLL_LOOP double discipline_freq_drift; // c.freq -//TODO: conditionally calculate wander? it's used only for logging + /* Maybe conditionally calculate wander? it's used only for logging */ double discipline_wander; // c.wander #endif }; @@ -821,6 +823,7 @@ typedef struct { peer_t *p; int type; double edge; + double opt_rd; /* optimization */ } point_t; static int compare_point_edge(const void *aa, const void *bb) @@ -876,6 +879,7 @@ fit(peer_t *p, double rd) static peer_t* select_and_cluster(void) { + peer_t *p; llist_t *item; int i, j; int size = 3 * G.peer_cnt; @@ -893,10 +897,11 @@ select_and_cluster(void) num_points = 0; item = G.ntp_peers; if (G.initial_poll_complete) while (item != NULL) { - peer_t *p = (peer_t *) item->data; - double rd = root_distance(p); - double offset = p->filter_offset; + double rd, offset; + p = (peer_t *) item->data; + rd = root_distance(p); + offset = p->filter_offset; if (!fit(p, rd)) { item = item->link; continue; @@ -911,14 +916,17 @@ select_and_cluster(void) point[num_points].p = p; point[num_points].type = -1; point[num_points].edge = offset - rd; + point[num_points].opt_rd = rd; num_points++; point[num_points].p = p; point[num_points].type = 0; point[num_points].edge = offset; + point[num_points].opt_rd = rd; num_points++; point[num_points].p = p; point[num_points].type = 1; point[num_points].edge = offset + rd; + point[num_points].opt_rd = rd; num_points++; item = item->link; } @@ -999,14 +1007,12 @@ select_and_cluster(void) */ num_survivors = 0; for (i = 0; i < num_points; i++) { - peer_t *p; - if (point[i].edge < low || point[i].edge > high) continue; p = point[i].p; survivor[num_survivors].p = p; -//TODO: save root_distance in point_t and reuse here? - survivor[num_survivors].metric = MAXDIST * p->lastpkt_stratum + root_distance(p); + /* x.opt_rd == root_distance(p); */ + survivor[num_survivors].metric = MAXDIST * p->lastpkt_stratum + point[i].opt_rd; VERB4 bb_error_msg("survivor[%d] metric:%f peer:%s", num_survivors, survivor[num_survivors].metric, p->p_dotted); num_survivors++; @@ -1050,8 +1056,8 @@ select_and_cluster(void) */ for (i = 0; i < num_survivors; i++) { double selection_jitter_sq; - peer_t *p = survivor[i].p; + p = survivor[i].p; if (i == 0 || p->filter_jitter < min_jitter) min_jitter = p->filter_jitter; @@ -1093,18 +1099,54 @@ select_and_cluster(void) } } + if (0) { + /* Combine the offsets of the clustering algorithm survivors + * using a weighted average with weight determined by the root + * distance. Compute the selection jitter as the weighted RMS + * difference between the first survivor and the remaining + * survivors. In some cases the inherent clock jitter can be + * reduced by not using this algorithm, especially when frequent + * clockhopping is involved. bbox: thus we don't do it. + */ + double x, y, z, w; + y = z = w = 0; + for (i = 0; i < num_survivors; i++) { + p = survivor[i].p; + x = root_distance(p); + y += 1 / x; + z += p->filter_offset / x; + w += SQUARE(p->filter_offset - survivor[0].p->filter_offset) / x; + } + //G.cluster_offset = z / y; + //G.cluster_jitter = SQRT(w / y); + } + /* Pick the best clock. If the old system peer is on the list * and at the same stratum as the first survivor on the list, * then don't do a clock hop. Otherwise, select the first * survivor on the list as the new system peer. */ -//TODO - see clock_combine() + p = survivor[0].p; + if (G.last_update_peer + && G.last_update_peer->lastpkt_stratum <= p->lastpkt_stratum + ) { + /* Starting from 1 is ok here */ + for (i = 1; i < num_survivors; i++) { + if (G.last_update_peer == survivor[i].p) { + VERB4 bb_error_msg("keeping old synced peer"); + p = G.last_update_peer; + goto keep_old; + } + } + } + G.last_update_peer = p; + keep_old: VERB3 bb_error_msg("selected peer %s filter_offset:%f age:%f", - survivor[0].p->p_dotted, - survivor[0].p->filter_offset, - G.cur_time - survivor[0].p->lastpkt_recv_time + p->p_dotted, + p->filter_offset, + G.cur_time - p->lastpkt_recv_time ); - return survivor[0].p; + return p; } @@ -1131,6 +1173,7 @@ update_local_clock(peer_t *p) int rc; long old_tmx_offset; struct timex tmx; + /* Note: can use G.cluster_offset instead: */ double offset = p->filter_offset; double recv_time = p->lastpkt_recv_time; double abs_offset; @@ -1343,7 +1386,7 @@ update_local_clock(peer_t *p) G.ntp_status = p->lastpkt_status; G.refid = p->lastpkt_refid; G.rootdelay = p->lastpkt_rootdelay + p->lastpkt_delay; - dtemp = p->filter_jitter; // SQRT(SQUARE(p->filter_jitter) + SQUARE(s.jitter)); + dtemp = p->filter_jitter; // SQRT(SQUARE(p->filter_jitter) + SQUARE(G.cluster_jitter)); dtemp += MAXD(p->filter_dispersion + FREQ_TOLERANCE * (G.cur_time - p->lastpkt_recv_time) + abs_offset, MINDISP); G.rootdisp = p->lastpkt_rootdisp + dtemp; VERB3 bb_error_msg("updating leap/refid/reftime/rootdisp from peer %s", p->p_dotted); @@ -1433,7 +1476,8 @@ update_local_clock(peer_t *p) } #endif G.kernel_freq_drift = tmx.freq / 65536; - VERB2 bb_error_msg("update offset:%f, clock drift:%ld ppm", G.last_update_offset, G.kernel_freq_drift); + VERB2 bb_error_msg("update peer:%s, offset:%f, clock drift:%ld ppm", + p->p_dotted, G.last_update_offset, G.kernel_freq_drift); return 1; /* "ok to increase poll interval" */ }