]>
Commit | Line | Data |
---|---|---|
88840341 | 1 | /* |
88840341 RC |
2 | chronyd/chronyc - Programs for keeping computer clocks accurate. |
3 | ||
4 | ********************************************************************** | |
6672f045 | 5 | * Copyright (C) Richard P. Curnow 1997-2003 |
5dc86c23 | 6 | * Copyright (C) Miroslav Lichvar 2011-2016, 2018 |
88840341 RC |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of version 2 of the GNU General Public License as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
8e23110a | 19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
88840341 RC |
20 | * |
21 | ********************************************************************** | |
22 | ||
23 | ======================================================================= | |
24 | ||
25 | The routines in this file manage the complete pool of sources that | |
26 | we might be synchronizing to. This includes NTP sources and others | |
27 | (e.g. local reference clocks, eyeball + wristwatch etc). | |
28 | ||
29 | */ | |
30 | ||
da2c8d90 ML |
31 | #include "config.h" |
32 | ||
88840341 RC |
33 | #include "sysincl.h" |
34 | ||
35 | #include "sources.h" | |
36 | #include "sourcestats.h" | |
37 | #include "memory.h" | |
38 | #include "ntp.h" /* For NTP_Leap */ | |
c743ecbf | 39 | #include "ntp_sources.h" |
88840341 RC |
40 | #include "local.h" |
41 | #include "reference.h" | |
42 | #include "util.h" | |
43 | #include "conf.h" | |
44 | #include "logging.h" | |
45 | #include "reports.h" | |
46 | #include "nameserv.h" | |
8aa9eb19 | 47 | #include "sched.h" |
3888b9dc | 48 | #include "regress.h" |
88840341 RC |
49 | |
50 | /* ================================================== */ | |
51 | /* Flag indicating that we are initialised */ | |
52 | static int initialised = 0; | |
53 | ||
54 | /* ================================================== */ | |
55 | /* Structure used to hold info for selecting between sources */ | |
56 | struct SelectInfo { | |
57 | int stratum; | |
58 | int select_ok; | |
68039e0d | 59 | double std_dev; |
88840341 | 60 | double root_distance; |
88840341 RC |
61 | double lo_limit; |
62 | double hi_limit; | |
8f062454 | 63 | double last_sample_ago; |
88840341 RC |
64 | }; |
65 | ||
66 | /* ================================================== */ | |
67 | /* This enum contains the flag values that are used to label | |
68 | each source */ | |
69 | typedef enum { | |
0f8368bc | 70 | SRC_OK, /* OK so far, not a final status! */ |
8f062454 | 71 | SRC_UNSELECTABLE, /* Has noselect option set */ |
0f8368bc | 72 | SRC_BAD_STATS, /* Doesn't have valid stats data */ |
5039f959 | 73 | SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */ |
8bbb8fa0 | 74 | SRC_JITTERY, /* Had std dev larger than allowed maximum */ |
0f8368bc | 75 | SRC_WAITS_STATS, /* Others have bad stats, selection postponed */ |
8f062454 | 76 | SRC_STALE, /* Has older samples than others */ |
5a92dbe7 | 77 | SRC_ORPHAN, /* Has stratum equal or larger than orphan stratum */ |
6e9bfac0 | 78 | SRC_UNTRUSTED, /* Overlaps trusted sources */ |
0f8368bc | 79 | SRC_FALSETICKER, /* Doesn't agree with others */ |
1bb27320 | 80 | SRC_WAITS_SOURCES, /* Not enough sources, selection postponed */ |
0f8368bc ML |
81 | SRC_NONPREFERRED, /* Others have prefer option */ |
82 | SRC_WAITS_UPDATE, /* No updates, selection postponed */ | |
83 | SRC_DISTANT, /* Others have shorter root distance */ | |
84 | SRC_OUTLIER, /* Outlier in clustering (not used yet) */ | |
85 | SRC_UNSELECTED, /* Used for synchronisation, not system peer */ | |
86 | SRC_SELECTED, /* Used for synchronisation, selected as system peer */ | |
88840341 RC |
87 | } SRC_Status; |
88 | ||
89 | /* ================================================== */ | |
90 | /* Define the instance structure used to hold information about each | |
91 | source */ | |
92 | struct SRC_Instance_Record { | |
93 | SST_Stats stats; | |
88840341 | 94 | int index; /* Index back into the array of source */ |
91279a0f ML |
95 | uint32_t ref_id; /* The reference ID of this source |
96 | (i.e. from its IP address, NOT the | |
88840341 | 97 | reference _it_ is sync'd to) */ |
8265ff28 | 98 | IPAddr *ip_addr; /* Its IP address if NTP source */ |
88840341 | 99 | |
8671002b ML |
100 | /* Flag indicating that the source is updating reachability */ |
101 | int active; | |
102 | ||
0c4968ec ML |
103 | /* Reachability register */ |
104 | int reachability; | |
88840341 | 105 | |
4932f9d0 ML |
106 | /* Number of set bits in the reachability register */ |
107 | int reachability_size; | |
1ad22e9a | 108 | |
e930d947 ML |
109 | /* Updates since last reference update */ |
110 | int updates; | |
111 | ||
8d80ce44 | 112 | /* Updates left before allowing combining */ |
0f8368bc | 113 | int distant; |
52272f4d | 114 | |
88840341 RC |
115 | /* Flag indicating the status of the source */ |
116 | SRC_Status status; | |
117 | ||
ac30bb06 ML |
118 | /* Type of the source */ |
119 | SRC_Type type; | |
120 | ||
f924862e | 121 | /* Options used when selecting sources */ |
fa15fb3d | 122 | int sel_options; |
f924862e | 123 | |
db510a95 ML |
124 | /* Score against currently selected source */ |
125 | double sel_score; | |
126 | ||
88840341 | 127 | struct SelectInfo sel_info; |
ff930156 ML |
128 | |
129 | /* Latest leap status */ | |
130 | NTP_Leap leap; | |
4883086f ML |
131 | |
132 | /* Flag indicating the source has a leap second vote */ | |
133 | int leap_vote; | |
88840341 RC |
134 | }; |
135 | ||
136 | /* ================================================== */ | |
137 | /* Structure used to build the sort list for finding falsetickers */ | |
138 | struct Sort_Element { | |
139 | int index; | |
140 | double offset; | |
6f84d2fa ML |
141 | enum { |
142 | LOW = -1, | |
143 | HIGH = 1 | |
144 | } tag; | |
88840341 RC |
145 | }; |
146 | ||
147 | /* ================================================== */ | |
148 | /* Table of sources */ | |
149 | static struct SRC_Instance_Record **sources; | |
150 | static struct Sort_Element *sort_list; | |
151 | static int *sel_sources; | |
152 | static int n_sources; /* Number of sources currently in the table */ | |
153 | static int max_n_sources; /* Capacity of the table */ | |
154 | ||
155 | #define INVALID_SOURCE (-1) | |
156 | static int selected_source_index; /* Which source index is currently | |
157 | selected (set to INVALID_SOURCE | |
158 | if no current valid reference) */ | |
159 | ||
db510a95 ML |
160 | /* Score needed to replace the currently selected source */ |
161 | #define SCORE_LIMIT 10.0 | |
162 | ||
0f8368bc ML |
163 | /* Number of updates needed to reset the distant status */ |
164 | #define DISTANT_PENALTY 32 | |
52272f4d | 165 | |
5039f959 | 166 | static double max_distance; |
8bbb8fa0 | 167 | static double max_jitter; |
db510a95 | 168 | static double reselect_distance; |
9cf08fc7 | 169 | static double stratum_weight; |
52272f4d | 170 | static double combine_limit; |
db510a95 | 171 | |
88840341 RC |
172 | /* ================================================== */ |
173 | /* Forward prototype */ | |
174 | ||
175 | static void | |
d0dfa1de | 176 | slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq, |
44c9744d | 177 | double doffset, LCL_ChangeType change_type, void *anything); |
0f70959d ML |
178 | static void |
179 | add_dispersion(double dispersion, void *anything); | |
ac30bb06 ML |
180 | static char * |
181 | source_to_string(SRC_Instance inst); | |
88840341 RC |
182 | |
183 | /* ================================================== */ | |
184 | /* Initialisation function */ | |
185 | void SRC_Initialise(void) { | |
186 | sources = NULL; | |
187 | sort_list = NULL; | |
f6ed7844 | 188 | sel_sources = NULL; |
88840341 RC |
189 | n_sources = 0; |
190 | max_n_sources = 0; | |
191 | selected_source_index = INVALID_SOURCE; | |
5039f959 | 192 | max_distance = CNF_GetMaxDistance(); |
8bbb8fa0 | 193 | max_jitter = CNF_GetMaxJitter(); |
db510a95 | 194 | reselect_distance = CNF_GetReselectDistance(); |
9cf08fc7 | 195 | stratum_weight = CNF_GetStratumWeight(); |
52272f4d | 196 | combine_limit = CNF_GetCombineLimit(); |
88840341 RC |
197 | initialised = 1; |
198 | ||
199 | LCL_AddParameterChangeHandler(slew_sources, NULL); | |
0f70959d | 200 | LCL_AddDispersionNotifyHandler(add_dispersion, NULL); |
88840341 RC |
201 | } |
202 | ||
203 | /* ================================================== */ | |
204 | /* Finalisation function */ | |
205 | void SRC_Finalise(void) | |
206 | { | |
207 | LCL_RemoveParameterChangeHandler(slew_sources, NULL); | |
0f70959d | 208 | LCL_RemoveDispersionNotifyHandler(add_dispersion, NULL); |
f6ed7844 ML |
209 | |
210 | Free(sources); | |
211 | Free(sort_list); | |
212 | Free(sel_sources); | |
213 | ||
88840341 | 214 | initialised = 0; |
88840341 RC |
215 | } |
216 | ||
217 | /* ================================================== */ | |
218 | /* Function to create a new instance. This would be called by one of | |
219 | the individual source-type instance creation routines. */ | |
220 | ||
b06d74ab ML |
221 | SRC_Instance SRC_CreateNewInstance(uint32_t ref_id, SRC_Type type, int sel_options, |
222 | IPAddr *addr, int min_samples, int max_samples, | |
223 | double min_delay, double asymmetry) | |
88840341 RC |
224 | { |
225 | SRC_Instance result; | |
226 | ||
6b0198c2 | 227 | assert(initialised); |
88840341 | 228 | |
6688f403 ML |
229 | if (min_samples == SRC_DEFAULT_MINSAMPLES) |
230 | min_samples = CNF_GetMinSamples(); | |
231 | if (max_samples == SRC_DEFAULT_MAXSAMPLES) | |
232 | max_samples = CNF_GetMaxSamples(); | |
233 | ||
88840341 | 234 | result = MallocNew(struct SRC_Instance_Record); |
b06d74ab ML |
235 | result->stats = SST_CreateInstance(ref_id, addr, min_samples, max_samples, |
236 | min_delay, asymmetry); | |
88840341 RC |
237 | |
238 | if (n_sources == max_n_sources) { | |
239 | /* Reallocate memory */ | |
b5a85bd2 | 240 | max_n_sources = max_n_sources > 0 ? 2 * max_n_sources : 4; |
88840341 RC |
241 | if (sources) { |
242 | sources = ReallocArray(struct SRC_Instance_Record *, max_n_sources, sources); | |
243 | sort_list = ReallocArray(struct Sort_Element, 3*max_n_sources, sort_list); | |
244 | sel_sources = ReallocArray(int, max_n_sources, sel_sources); | |
245 | } else { | |
246 | sources = MallocArray(struct SRC_Instance_Record *, max_n_sources); | |
247 | sort_list = MallocArray(struct Sort_Element, 3*max_n_sources); | |
248 | sel_sources = MallocArray(int, max_n_sources); | |
249 | } | |
250 | } | |
251 | ||
252 | sources[n_sources] = result; | |
42dd5caa | 253 | |
88840341 | 254 | result->index = n_sources; |
ac30bb06 | 255 | result->type = type; |
fa15fb3d | 256 | result->sel_options = sel_options; |
1e727c44 | 257 | result->active = 0; |
88840341 | 258 | |
42dd5caa ML |
259 | SRC_SetRefid(result, ref_id, addr); |
260 | SRC_ResetInstance(result); | |
261 | ||
88840341 RC |
262 | n_sources++; |
263 | ||
264 | return result; | |
265 | } | |
266 | ||
267 | /* ================================================== */ | |
268 | /* Function to get rid of a source when it is being unconfigured. | |
269 | This may cause the current reference source to be reselected, if this | |
270 | was the reference source or contributed significantly to a | |
271 | falseticker decision. */ | |
272 | ||
273 | void SRC_DestroyInstance(SRC_Instance instance) | |
274 | { | |
275 | int dead_index, i; | |
276 | ||
6b0198c2 | 277 | assert(initialised); |
88840341 | 278 | |
88840341 RC |
279 | SST_DeleteInstance(instance->stats); |
280 | dead_index = instance->index; | |
281 | for (i=dead_index; i<n_sources-1; i++) { | |
282 | sources[i] = sources[i+1]; | |
283 | sources[i]->index = i; | |
284 | } | |
285 | --n_sources; | |
286 | Free(instance); | |
287 | ||
63af4889 ML |
288 | /* If this was the previous reference source, we have to reselect! */ |
289 | if (selected_source_index == dead_index) | |
290 | SRC_ReselectSource(); | |
291 | else if (selected_source_index > dead_index) | |
88840341 | 292 | --selected_source_index; |
88840341 RC |
293 | } |
294 | ||
42dd5caa ML |
295 | /* ================================================== */ |
296 | ||
297 | void | |
298 | SRC_ResetInstance(SRC_Instance instance) | |
299 | { | |
42dd5caa ML |
300 | instance->updates = 0; |
301 | instance->reachability = 0; | |
302 | instance->reachability_size = 0; | |
303 | instance->distant = 0; | |
304 | instance->status = SRC_BAD_STATS; | |
305 | instance->sel_score = 1.0; | |
ff930156 | 306 | instance->leap = LEAP_Unsynchronised; |
4883086f | 307 | instance->leap_vote = 0; |
42dd5caa ML |
308 | |
309 | SST_ResetInstance(instance->stats); | |
310 | } | |
311 | ||
312 | /* ================================================== */ | |
313 | ||
314 | void | |
315 | SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr) | |
316 | { | |
317 | instance->ref_id = ref_id; | |
318 | instance->ip_addr = addr; | |
319 | SST_SetRefid(instance->stats, ref_id, addr); | |
320 | } | |
321 | ||
88840341 | 322 | /* ================================================== */ |
1045adaa ML |
323 | |
324 | SST_Stats | |
325 | SRC_GetSourcestats(SRC_Instance instance) | |
88840341 | 326 | { |
6b0198c2 | 327 | assert(initialised); |
1045adaa | 328 | return instance->stats; |
88840341 RC |
329 | } |
330 | ||
331 | /* ================================================== */ | |
332 | ||
4883086f ML |
333 | static NTP_Leap |
334 | get_leap_status(void) | |
335 | { | |
336 | int i, leap_votes, leap_ins, leap_del; | |
337 | ||
338 | /* Accept a leap second if more than half of the sources with a vote agree */ | |
339 | ||
340 | for (i = leap_ins = leap_del = leap_votes = 0; i < n_sources; i++) { | |
341 | if (!sources[i]->leap_vote) | |
342 | continue; | |
343 | ||
344 | leap_votes++; | |
345 | if (sources[i]->leap == LEAP_InsertSecond) | |
346 | leap_ins++; | |
347 | else if (sources[i]->leap == LEAP_DeleteSecond) | |
348 | leap_del++; | |
349 | } | |
350 | ||
351 | if (leap_ins > leap_votes / 2) | |
352 | return LEAP_InsertSecond; | |
353 | else if (leap_del > leap_votes / 2) | |
354 | return LEAP_DeleteSecond; | |
355 | else | |
356 | return LEAP_Normal; | |
357 | } | |
358 | ||
359 | /* ================================================== */ | |
360 | ||
2582be87 ML |
361 | void |
362 | SRC_SetLeapStatus(SRC_Instance inst, NTP_Leap leap) | |
363 | { | |
c687224a | 364 | if (REF_IsLeapSecondClose(NULL, 0.0)) |
2582be87 ML |
365 | return; |
366 | ||
367 | inst->leap = leap; | |
4883086f ML |
368 | |
369 | if (inst->leap_vote) | |
370 | REF_UpdateLeapStatus(get_leap_status()); | |
2582be87 ML |
371 | } |
372 | ||
373 | /* ================================================== */ | |
374 | ||
88840341 RC |
375 | /* This function is called by one of the source drivers when it has |
376 | a new sample that is to be accumulated. | |
377 | ||
378 | This function causes the frequency estimation to be re-run for the | |
379 | designated source, and the clock selection procedure to be re-run | |
380 | afterwards. | |
88840341 RC |
381 | */ |
382 | ||
6bef8aa0 ML |
383 | void |
384 | SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample) | |
88840341 RC |
385 | { |
386 | ||
6b0198c2 | 387 | assert(initialised); |
88840341 | 388 | |
f282856c | 389 | DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d", |
6bef8aa0 ML |
390 | source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset, |
391 | sample->root_delay, sample->root_dispersion, sample->stratum); | |
88840341 | 392 | |
c687224a | 393 | if (REF_IsLeapSecondClose(&sample->time, sample->offset)) { |
f282856c | 394 | LOG(LOGS_INFO, "Dropping sample around leap second"); |
0e786f59 ML |
395 | return; |
396 | } | |
397 | ||
6bef8aa0 | 398 | SST_AccumulateSample(inst->stats, sample); |
88840341 | 399 | SST_DoNewRegression(inst->stats); |
88840341 RC |
400 | } |
401 | ||
402 | /* ================================================== */ | |
403 | ||
8671002b ML |
404 | void |
405 | SRC_SetActive(SRC_Instance inst) | |
406 | { | |
407 | inst->active = 1; | |
408 | } | |
409 | ||
410 | /* ================================================== */ | |
411 | ||
412 | void | |
413 | SRC_UnsetActive(SRC_Instance inst) | |
414 | { | |
415 | inst->active = 0; | |
416 | } | |
417 | ||
418 | /* ================================================== */ | |
419 | ||
3888b9dc ML |
420 | static int |
421 | special_mode_end(void) | |
422 | { | |
423 | int i; | |
424 | ||
425 | for (i = 0; i < n_sources; i++) { | |
7fa22d4c ML |
426 | /* No updates from inactive sources */ |
427 | if (!sources[i]->active) | |
428 | continue; | |
429 | ||
3888b9dc ML |
430 | /* Don't expect more updates than from an offline iburst NTP source */ |
431 | if (sources[i]->reachability_size >= SOURCE_REACH_BITS - 1) | |
432 | continue; | |
433 | ||
434 | /* Check if the source could still have enough samples to be selectable */ | |
435 | if (SOURCE_REACH_BITS - 1 - sources[i]->reachability_size + | |
1045adaa | 436 | SST_Samples(sources[i]->stats) >= MIN_SAMPLES_FOR_REGRESS) |
3888b9dc ML |
437 | return 0; |
438 | } | |
439 | ||
440 | return 1; | |
441 | } | |
442 | ||
0c4968ec ML |
443 | void |
444 | SRC_UpdateReachability(SRC_Instance inst, int reachable) | |
445 | { | |
446 | inst->reachability <<= 1; | |
447 | inst->reachability |= !!reachable; | |
8e71a461 | 448 | inst->reachability %= 1U << SOURCE_REACH_BITS; |
0c4968ec | 449 | |
70928dba | 450 | if (inst->reachability_size < SOURCE_REACH_BITS) |
4932f9d0 | 451 | inst->reachability_size++; |
1ad22e9a | 452 | |
0c4968ec ML |
453 | if (!reachable && inst->index == selected_source_index) { |
454 | /* Try to select a better source */ | |
0094128c | 455 | SRC_SelectSource(NULL); |
0c4968ec | 456 | } |
7fda9c67 | 457 | |
3888b9dc ML |
458 | /* Check if special reference update mode failed */ |
459 | if (REF_GetMode() != REF_ModeNormal && special_mode_end()) { | |
7fda9c67 ML |
460 | REF_SetUnsynchronised(); |
461 | } | |
c743ecbf | 462 | |
d70e815e | 463 | /* Try to replace NTP sources that are unreachable, falsetickers, or |
4432f29b | 464 | have root distance or jitter larger than the allowed maximums */ |
d70e815e ML |
465 | if (inst->type == SRC_NTP && |
466 | ((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) || | |
4432f29b ML |
467 | inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY || |
468 | inst->status == SRC_FALSETICKER)) { | |
c743ecbf ML |
469 | NSR_HandleBadSource(inst->ip_addr); |
470 | } | |
0c4968ec ML |
471 | } |
472 | ||
473 | /* ================================================== */ | |
474 | ||
475 | void | |
476 | SRC_ResetReachability(SRC_Instance inst) | |
477 | { | |
478 | inst->reachability = 0; | |
4932f9d0 | 479 | inst->reachability_size = 0; |
0c4968ec ML |
480 | SRC_UpdateReachability(inst, 0); |
481 | } | |
482 | ||
483 | /* ================================================== */ | |
484 | ||
610284dc ML |
485 | static void |
486 | log_selection_message(char *format, char *arg) | |
487 | { | |
488 | if (REF_GetMode() != REF_ModeNormal) | |
489 | return; | |
f282856c | 490 | LOG(LOGS_INFO, format, arg); |
610284dc ML |
491 | } |
492 | ||
493 | /* ================================================== */ | |
494 | ||
88840341 RC |
495 | static int |
496 | compare_sort_elements(const void *a, const void *b) | |
497 | { | |
498 | const struct Sort_Element *u = (const struct Sort_Element *) a; | |
499 | const struct Sort_Element *v = (const struct Sort_Element *) b; | |
500 | ||
501 | if (u->offset < v->offset) { | |
502 | return -1; | |
503 | } else if (u->offset > v->offset) { | |
504 | return +1; | |
505 | } else if (u->tag < v->tag) { | |
506 | return -1; | |
507 | } else if (u->tag > v->tag) { | |
508 | return +1; | |
509 | } else { | |
510 | return 0; | |
511 | } | |
512 | } | |
513 | ||
ac30bb06 ML |
514 | /* ================================================== */ |
515 | ||
516 | static char * | |
517 | source_to_string(SRC_Instance inst) | |
518 | { | |
519 | switch (inst->type) { | |
520 | case SRC_NTP: | |
8265ff28 | 521 | return UTI_IPToString(inst->ip_addr); |
ac30bb06 ML |
522 | case SRC_REFCLOCK: |
523 | return UTI_RefidToString(inst->ref_id); | |
524 | default: | |
6b0198c2 | 525 | assert(0); |
ac30bb06 ML |
526 | } |
527 | return NULL; | |
528 | } | |
529 | ||
18a66a2b ML |
530 | /* ================================================== */ |
531 | ||
0f8368bc ML |
532 | static void |
533 | mark_ok_sources(SRC_Status status) | |
534 | { | |
535 | int i; | |
536 | ||
537 | for (i = 0; i < n_sources; i++) { | |
538 | if (sources[i]->status != SRC_OK) | |
539 | continue; | |
540 | sources[i]->status = status; | |
541 | } | |
542 | } | |
543 | ||
544 | /* ================================================== */ | |
545 | ||
6fa11a85 | 546 | static int |
d0dfa1de | 547 | combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset, |
ca73e34f | 548 | double *offset_sd, double *frequency, double *frequency_sd, double *skew) |
18a66a2b | 549 | { |
d0dfa1de | 550 | struct timespec src_ref_time; |
cca2ef46 | 551 | double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew; |
05278c3b | 552 | double src_root_delay, src_root_dispersion, sel_src_distance, elapsed; |
707b857b | 553 | double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd; |
ca73e34f ML |
554 | double frequency_weight, sum_frequency_weight, sum_frequency; |
555 | double inv_sum2_frequency_sd, inv_sum2_skew; | |
6fa11a85 ML |
556 | int i, index, combined; |
557 | ||
558 | if (n_sel_sources == 1) | |
559 | return 1; | |
18a66a2b | 560 | |
707b857b | 561 | sum_offset_weight = sum_offset = sum2_offset_sd = 0.0; |
ca73e34f | 562 | sum_frequency_weight = sum_frequency = inv_sum2_frequency_sd = inv_sum2_skew = 0.0; |
18a66a2b | 563 | |
05278c3b ML |
564 | sel_src_distance = sources[selected_source_index]->sel_info.root_distance; |
565 | if (sources[selected_source_index]->type == SRC_NTP) | |
566 | sel_src_distance += reselect_distance; | |
567 | ||
6fa11a85 | 568 | for (i = combined = 0; i < n_sel_sources; i++) { |
18a66a2b ML |
569 | index = sel_sources[i]; |
570 | SST_GetTrackingData(sources[index]->stats, &src_ref_time, | |
571 | &src_offset, &src_offset_sd, | |
cca2ef46 | 572 | &src_frequency, &src_frequency_sd, &src_skew, |
18a66a2b ML |
573 | &src_root_delay, &src_root_dispersion); |
574 | ||
52272f4d ML |
575 | /* Don't include this source if its distance is longer than the distance of |
576 | the selected source multiplied by the limit, their estimated frequencies | |
0f8368bc | 577 | are not close, or it was recently marked as distant */ |
52272f4d ML |
578 | |
579 | if (index != selected_source_index && | |
05278c3b | 580 | (sources[index]->sel_info.root_distance > combine_limit * sel_src_distance || |
52272f4d ML |
581 | fabs(*frequency - src_frequency) > |
582 | combine_limit * (*skew + src_skew + LCL_GetMaxClockError()))) { | |
4932f9d0 | 583 | /* Use a smaller penalty in first few updates */ |
0f8368bc ML |
584 | sources[index]->distant = sources[index]->reachability_size >= SOURCE_REACH_BITS ? |
585 | DISTANT_PENALTY : 1; | |
586 | } else if (sources[index]->distant) { | |
587 | sources[index]->distant--; | |
52272f4d ML |
588 | } |
589 | ||
0f8368bc ML |
590 | if (sources[index]->distant) { |
591 | sources[index]->status = SRC_DISTANT; | |
52272f4d | 592 | continue; |
0f8368bc ML |
593 | } |
594 | ||
595 | if (sources[index]->status == SRC_OK) | |
596 | sources[index]->status = SRC_UNSELECTED; | |
52272f4d | 597 | |
cfe706f0 | 598 | elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time); |
18a66a2b | 599 | src_offset += elapsed * src_frequency; |
2240eefb | 600 | src_offset_sd += elapsed * src_frequency_sd; |
707b857b | 601 | offset_weight = 1.0 / sources[index]->sel_info.root_distance; |
0b709ab1 | 602 | frequency_weight = 1.0 / SQUARE(src_frequency_sd); |
18a66a2b | 603 | |
ca73e34f ML |
604 | DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e", |
605 | index, offset_weight, src_offset, src_offset_sd, | |
606 | frequency_weight, src_frequency, src_frequency_sd, src_skew); | |
18a66a2b | 607 | |
707b857b ML |
608 | sum_offset_weight += offset_weight; |
609 | sum_offset += offset_weight * src_offset; | |
0b709ab1 | 610 | sum2_offset_sd += offset_weight * (SQUARE(src_offset_sd) + |
6045023a | 611 | SQUARE(src_offset - *offset)); |
707b857b ML |
612 | |
613 | sum_frequency_weight += frequency_weight; | |
614 | sum_frequency += frequency_weight * src_frequency; | |
0b709ab1 ML |
615 | inv_sum2_frequency_sd += 1.0 / SQUARE(src_frequency_sd); |
616 | inv_sum2_skew += 1.0 / SQUARE(src_skew); | |
6fa11a85 ML |
617 | |
618 | combined++; | |
18a66a2b ML |
619 | } |
620 | ||
707b857b ML |
621 | assert(combined); |
622 | *offset = sum_offset / sum_offset_weight; | |
623 | *offset_sd = sqrt(sum2_offset_sd / sum_offset_weight); | |
624 | *frequency = sum_frequency / sum_frequency_weight; | |
ca73e34f | 625 | *frequency_sd = 1.0 / sqrt(inv_sum2_frequency_sd); |
707b857b | 626 | *skew = 1.0 / sqrt(inv_sum2_skew); |
18a66a2b | 627 | |
ca73e34f ML |
628 | DEBUG_LOG("combined result offset=%e osd=%e freq=%e fsd=%e skew=%e", |
629 | *offset, *offset_sd, *frequency, *frequency_sd, *skew); | |
6fa11a85 ML |
630 | |
631 | return combined; | |
18a66a2b ML |
632 | } |
633 | ||
88840341 RC |
634 | /* ================================================== */ |
635 | /* This function selects the current reference from amongst the pool | |
e930d947 | 636 | of sources we are holding and updates the local reference */ |
88840341 RC |
637 | |
638 | void | |
0094128c | 639 | SRC_SelectSource(SRC_Instance updated_inst) |
88840341 | 640 | { |
6f84d2fa | 641 | struct SelectInfo *si; |
d0dfa1de | 642 | struct timespec now, ref_time; |
5bc9c0d0 ML |
643 | int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source; |
644 | int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach; | |
936f5cb0 ML |
645 | int depth, best_depth, trust_depth, best_trust_depth; |
646 | int combined, stratum, min_stratum, max_score_index; | |
4883086f | 647 | int orphan_stratum, orphan_source; |
cca2ef46 | 648 | double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew; |
c5587b60 | 649 | double src_root_delay, src_root_dispersion; |
6f84d2fa | 650 | double best_lo, best_hi, distance, sel_src_distance, max_score; |
8f062454 | 651 | double first_sample_ago, max_reach_sample_ago; |
6f84d2fa | 652 | NTP_Leap leap_status; |
88840341 | 653 | |
e930d947 ML |
654 | if (updated_inst) |
655 | updated_inst->updates++; | |
656 | ||
88840341 RC |
657 | if (n_sources == 0) { |
658 | /* In this case, we clearly cannot synchronise to anything */ | |
659 | if (selected_source_index != INVALID_SOURCE) { | |
610284dc | 660 | log_selection_message("Can't synchronise: no sources", NULL); |
7f6ed731 | 661 | selected_source_index = INVALID_SOURCE; |
88840341 | 662 | } |
88840341 RC |
663 | return; |
664 | } | |
665 | ||
8aa9eb19 ML |
666 | /* This is accurate enough and cheaper than calling LCL_ReadCookedTime */ |
667 | SCH_GetLastEventTime(&now, NULL, NULL); | |
88840341 RC |
668 | |
669 | /* Step 1 - build intervals about each source */ | |
6f84d2fa | 670 | |
88840341 | 671 | n_endpoints = 0; |
98f79404 | 672 | n_sel_sources = 0; |
05e002cd | 673 | n_badstats_sources = 0; |
e98f76e0 | 674 | sel_req_source = 0; |
05e002cd | 675 | max_sel_reach = max_badstat_reach = 0; |
5bc9c0d0 | 676 | max_sel_reach_size = 0; |
8f062454 | 677 | max_reach_sample_ago = 0.0; |
88840341 | 678 | |
6f84d2fa | 679 | for (i = 0; i < n_sources; i++) { |
0f8368bc ML |
680 | assert(sources[i]->status != SRC_OK); |
681 | ||
e98f76e0 ML |
682 | /* If some sources are specified with the require option, at least one |
683 | of them will have to be selectable in order to update the clock */ | |
684 | if (sources[i]->sel_options & SRC_SELECT_REQUIRE) | |
685 | sel_req_source = 1; | |
686 | ||
8f062454 | 687 | /* Ignore sources which were added with the noselect option */ |
fa15fb3d | 688 | if (sources[i]->sel_options & SRC_SELECT_NOSELECT) { |
8f062454 | 689 | sources[i]->status = SRC_UNSELECTABLE; |
6f84d2fa ML |
690 | continue; |
691 | } | |
88840341 | 692 | |
6f84d2fa | 693 | si = &sources[i]->sel_info; |
ff930156 | 694 | SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, |
6f84d2fa | 695 | &si->lo_limit, &si->hi_limit, &si->root_distance, |
68039e0d | 696 | &si->std_dev, &first_sample_ago, |
8f062454 | 697 | &si->last_sample_ago, &si->select_ok); |
88840341 | 698 | |
6f84d2fa ML |
699 | if (!si->select_ok) { |
700 | ++n_badstats_sources; | |
701 | sources[i]->status = SRC_BAD_STATS; | |
702 | if (max_badstat_reach < sources[i]->reachability) | |
703 | max_badstat_reach = sources[i]->reachability; | |
704 | continue; | |
705 | } | |
88840341 | 706 | |
5308e0a2 ML |
707 | /* Include extra dispersion in the root distance of sources that don't |
708 | have new samples (the last sample is older than span of all samples) */ | |
709 | if (first_sample_ago < 2.0 * si->last_sample_ago) { | |
710 | double extra_disp = LCL_GetMaxClockError() * | |
711 | (2.0 * si->last_sample_ago - first_sample_ago); | |
712 | si->root_distance += extra_disp; | |
713 | si->lo_limit -= extra_disp; | |
714 | si->hi_limit += extra_disp; | |
715 | } | |
716 | ||
5039f959 ML |
717 | /* Require the root distance to be below the allowed maximum */ |
718 | if (si->root_distance > max_distance) { | |
719 | sources[i]->status = SRC_BAD_DISTANCE; | |
720 | continue; | |
721 | } | |
722 | ||
8bbb8fa0 ML |
723 | /* And the same applies for the estimated standard deviation */ |
724 | if (si->std_dev > max_jitter) { | |
725 | sources[i]->status = SRC_JITTERY; | |
726 | continue; | |
727 | } | |
728 | ||
6f84d2fa | 729 | sources[i]->status = SRC_OK; /* For now */ |
4883086f | 730 | sources[i]->leap_vote = 0; |
88840341 | 731 | |
8f062454 ML |
732 | if (sources[i]->reachability && max_reach_sample_ago < first_sample_ago) |
733 | max_reach_sample_ago = first_sample_ago; | |
734 | ||
735 | if (max_sel_reach < sources[i]->reachability) | |
736 | max_sel_reach = sources[i]->reachability; | |
5bc9c0d0 ML |
737 | |
738 | if (max_sel_reach_size < sources[i]->reachability_size) | |
739 | max_sel_reach_size = sources[i]->reachability_size; | |
8f062454 ML |
740 | } |
741 | ||
5a92dbe7 ML |
742 | orphan_stratum = REF_GetOrphanStratum(); |
743 | orphan_source = INVALID_SOURCE; | |
744 | ||
8f062454 ML |
745 | for (i = 0; i < n_sources; i++) { |
746 | if (sources[i]->status != SRC_OK) | |
747 | continue; | |
748 | ||
749 | si = &sources[i]->sel_info; | |
750 | ||
751 | /* Reachability is not a requirement for selection. An unreachable source | |
752 | can still be selected if its newest sample is not older than the oldest | |
753 | sample from reachable sources. */ | |
754 | if (!sources[i]->reachability && max_reach_sample_ago < si->last_sample_ago) { | |
755 | sources[i]->status = SRC_STALE; | |
756 | continue; | |
757 | } | |
758 | ||
5a92dbe7 ML |
759 | /* When the local reference is configured with the orphan option, NTP |
760 | sources that have stratum equal to the configured local stratum are | |
761 | considered to be orphans (i.e. serving local time while not being | |
762 | synchronised with real time) and are excluded from the normal source | |
763 | selection. Sources with stratum larger than the local stratum are | |
764 | considered to be directly on indirectly synchronised to an orphan and | |
765 | are always ignored. | |
766 | ||
767 | If no selectable source is available and all orphan sources have | |
768 | reference IDs larger than the local ID, no source will be selected and | |
769 | the local reference mode will be activated at some point, i.e. this host | |
770 | will become an orphan. Otherwise, the orphan source with the smallest | |
771 | reference ID will be selected. This ensures a group of servers polling | |
772 | each other (with the same orphan configuration) which have no external | |
773 | source can settle down to a state where only one server is serving its | |
774 | local unsychronised time and others are synchronised to it. */ | |
775 | ||
776 | if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) { | |
777 | sources[i]->status = SRC_ORPHAN; | |
778 | ||
26b87b84 | 779 | if (si->stratum == orphan_stratum && sources[i]->reachability && |
5a92dbe7 ML |
780 | (orphan_source == INVALID_SOURCE || |
781 | sources[i]->ref_id < sources[orphan_source]->ref_id)) | |
782 | orphan_source = i; | |
783 | ||
784 | continue; | |
785 | } | |
786 | ||
8f062454 | 787 | ++n_sel_sources; |
5a92dbe7 ML |
788 | } |
789 | ||
790 | /* If no selectable source is available, consider the orphan source */ | |
6accd19e ML |
791 | if (!n_sel_sources && orphan_source != INVALID_SOURCE) { |
792 | uint32_t local_ref_id = NSR_GetLocalRefid(sources[orphan_source]->ip_addr); | |
793 | ||
794 | if (!local_ref_id) { | |
f282856c | 795 | LOG(LOGS_ERR, "Unknown local refid in orphan mode"); |
6accd19e ML |
796 | } else if (sources[orphan_source]->ref_id < local_ref_id) { |
797 | sources[orphan_source]->status = SRC_OK; | |
798 | n_sel_sources = 1; | |
f282856c | 799 | DEBUG_LOG("selecting orphan refid=%"PRIx32, sources[orphan_source]->ref_id); |
6accd19e | 800 | } |
5a92dbe7 ML |
801 | } |
802 | ||
803 | for (i = 0; i < n_sources; i++) { | |
804 | if (sources[i]->status != SRC_OK) | |
805 | continue; | |
806 | ||
807 | si = &sources[i]->sel_info; | |
8f062454 | 808 | |
6f84d2fa ML |
809 | j1 = n_endpoints; |
810 | j2 = j1 + 1; | |
88840341 | 811 | |
6f84d2fa ML |
812 | sort_list[j1].index = i; |
813 | sort_list[j1].offset = si->lo_limit; | |
814 | sort_list[j1].tag = LOW; | |
88840341 | 815 | |
6f84d2fa ML |
816 | sort_list[j2].index = i; |
817 | sort_list[j2].offset = si->hi_limit; | |
818 | sort_list[j2].tag = HIGH; | |
88840341 | 819 | |
6f84d2fa | 820 | n_endpoints += 2; |
88840341 RC |
821 | } |
822 | ||
5bc9c0d0 | 823 | DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f", |
9bc774d6 | 824 | n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach, |
5bc9c0d0 | 825 | (unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago); |
05e002cd ML |
826 | |
827 | /* Wait for the next call if we have no source selected and there is | |
828 | a source with bad stats (has less than 3 samples) with reachability | |
829 | equal to shifted maximum reachability of sources with valid stats. | |
830 | This delays selecting source on start with servers using the same | |
831 | polling interval until they all have valid stats. */ | |
5bc9c0d0 ML |
832 | if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE && |
833 | max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) { | |
0f8368bc | 834 | mark_ok_sources(SRC_WAITS_STATS); |
6f84d2fa | 835 | return; |
0f8368bc | 836 | } |
6f84d2fa ML |
837 | |
838 | if (n_endpoints == 0) { | |
839 | /* No sources provided valid endpoints */ | |
840 | if (selected_source_index != INVALID_SOURCE) { | |
8f062454 | 841 | log_selection_message("Can't synchronise: no selectable sources", NULL); |
6f84d2fa ML |
842 | selected_source_index = INVALID_SOURCE; |
843 | } | |
05e002cd ML |
844 | return; |
845 | } | |
846 | ||
88840341 | 847 | /* Now sort the endpoint list */ |
6f84d2fa ML |
848 | qsort((void *) sort_list, n_endpoints, sizeof(struct Sort_Element), compare_sort_elements); |
849 | ||
850 | /* Now search for the interval which is contained in the most | |
851 | individual source intervals. Any source which overlaps this | |
852 | will be a candidate. | |
853 | ||
854 | If we get a case like | |
855 | ||
856 | <-----------------------> | |
62d61de9 ML |
857 | <--> |
858 | <--> | |
859 | <===========> | |
6f84d2fa ML |
860 | |
861 | we will build the interval as shown with '=', whereas with an extra source we get | |
62d61de9 | 862 | |
6f84d2fa | 863 | <-----------------------> |
62d61de9 ML |
864 | <-------> |
865 | <--> | |
866 | <--> | |
867 | <==> | |
6f84d2fa ML |
868 | |
869 | The first case is just bad luck - we need extra sources to | |
870 | detect the falseticker, so just make an arbitrary choice based | |
871 | on stratum & stability etc. | |
936f5cb0 ML |
872 | |
873 | Intervals from sources specified with the trust option have higher | |
874 | priority in the search. | |
6f84d2fa ML |
875 | */ |
876 | ||
936f5cb0 | 877 | trust_depth = best_trust_depth = 0; |
6f84d2fa ML |
878 | depth = best_depth = 0; |
879 | best_lo = best_hi = 0.0; | |
880 | ||
881 | for (i = 0; i < n_endpoints; i++) { | |
882 | switch (sort_list[i].tag) { | |
883 | case LOW: | |
884 | depth++; | |
936f5cb0 ML |
885 | if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST) |
886 | trust_depth++; | |
887 | if (trust_depth > best_trust_depth || | |
888 | (trust_depth == best_trust_depth && depth > best_depth)) { | |
889 | best_trust_depth = trust_depth; | |
6f84d2fa ML |
890 | best_depth = depth; |
891 | best_lo = sort_list[i].offset; | |
892 | } | |
893 | break; | |
894 | case HIGH: | |
936f5cb0 | 895 | if (trust_depth == best_trust_depth && depth == best_depth) |
6f84d2fa | 896 | best_hi = sort_list[i].offset; |
936f5cb0 ML |
897 | if (sources[sort_list[i].index]->sel_options & SRC_SELECT_TRUST) |
898 | trust_depth--; | |
6f84d2fa ML |
899 | depth--; |
900 | break; | |
901 | default: | |
902 | assert(0); | |
903 | } | |
904 | } | |
88840341 | 905 | |
936f5cb0 ML |
906 | if (best_depth <= n_sel_sources / 2 && !best_trust_depth) { |
907 | /* Could not even get half the reachable sources to agree and there | |
908 | are no trusted sources - clearly we can't synchronise */ | |
6f84d2fa ML |
909 | |
910 | if (selected_source_index != INVALID_SOURCE) { | |
911 | log_selection_message("Can't synchronise: no majority", NULL); | |
912 | REF_SetUnsynchronised(); | |
913 | selected_source_index = INVALID_SOURCE; | |
88840341 RC |
914 | } |
915 | ||
6f84d2fa ML |
916 | /* .. and mark all sources as falsetickers (so they appear thus |
917 | on the outputs from the command client) */ | |
0f8368bc | 918 | mark_ok_sources(SRC_FALSETICKER); |
88840341 | 919 | |
6f84d2fa ML |
920 | return; |
921 | } | |
88840341 | 922 | |
6f84d2fa ML |
923 | /* We have our interval, now work out which source are in it, |
924 | i.e. build list of admissible sources. */ | |
88840341 | 925 | |
6f84d2fa | 926 | n_sel_sources = 0; |
88840341 | 927 | |
6f84d2fa | 928 | for (i = 0; i < n_sources; i++) { |
936f5cb0 ML |
929 | /* This should be the same condition to get into the endpoint |
930 | list */ | |
6f84d2fa ML |
931 | if (sources[i]->status != SRC_OK) |
932 | continue; | |
933 | ||
936f5cb0 ML |
934 | /* Check if source's interval contains the best interval, or is wholly |
935 | contained within it. If there are any trusted sources the first | |
936 | condition is applied only to them to not allow non-trusted sources to | |
937 | move the final offset outside the interval. */ | |
938 | if (((!best_trust_depth || sources[i]->sel_options & SRC_SELECT_TRUST) && | |
939 | sources[i]->sel_info.lo_limit <= best_lo && | |
6f84d2fa ML |
940 | sources[i]->sel_info.hi_limit >= best_hi) || |
941 | (sources[i]->sel_info.lo_limit >= best_lo && | |
942 | sources[i]->sel_info.hi_limit <= best_hi)) { | |
88840341 | 943 | |
6f84d2fa | 944 | sel_sources[n_sel_sources++] = i; |
e98f76e0 ML |
945 | |
946 | if (sources[i]->sel_options & SRC_SELECT_REQUIRE) | |
947 | sel_req_source = 0; | |
6e9bfac0 ML |
948 | } else if (sources[i]->sel_info.lo_limit <= best_lo && |
949 | sources[i]->sel_info.hi_limit >= best_hi) { | |
950 | sources[i]->status = SRC_UNTRUSTED; | |
88840341 | 951 | } else { |
6f84d2fa ML |
952 | sources[i]->status = SRC_FALSETICKER; |
953 | } | |
954 | } | |
88840341 | 955 | |
e98f76e0 | 956 | if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) { |
6f84d2fa | 957 | if (selected_source_index != INVALID_SOURCE) { |
1bb27320 | 958 | log_selection_message("Can't synchronise: %s selectable sources", |
e98f76e0 ML |
959 | !n_sel_sources ? "no" : |
960 | sel_req_source ? "no required source in" : "not enough"); | |
6f84d2fa ML |
961 | selected_source_index = INVALID_SOURCE; |
962 | } | |
1bb27320 | 963 | mark_ok_sources(SRC_WAITS_SOURCES); |
6f84d2fa ML |
964 | return; |
965 | } | |
f924862e | 966 | |
4883086f ML |
967 | /* Enable the selectable sources (and trusted if there are any) to |
968 | vote on leap seconds */ | |
969 | for (i = 0; i < n_sel_sources; i++) { | |
6f84d2fa | 970 | index = sel_sources[i]; |
82c4bfe5 ML |
971 | if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST)) |
972 | continue; | |
4883086f | 973 | sources[index]->leap_vote = 1; |
6f84d2fa | 974 | } |
f924862e | 975 | |
6f84d2fa ML |
976 | /* If there are any sources with prefer option, reduce the list again |
977 | only to the preferred sources */ | |
4253075a | 978 | for (i = 0; i < n_sel_sources; i++) { |
fa15fb3d | 979 | if (sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER) |
4253075a | 980 | break; |
6f84d2fa | 981 | } |
4253075a ML |
982 | if (i < n_sel_sources) { |
983 | for (i = j = 0; i < n_sel_sources; i++) { | |
fa15fb3d | 984 | if (!(sources[sel_sources[i]]->sel_options & SRC_SELECT_PREFER)) |
0f8368bc | 985 | sources[sel_sources[i]]->status = SRC_NONPREFERRED; |
4253075a ML |
986 | else |
987 | sel_sources[j++] = sel_sources[i]; | |
0f8368bc | 988 | } |
4253075a | 989 | assert(j > 0); |
6f84d2fa ML |
990 | n_sel_sources = j; |
991 | sel_prefer = 1; | |
992 | } else { | |
993 | sel_prefer = 0; | |
994 | } | |
88840341 | 995 | |
6f84d2fa | 996 | /* Find minimum stratum */ |
db510a95 | 997 | |
6f84d2fa ML |
998 | index = sel_sources[0]; |
999 | min_stratum = sources[index]->sel_info.stratum; | |
1000 | for (i = 1; i < n_sel_sources; i++) { | |
1001 | index = sel_sources[i]; | |
1002 | stratum = sources[index]->sel_info.stratum; | |
1003 | if (stratum < min_stratum) | |
1004 | min_stratum = stratum; | |
1005 | } | |
9cf08fc7 | 1006 | |
6f84d2fa | 1007 | /* Update scores and find the source with maximum score */ |
db510a95 | 1008 | |
6f84d2fa ML |
1009 | max_score_index = INVALID_SOURCE; |
1010 | max_score = 0.0; | |
1011 | sel_src_distance = 0.0; | |
db510a95 | 1012 | |
6f84d2fa ML |
1013 | if (selected_source_index != INVALID_SOURCE) |
1014 | sel_src_distance = sources[selected_source_index]->sel_info.root_distance + | |
1015 | (sources[selected_source_index]->sel_info.stratum - min_stratum) * stratum_weight; | |
db510a95 | 1016 | |
6f84d2fa ML |
1017 | for (i = 0; i < n_sources; i++) { |
1018 | /* Reset score for non-selectable sources */ | |
0f8368bc | 1019 | if (sources[i]->status != SRC_OK || |
fa15fb3d | 1020 | (sel_prefer && !(sources[i]->sel_options & SRC_SELECT_PREFER))) { |
6f84d2fa | 1021 | sources[i]->sel_score = 1.0; |
0f8368bc | 1022 | sources[i]->distant = DISTANT_PENALTY; |
6f84d2fa ML |
1023 | continue; |
1024 | } | |
db510a95 | 1025 | |
6f84d2fa ML |
1026 | distance = sources[i]->sel_info.root_distance + |
1027 | (sources[i]->sel_info.stratum - min_stratum) * stratum_weight; | |
1028 | if (sources[i]->type == SRC_NTP) | |
1029 | distance += reselect_distance; | |
db510a95 | 1030 | |
6f84d2fa ML |
1031 | if (selected_source_index != INVALID_SOURCE) { |
1032 | /* Update score, but only for source pairs where one source | |
1033 | has a new sample */ | |
1034 | if (sources[i] == updated_inst || | |
1035 | sources[selected_source_index] == updated_inst) { | |
db510a95 | 1036 | |
6f84d2fa | 1037 | sources[i]->sel_score *= sel_src_distance / distance; |
db510a95 | 1038 | |
6f84d2fa ML |
1039 | if (sources[i]->sel_score < 1.0) |
1040 | sources[i]->sel_score = 1.0; | |
1041 | } | |
1042 | } else { | |
1043 | /* When there is no selected source yet, assign scores so that the | |
1044 | source with minimum distance will have maximum score. The scores | |
1045 | will be reset when the source is selected later in this function. */ | |
1046 | sources[i]->sel_score = 1.0 / distance; | |
1047 | } | |
db510a95 | 1048 | |
9bc774d6 | 1049 | DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f", |
6f84d2fa ML |
1050 | sources[i]->sel_score, sources[i]->ref_id, |
1051 | updated_inst ? updated_inst->ref_id : 0, | |
1052 | sources[i]->status, distance); | |
db510a95 | 1053 | |
6f84d2fa ML |
1054 | if (max_score < sources[i]->sel_score) { |
1055 | max_score = sources[i]->sel_score; | |
1056 | max_score_index = i; | |
1057 | } | |
1058 | } | |
352f03d4 | 1059 | |
6f84d2fa | 1060 | assert(max_score_index != INVALID_SOURCE); |
db510a95 | 1061 | |
6f84d2fa ML |
1062 | /* Is the current source still a survivor and no other source has reached |
1063 | the score limit? */ | |
1064 | if (selected_source_index == INVALID_SOURCE || | |
0f8368bc | 1065 | sources[selected_source_index]->status != SRC_OK || |
6f84d2fa | 1066 | (max_score_index != selected_source_index && max_score > SCORE_LIMIT)) { |
88840341 | 1067 | |
5f689412 ML |
1068 | /* Before selecting the new synchronisation source wait until the reference |
1069 | can be updated */ | |
1070 | if (sources[max_score_index]->updates == 0) { | |
1071 | selected_source_index = INVALID_SOURCE; | |
0f8368bc | 1072 | mark_ok_sources(SRC_WAITS_UPDATE); |
f282856c | 1073 | DEBUG_LOG("best source has no updates"); |
5f689412 ML |
1074 | return; |
1075 | } | |
88840341 | 1076 | |
6f84d2fa ML |
1077 | selected_source_index = max_score_index; |
1078 | log_selection_message("Selected source %s", | |
1079 | source_to_string(sources[selected_source_index])); | |
88840341 | 1080 | |
6f84d2fa ML |
1081 | /* New source has been selected, reset all scores */ |
1082 | for (i = 0; i < n_sources; i++) { | |
1083 | sources[i]->sel_score = 1.0; | |
0f8368bc | 1084 | sources[i]->distant = 0; |
88840341 | 1085 | } |
88840341 | 1086 | } |
6f84d2fa | 1087 | |
0f8368bc | 1088 | sources[selected_source_index]->status = SRC_SELECTED; |
6f84d2fa ML |
1089 | |
1090 | /* Don't update reference when the selected source has no new samples */ | |
1091 | ||
0f8368bc ML |
1092 | if (sources[selected_source_index]->updates == 0) { |
1093 | /* Mark the remaining sources as last combine_sources() call */ | |
1094 | ||
1095 | for (i = 0; i < n_sel_sources; i++) { | |
1096 | index = sel_sources[i]; | |
1097 | if (sources[index]->status == SRC_OK) | |
1098 | sources[index]->status = sources[index]->distant ? | |
1099 | SRC_DISTANT : SRC_UNSELECTED; | |
1100 | } | |
6f84d2fa | 1101 | return; |
0f8368bc | 1102 | } |
6f84d2fa ML |
1103 | |
1104 | for (i = 0; i < n_sources; i++) | |
1105 | sources[i]->updates = 0; | |
1106 | ||
4883086f ML |
1107 | leap_status = get_leap_status(); |
1108 | ||
6f84d2fa ML |
1109 | /* Now just use the statistics of the selected source combined with |
1110 | the other selectable sources for trimming the local clock */ | |
1111 | ||
1112 | SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time, | |
1113 | &src_offset, &src_offset_sd, | |
cca2ef46 | 1114 | &src_frequency, &src_frequency_sd, &src_skew, |
6f84d2fa ML |
1115 | &src_root_delay, &src_root_dispersion); |
1116 | ||
ca73e34f ML |
1117 | combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd, |
1118 | &src_frequency, &src_frequency_sd, &src_skew); | |
6f84d2fa ML |
1119 | |
1120 | REF_SetReference(sources[selected_source_index]->sel_info.stratum, | |
1121 | leap_status, combined, | |
1122 | sources[selected_source_index]->ref_id, | |
1123 | sources[selected_source_index]->ip_addr, | |
1124 | &ref_time, src_offset, src_offset_sd, | |
ca73e34f | 1125 | src_frequency, src_frequency_sd, src_skew, |
6f84d2fa | 1126 | src_root_delay, src_root_dispersion); |
88840341 RC |
1127 | } |
1128 | ||
6e96b4ba ML |
1129 | /* ================================================== */ |
1130 | /* Force reselecting the best source */ | |
1131 | ||
1132 | void | |
1133 | SRC_ReselectSource(void) | |
1134 | { | |
1135 | selected_source_index = INVALID_SOURCE; | |
0094128c | 1136 | SRC_SelectSource(NULL); |
6e96b4ba ML |
1137 | } |
1138 | ||
88840341 RC |
1139 | /* ================================================== */ |
1140 | ||
8d3d45ea ML |
1141 | void |
1142 | SRC_SetReselectDistance(double distance) | |
1143 | { | |
1144 | if (reselect_distance != distance) { | |
1145 | reselect_distance = distance; | |
f282856c | 1146 | LOG(LOGS_INFO, "New reselect distance %f", distance); |
8d3d45ea ML |
1147 | } |
1148 | } | |
1149 | ||
88840341 RC |
1150 | /* ================================================== */ |
1151 | /* This routine is registered as a callback with the local clock | |
1152 | module, to be called whenever the local clock changes frequency or | |
1153 | is slewed. It runs through all the existing source statistics, and | |
1154 | adjusts them to make them look as though they were sampled under | |
1155 | the new regime. */ | |
1156 | ||
1157 | static void | |
d0dfa1de ML |
1158 | slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq, |
1159 | double doffset, LCL_ChangeType change_type, void *anything) | |
88840341 RC |
1160 | { |
1161 | int i; | |
1162 | ||
1163 | for (i=0; i<n_sources; i++) { | |
a33a9551 ML |
1164 | if (change_type == LCL_ChangeUnknownStep) { |
1165 | SST_ResetInstance(sources[i]->stats); | |
1166 | } else { | |
1167 | SST_SlewSamples(sources[i]->stats, cooked, dfreq, doffset); | |
1168 | } | |
1169 | } | |
1170 | ||
1171 | if (change_type == LCL_ChangeUnknownStep) { | |
18d7ea62 | 1172 | /* Update selection status */ |
a33a9551 | 1173 | SRC_SelectSource(NULL); |
88840341 | 1174 | } |
88840341 RC |
1175 | } |
1176 | ||
0f70959d ML |
1177 | /* ================================================== */ |
1178 | /* This routine is called when an indeterminate offset is introduced | |
1179 | into the local time. */ | |
1180 | ||
1181 | static void | |
1182 | add_dispersion(double dispersion, void *anything) | |
1183 | { | |
1184 | int i; | |
1185 | ||
1186 | for (i = 0; i < n_sources; i++) { | |
1187 | SST_AddDispersion(sources[i]->stats, dispersion); | |
1188 | } | |
1189 | } | |
1190 | ||
5c45e4cc ML |
1191 | /* ================================================== */ |
1192 | ||
1193 | static | |
e18903a6 | 1194 | FILE *open_dumpfile(SRC_Instance inst, char mode) |
5c45e4cc | 1195 | { |
e18903a6 | 1196 | char filename[64], *dumpdir; |
fb5d4f1d ML |
1197 | |
1198 | dumpdir = CNF_GetDumpDir(); | |
1199 | if (dumpdir[0] == '\0') { | |
f282856c | 1200 | LOG(LOGS_WARN, "dumpdir not specified"); |
fb5d4f1d ML |
1201 | return NULL; |
1202 | } | |
5c45e4cc ML |
1203 | |
1204 | /* Include IP address in the name for NTP sources, or reference ID in hex */ | |
672b98dd | 1205 | if (inst->type == SRC_NTP && UTI_IsIPReal(inst->ip_addr)) |
e18903a6 | 1206 | snprintf(filename, sizeof (filename), "%s", source_to_string(inst)); |
672b98dd | 1207 | else if (inst->type == SRC_REFCLOCK) |
e18903a6 | 1208 | snprintf(filename, sizeof (filename), "refid:%08"PRIx32, inst->ref_id); |
672b98dd ML |
1209 | else |
1210 | return NULL; | |
5c45e4cc | 1211 | |
e18903a6 | 1212 | return UTI_OpenFile(dumpdir, filename, ".dat", mode, 0644); |
5c45e4cc ML |
1213 | } |
1214 | ||
88840341 RC |
1215 | /* ================================================== */ |
1216 | /* This is called to dump out the source measurement registers */ | |
1217 | ||
1218 | void | |
1219 | SRC_DumpSources(void) | |
1220 | { | |
1221 | FILE *out; | |
88840341 | 1222 | int i; |
6d42dd86 ML |
1223 | |
1224 | for (i = 0; i < n_sources; i++) { | |
e18903a6 | 1225 | out = open_dumpfile(sources[i], 'w'); |
5c45e4cc ML |
1226 | if (!out) |
1227 | continue; | |
1228 | SST_SaveToFile(sources[i]->stats, out); | |
1229 | fclose(out); | |
88840341 | 1230 | } |
88840341 RC |
1231 | } |
1232 | ||
1233 | /* ================================================== */ | |
1234 | ||
1235 | void | |
1236 | SRC_ReloadSources(void) | |
1237 | { | |
1238 | FILE *in; | |
88840341 | 1239 | int i; |
88840341 | 1240 | |
5c45e4cc | 1241 | for (i = 0; i < n_sources; i++) { |
e18903a6 | 1242 | in = open_dumpfile(sources[i], 'r'); |
5c45e4cc ML |
1243 | if (!in) |
1244 | continue; | |
1245 | if (!SST_LoadFromFile(sources[i]->stats, in)) | |
f282856c | 1246 | LOG(LOGS_WARN, "Could not load dump file for %s", |
5c45e4cc | 1247 | source_to_string(sources[i])); |
06f93e7b | 1248 | else |
f282856c | 1249 | LOG(LOGS_INFO, "Loaded dump file for %s", |
06f93e7b | 1250 | source_to_string(sources[i])); |
5c45e4cc | 1251 | fclose(in); |
88840341 RC |
1252 | } |
1253 | } | |
1254 | ||
1255 | /* ================================================== */ | |
1256 | ||
a06a5f1b ML |
1257 | void |
1258 | SRC_RemoveDumpFiles(void) | |
1259 | { | |
2fc8edac | 1260 | char pattern[PATH_MAX], name[64], *dumpdir, *s; |
a06a5f1b ML |
1261 | IPAddr ip_addr; |
1262 | glob_t gl; | |
1263 | size_t i; | |
1264 | ||
1265 | dumpdir = CNF_GetDumpDir(); | |
1266 | if (dumpdir[0] == '\0' || | |
1267 | snprintf(pattern, sizeof (pattern), "%s/*.dat", dumpdir) >= sizeof (pattern)) | |
1268 | return; | |
1269 | ||
1270 | if (glob(pattern, 0, NULL, &gl)) | |
1271 | return; | |
1272 | ||
1273 | for (i = 0; i < gl.gl_pathc; i++) { | |
1274 | s = strrchr(gl.gl_pathv[i], '/'); | |
1275 | if (!s || snprintf(name, sizeof (name), "%s", s + 1) >= sizeof (name)) | |
1276 | continue; | |
1277 | ||
1278 | /* Remove .dat extension */ | |
1279 | if (strlen(name) < 4) | |
1280 | continue; | |
1281 | name[strlen(name) - 4] = '\0'; | |
1282 | ||
1283 | /* Check if it looks like name of an actual dump file */ | |
1284 | if (strncmp(name, "refid:", 6) && !UTI_StringToIP(name, &ip_addr)) | |
1285 | continue; | |
1286 | ||
e18903a6 ML |
1287 | if (!UTI_RemoveFile(NULL, gl.gl_pathv[i], NULL)) |
1288 | ; | |
a06a5f1b ML |
1289 | } |
1290 | ||
1291 | globfree(&gl); | |
1292 | } | |
1293 | ||
1294 | /* ================================================== */ | |
1295 | ||
e7a25426 ML |
1296 | void |
1297 | SRC_ResetSources(void) | |
1298 | { | |
1299 | int i; | |
1300 | ||
1301 | for (i = 0; i < n_sources; i++) | |
1302 | SRC_ResetInstance(sources[i]); | |
1303 | } | |
1304 | ||
1305 | /* ================================================== */ | |
1306 | ||
88840341 RC |
1307 | int |
1308 | SRC_IsSyncPeer(SRC_Instance inst) | |
1309 | { | |
1310 | if (inst->index == selected_source_index) { | |
1311 | return 1; | |
1312 | } else { | |
1313 | return 0; | |
1314 | } | |
1315 | ||
1316 | } | |
1317 | ||
1318 | /* ================================================== */ | |
1319 | ||
4e66b5ce ML |
1320 | int |
1321 | SRC_IsReachable(SRC_Instance inst) | |
1322 | { | |
1323 | return inst->reachability != 0; | |
1324 | } | |
1325 | ||
1326 | /* ================================================== */ | |
1327 | ||
88840341 RC |
1328 | int |
1329 | SRC_ReadNumberOfSources(void) | |
1330 | { | |
1331 | return n_sources; | |
1332 | } | |
1333 | ||
1334 | /* ================================================== */ | |
1335 | ||
8671002b ML |
1336 | int |
1337 | SRC_ActiveSources(void) | |
1338 | { | |
1339 | int i, r; | |
1340 | ||
1341 | for (i = r = 0; i < n_sources; i++) | |
1342 | if (sources[i]->active) | |
1343 | r++; | |
1344 | ||
1345 | return r; | |
1346 | } | |
1347 | ||
1348 | /* ================================================== */ | |
1349 | ||
88840341 | 1350 | int |
d0dfa1de | 1351 | SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now) |
88840341 RC |
1352 | { |
1353 | SRC_Instance src; | |
1354 | if ((index >= n_sources) || (index < 0)) { | |
1355 | return 0; | |
1356 | } else { | |
1357 | src = sources[index]; | |
8265ff28 | 1358 | |
8265ff28 ML |
1359 | if (src->ip_addr) |
1360 | report->ip_addr = *src->ip_addr; | |
1361 | else { | |
1362 | /* Use refid as an address */ | |
1363 | report->ip_addr.addr.in4 = src->ref_id; | |
1364 | report->ip_addr.family = IPADDR_INET4; | |
1365 | } | |
1366 | ||
88840341 | 1367 | switch (src->status) { |
88840341 RC |
1368 | case SRC_FALSETICKER: |
1369 | report->state = RPT_FALSETICKER; | |
1370 | break; | |
0f8368bc ML |
1371 | case SRC_JITTERY: |
1372 | report->state = RPT_JITTERY; | |
1373 | break; | |
6e9bfac0 | 1374 | case SRC_UNTRUSTED: |
1bb27320 | 1375 | case SRC_WAITS_SOURCES: |
0f8368bc ML |
1376 | case SRC_NONPREFERRED: |
1377 | case SRC_WAITS_UPDATE: | |
1378 | case SRC_DISTANT: | |
1379 | case SRC_OUTLIER: | |
1380 | report->state = RPT_OUTLIER; | |
88840341 | 1381 | break; |
0f8368bc ML |
1382 | case SRC_UNSELECTED: |
1383 | report->state = RPT_CANDIDATE; | |
1384 | break; | |
1385 | case SRC_SELECTED: | |
1386 | report->state = RPT_SYNC; | |
1387 | break; | |
2e74beeb | 1388 | default: |
316d50d6 | 1389 | report->state = RPT_UNREACH; |
2e74beeb | 1390 | break; |
88840341 | 1391 | } |
19b3c5be | 1392 | |
fa15fb3d | 1393 | report->sel_options = src->sel_options; |
19b3c5be ML |
1394 | report->reachability = src->reachability; |
1395 | ||
88840341 RC |
1396 | /* Call stats module to fill out estimates */ |
1397 | SST_DoSourceReport(src->stats, report, now); | |
1398 | ||
1399 | return 1; | |
1400 | } | |
1401 | ||
1402 | } | |
1403 | ||
1404 | /* ================================================== */ | |
1405 | ||
1406 | int | |
d0dfa1de | 1407 | SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now) |
88840341 RC |
1408 | { |
1409 | SRC_Instance src; | |
1410 | ||
1411 | if ((index >= n_sources) || (index < 0)) { | |
1412 | return 0; | |
1413 | } else { | |
1414 | src = sources[index]; | |
1570f97e | 1415 | report->ref_id = src->ref_id; |
8265ff28 ML |
1416 | if (src->ip_addr) |
1417 | report->ip_addr = *src->ip_addr; | |
1418 | else | |
1419 | report->ip_addr.family = IPADDR_UNSPEC; | |
b32432c2 | 1420 | SST_DoSourcestatsReport(src->stats, report, now); |
88840341 RC |
1421 | return 1; |
1422 | } | |
1423 | } | |
1424 | ||
1425 | /* ================================================== */ | |
1426 | ||
ac30bb06 ML |
1427 | SRC_Type |
1428 | SRC_GetType(int index) | |
1429 | { | |
1430 | if ((index >= n_sources) || (index < 0)) | |
1431 | return -1; | |
1432 | return sources[index]->type; | |
1433 | } | |
1434 | ||
1435 | /* ================================================== */ |