forked from mlichvar/chrony
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ntp_core.c
2999 lines (2450 loc) · 102 KB
/
ntp_core.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2022
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Core NTP protocol engine
*/
#include "config.h"
#include "sysincl.h"
#include "array.h"
#include "ntp_auth.h"
#include "ntp_core.h"
#include "ntp_ext.h"
#include "ntp_io.h"
#include "memory.h"
#include "quantiles.h"
#include "sched.h"
#include "reference.h"
#include "local.h"
#include "samplefilt.h"
#include "smooth.h"
#include "sources.h"
#include "util.h"
#include "conf.h"
#include "logging.h"
#include "addrfilt.h"
#include "clientlog.h"
/* ================================================== */
static LOG_FileID logfileid;
static int log_raw_measurements;
/* ================================================== */
/* Enumeration used for remembering the operating mode of one of the
sources */
typedef enum {
MD_OFFLINE, /* No sampling at all */
MD_ONLINE, /* Normal sampling based on sampling interval */
MD_BURST_WAS_OFFLINE, /* Burst sampling, return to offline afterwards */
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
} OperatingMode;
/* ================================================== */
/* Structure used for holding a single peer/server's
protocol machine */
struct NCR_Instance_Record {
NTP_Remote_Address remote_addr; /* Needed for routing transmit packets */
NTP_Local_Address local_addr; /* Local address/socket used to send packets */
NTP_Mode mode; /* The source's NTP mode
(client/server or symmetric active peer) */
int interleaved; /* Boolean enabling interleaved NTP mode */
OperatingMode opmode; /* Whether we are sampling this source
or not and in what way */
SCH_TimeoutID rx_timeout_id; /* Timeout ID for latest received response */
SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */
int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_iburst; /* If 1, initiate a burst when going online */
int auto_burst; /* If 1, initiate a burst on each poll */
int auto_offline; /* If 1, automatically go offline when requests
cannot be sent */
int local_poll; /* Log2 of polling interval at our end */
int remote_poll; /* Log2 of server/peer's polling interval (recovered
from received packets) */
int remote_stratum; /* Stratum of the server/peer (recovered from
received packets) */
double remote_root_delay; /* Root delay from last valid packet */
double remote_root_dispersion;/* Root dispersion from last valid packet */
int presend_minpoll; /* If the current polling interval is
at least this, an extra client packet
will be send some time before normal
transmit. This ensures that both
us and the server/peer have an ARP
entry for each other ready, which
means our measurement is not
botched by an ARP round-trip on one
side or the other. */
int presend_done; /* The presend packet has been sent */
int minpoll; /* Log2 of minimum defined polling interval */
int maxpoll; /* Log2 of maximum defined polling interval */
int min_stratum; /* Increase stratum in received packets to the
minimum */
int copy; /* Boolean suppressing own refid and stratum */
int poll_target; /* Target number of sourcestats samples */
int version; /* Version set in packets for server/peer */
double poll_score; /* Score of current local poll */
double max_delay; /* Maximum round-trip delay to the
peer that we can tolerate and still
use the sample for generating
statistics from */
double max_delay_ratio; /* Largest ratio of delay /
min_delay_in_register that we can
tolerate. */
double max_delay_dev_ratio; /* Maximum ratio of increase in delay / stddev */
double offset_correction; /* Correction applied to measured offset
(e.g. for asymmetry in network delay) */
int ext_field_flags; /* Enabled extension fields */
uint32_t remote_mono_epoch; /* ID of the source's monotonic scale */
double mono_doffset; /* Accumulated offset between source's
real-time and monotonic scales */
NAU_Instance auth; /* Authentication */
/* Count of transmitted packets since last valid response */
unsigned int tx_count;
/* Flag indicating a valid response was received since last request */
int valid_rx;
/* Flag indicating the timestamps below are from a valid packet and may
be used for synchronisation */
int valid_timestamps;
/* Receive and transmit timestamps from the last valid response */
NTP_int64 remote_ntp_monorx;
NTP_int64 remote_ntp_rx;
NTP_int64 remote_ntp_tx;
/* Local timestamp when the last valid response was received from the
source. We have to be prepared to tinker with this if the local
clock has its frequency adjusted before we repond. The value we
store here is what our own local time was when the same arrived.
Before replying, we have to correct this to fit with the
parameters for the current reference. (It must be stored
relative to local time to permit frequency and offset adjustments
to be made when we trim the local clock). */
NTP_int64 local_ntp_rx;
NTP_Local_Timestamp local_rx;
/* Local timestamp when we last transmitted a packet to the source.
We store two versions. The first is in NTP format, and is used
to validate the next received packet from the source.
Additionally, this is corrected to bring it into line with the
current reference. The second is in timespec format, and is kept
relative to the local clock. We modify this in accordance with
local clock frequency/offset changes, and use this for computing
statistics about the source when a return packet arrives. */
NTP_int64 local_ntp_tx;
NTP_Local_Timestamp local_tx;
/* Previous values of some variables needed in interleaved mode */
NTP_Local_Timestamp prev_local_tx;
int prev_local_poll;
unsigned int prev_tx_count;
/* Flag indicating the two timestamps below were updated since the
last transmission */
int updated_init_timestamps;
/* Timestamps used for (re)starting the symmetric protocol, when we
need to respond to a packet which is not a valid response */
NTP_int64 init_remote_ntp_tx;
NTP_Local_Timestamp init_local_rx;
/* The instance record in the main source management module. This
performs the statistical analysis on the samples we generate */
SRC_Instance source;
/* Optional long-term quantile estimate of peer delay */
QNT_Instance delay_quant;
/* Optional median filter for NTP measurements */
SPF_Instance filter;
int filter_count;
int burst_good_samples_to_go;
int burst_total_samples_to_go;
/* Report from last valid response */
RPT_NTPReport report;
};
typedef struct {
NTP_Remote_Address addr;
NTP_Local_Address local_addr;
NAU_Instance auth;
int interval;
} BroadcastDestination;
/* Array of BroadcastDestination */
static ARR_Instance broadcasts;
/* ================================================== */
/* Initial delay period before first packet is transmitted (in seconds) */
#define INITIAL_DELAY 0.2
/* Spacing required between samples for any two servers/peers (to
minimise risk of network collisions) (in seconds) */
#define MIN_SAMPLING_SEPARATION 0.002
#define MAX_SAMPLING_SEPARATION 0.2
/* Randomness added to spacing between samples for one server/peer */
#define SAMPLING_RANDOMNESS 0.02
/* Adjustment of the peer polling interval */
#define PEER_SAMPLING_ADJ 1.1
/* Maximum spacing between samples in the burst mode as an absolute
value and ratio to the normal polling interval */
#define MAX_BURST_INTERVAL 2.0
#define MAX_BURST_POLL_RATIO 0.25
/* Number of samples in initial burst */
#define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
/* Number of samples in automatic burst */
#define BURST_GOOD_SAMPLES 1
#define MAX_BURST_TOTAL_SAMPLES 4
/* Time to wait after sending packet to 'warm up' link */
#define WARM_UP_DELAY 2.0
/* Compatible NTP protocol versions */
#define NTP_MAX_COMPAT_VERSION NTP_VERSION
#define NTP_MIN_COMPAT_VERSION 1
/* Maximum allowed dispersion - as defined in RFC 5905 (16 seconds) */
#define NTP_MAX_DISPERSION 16.0
/* Maximum allowed time for server to process client packet */
#define MAX_SERVER_INTERVAL 4.0
/* Maximum acceptable delay in transmission for timestamp correction */
#define MAX_TX_DELAY 1.0
/* Maximum allowed values of maxdelay parameters */
#define MAX_MAXDELAY 1.0e3
#define MAX_MAXDELAYRATIO 1.0e6
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Parameters for the peer delay quantile */
#define DELAY_QUANT_Q 100
#define DELAY_QUANT_REPEAT 7
/* Minimum and maximum allowed poll interval */
#define MIN_POLL -7
#define MAX_POLL 24
/* Enable sub-second polling intervals only when the peer delay is not
longer than 10 milliseconds to restrict them to local networks */
#define MIN_NONLAN_POLL 0
#define MAX_LAN_PEER_DELAY 0.01
/* Kiss-o'-Death codes */
#define KOD_RATE 0x52415445UL /* RATE */
/* Maximum poll interval set by KoD RATE */
#define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
/* Maximum number of missed responses to accept samples using old timestamps
in the interleaved client/server mode */
#define MAX_CLIENT_INTERLEAVED_TX 4
/* Maximum ratio of local intervals in the timestamp selection of the
interleaved mode to prefer a sample using previous timestamps */
#define MAX_INTERLEAVED_L2L_RATIO 0.1
/* Maximum acceptable change in server mono<->real offset */
#define MAX_MONO_DOFFSET 16.0
/* Invalid socket, different from the one in ntp_io.c */
#define INVALID_SOCK_FD -2
/* ================================================== */
/* Server IPv4/IPv6 sockets */
static int server_sock_fd4;
static int server_sock_fd6;
static ADF_AuthTable access_auth_table;
/* Current offset between monotonic and cooked time, and its epoch ID
which is reset on clock steps */
static double server_mono_offset;
static uint32_t server_mono_epoch;
/* Characters for printing synchronisation status and timestamping source */
static const char leap_chars[4] = {'N', '+', '-', '?'};
static const char tss_chars[3] = {'D', 'K', 'H'};
/* ================================================== */
/* Forward prototypes */
static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
static double get_separation(int poll);
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
static void process_sample(NCR_Instance inst, NTP_Sample *sample);
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
/* ================================================== */
static void
do_size_checks(void)
{
/* Assertions to check the sizes of certain data types
and the positions of certain record fields */
/* Check that certain invariants are true */
assert(sizeof(NTP_int32) == 4);
assert(sizeof(NTP_int64) == 8);
/* Check offsets of all fields in the NTP packet format */
assert(offsetof(NTP_Packet, lvm) == 0);
assert(offsetof(NTP_Packet, stratum) == 1);
assert(offsetof(NTP_Packet, poll) == 2);
assert(offsetof(NTP_Packet, precision) == 3);
assert(offsetof(NTP_Packet, root_delay) == 4);
assert(offsetof(NTP_Packet, root_dispersion) == 8);
assert(offsetof(NTP_Packet, reference_id) == 12);
assert(offsetof(NTP_Packet, reference_ts) == 16);
assert(offsetof(NTP_Packet, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40);
}
/* ================================================== */
static void
do_time_checks(void)
{
struct timespec now;
time_t warning_advance = 3600 * 24 * 365 * 10; /* 10 years */
#ifdef HAVE_LONG_TIME_T
/* Check that time before NTP_ERA_SPLIT underflows correctly */
struct timespec ts1 = {NTP_ERA_SPLIT, 1}, ts2 = {NTP_ERA_SPLIT - 1, 1};
NTP_int64 nts1, nts2;
int r;
UTI_TimespecToNtp64(&ts1, &nts1, NULL);
UTI_TimespecToNtp64(&ts2, &nts2, NULL);
UTI_Ntp64ToTimespec(&nts1, &ts1);
UTI_Ntp64ToTimespec(&nts2, &ts2);
r = ts1.tv_sec == NTP_ERA_SPLIT &&
ts1.tv_sec + (1ULL << 32) - 1 == ts2.tv_sec;
assert(r);
LCL_ReadRawTime(&now);
if (ts2.tv_sec - now.tv_sec < warning_advance)
LOG(LOGS_WARN, "Assumed NTP time ends at %s!", UTI_TimeToLogForm(ts2.tv_sec));
#else
LCL_ReadRawTime(&now);
if (now.tv_sec > 0x7fffffff - warning_advance)
LOG(LOGS_WARN, "System time ends at %s!", UTI_TimeToLogForm(0x7fffffff));
#endif
}
/* ================================================== */
static void
zero_local_timestamp(NTP_Local_Timestamp *ts)
{
UTI_ZeroTimespec(&ts->ts);
ts->err = 0.0;
ts->source = NTP_TS_DAEMON;
}
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type == LCL_ChangeAdjust) {
server_mono_offset += doffset;
} else {
UTI_GetRandomBytes(&server_mono_epoch, sizeof (server_mono_epoch));
server_mono_offset = 0.0;
}
}
/* ================================================== */
void
NCR_Initialise(void)
{
do_size_checks();
do_time_checks();
logfileid = CNF_GetLogMeasurements(&log_raw_measurements) ? LOG_FileOpen("measurements",
" Date (UTC) Time IP Address L St 123 567 ABCD LP RP Score Offset Peer del. Peer disp. Root del. Root disp. Refid MTxRx")
: -1;
access_auth_table = ADF_CreateTable();
broadcasts = ARR_CreateInstance(sizeof (BroadcastDestination));
/* Server socket will be opened when access is allowed */
server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD;
LCL_AddParameterChangeHandler(handle_slew, NULL);
handle_slew(NULL, NULL, 0.0, 0.0, LCL_ChangeUnknownStep, NULL);
}
/* ================================================== */
void
NCR_Finalise(void)
{
unsigned int i;
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
if (server_sock_fd4 != INVALID_SOCK_FD)
NIO_CloseServerSocket(server_sock_fd4);
if (server_sock_fd6 != INVALID_SOCK_FD)
NIO_CloseServerSocket(server_sock_fd6);
for (i = 0; i < ARR_GetSize(broadcasts); i++) {
NIO_CloseServerSocket(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->local_addr.sock_fd);
NAU_DestroyInstance(((BroadcastDestination *)ARR_GetElement(broadcasts, i))->auth);
}
ARR_DestroyInstance(broadcasts);
ADF_DestroyTable(access_auth_table);
}
/* ================================================== */
static void
restart_timeout(NCR_Instance inst, double delay)
{
/* Check if we can transmit */
if (inst->tx_suspended) {
assert(!inst->tx_timeout_id);
return;
}
/* Stop both rx and tx timers if running */
SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0;
SCH_RemoveTimeout(inst->tx_timeout_id);
/* Start new timer for transmission */
inst->tx_timeout_id = SCH_AddTimeoutInClass(delay, get_separation(inst->local_poll),
SAMPLING_RANDOMNESS,
inst->mode == MODE_CLIENT ?
SCH_NtpClientClass : SCH_NtpPeerClass,
transmit_timeout, (void *)inst);
}
/* ================================================== */
static void
start_initial_timeout(NCR_Instance inst)
{
double delay, last_tx;
struct timespec now;
if (!inst->tx_timeout_id) {
/* This will be the first transmission after mode change */
/* Mark source active */
SRC_SetActive(inst->source);
}
/* In case the offline period was too short, adjust the delay to keep
the interval between packets at least as long as the current polling
interval */
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
SCH_GetLastEventTime(&now, NULL, NULL);
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
} else {
delay = 0.0;
}
if (delay < INITIAL_DELAY)
delay = INITIAL_DELAY;
restart_timeout(inst, delay);
}
/* ================================================== */
static void
close_client_socket(NCR_Instance inst)
{
if (inst->mode == MODE_CLIENT && inst->local_addr.sock_fd != INVALID_SOCK_FD) {
NIO_CloseClientSocket(inst->local_addr.sock_fd);
inst->local_addr.sock_fd = INVALID_SOCK_FD;
}
SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0;
}
/* ================================================== */
static void
take_offline(NCR_Instance inst)
{
inst->opmode = MD_OFFLINE;
SCH_RemoveTimeout(inst->tx_timeout_id);
inst->tx_timeout_id = 0;
/* Mark source unreachable */
SRC_ResetReachability(inst->source);
/* And inactive */
SRC_UnsetActive(inst->source);
close_client_socket(inst);
NCR_ResetInstance(inst);
}
/* ================================================== */
static void
reset_report(NCR_Instance inst)
{
memset(&inst->report, 0, sizeof (inst->report));
inst->report.remote_addr = inst->remote_addr.ip_addr;
inst->report.remote_port = inst->remote_addr.port;
}
/* ================================================== */
NCR_Instance
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name)
{
NCR_Instance result;
result = MallocNew(struct NCR_Instance_Record);
result->remote_addr = *remote_addr;
result->local_addr.ip_addr.family = IPADDR_UNSPEC;
result->local_addr.if_index = INVALID_IF_INDEX;
switch (type) {
case NTP_SERVER:
/* Client socket will be obtained when sending request */
result->local_addr.sock_fd = INVALID_SOCK_FD;
result->mode = MODE_CLIENT;
break;
case NTP_PEER:
result->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
result->mode = MODE_ACTIVE;
break;
default:
assert(0);
}
result->interleaved = params->interleaved;
result->minpoll = params->minpoll;
if (result->minpoll < MIN_POLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
else if (result->minpoll > MAX_POLL)
result->minpoll = MAX_POLL;
result->maxpoll = params->maxpoll;
if (result->maxpoll < MIN_POLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL;
else if (result->maxpoll > MAX_POLL)
result->maxpoll = MAX_POLL;
if (result->maxpoll < result->minpoll)
result->maxpoll = result->minpoll;
result->min_stratum = params->min_stratum;
if (result->min_stratum >= NTP_MAX_STRATUM)
result->min_stratum = NTP_MAX_STRATUM - 1;
/* Presend doesn't work in symmetric mode */
result->presend_minpoll = params->presend_minpoll;
if (result->presend_minpoll <= MAX_POLL && result->mode != MODE_CLIENT)
result->presend_minpoll = MAX_POLL + 1;
result->max_delay = CLAMP(0.0, params->max_delay, MAX_MAXDELAY);
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset;
result->auto_iburst = params->iburst;
result->auto_burst = params->burst;
result->auto_offline = params->auto_offline;
result->copy = params->copy && result->mode == MODE_CLIENT;
result->poll_target = params->poll_target;
result->ext_field_flags = params->ext_fields;
if (params->nts) {
IPSockAddr nts_address;
if (result->mode == MODE_ACTIVE)
LOG(LOGS_WARN, "NTS not supported with peers");
nts_address.ip_addr = remote_addr->ip_addr;
nts_address.port = params->nts_port;
result->auth = NAU_CreateNtsInstance(&nts_address, name, params->cert_set,
result->remote_addr.port);
} else if (params->authkey != INACTIVE_AUTHKEY) {
result->auth = NAU_CreateSymmetricInstance(params->authkey);
} else {
result->auth = NAU_CreateNoneInstance();
}
if (result->ext_field_flags || result->interleaved)
result->version = NTP_VERSION;
else
result->version = NAU_GetSuggestedNtpVersion(result->auth);
if (params->version)
result->version = CLAMP(NTP_MIN_COMPAT_VERSION, params->version, NTP_VERSION);
/* Create a source instance for this NTP source */
result->source = SRC_CreateNewInstance(UTI_IPToRefid(&remote_addr->ip_addr),
SRC_NTP, NAU_IsAuthEnabled(result->auth),
params->sel_options, &result->remote_addr.ip_addr,
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
if (params->max_delay_quant > 0.0) {
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);
result->delay_quant = QNT_CreateInstance(k, k, DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
LCL_GetSysPrecisionAsQuantum() / 2.0);
} else {
result->delay_quant = NULL;
}
if (params->filter_length >= 1)
result->filter = SPF_CreateInstance(1, params->filter_length, NTP_MAX_DISPERSION, 0.0);
else
result->filter = NULL;
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
result->opmode = MD_OFFLINE;
result->local_poll = MAX(result->minpoll, MIN_NONLAN_POLL);
result->poll_score = 0.0;
zero_local_timestamp(&result->local_tx);
result->burst_good_samples_to_go = 0;
result->burst_total_samples_to_go = 0;
NCR_ResetInstance(result);
set_connectivity(result, params->connectivity);
reset_report(result);
return result;
}
/* ================================================== */
/* Destroy an instance */
void
NCR_DestroyInstance(NCR_Instance instance)
{
if (instance->opmode != MD_OFFLINE)
take_offline(instance);
if (instance->mode == MODE_ACTIVE)
NIO_CloseServerSocket(instance->local_addr.sock_fd);
if (instance->delay_quant)
QNT_DestroyInstance(instance->delay_quant);
if (instance->filter)
SPF_DestroyInstance(instance->filter);
NAU_DestroyInstance(instance->auth);
/* This will destroy the source instance inside the
structure, which will cause reselection if this was the
synchronising source etc. */
SRC_DestroyInstance(instance->source);
/* Free the data structure */
Free(instance);
}
/* ================================================== */
void
NCR_StartInstance(NCR_Instance instance)
{
instance->tx_suspended = 0;
if (instance->opmode != MD_OFFLINE)
start_initial_timeout(instance);
}
/* ================================================== */
void
NCR_ResetInstance(NCR_Instance instance)
{
instance->tx_count = 0;
instance->presend_done = 0;
instance->remote_poll = 0;
instance->remote_stratum = 0;
instance->remote_root_delay = 0.0;
instance->remote_root_dispersion = 0.0;
instance->remote_mono_epoch = 0;
instance->mono_doffset = 0.0;
instance->valid_rx = 0;
instance->valid_timestamps = 0;
UTI_ZeroNtp64(&instance->remote_ntp_monorx);
UTI_ZeroNtp64(&instance->remote_ntp_rx);
UTI_ZeroNtp64(&instance->remote_ntp_tx);
UTI_ZeroNtp64(&instance->local_ntp_rx);
UTI_ZeroNtp64(&instance->local_ntp_tx);
zero_local_timestamp(&instance->local_rx);
zero_local_timestamp(&instance->prev_local_tx);
instance->prev_local_poll = 0;
instance->prev_tx_count = 0;
instance->updated_init_timestamps = 0;
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
zero_local_timestamp(&instance->init_local_rx);
if (instance->delay_quant)
QNT_Reset(instance->delay_quant);
if (instance->filter)
SPF_DropSamples(instance->filter);
instance->filter_count = 0;
}
/* ================================================== */
void
NCR_ResetPoll(NCR_Instance instance)
{
if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll;
/* The timer was set with a longer poll interval, restart it */
if (instance->tx_timeout_id)
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
}
}
/* ================================================== */
void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
{
NCR_ResetInstance(inst);
if (!ntp_only)
NAU_ChangeAddress(inst->auth, &remote_addr->ip_addr);
inst->remote_addr = *remote_addr;
if (inst->mode == MODE_CLIENT)
close_client_socket(inst);
else {
NIO_CloseServerSocket(inst->local_addr.sock_fd);
inst->local_addr.ip_addr.family = IPADDR_UNSPEC;
inst->local_addr.if_index = INVALID_IF_INDEX;
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
}
/* Update the reference ID and reset the source/sourcestats instances */
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
&inst->remote_addr.ip_addr);
SRC_ResetInstance(inst->source);
reset_report(inst);
}
/* ================================================== */
static void
adjust_poll(NCR_Instance inst, double adj)
{
NTP_Sample last_sample;
inst->poll_score += adj;
if (inst->poll_score >= 1.0) {
inst->local_poll += (int)inst->poll_score;
inst->poll_score -= (int)inst->poll_score;
}
if (inst->poll_score < 0.0) {
inst->local_poll += (int)(inst->poll_score - 1.0);
inst->poll_score -= (int)(inst->poll_score - 1.0);
}
/* Clamp polling interval to defined range */
if (inst->local_poll < inst->minpoll) {
inst->local_poll = inst->minpoll;
inst->poll_score = 0;
} else if (inst->local_poll > inst->maxpoll) {
inst->local_poll = inst->maxpoll;
inst->poll_score = 1.0;
}
/* Don't allow a sub-second polling interval if the source is not reachable
or it is not in a local network according to the measured delay */
if (inst->local_poll < MIN_NONLAN_POLL &&
(!SRC_IsReachable(inst->source) ||
(SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY &&
(!inst->filter || !SPF_GetLastSample(inst->filter, &last_sample) ||
last_sample.peer_delay > MAX_LAN_PEER_DELAY))))
inst->local_poll = MIN_NONLAN_POLL;
}
/* ================================================== */
static double
get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance)
{
double poll_adj;
int samples;
if (error_in_estimate > peer_distance) {
/* If the prediction is not even within +/- the peer distance of the peer,
we are clearly not tracking the peer at all well, so we back off the
sampling rate depending on just how bad the situation is */
poll_adj = -log(error_in_estimate / peer_distance) / log(2.0);
} else {
samples = SST_Samples(SRC_GetSourcestats(inst->source));
/* Adjust polling interval so that the number of sourcestats samples
remains close to the target value */
poll_adj = ((double)samples / inst->poll_target - 1.0) / inst->poll_target;
/* Make interval shortening quicker */
if (samples < inst->poll_target) {
poll_adj *= 2.0;
}
}
return poll_adj;
}
/* ================================================== */
static int
get_transmit_poll(NCR_Instance inst)
{
int poll;
poll = inst->local_poll;
/* In symmetric mode, if the peer is responding, use shorter of the local
and remote poll interval, but not shorter than the minimum */
if (inst->mode == MODE_ACTIVE && poll > inst->remote_poll &&
SRC_IsReachable(inst->source))
poll = MAX(inst->remote_poll, inst->minpoll);
return poll;
}
/* ================================================== */
static double
get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
{
int poll_to_use, stratum_diff;
double delay_time;
/* If we're in burst mode, queue for immediate dispatch.
If we're operating in client/server mode, queue the timeout for
the poll interval hence. The fact that a timeout has been queued
in the transmit handler is immaterial - that is only done so that
we at least send something, if no reply is heard.
If we're in symmetric mode, we have to take account of the peer's
wishes, otherwise his sampling regime will fall to pieces. If
we're in client/server mode, we don't care what poll interval the
server responded with last time. */
poll_to_use = get_transmit_poll(inst);
delay_time = UTI_Log2ToDouble(poll_to_use);
switch (inst->opmode) {
case MD_OFFLINE:
assert(0);
break;
case MD_ONLINE:
switch(inst->mode) {
case MODE_CLIENT:
if (inst->presend_done)
delay_time = WARM_UP_DELAY;
break;
case MODE_ACTIVE:
/* If the remote stratum is higher than ours, wait a bit for the next
packet before responding in order to minimize the delay of the
measurement and its error for the peer which has higher stratum.
If the remote stratum is equal to ours, try to interleave packets
evenly with the peer. */
stratum_diff = inst->remote_stratum - REF_GetOurStratum();
if ((stratum_diff > 0 && last_tx * PEER_SAMPLING_ADJ < delay_time) ||
(!on_tx && !stratum_diff &&
last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5))
delay_time *= PEER_SAMPLING_ADJ;
/* Substract the already spend time */
if (last_tx > 0.0)
delay_time -= last_tx;
if (delay_time < 0.0)
delay_time = 0.0;
break;
default:
assert(0);
break;
}
break;
case MD_BURST_WAS_ONLINE:
case MD_BURST_WAS_OFFLINE:
/* Burst modes */
delay_time = MIN(MAX_BURST_INTERVAL, MAX_BURST_POLL_RATIO * delay_time);
break;
default:
assert(0);
break;
}
return delay_time;
}
/* ================================================== */
/* Calculate sampling separation for given polling interval */
static double
get_separation(int poll)
{
double separation;
assert(poll >= MIN_POLL && poll <= MAX_POLL);
/* Allow up to 8 sources using the same short interval to not be limited
by the separation */
separation = UTI_Log2ToDouble(poll - 3);
return CLAMP(MIN_SAMPLING_SEPARATION, separation, MAX_SAMPLING_SEPARATION);
}
/* ================================================== */
/* Timeout handler for closing the client socket when no acceptable
reply can be received from the server */
static void
receive_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance)arg;
DEBUG_LOG("Receive timeout for %s", UTI_IPSockAddrToString(&inst->remote_addr));
inst->rx_timeout_id = 0;