mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-04 00:15:07 -05:00
Compare commits
562 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9848c0176 | ||
|
|
2cfe969940 | ||
|
|
487cf3840f | ||
|
|
4886c776d5 | ||
|
|
d3f3638b3d | ||
|
|
6c5973741b | ||
|
|
51d161a028 | ||
|
|
5535384878 | ||
|
|
f78e4681ef | ||
|
|
b365edb48e | ||
|
|
93a78c73ad | ||
|
|
abc267a556 | ||
|
|
9b183fe98f | ||
|
|
be7f5e8916 | ||
|
|
5e2cd47ad1 | ||
|
|
9eaf8bc521 | ||
|
|
54010586aa | ||
|
|
90d808ed28 | ||
|
|
1d9e080749 | ||
|
|
d30913e78c | ||
|
|
c5d3be8cc4 | ||
|
|
3e32e7e694 | ||
|
|
52cce3dea8 | ||
|
|
1bcbea9bd2 | ||
|
|
2ac581e04a | ||
|
|
4a8da7e02d | ||
|
|
e463fcab49 | ||
|
|
df98fb4fc7 | ||
|
|
551bc266e4 | ||
|
|
c4234dd1f7 | ||
|
|
fd50f3c80c | ||
|
|
9b9823b377 | ||
|
|
c900dff314 | ||
|
|
2c6cbde058 | ||
|
|
cacd64bf4a | ||
|
|
6c2ee89970 | ||
|
|
68b2ffa97c | ||
|
|
f591133f4c | ||
|
|
48cc072c06 | ||
|
|
8211978570 | ||
|
|
4b2b6bbf97 | ||
|
|
551ad40a04 | ||
|
|
09cd057c23 | ||
|
|
363aa86861 | ||
|
|
bda2ff77e8 | ||
|
|
545bd59563 | ||
|
|
082af24114 | ||
|
|
577221295f | ||
|
|
eb08c3b013 | ||
|
|
391882dc41 | ||
|
|
b32d85bc54 | ||
|
|
0bf7e49148 | ||
|
|
f5fdfee150 | ||
|
|
c17f481a20 | ||
|
|
8140feb4fd | ||
|
|
6e541afbe9 | ||
|
|
81362fa201 | ||
|
|
ec57de02c7 | ||
|
|
f7da309b83 | ||
|
|
9b28b2f493 | ||
|
|
ebf19ca760 | ||
|
|
2d96077b9f | ||
|
|
2c2dd2d126 | ||
|
|
f7daec0693 | ||
|
|
d115449e85 | ||
|
|
dab98fa8da | ||
|
|
dd8738119b | ||
|
|
75bbccf518 | ||
|
|
1b24a66b3c | ||
|
|
8fd386f287 | ||
|
|
e694ae769a | ||
|
|
2b127b2e93 | ||
|
|
2d3c89e1a3 | ||
|
|
b5d1ae147d | ||
|
|
bf964673cb | ||
|
|
c66c774e26 | ||
|
|
274c1767f2 | ||
|
|
c27a298d9c | ||
|
|
072ec20e2c | ||
|
|
711c7c0c8a | ||
|
|
454ce62672 | ||
|
|
ab850d41f4 | ||
|
|
744768306b | ||
|
|
26d4f0c401 | ||
|
|
717db2cfdd | ||
|
|
55898e9b07 | ||
|
|
f7bb283536 | ||
|
|
1967fbf1f2 | ||
|
|
51da7a0694 | ||
|
|
9ba6e7655c | ||
|
|
3dea7dd723 | ||
|
|
cb1118456b | ||
|
|
8ee725ff18 | ||
|
|
a90a7cf51c | ||
|
|
4f22883f4e | ||
|
|
65be9d9a02 | ||
|
|
a1191f8392 | ||
|
|
924199ef69 | ||
|
|
8028984f34 | ||
|
|
68301238a0 | ||
|
|
13db3d6902 | ||
|
|
8c24086c6d | ||
|
|
e30d5a6e6f | ||
|
|
a0d34eb372 | ||
|
|
7196943f11 | ||
|
|
26fef1751a | ||
|
|
d71e22594e | ||
|
|
d7a578fed2 | ||
|
|
014a67b29e | ||
|
|
d22c8fbcb2 | ||
|
|
2da4e3ce53 | ||
|
|
c92358e041 | ||
|
|
77962bb527 | ||
|
|
65b5f111d2 | ||
|
|
9856bf2d5f | ||
|
|
2205eae2a1 | ||
|
|
6f381fa78b | ||
|
|
bb8050d884 | ||
|
|
c85ec4ff0f | ||
|
|
d9d97f76d7 | ||
|
|
837323d687 | ||
|
|
12237bf283 | ||
|
|
b9b338a8df | ||
|
|
a5d73f692f | ||
|
|
b0ac5992fb | ||
|
|
cd65e32cf0 | ||
|
|
b9f5278846 | ||
|
|
b8b166044f | ||
|
|
42fbf41686 | ||
|
|
79a790e6b5 | ||
|
|
f5cd79d2df | ||
|
|
689605b6a2 | ||
|
|
0707865413 | ||
|
|
2adda9c12c | ||
|
|
113d1134d1 | ||
|
|
b363af754d | ||
|
|
0f5cf57bc2 | ||
|
|
5a43f0c39b | ||
|
|
5a6fbe7a4b | ||
|
|
bb34e92f96 | ||
|
|
78b9c13a11 | ||
|
|
1ab5b88939 | ||
|
|
e30f937f6a | ||
|
|
08b67dba98 | ||
|
|
61f15fedcd | ||
|
|
6d59234995 | ||
|
|
d4a4f89329 | ||
|
|
916ed70c4a | ||
|
|
8ba2da52df | ||
|
|
fd9e956d27 | ||
|
|
43189651b0 | ||
|
|
f518b8d00f | ||
|
|
42b3c40c32 | ||
|
|
66512ebcb3 | ||
|
|
3940d2aae3 | ||
|
|
34be117c9c | ||
|
|
7915f52495 | ||
|
|
05bd4898a9 | ||
|
|
4da088ec2f | ||
|
|
c46e0549ab | ||
|
|
8f5b308414 | ||
|
|
084fe6b0cc | ||
|
|
ebfc676d74 | ||
|
|
adaca0ff19 | ||
|
|
84d6c7a527 | ||
|
|
c43efccf02 | ||
|
|
1affd03cca | ||
|
|
276591172e | ||
|
|
989ef702aa | ||
|
|
1920b1efde | ||
|
|
bb5db828c6 | ||
|
|
dcc94a4c10 | ||
|
|
2ed72c49c9 | ||
|
|
342b588e3b | ||
|
|
a914140bd4 | ||
|
|
28e4eec1c4 | ||
|
|
5235c51801 | ||
|
|
26ea4e35e7 | ||
|
|
9397ae2b0a | ||
|
|
b8ead3485b | ||
|
|
24d28cd679 | ||
|
|
aac898343e | ||
|
|
c8c7f518b1 | ||
|
|
ce956c99a8 | ||
|
|
863866354d | ||
|
|
6e5513c80b | ||
|
|
6d0143e963 | ||
|
|
f49be7f063 | ||
|
|
7fe98a83b8 | ||
|
|
ad37c409c9 | ||
|
|
719c6f6a8a | ||
|
|
b0750136b5 | ||
|
|
ad79aec946 | ||
|
|
008dc16727 | ||
|
|
6cf9fe2f16 | ||
|
|
637b77d1bd | ||
|
|
53823b9f1c | ||
|
|
83f90279b0 | ||
|
|
02ae9a8607 | ||
|
|
017d6f8f56 | ||
|
|
eb26d13140 | ||
|
|
8d19f49341 | ||
|
|
637fa29e1e | ||
|
|
2d349595ee | ||
|
|
5cb584d6c1 | ||
|
|
d7c2b1d2f3 | ||
|
|
e11b518a1f | ||
|
|
120dfb8b36 | ||
|
|
598b893e1d | ||
|
|
89aa8fa342 | ||
|
|
42fdad5dcc | ||
|
|
3ee7b3e786 | ||
|
|
426fe2ee58 | ||
|
|
3f66202d79 | ||
|
|
ed6b0b55c7 | ||
|
|
5e5adbea0c | ||
|
|
82959431df | ||
|
|
b92b2da24a | ||
|
|
68a3d52086 | ||
|
|
1a15be1e9e | ||
|
|
5dd288dc0c | ||
|
|
cbee464c75 | ||
|
|
4e36295889 | ||
|
|
2d2642bb82 | ||
|
|
9c6eaccc32 | ||
|
|
0aa4d5ac14 | ||
|
|
ee9d721b7b | ||
|
|
b6eec0068a | ||
|
|
e6a0476eb7 | ||
|
|
c063b9e78a | ||
|
|
f6f1863fe2 | ||
|
|
51a621bc2b | ||
|
|
1488b31a38 | ||
|
|
70cdd8b1ef | ||
|
|
8eef631009 | ||
|
|
d9ae724c60 | ||
|
|
6372a9f93f | ||
|
|
b0267475e3 | ||
|
|
07134f2625 | ||
|
|
85db8e3a9c | ||
|
|
05f4f79cbf | ||
|
|
bf616eafa1 | ||
|
|
e08a0ee668 | ||
|
|
f2d7baa94f | ||
|
|
558931524d | ||
|
|
a74b63277a | ||
|
|
aa8196328c | ||
|
|
37deee7140 | ||
|
|
7ff74d9efe | ||
|
|
43320a1d6b | ||
|
|
8caaa0b056 | ||
|
|
e48a34392c | ||
|
|
8bc8bf9cc4 | ||
|
|
3dc9f1ff92 | ||
|
|
7bc7d00297 | ||
|
|
b5cf861cd7 | ||
|
|
25cc84d5e2 | ||
|
|
f74e4cf1fe | ||
|
|
5f66722b66 | ||
|
|
b31461af7a | ||
|
|
ae177f2742 | ||
|
|
1a736078df | ||
|
|
9b46ea7255 | ||
|
|
ff4e932f17 | ||
|
|
68c35a0072 | ||
|
|
b6c634298d | ||
|
|
010df12459 | ||
|
|
22ef2fbb0e | ||
|
|
7a03206222 | ||
|
|
b86c50bb9f | ||
|
|
36f9b24dfe | ||
|
|
e0b75b87bf | ||
|
|
6661a61486 | ||
|
|
bc76291750 | ||
|
|
2aefadd129 | ||
|
|
123cb497b9 | ||
|
|
0c38e4a6ca | ||
|
|
0db30fd0b1 | ||
|
|
b90d2c084f | ||
|
|
ab8da7ecb9 | ||
|
|
05809e937c | ||
|
|
8265fe2e30 | ||
|
|
c11a052955 | ||
|
|
109970f687 | ||
|
|
ca10b9e072 | ||
|
|
19da1d95a8 | ||
|
|
61da7d0913 | ||
|
|
105f1f90c1 | ||
|
|
c9d791e02d | ||
|
|
de678ff780 | ||
|
|
e16bcca617 | ||
|
|
b57d7040b3 | ||
|
|
c80858f738 | ||
|
|
81bf7cdcdc | ||
|
|
b8b3830dc4 | ||
|
|
d4738e1259 | ||
|
|
5ba42cee45 | ||
|
|
b2dac47c82 | ||
|
|
6a6161dc0f | ||
|
|
a4eb5be8ea | ||
|
|
3050e29b1d | ||
|
|
fb1af6e55b | ||
|
|
47a13ae88c | ||
|
|
a8496658a0 | ||
|
|
6ea1082a72 | ||
|
|
4f674539fd | ||
|
|
68d3fb4af8 | ||
|
|
3c24f2c2ed | ||
|
|
0189dac7d8 | ||
|
|
4a11399c2e | ||
|
|
cf98551ea1 | ||
|
|
5508b01bd8 | ||
|
|
907accec87 | ||
|
|
a511029cc2 | ||
|
|
0845df7684 | ||
|
|
2f961ab36a | ||
|
|
a0cf7f7f12 | ||
|
|
a5f1a113f0 | ||
|
|
5160f14fdc | ||
|
|
b0a2ad2535 | ||
|
|
ecdde75f8f | ||
|
|
2d80be9541 | ||
|
|
ab776ed9d8 | ||
|
|
ccebec3eb6 | ||
|
|
3ea3e0efd7 | ||
|
|
c3e4e3e47a | ||
|
|
e949e1d991 | ||
|
|
c8649ccb7e | ||
|
|
39ff7ceeca | ||
|
|
06945d927b | ||
|
|
caf82b1a45 | ||
|
|
f99b2f633b | ||
|
|
6270a3eb7c | ||
|
|
1daa40a2f7 | ||
|
|
a1406eded3 | ||
|
|
1eb8994c00 | ||
|
|
221e5fb501 | ||
|
|
ecfbde9872 | ||
|
|
dec07aa844 | ||
|
|
5b3d4dfe76 | ||
|
|
dc0f0cd134 | ||
|
|
bd37efa52e | ||
|
|
c71185a0e5 | ||
|
|
f149b7b758 | ||
|
|
883b0dde94 | ||
|
|
9cba9c8585 | ||
|
|
88e711ad9a | ||
|
|
badaa83c31 | ||
|
|
bbeec7361c | ||
|
|
6fba5a4a7f | ||
|
|
26889a8cb7 | ||
|
|
cd278d1826 | ||
|
|
3877734814 | ||
|
|
19f2ab9e09 | ||
|
|
3260dc82fe | ||
|
|
1a98c5ffa9 | ||
|
|
8247b8525f | ||
|
|
8901293be8 | ||
|
|
e789b0817f | ||
|
|
d0fd04c0a2 | ||
|
|
7122321249 | ||
|
|
b328c8c348 | ||
|
|
7b97668319 | ||
|
|
6f5df7e4a4 | ||
|
|
5a39074e01 | ||
|
|
c8e57f4350 | ||
|
|
b1230efac3 | ||
|
|
4e1ce88981 | ||
|
|
790a336eb2 | ||
|
|
cc706b50b9 | ||
|
|
73042494bd | ||
|
|
ec89739d50 | ||
|
|
4baf999cc3 | ||
|
|
9afd19c29b | ||
|
|
5dd173c050 | ||
|
|
5caf0ad187 | ||
|
|
17d2291a84 | ||
|
|
a6179261a7 | ||
|
|
098e0c43fc | ||
|
|
7b197953e8 | ||
|
|
9dcace0fc4 | ||
|
|
a07ac38331 | ||
|
|
166e43b13e | ||
|
|
b84d6759f9 | ||
|
|
f323c814af | ||
|
|
19b47dcbc9 | ||
|
|
5edeadcbd9 | ||
|
|
d91ae2094f | ||
|
|
30a5845098 | ||
|
|
0f367efac5 | ||
|
|
24c011d4a6 | ||
|
|
0c2cdd2fb1 | ||
|
|
cd1a666e1b | ||
|
|
070b4f69d0 | ||
|
|
851c823b42 | ||
|
|
df80274644 | ||
|
|
bb2d68ddf9 | ||
|
|
685d8f725b | ||
|
|
4234732b08 | ||
|
|
a16094adfb | ||
|
|
a4349b13df | ||
|
|
3556dadea1 | ||
|
|
220e6d1907 | ||
|
|
a738037705 | ||
|
|
7daf34675a | ||
|
|
de598c2310 | ||
|
|
91cc4dbb12 | ||
|
|
0ae6f2485b | ||
|
|
52ec694d2b | ||
|
|
e2e07af8a4 | ||
|
|
2ed88c31c7 | ||
|
|
af8e4a5115 | ||
|
|
f503a9a490 | ||
|
|
9c64fbb9c4 | ||
|
|
b428f901c7 | ||
|
|
09b7f77f9a | ||
|
|
c23c0b8484 | ||
|
|
d530055917 | ||
|
|
f41d09e19f | ||
|
|
46030d9d3e | ||
|
|
02ccd3a3c7 | ||
|
|
9cc609c4b0 | ||
|
|
a0a496dcb4 | ||
|
|
8d08486edf | ||
|
|
a3b376cf0a | ||
|
|
e66f1df89d | ||
|
|
35220aac9d | ||
|
|
5b04f3ca90 | ||
|
|
beb1c36136 | ||
|
|
da3495c472 | ||
|
|
356771c0c3 | ||
|
|
fca8966ada | ||
|
|
25f80a1a9d | ||
|
|
1219f99935 | ||
|
|
33a1fe7a9c | ||
|
|
eed0a0de56 | ||
|
|
07600cbd71 | ||
|
|
f2e341b5ed | ||
|
|
55717c1ccd | ||
|
|
d5e645eb38 | ||
|
|
3196630fb9 | ||
|
|
663dde1ad7 | ||
|
|
62757cda49 | ||
|
|
af6ae9186b | ||
|
|
4c29f8888c | ||
|
|
d06ae4a60e | ||
|
|
f9af2f9733 | ||
|
|
43ae0131cd | ||
|
|
8bb8f15a7d | ||
|
|
e55f174bd3 | ||
|
|
5bd13c8d59 | ||
|
|
759580aa6f | ||
|
|
b61cbed689 | ||
|
|
2ac2247756 | ||
|
|
55f48b14b7 | ||
|
|
3dfac33858 | ||
|
|
d5f2401421 | ||
|
|
fb0570cc73 | ||
|
|
43936ba0d1 | ||
|
|
f2ba20f293 | ||
|
|
fcd384523b | ||
|
|
48bce351bf | ||
|
|
25f93875d9 | ||
|
|
ebc610fcb3 | ||
|
|
264957a443 | ||
|
|
af611b5842 | ||
|
|
1c1ca1d12f | ||
|
|
c506b9aac8 | ||
|
|
2eefa61f10 | ||
|
|
89a5e21e4d | ||
|
|
6a79771898 | ||
|
|
53353529cf | ||
|
|
22bfdf204f | ||
|
|
fc28e9ae56 | ||
|
|
17e6258694 | ||
|
|
d7a444593f | ||
|
|
701b9415a5 | ||
|
|
d5894c0738 | ||
|
|
a0a9560258 | ||
|
|
09067e06d3 | ||
|
|
dbbdd5af06 | ||
|
|
7f984cf7fa | ||
|
|
8df49b799f | ||
|
|
e7c2f71cea | ||
|
|
219085b8f6 | ||
|
|
2319f72b29 | ||
|
|
72f7d09f58 | ||
|
|
0bf39c0ab9 | ||
|
|
2e126ed2b5 | ||
|
|
a652ce7d0e | ||
|
|
a97ca73704 | ||
|
|
125d7a5c32 | ||
|
|
36356ef033 | ||
|
|
a2d1569455 | ||
|
|
952c3b2528 | ||
|
|
d92d24ad7f | ||
|
|
bc33e1cda1 | ||
|
|
189bf9c536 | ||
|
|
c5dde9b66a | ||
|
|
1fb60f8db8 | ||
|
|
2f05287e15 | ||
|
|
61226cda8c | ||
|
|
26b51d841e | ||
|
|
5f4cbaab7e | ||
|
|
7a80647fb4 | ||
|
|
14b8df3702 | ||
|
|
5cb469b204 | ||
|
|
29d7d3176d | ||
|
|
76a905d652 | ||
|
|
83f96efdfd | ||
|
|
127826a399 | ||
|
|
7ee5f4888e | ||
|
|
9ed1d1afc2 | ||
|
|
d0d9a3fa43 | ||
|
|
9600993c28 | ||
|
|
5e6f8458ff | ||
|
|
f5fe5452f6 | ||
|
|
3ac6a0c26c | ||
|
|
c2872d1e12 | ||
|
|
e47e7e3661 | ||
|
|
d8f14ec59b | ||
|
|
274a51bc38 | ||
|
|
92700e194c | ||
|
|
87df268723 | ||
|
|
17a9caf5c8 | ||
|
|
36441fabde | ||
|
|
f363998517 | ||
|
|
6fc30baba8 | ||
|
|
70a0f18d52 | ||
|
|
0ad5f5ea89 | ||
|
|
d676f39b84 | ||
|
|
31690261f5 | ||
|
|
93326488a3 | ||
|
|
d5ca98eaaa | ||
|
|
be3158c4e5 | ||
|
|
2f1d5d9255 | ||
|
|
b2c2132e4b | ||
|
|
aab6d1b153 | ||
|
|
bbbd80bf03 | ||
|
|
f27d719a4e | ||
|
|
789817cd91 | ||
|
|
885e7774fd | ||
|
|
883b7eed8a | ||
|
|
4049ed8766 | ||
|
|
f9f6803b8a | ||
|
|
385f7ebfd9 | ||
|
|
f9cbc4803d | ||
|
|
97973b1833 | ||
|
|
9cdfc15e31 | ||
|
|
fc99317291 | ||
|
|
bb9ba3e4bd | ||
|
|
649f54a1e6 | ||
|
|
4070d7ffa6 | ||
|
|
0493abb68a | ||
|
|
8c1e16711d | ||
|
|
1d03908646 | ||
|
|
49d718c025 | ||
|
|
c536b2561b | ||
|
|
b9f5ce83b0 | ||
|
|
8baab00ae0 | ||
|
|
d01cb5af46 | ||
|
|
7925ed39b8 |
@@ -33,9 +33,13 @@ CFLAGS = @CFLAGS@
|
|||||||
CPPFLAGS = @CPPFLAGS@
|
CPPFLAGS = @CPPFLAGS@
|
||||||
LDFLAGS = @LDFLAGS@
|
LDFLAGS = @LDFLAGS@
|
||||||
|
|
||||||
|
GETDATE_CFLAGS = @GETDATE_CFLAGS@
|
||||||
|
|
||||||
EXTRA_OBJS = @EXTRA_OBJS@
|
EXTRA_OBJS = @EXTRA_OBJS@
|
||||||
|
|
||||||
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
|
OBJS = addrfilt.o array.o clientlog.o cmdparse.o conf.o keys.o leapdb.o \
|
||||||
|
local.o logging.o main.o memory.o nameserv.o nameserv_async.o \
|
||||||
|
ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o quantiles.o \
|
||||||
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
|
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
|
||||||
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
|
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
|
||||||
|
|
||||||
@@ -61,6 +65,8 @@ chronyd : $(OBJS)
|
|||||||
chronyc : $(CLI_OBJS)
|
chronyc : $(CLI_OBJS)
|
||||||
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
|
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
|
||||||
|
|
||||||
|
getdate.o: CFLAGS += $(GETDATE_CFLAGS)
|
||||||
|
|
||||||
distclean : clean
|
distclean : clean
|
||||||
$(MAKE) -C doc distclean
|
$(MAKE) -C doc distclean
|
||||||
$(MAKE) -C test/unit distclean
|
$(MAKE) -C test/unit distclean
|
||||||
|
|||||||
167
NEWS
167
NEWS
@@ -1,3 +1,168 @@
|
|||||||
|
New in version 4.8
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add maxunreach option to limit selection of unreachable sources
|
||||||
|
* Add -u option to chronyc to drop root privileges (default chronyc
|
||||||
|
user is set by configure script)
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Hide chronyc socket to mitigate unsafe permissions change
|
||||||
|
* Fix refclock extpps option to work on Linux >= 6.15
|
||||||
|
* Validate refclock samples for reachability updates
|
||||||
|
|
||||||
|
New in version 4.7
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add opencommands directive to select remote monitoring commands
|
||||||
|
* Add interval option to driftfile directive
|
||||||
|
* Add waitsynced and waitunsynced options to local directive
|
||||||
|
* Add sanity checks for integer values in configuration
|
||||||
|
* Add support for systemd Type=notify service
|
||||||
|
* Add RTC refclock driver
|
||||||
|
* Allow PHC refclock to be specified with network interface name
|
||||||
|
* Don't require multiple refclock samples per poll to simplify
|
||||||
|
filter configuration
|
||||||
|
* Keep refclock reachable when dropping samples with large delay
|
||||||
|
* Improve quantile-based filtering to adapt faster to larger delay
|
||||||
|
* Improve logging of selection failures
|
||||||
|
* Detect clock interference from other processes
|
||||||
|
* Try to reopen message log (-l option) on cyclelogs command
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Fix sourcedir reloading to not multiply sources
|
||||||
|
* Fix tracking offset after failed clock step
|
||||||
|
|
||||||
|
Removed features
|
||||||
|
----------------
|
||||||
|
* Drop support for NTS with Nettle < 3.6 and GnuTLS < 3.6.14
|
||||||
|
* Drop support for building without POSIX threads
|
||||||
|
|
||||||
|
New in version 4.6.1
|
||||||
|
====================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add ntsaeads directive to enable only selected AEAD algorithms for NTS
|
||||||
|
|
||||||
|
Workarounds
|
||||||
|
-----------
|
||||||
|
* Negotiate use of compliant NTS keys with AES-128-GCM-SIV AEAD algorithm
|
||||||
|
(by default the keys are generated differently than in RFC 8915 for
|
||||||
|
compatibility with chrony server and client versions 4.4, 4.5, and 4.6)
|
||||||
|
* Switch to compliant NTS keys if first response from server is NTS NAK
|
||||||
|
|
||||||
|
New in version 4.6
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add activate option to local directive to set activation threshold
|
||||||
|
* Add ipv4 and ipv6 options to server/pool/peer directive
|
||||||
|
* Add kod option to ratelimit directive for server KoD RATE support
|
||||||
|
* Add leapseclist directive to read NIST/IERS leap-seconds.list file
|
||||||
|
* Add ptpdomain directive to set PTP domain for NTP over PTP
|
||||||
|
* Allow disabling pidfile
|
||||||
|
* Improve copy server option to accept unsynchronised status instantly
|
||||||
|
* Log one selection failure on start
|
||||||
|
* Add offset command to modify source offset correction
|
||||||
|
* Add timestamp sources to ntpdata report
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Fix crash on sources reload during initstepslew or RTC initialisation
|
||||||
|
* Fix source refreshment to not repeat failed name resolving attempts
|
||||||
|
|
||||||
|
New in version 4.5
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add support for AES-GCM-SIV in GnuTLS
|
||||||
|
* Add support for corrections from PTP transparent clocks
|
||||||
|
* Add support for systemd socket activation
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Fix presend in interleaved mode
|
||||||
|
* Fix reloading of modified sources from sourcedir
|
||||||
|
|
||||||
|
New in version 4.4
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add support for AES-GCM-SIV with Nettle >= 3.9 to shorten NTS
|
||||||
|
cookies to avoid some length-specific blocking of NTP on Internet
|
||||||
|
* Add support for multiple refclocks using extpps option on one PHC
|
||||||
|
* Add maxpoll option to hwtimestamp directive to improve PHC tracking
|
||||||
|
with low packet rates
|
||||||
|
* Add hwtstimeout directive to configure timeout for late timestamps
|
||||||
|
* Handle late hardware transmit timestamps of NTP requests on all sockets
|
||||||
|
* Handle mismatched 32/64-bit time_t in SOCK refclock samples
|
||||||
|
* Improve source replacement
|
||||||
|
* Log important changes made by command requests (chronyc)
|
||||||
|
* Refresh address of NTP sources periodically
|
||||||
|
* Request nanosecond kernel RX timestamping on FreeBSD
|
||||||
|
* Set DSCP for IPv6 packets
|
||||||
|
* Shorten NTS-KE retry interval when network is down
|
||||||
|
* Update seccomp filter for musl
|
||||||
|
* Warn if loading keys from file with unexpected permissions
|
||||||
|
* Warn if source selection fails or falseticker is detected
|
||||||
|
* Add selectopts command to modify source-specific selection options
|
||||||
|
* Add timestamp sources to serverstats report and make its fields 64-bit
|
||||||
|
* Add -e option to chronyc to indicate end of response
|
||||||
|
|
||||||
|
New in version 4.3
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add local option to refclock directive to stabilise system clock
|
||||||
|
with more stable free-running clock (e.g. TCXO, OCXO)
|
||||||
|
* Add maxdelayquant option to server/pool/peer directive to replace
|
||||||
|
maxdelaydevratio filter with long-term quantile-based filtering
|
||||||
|
* Add selection option to log directive
|
||||||
|
* Allow external PPS in PHC refclock without configurable pin
|
||||||
|
* Don't accept first interleaved response to minimise error in delay
|
||||||
|
* Don't use arc4random on Linux to avoid server performance loss
|
||||||
|
* Improve filter option to better handle missing NTP samples
|
||||||
|
* Improve stability with hardware timestamping and PHC refclock
|
||||||
|
* Update seccomp filter
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Fix waitsync command to reconnect when not getting response
|
||||||
|
|
||||||
|
New in version 4.2
|
||||||
|
==================
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
* Add support for NTPv4 extension field improving synchronisation
|
||||||
|
stability and resolution of root delay and dispersion (experimental)
|
||||||
|
* Add support for NTP over PTP (experimental)
|
||||||
|
* Add support for AES-CMAC and hash functions in GnuTLS
|
||||||
|
* Improve server interleaved mode to be more reliable and support
|
||||||
|
multiple clients behind NAT
|
||||||
|
* Update seccomp filter
|
||||||
|
* Add statistics about interleaved mode to serverstats report
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
* Fix RTC support with 64-bit time_t on 32-bit Linux
|
||||||
|
* Fix seccomp filter to work correctly with bind*device directives
|
||||||
|
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
|
||||||
|
|
||||||
|
Other changes
|
||||||
|
-------------
|
||||||
|
* Switch Solaris support to illumos
|
||||||
|
|
||||||
New in version 4.1
|
New in version 4.1
|
||||||
==================
|
==================
|
||||||
|
|
||||||
@@ -12,7 +177,7 @@ Enhancements
|
|||||||
* Increase PPS lock limit to 40% of pulse interval
|
* Increase PPS lock limit to 40% of pulse interval
|
||||||
* Perform source selection immediately after loading dump files
|
* Perform source selection immediately after loading dump files
|
||||||
* Reload dump files for addresses negotiated by NTS-KE server
|
* Reload dump files for addresses negotiated by NTS-KE server
|
||||||
* Update seccomp filter
|
* Update seccomp filter and add less restrictive level
|
||||||
* Restart ongoing name resolution on online command
|
* Restart ongoing name resolution on online command
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
|
|||||||
46
README
46
README
@@ -12,7 +12,7 @@ a time service to other computers in the network.
|
|||||||
It is designed to perform well in a wide range of conditions, including
|
It is designed to perform well in a wide range of conditions, including
|
||||||
intermittent network connections, heavily congested networks, changing
|
intermittent network connections, heavily congested networks, changing
|
||||||
temperatures (ordinary computer clocks are sensitive to temperature),
|
temperatures (ordinary computer clocks are sensitive to temperature),
|
||||||
and systems that do not run continuosly, or run on a virtual machine.
|
and systems that do not run continuously, or run on a virtual machine.
|
||||||
|
|
||||||
Typical accuracy between two machines synchronised over the Internet is
|
Typical accuracy between two machines synchronised over the Internet is
|
||||||
within a few milliseconds; on a LAN, accuracy is typically in tens of
|
within a few milliseconds; on a LAN, accuracy is typically in tens of
|
||||||
@@ -28,7 +28,7 @@ What will chrony run on?
|
|||||||
========================
|
========================
|
||||||
|
|
||||||
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
|
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
|
||||||
Solaris. Closely related systems may work too. Any other system will
|
illumos. Closely related systems may work too. Any other system will
|
||||||
likely require a porting exercise.
|
likely require a porting exercise.
|
||||||
|
|
||||||
How do I set it up?
|
How do I set it up?
|
||||||
@@ -47,32 +47,7 @@ Frequently Asked Questions (FAQ).
|
|||||||
The documentation is also available on the chrony web pages, accessible
|
The documentation is also available on the chrony web pages, accessible
|
||||||
through the URL
|
through the URL
|
||||||
|
|
||||||
https://chrony.tuxfamily.org/
|
https://chrony-project.org/
|
||||||
|
|
||||||
Where are new versions announced?
|
|
||||||
=================================
|
|
||||||
|
|
||||||
There is a low volume mailing list where new versions and other
|
|
||||||
important news relating to chrony are announced. You can join this list
|
|
||||||
by sending mail with the subject "subscribe" to
|
|
||||||
|
|
||||||
chrony-announce-request@chrony.tuxfamily.org
|
|
||||||
|
|
||||||
How can I get support for chrony?
|
|
||||||
=================================
|
|
||||||
|
|
||||||
There are two other mailing lists relating to chrony. chrony-users is a
|
|
||||||
discussion list for users, e.g. for questions about chrony configuration
|
|
||||||
and bug reports. chrony-dev is a more technical list for developers,
|
|
||||||
e.g. for submitting patches and discussing how new features should be
|
|
||||||
implemented. To subscribe to either of these lists, send a message with
|
|
||||||
the subject "subscribe" to
|
|
||||||
|
|
||||||
chrony-users-request@chrony.tuxfamily.org
|
|
||||||
or
|
|
||||||
chrony-dev-request@chrony.tuxfamily.org
|
|
||||||
|
|
||||||
as applicable.
|
|
||||||
|
|
||||||
License
|
License
|
||||||
=======
|
=======
|
||||||
@@ -100,20 +75,28 @@ Lonnie Abelbeck <lonnie@abelbeck.com>
|
|||||||
Benny Lyne Amorsen <benny@amorsen.dk>
|
Benny Lyne Amorsen <benny@amorsen.dk>
|
||||||
Andrew Bishop <amb@gedanken.demon.co.uk>
|
Andrew Bishop <amb@gedanken.demon.co.uk>
|
||||||
Vincent Blut <vincent.debian@free.fr>
|
Vincent Blut <vincent.debian@free.fr>
|
||||||
|
Luca Boccassi <bluca@debian.org>
|
||||||
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
|
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
|
||||||
David Bohman <debohman@gmail.com>
|
David Bohman <debohman@gmail.com>
|
||||||
|
Anthony Brandon <anthony@amarulasolutions.com>
|
||||||
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
|
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
|
||||||
Leigh Brown <leigh@solinno.co.uk>
|
Leigh Brown <leigh@solinno.co.uk>
|
||||||
Erik Bryer <ebryer@spots.ab.ca>
|
Erik Bryer <ebryer@spots.ab.ca>
|
||||||
Jonathan Cameron <jic23@cam.ac.uk>
|
Jonathan Cameron <jic23@cam.ac.uk>
|
||||||
Bryan Christianson <bryan@whatroute.net>
|
Bryan Christianson <bryan@whatroute.net>
|
||||||
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
Juliusz Chroboczek <jch@pps.jussieu.fr>
|
||||||
|
Paul Donald <newtwen+gitlab@gmail.com>
|
||||||
|
Dan Drown <dan-ntp@drown.org>
|
||||||
Kamil Dudka <kdudka@redhat.com>
|
Kamil Dudka <kdudka@redhat.com>
|
||||||
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
Christian Ehrhardt <christian.ehrhardt@canonical.com>
|
||||||
Paul Elliott <pelliott@io.com>
|
Paul Elliott <pelliott@io.com>
|
||||||
Robert Fairley <rfairley@redhat.com>
|
Robert Fairley <rfairley@redhat.com>
|
||||||
|
Ahmad Fatoum <a.fatoum@pengutronix.de>
|
||||||
|
Andreas Fenkart <extern-afe@mission-embedded.com>
|
||||||
Stefan R. Filipek <srfilipek@gmail.com>
|
Stefan R. Filipek <srfilipek@gmail.com>
|
||||||
|
Andy Fiddaman <illumos@fiddaman.net>
|
||||||
Mike Fleetwood <mike@rockover.demon.co.uk>
|
Mike Fleetwood <mike@rockover.demon.co.uk>
|
||||||
|
Rob Gill <rrobgill@protonmail.com>
|
||||||
Alexander Gretencord <arutha@gmx.de>
|
Alexander Gretencord <arutha@gmx.de>
|
||||||
Andrew Griffiths <agriffit@redhat.com>
|
Andrew Griffiths <agriffit@redhat.com>
|
||||||
Walter Haidinger <walter.haidinger@gmx.at>
|
Walter Haidinger <walter.haidinger@gmx.at>
|
||||||
@@ -121,6 +104,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
|
|||||||
John Hasler <john@dhh.gt.org>
|
John Hasler <john@dhh.gt.org>
|
||||||
Tjalling Hattink <t.hattink@fugro.nl>
|
Tjalling Hattink <t.hattink@fugro.nl>
|
||||||
Liam Hatton <me@liamhatton.com>
|
Liam Hatton <me@liamhatton.com>
|
||||||
|
Holger Hoffstätte <holger@applied-asynchrony.com>
|
||||||
Jachym Holecek <jakym@volny.cz>
|
Jachym Holecek <jakym@volny.cz>
|
||||||
Håkan Johansson <f96hajo@chalmers.se>
|
Håkan Johansson <f96hajo@chalmers.se>
|
||||||
Jim Knoble <jmknoble@pobox.com>
|
Jim Knoble <jmknoble@pobox.com>
|
||||||
@@ -134,17 +118,23 @@ Paul Menzel <paulepanter@users.sourceforge.net>
|
|||||||
Vladimir Michl <vladimir.michl@seznam.cz>
|
Vladimir Michl <vladimir.michl@seznam.cz>
|
||||||
Victor Moroz <vim@prv.adlum.ru>
|
Victor Moroz <vim@prv.adlum.ru>
|
||||||
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
|
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
|
||||||
|
Patrick Oppenlander <patrick.oppenlander@gmail.com>
|
||||||
Frank Otto <sandwichmacher@web.de>
|
Frank Otto <sandwichmacher@web.de>
|
||||||
Denny Page <dennypage@me.com>
|
Denny Page <dennypage@me.com>
|
||||||
|
Rupesh Patel <rupatel@redhat.com>
|
||||||
Chris Perl <cperl@janestreet.com>
|
Chris Perl <cperl@janestreet.com>
|
||||||
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
|
||||||
Andreas Piesk <apiesk@virbus.de>
|
Andreas Piesk <apiesk@virbus.de>
|
||||||
|
Shachar Raindel <shacharr@google.com>
|
||||||
|
Mike Ryan <msr@hsilop.net>
|
||||||
Baruch Siach <baruch@tkos.co.il>
|
Baruch Siach <baruch@tkos.co.il>
|
||||||
|
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
|
||||||
Foster Snowhill <forst@forstwoof.ru>
|
Foster Snowhill <forst@forstwoof.ru>
|
||||||
Andreas Steinmetz <ast@domdv.de>
|
Andreas Steinmetz <ast@domdv.de>
|
||||||
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
|
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
|
||||||
Timo Teras <timo.teras@iki.fi>
|
Timo Teras <timo.teras@iki.fi>
|
||||||
Bill Unruh <unruh@physics.ubc.ca>
|
Bill Unruh <unruh@physics.ubc.ca>
|
||||||
|
Luke Valenta <lvalenta@cloudflare.com>
|
||||||
Stephen Wadeley <swadeley@redhat.com>
|
Stephen Wadeley <swadeley@redhat.com>
|
||||||
Bernhard Weiss <lisnablagh@web.de>
|
Bernhard Weiss <lisnablagh@web.de>
|
||||||
Wolfgang Weisselberg <weissel@netcologne.de>
|
Wolfgang Weisselberg <weissel@netcologne.de>
|
||||||
|
|||||||
15
array.c
15
array.c
@@ -116,6 +116,21 @@ ARR_AppendElement(ARR_Instance array, void *element)
|
|||||||
memcpy(e, element, array->elem_size);
|
memcpy(e, element, array->elem_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ARR_RemoveElement(ARR_Instance array, unsigned int index)
|
||||||
|
{
|
||||||
|
void *e, *l;
|
||||||
|
|
||||||
|
e = ARR_GetElement(array, index);
|
||||||
|
l = ARR_GetElement(array, array->used - 1);
|
||||||
|
|
||||||
|
if (e < l)
|
||||||
|
memmove(e, (char *)e + array->elem_size, (char *)l - (char *)e);
|
||||||
|
array->used--;
|
||||||
|
|
||||||
|
realloc_array(array, array->used);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ARR_SetSize(ARR_Instance array, unsigned int size)
|
ARR_SetSize(ARR_Instance array, unsigned int size)
|
||||||
{
|
{
|
||||||
|
|||||||
3
array.h
3
array.h
@@ -47,6 +47,9 @@ extern void *ARR_GetElements(ARR_Instance array);
|
|||||||
/* Add a new element to the end of the array */
|
/* Add a new element to the end of the array */
|
||||||
extern void ARR_AppendElement(ARR_Instance array, void *element);
|
extern void ARR_AppendElement(ARR_Instance array, void *element);
|
||||||
|
|
||||||
|
/* Remove element with given index */
|
||||||
|
extern void ARR_RemoveElement(ARR_Instance array, unsigned int index);
|
||||||
|
|
||||||
/* Set the size of the array */
|
/* Set the size of the array */
|
||||||
extern void ARR_SetSize(ARR_Instance array, unsigned int size);
|
extern void ARR_SetSize(ARR_Instance array, unsigned int size);
|
||||||
|
|
||||||
|
|||||||
92
candm.h
92
candm.h
@@ -37,7 +37,6 @@
|
|||||||
#define DEFAULT_CANDM_PORT 323
|
#define DEFAULT_CANDM_PORT 323
|
||||||
|
|
||||||
/* Request codes */
|
/* Request codes */
|
||||||
#define REQ_NULL 0
|
|
||||||
#define REQ_ONLINE 1
|
#define REQ_ONLINE 1
|
||||||
#define REQ_OFFLINE 2
|
#define REQ_OFFLINE 2
|
||||||
#define REQ_BURST 3
|
#define REQ_BURST 3
|
||||||
@@ -109,7 +108,10 @@
|
|||||||
#define REQ_SELECT_DATA 69
|
#define REQ_SELECT_DATA 69
|
||||||
#define REQ_RELOAD_SOURCES 70
|
#define REQ_RELOAD_SOURCES 70
|
||||||
#define REQ_DOFFSET2 71
|
#define REQ_DOFFSET2 71
|
||||||
#define N_REQUEST_TYPES 72
|
#define REQ_MODIFY_SELECTOPTS 72
|
||||||
|
#define REQ_MODIFY_OFFSET 73
|
||||||
|
#define REQ_LOCAL3 74
|
||||||
|
#define N_REQUEST_TYPES 75
|
||||||
|
|
||||||
/* Structure used to exchange timespecs independent of time_t size */
|
/* Structure used to exchange timespecs independent of time_t size */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -121,6 +123,12 @@ typedef struct {
|
|||||||
/* This is used in tv_sec_high for 32-bit timestamps */
|
/* This is used in tv_sec_high for 32-bit timestamps */
|
||||||
#define TV_NOHIGHSEC 0x7fffffff
|
#define TV_NOHIGHSEC 0x7fffffff
|
||||||
|
|
||||||
|
/* Structure for 64-bit integers (not requiring 64-bit alignment) */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t high;
|
||||||
|
uint32_t low;
|
||||||
|
} Integer64;
|
||||||
|
|
||||||
/* 32-bit floating-point format consisting of 7-bit signed exponent
|
/* 32-bit floating-point format consisting of 7-bit signed exponent
|
||||||
and 25-bit signed coefficient without hidden bit.
|
and 25-bit signed coefficient without hidden bit.
|
||||||
The result is calculated as: 2^(exp - 25) * coef */
|
The result is calculated as: 2^(exp - 25) * coef */
|
||||||
@@ -214,11 +222,6 @@ typedef struct {
|
|||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} REQ_Modify_Makestep;
|
} REQ_Modify_Makestep;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Timespec ts;
|
|
||||||
int32_t EOR;
|
|
||||||
} REQ_Logon;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Timespec ts;
|
Timespec ts;
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
@@ -229,6 +232,9 @@ typedef struct {
|
|||||||
int32_t stratum;
|
int32_t stratum;
|
||||||
Float distance;
|
Float distance;
|
||||||
int32_t orphan;
|
int32_t orphan;
|
||||||
|
Float activate;
|
||||||
|
Float wait_synced;
|
||||||
|
Float wait_unsynced;
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} REQ_Local;
|
} REQ_Local;
|
||||||
|
|
||||||
@@ -270,6 +276,10 @@ typedef struct {
|
|||||||
#define REQ_ADDSRC_BURST 0x100
|
#define REQ_ADDSRC_BURST 0x100
|
||||||
#define REQ_ADDSRC_NTS 0x200
|
#define REQ_ADDSRC_NTS 0x200
|
||||||
#define REQ_ADDSRC_COPY 0x400
|
#define REQ_ADDSRC_COPY 0x400
|
||||||
|
#define REQ_ADDSRC_EF_EXP_MONO_ROOT 0x800
|
||||||
|
#define REQ_ADDSRC_EF_EXP_NET_CORRECTION 0x1000
|
||||||
|
#define REQ_ADDSRC_IPV4 0x2000
|
||||||
|
#define REQ_ADDSRC_IPV6 0x4000
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
@@ -295,7 +305,8 @@ typedef struct {
|
|||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
int32_t filter_length;
|
int32_t filter_length;
|
||||||
uint32_t cert_set;
|
uint32_t cert_set;
|
||||||
uint32_t reserved[2];
|
Float max_delay_quant;
|
||||||
|
int32_t max_unreach;
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} REQ_NTP_Source;
|
} REQ_NTP_Source;
|
||||||
|
|
||||||
@@ -369,6 +380,22 @@ typedef struct {
|
|||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} REQ_SelectData;
|
} REQ_SelectData;
|
||||||
|
|
||||||
|
/* Mask and options reuse the REQ_ADDSRC flags */
|
||||||
|
typedef struct {
|
||||||
|
IPAddr address;
|
||||||
|
uint32_t ref_id;
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t options;
|
||||||
|
int32_t EOR;
|
||||||
|
} REQ_Modify_SelectOpts;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
IPAddr address;
|
||||||
|
uint32_t ref_id;
|
||||||
|
Float new_offset;
|
||||||
|
int32_t EOR;
|
||||||
|
} REQ_Modify_Offset;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
#define PKT_TYPE_CMD_REQUEST 1
|
#define PKT_TYPE_CMD_REQUEST 1
|
||||||
@@ -455,7 +482,6 @@ typedef struct {
|
|||||||
REQ_Modify_Polltarget modify_polltarget;
|
REQ_Modify_Polltarget modify_polltarget;
|
||||||
REQ_Modify_Maxupdateskew modify_maxupdateskew;
|
REQ_Modify_Maxupdateskew modify_maxupdateskew;
|
||||||
REQ_Modify_Makestep modify_makestep;
|
REQ_Modify_Makestep modify_makestep;
|
||||||
REQ_Logon logon;
|
|
||||||
REQ_Settime settime;
|
REQ_Settime settime;
|
||||||
REQ_Local local;
|
REQ_Local local;
|
||||||
REQ_Manual manual;
|
REQ_Manual manual;
|
||||||
@@ -475,6 +501,8 @@ typedef struct {
|
|||||||
REQ_NTPSourceName ntp_source_name;
|
REQ_NTPSourceName ntp_source_name;
|
||||||
REQ_AuthData auth_data;
|
REQ_AuthData auth_data;
|
||||||
REQ_SelectData select_data;
|
REQ_SelectData select_data;
|
||||||
|
REQ_Modify_SelectOpts modify_select_opts;
|
||||||
|
REQ_Modify_Offset modify_offset;
|
||||||
} data; /* Command specific parameters */
|
} data; /* Command specific parameters */
|
||||||
|
|
||||||
/* Padding used to prevent traffic amplification. It only defines the
|
/* Padding used to prevent traffic amplification. It only defines the
|
||||||
@@ -483,13 +511,6 @@ typedef struct {
|
|||||||
|
|
||||||
} CMD_Request;
|
} CMD_Request;
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
/* Authority codes for command types */
|
|
||||||
|
|
||||||
#define PERMIT_OPEN 0
|
|
||||||
#define PERMIT_LOCAL 1
|
|
||||||
#define PERMIT_AUTH 2
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
/* Reply codes */
|
/* Reply codes */
|
||||||
@@ -516,7 +537,10 @@ typedef struct {
|
|||||||
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
|
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
|
||||||
#define RPY_SERVER_STATS2 22
|
#define RPY_SERVER_STATS2 22
|
||||||
#define RPY_SELECT_DATA 23
|
#define RPY_SELECT_DATA 23
|
||||||
#define N_REPLY_TYPES 24
|
#define RPY_SERVER_STATS3 24
|
||||||
|
#define RPY_SERVER_STATS4 25
|
||||||
|
#define RPY_NTP_DATA2 26
|
||||||
|
#define N_REPLY_TYPES 27
|
||||||
|
|
||||||
/* Status codes */
|
/* Status codes */
|
||||||
#define STT_SUCCESS 0
|
#define STT_SUCCESS 0
|
||||||
@@ -529,8 +553,7 @@ typedef struct {
|
|||||||
#define STT_BADSUBNET 7
|
#define STT_BADSUBNET 7
|
||||||
#define STT_ACCESSALLOWED 8
|
#define STT_ACCESSALLOWED 8
|
||||||
#define STT_ACCESSDENIED 9
|
#define STT_ACCESSDENIED 9
|
||||||
/* Deprecated */
|
#define STT_NOHOSTACCESS 10 /* Deprecated */
|
||||||
#define STT_NOHOSTACCESS 10
|
|
||||||
#define STT_SOURCEALREADYKNOWN 11
|
#define STT_SOURCEALREADYKNOWN 11
|
||||||
#define STT_TOOMANYSOURCES 12
|
#define STT_TOOMANYSOURCES 12
|
||||||
#define STT_NORTC 13
|
#define STT_NORTC 13
|
||||||
@@ -652,14 +675,24 @@ typedef struct {
|
|||||||
} RPY_ClientAccessesByIndex;
|
} RPY_ClientAccessesByIndex;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t ntp_hits;
|
Integer64 ntp_hits;
|
||||||
uint32_t nke_hits;
|
Integer64 nke_hits;
|
||||||
uint32_t cmd_hits;
|
Integer64 cmd_hits;
|
||||||
uint32_t ntp_drops;
|
Integer64 ntp_drops;
|
||||||
uint32_t nke_drops;
|
Integer64 nke_drops;
|
||||||
uint32_t cmd_drops;
|
Integer64 cmd_drops;
|
||||||
uint32_t log_drops;
|
Integer64 log_drops;
|
||||||
uint32_t ntp_auth_hits;
|
Integer64 ntp_auth_hits;
|
||||||
|
Integer64 ntp_interleaved_hits;
|
||||||
|
Integer64 ntp_timestamps;
|
||||||
|
Integer64 ntp_span_seconds;
|
||||||
|
Integer64 ntp_daemon_rx_timestamps;
|
||||||
|
Integer64 ntp_daemon_tx_timestamps;
|
||||||
|
Integer64 ntp_kernel_rx_timestamps;
|
||||||
|
Integer64 ntp_kernel_tx_timestamps;
|
||||||
|
Integer64 ntp_hw_rx_timestamps;
|
||||||
|
Integer64 ntp_hw_tx_timestamps;
|
||||||
|
Integer64 reserved[4];
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} RPY_ServerStats;
|
} RPY_ServerStats;
|
||||||
|
|
||||||
@@ -729,6 +762,11 @@ typedef struct {
|
|||||||
uint32_t total_tx_count;
|
uint32_t total_tx_count;
|
||||||
uint32_t total_rx_count;
|
uint32_t total_rx_count;
|
||||||
uint32_t total_valid_count;
|
uint32_t total_valid_count;
|
||||||
|
uint32_t total_good_count;
|
||||||
|
uint32_t total_kernel_tx_ts;
|
||||||
|
uint32_t total_kernel_rx_ts;
|
||||||
|
uint32_t total_hw_tx_ts;
|
||||||
|
uint32_t total_hw_rx_ts;
|
||||||
uint32_t reserved[4];
|
uint32_t reserved[4];
|
||||||
int32_t EOR;
|
int32_t EOR;
|
||||||
} RPY_NTPData;
|
} RPY_NTPData;
|
||||||
|
|||||||
486
clientlog.c
486
clientlog.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
|
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "array.h"
|
#include "array.h"
|
||||||
#include "clientlog.h"
|
#include "clientlog.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
#include "local.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "ntp.h"
|
#include "ntp.h"
|
||||||
#include "reports.h"
|
#include "reports.h"
|
||||||
@@ -55,8 +56,6 @@ typedef struct {
|
|||||||
int8_t rate[MAX_SERVICES];
|
int8_t rate[MAX_SERVICES];
|
||||||
int8_t ntp_timeout_rate;
|
int8_t ntp_timeout_rate;
|
||||||
uint8_t drop_flags;
|
uint8_t drop_flags;
|
||||||
NTP_int64 ntp_rx_ts;
|
|
||||||
NTP_int64 ntp_tx_ts;
|
|
||||||
} Record;
|
} Record;
|
||||||
|
|
||||||
/* Hash table of records, there is a fixed number of records per slot */
|
/* Hash table of records, there is a fixed number of records per slot */
|
||||||
@@ -118,23 +117,72 @@ static int token_shift[MAX_SERVICES];
|
|||||||
|
|
||||||
static int leak_rate[MAX_SERVICES];
|
static int leak_rate[MAX_SERVICES];
|
||||||
|
|
||||||
|
/* Rates at which responses requesting clients to reduce their rate
|
||||||
|
(e.g. NTP KoD RATE) are randomly allowed (in log2, but 0 means disabled) */
|
||||||
|
|
||||||
|
#define MIN_KOD_RATE 0
|
||||||
|
#define MAX_KOD_RATE 4
|
||||||
|
|
||||||
|
static int kod_rate[MAX_SERVICES];
|
||||||
|
|
||||||
/* Limit intervals in log2 */
|
/* Limit intervals in log2 */
|
||||||
static int limit_interval[MAX_SERVICES];
|
static int limit_interval[MAX_SERVICES];
|
||||||
|
|
||||||
/* Flag indicating whether facility is turned on or not */
|
/* Flag indicating whether facility is turned on or not */
|
||||||
static int active;
|
static int active;
|
||||||
|
|
||||||
|
/* RX and TX timestamp saved for clients using interleaved mode */
|
||||||
|
typedef struct {
|
||||||
|
uint64_t rx_ts;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t tx_ts_source;
|
||||||
|
uint16_t slew_epoch;
|
||||||
|
int32_t tx_ts_offset;
|
||||||
|
} NtpTimestamps;
|
||||||
|
|
||||||
|
/* Flags for NTP timestamps */
|
||||||
|
#define NTPTS_DISABLED 1
|
||||||
|
#define NTPTS_VALID_TX 2
|
||||||
|
|
||||||
|
/* RX->TX map using a circular buffer with ordered timestamps */
|
||||||
|
typedef struct {
|
||||||
|
ARR_Instance timestamps;
|
||||||
|
uint32_t first;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t max_size;
|
||||||
|
uint32_t cached_index;
|
||||||
|
uint64_t cached_rx_ts;
|
||||||
|
uint16_t slew_epoch;
|
||||||
|
double slew_offset;
|
||||||
|
} NtpTimestampMap;
|
||||||
|
|
||||||
|
static NtpTimestampMap ntp_ts_map;
|
||||||
|
|
||||||
|
/* Maximum interval of NTP timestamps in future after a backward step */
|
||||||
|
#define NTPTS_FUTURE_LIMIT (1LL << 32) /* 1 second */
|
||||||
|
|
||||||
|
/* Maximum number of timestamps moved in the array to insert a new timestamp */
|
||||||
|
#define NTPTS_INSERT_LIMIT 64
|
||||||
|
|
||||||
|
/* Maximum expected value of the timestamp source */
|
||||||
|
#define MAX_NTP_TS NTP_TS_HARDWARE
|
||||||
|
|
||||||
/* Global statistics */
|
/* Global statistics */
|
||||||
static uint32_t total_hits[MAX_SERVICES];
|
static uint64_t total_hits[MAX_SERVICES];
|
||||||
static uint32_t total_drops[MAX_SERVICES];
|
static uint64_t total_drops[MAX_SERVICES];
|
||||||
static uint32_t total_ntp_auth_hits;
|
static uint64_t total_ntp_auth_hits;
|
||||||
static uint32_t total_record_drops;
|
static uint64_t total_ntp_interleaved_hits;
|
||||||
|
static uint64_t total_record_drops;
|
||||||
|
static uint64_t total_ntp_rx_timestamps[MAX_NTP_TS + 1];
|
||||||
|
static uint64_t total_ntp_tx_timestamps[MAX_NTP_TS + 1];
|
||||||
|
|
||||||
#define NSEC_PER_SEC 1000000000U
|
#define NSEC_PER_SEC 1000000000U
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int expand_hashtable(void);
|
static int expand_hashtable(void);
|
||||||
|
static void handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||||
|
double doffset, LCL_ChangeType change_type, void *anything);
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
@@ -217,20 +265,15 @@ get_record(IPAddr *ip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
record->ip_addr = *ip;
|
record->ip_addr = *ip;
|
||||||
for (i = 0; i < MAX_SERVICES; i++)
|
for (i = 0; i < MAX_SERVICES; i++) {
|
||||||
record->last_hit[i] = INVALID_TS;
|
record->last_hit[i] = INVALID_TS;
|
||||||
for (i = 0; i < MAX_SERVICES; i++)
|
|
||||||
record->hits[i] = 0;
|
record->hits[i] = 0;
|
||||||
for (i = 0; i < MAX_SERVICES; i++)
|
|
||||||
record->drops[i] = 0;
|
record->drops[i] = 0;
|
||||||
for (i = 0; i < MAX_SERVICES; i++)
|
|
||||||
record->tokens[i] = max_tokens[i];
|
record->tokens[i] = max_tokens[i];
|
||||||
for (i = 0; i < MAX_SERVICES; i++)
|
|
||||||
record->rate[i] = INVALID_RATE;
|
record->rate[i] = INVALID_RATE;
|
||||||
|
}
|
||||||
record->ntp_timeout_rate = INVALID_RATE;
|
record->ntp_timeout_rate = INVALID_RATE;
|
||||||
record->drop_flags = 0;
|
record->drop_flags = 0;
|
||||||
UTI_ZeroNtp64(&record->ntp_rx_ts);
|
|
||||||
UTI_ZeroNtp64(&record->ntp_tx_ts);
|
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
@@ -316,18 +359,19 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
|
|||||||
void
|
void
|
||||||
CLG_Initialise(void)
|
CLG_Initialise(void)
|
||||||
{
|
{
|
||||||
int i, interval, burst, lrate;
|
int i, interval, burst, lrate, krate, slots2;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SERVICES; i++) {
|
for (i = 0; i < MAX_SERVICES; i++) {
|
||||||
max_tokens[i] = 0;
|
max_tokens[i] = 0;
|
||||||
tokens_per_hit[i] = 0;
|
tokens_per_hit[i] = 0;
|
||||||
token_shift[i] = 0;
|
token_shift[i] = 0;
|
||||||
leak_rate[i] = 0;
|
leak_rate[i] = 0;
|
||||||
|
kod_rate[i] = 0;
|
||||||
limit_interval[i] = MIN_LIMIT_INTERVAL;
|
limit_interval[i] = MIN_LIMIT_INTERVAL;
|
||||||
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case CLG_NTP:
|
case CLG_NTP:
|
||||||
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
|
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate, &krate))
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
case CLG_NTSKE:
|
case CLG_NTSKE:
|
||||||
@@ -344,6 +388,7 @@ CLG_Initialise(void)
|
|||||||
|
|
||||||
set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
|
set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
|
||||||
leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
|
leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
|
||||||
|
kod_rate[i] = CLAMP(MIN_KOD_RATE, krate, MAX_KOD_RATE);
|
||||||
limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
|
limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,9 +404,13 @@ CLG_Initialise(void)
|
|||||||
/* Calculate the maximum number of slots that can be allocated in the
|
/* Calculate the maximum number of slots that can be allocated in the
|
||||||
configured memory limit. Take into account expanding of the hash
|
configured memory limit. Take into account expanding of the hash
|
||||||
table where two copies exist at the same time. */
|
table where two copies exist at the same time. */
|
||||||
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2);
|
max_slots = CNF_GetClientLogLimit() /
|
||||||
|
((sizeof (Record) + sizeof (NtpTimestamps)) * SLOT_SIZE * 3 / 2);
|
||||||
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
|
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
|
||||||
DEBUG_LOG("Max records %u", 1U << ((int)round(log(max_slots) / log(2)) + SLOT_BITS));
|
for (slots2 = 0; 1U << (slots2 + 1) <= max_slots; slots2++)
|
||||||
|
;
|
||||||
|
|
||||||
|
DEBUG_LOG("Max records %u", 1U << (slots2 + SLOT_BITS));
|
||||||
|
|
||||||
slots = 0;
|
slots = 0;
|
||||||
records = NULL;
|
records = NULL;
|
||||||
@@ -370,6 +419,17 @@ CLG_Initialise(void)
|
|||||||
|
|
||||||
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
|
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
|
||||||
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
|
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
|
||||||
|
|
||||||
|
ntp_ts_map.timestamps = NULL;
|
||||||
|
ntp_ts_map.first = 0;
|
||||||
|
ntp_ts_map.size = 0;
|
||||||
|
ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
|
||||||
|
ntp_ts_map.cached_index = 0;
|
||||||
|
ntp_ts_map.cached_rx_ts = 0ULL;
|
||||||
|
ntp_ts_map.slew_epoch = 0;
|
||||||
|
ntp_ts_map.slew_offset = 0.0;
|
||||||
|
|
||||||
|
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -381,6 +441,10 @@ CLG_Finalise(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ARR_DestroyInstance(records);
|
ARR_DestroyInstance(records);
|
||||||
|
if (ntp_ts_map.timestamps)
|
||||||
|
ARR_DestroyInstance(ntp_ts_map.timestamps);
|
||||||
|
|
||||||
|
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -522,28 +586,28 @@ CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
limit_response_random(int leak_rate)
|
limit_response_random(int rate)
|
||||||
{
|
{
|
||||||
static uint32_t rnd;
|
static uint32_t rnd;
|
||||||
static int bits_left = 0;
|
static int bits_left = 0;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (bits_left < leak_rate) {
|
if (bits_left < rate) {
|
||||||
UTI_GetRandomBytes(&rnd, sizeof (rnd));
|
UTI_GetRandomBytes(&rnd, sizeof (rnd));
|
||||||
bits_left = 8 * sizeof (rnd);
|
bits_left = 8 * sizeof (rnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return zero on average once per 2^leak_rate */
|
/* Return zero on average once per 2^rate */
|
||||||
r = rnd % (1U << leak_rate) ? 1 : 0;
|
r = rnd % (1U << rate) ? 1 : 0;
|
||||||
rnd >>= leak_rate;
|
rnd >>= rate;
|
||||||
bits_left -= leak_rate;
|
bits_left -= rate;
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
CLG_Limit
|
||||||
CLG_LimitServiceRate(CLG_Service service, int index)
|
CLG_LimitServiceRate(CLG_Service service, int index)
|
||||||
{
|
{
|
||||||
Record *record;
|
Record *record;
|
||||||
@@ -552,14 +616,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
|
|||||||
check_service_number(service);
|
check_service_number(service);
|
||||||
|
|
||||||
if (tokens_per_hit[service] == 0)
|
if (tokens_per_hit[service] == 0)
|
||||||
return 0;
|
return CLG_PASS;
|
||||||
|
|
||||||
record = ARR_GetElement(records, index);
|
record = ARR_GetElement(records, index);
|
||||||
record->drop_flags &= ~(1U << service);
|
record->drop_flags &= ~(1U << service);
|
||||||
|
|
||||||
if (record->tokens[service] >= tokens_per_hit[service]) {
|
if (record->tokens[service] >= tokens_per_hit[service]) {
|
||||||
record->tokens[service] -= tokens_per_hit[service];
|
record->tokens[service] -= tokens_per_hit[service];
|
||||||
return 0;
|
return CLG_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
drop = limit_response_random(leak_rate[service]);
|
drop = limit_response_random(leak_rate[service]);
|
||||||
@@ -575,34 +639,31 @@ CLG_LimitServiceRate(CLG_Service service, int index)
|
|||||||
|
|
||||||
if (!drop) {
|
if (!drop) {
|
||||||
record->tokens[service] = 0;
|
record->tokens[service] = 0;
|
||||||
return 0;
|
return CLG_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kod_rate[service] > 0 && !limit_response_random(kod_rate[service])) {
|
||||||
|
return CLG_KOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
record->drop_flags |= 1U << service;
|
record->drop_flags |= 1U << service;
|
||||||
record->drops[service]++;
|
record->drops[service]++;
|
||||||
total_drops[service]++;
|
total_drops[service]++;
|
||||||
|
|
||||||
return 1;
|
return CLG_DROP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
CLG_LogAuthNtpRequest(void)
|
CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src, NTP_Timestamp_Source tx_ts_src)
|
||||||
{
|
{
|
||||||
|
if (auth)
|
||||||
total_ntp_auth_hits++;
|
total_ntp_auth_hits++;
|
||||||
}
|
if (rx_ts_src >= 0 && rx_ts_src <= MAX_NTP_TS)
|
||||||
|
total_ntp_rx_timestamps[rx_ts_src]++;
|
||||||
/* ================================================== */
|
if (tx_ts_src >= 0 && tx_ts_src <= MAX_NTP_TS)
|
||||||
|
total_ntp_tx_timestamps[tx_ts_src]++;
|
||||||
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts)
|
|
||||||
{
|
|
||||||
Record *record;
|
|
||||||
|
|
||||||
record = ARR_GetElement(records, index);
|
|
||||||
|
|
||||||
*rx_ts = &record->ntp_rx_ts;
|
|
||||||
*tx_ts = &record->ntp_tx_ts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -615,6 +676,336 @@ CLG_GetNtpMinPoll(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static NtpTimestamps *
|
||||||
|
get_ntp_tss(uint32_t index)
|
||||||
|
{
|
||||||
|
return ARR_GetElement(ntp_ts_map.timestamps,
|
||||||
|
(ntp_ts_map.first + index) & (ntp_ts_map.max_size - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
find_ntp_rx_ts(uint64_t rx_ts, uint32_t *index)
|
||||||
|
{
|
||||||
|
uint64_t rx_x, rx_lo, rx_hi, step;
|
||||||
|
uint32_t i, x, lo, hi;
|
||||||
|
|
||||||
|
if (ntp_ts_map.cached_rx_ts == rx_ts && rx_ts != 0ULL) {
|
||||||
|
*index = ntp_ts_map.cached_index;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntp_ts_map.size == 0) {
|
||||||
|
*index = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lo = 0;
|
||||||
|
hi = ntp_ts_map.size - 1;
|
||||||
|
rx_lo = get_ntp_tss(lo)->rx_ts;
|
||||||
|
rx_hi = get_ntp_tss(hi)->rx_ts;
|
||||||
|
|
||||||
|
/* Check for ts < lo before ts > hi to trim timestamps from "future" later
|
||||||
|
if both conditions are true to not break the order of the endpoints.
|
||||||
|
Compare timestamps by their difference to allow adjacent NTP eras. */
|
||||||
|
if ((int64_t)(rx_ts - rx_lo) < 0) {
|
||||||
|
*index = 0;
|
||||||
|
return 0;
|
||||||
|
} else if ((int64_t)(rx_ts - rx_hi) > 0) {
|
||||||
|
*index = ntp_ts_map.size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform a combined linear interpolation and binary search */
|
||||||
|
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
if (rx_ts == rx_hi) {
|
||||||
|
*index = ntp_ts_map.cached_index = hi;
|
||||||
|
ntp_ts_map.cached_rx_ts = rx_ts;
|
||||||
|
return 1;
|
||||||
|
} else if (rx_ts == rx_lo) {
|
||||||
|
*index = ntp_ts_map.cached_index = lo;
|
||||||
|
ntp_ts_map.cached_rx_ts = rx_ts;
|
||||||
|
return 1;
|
||||||
|
} else if (lo + 1 == hi) {
|
||||||
|
*index = hi;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hi - lo > 3 && i % 2 == 0) {
|
||||||
|
step = (rx_hi - rx_lo) / (hi - lo);
|
||||||
|
if (step == 0)
|
||||||
|
step = 1;
|
||||||
|
x = lo + (rx_ts - rx_lo) / step;
|
||||||
|
} else {
|
||||||
|
x = lo + (hi - lo) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x <= lo)
|
||||||
|
x = lo + 1;
|
||||||
|
else if (x >= hi)
|
||||||
|
x = hi - 1;
|
||||||
|
|
||||||
|
rx_x = get_ntp_tss(x)->rx_ts;
|
||||||
|
|
||||||
|
if ((int64_t)(rx_x - rx_ts) <= 0) {
|
||||||
|
lo = x;
|
||||||
|
rx_lo = rx_x;
|
||||||
|
} else {
|
||||||
|
hi = x;
|
||||||
|
rx_hi = rx_x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static uint64_t
|
||||||
|
ntp64_to_int64(NTP_int64 *ts)
|
||||||
|
{
|
||||||
|
return (uint64_t)ntohl(ts->hi) << 32 | ntohl(ts->lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
|
||||||
|
{
|
||||||
|
ntp_ts->hi = htonl(ts >> 32);
|
||||||
|
ntp_ts->lo = htonl(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static uint32_t
|
||||||
|
push_ntp_tss(uint32_t index)
|
||||||
|
{
|
||||||
|
if (ntp_ts_map.size < ntp_ts_map.max_size) {
|
||||||
|
ntp_ts_map.size++;
|
||||||
|
} else {
|
||||||
|
ntp_ts_map.first = (ntp_ts_map.first + 1) % (ntp_ts_map.max_size);
|
||||||
|
if (index > 0)
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_ntp_tx(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
if (!tx_ts) {
|
||||||
|
tss->flags &= ~NTPTS_VALID_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTI_Ntp64ToTimespec(rx_ts, &ts);
|
||||||
|
UTI_DiffTimespecs(&ts, tx_ts, &ts);
|
||||||
|
|
||||||
|
if (ts.tv_sec < -2 || ts.tv_sec > 1) {
|
||||||
|
tss->flags &= ~NTPTS_VALID_TX;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
|
||||||
|
tss->flags |= NTPTS_VALID_TX;
|
||||||
|
tss->tx_ts_source = tx_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts, NTP_Timestamp_Source *tx_src)
|
||||||
|
{
|
||||||
|
int32_t offset = tss->tx_ts_offset;
|
||||||
|
NTP_int64 ntp_ts;
|
||||||
|
|
||||||
|
if (tss->flags & NTPTS_VALID_TX) {
|
||||||
|
int64_to_ntp64(tss->rx_ts, &ntp_ts);
|
||||||
|
UTI_Ntp64ToTimespec(&ntp_ts, tx_ts);
|
||||||
|
if (offset >= (int32_t)NSEC_PER_SEC) {
|
||||||
|
offset -= NSEC_PER_SEC;
|
||||||
|
tx_ts->tv_sec++;
|
||||||
|
}
|
||||||
|
tx_ts->tv_nsec += offset;
|
||||||
|
UTI_NormaliseTimespec(tx_ts);
|
||||||
|
} else {
|
||||||
|
UTI_ZeroTimespec(tx_ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
*tx_src = tss->tx_ts_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts, NTP_Timestamp_Source tx_src)
|
||||||
|
{
|
||||||
|
NtpTimestamps *tss;
|
||||||
|
uint32_t i, index;
|
||||||
|
uint64_t rx;
|
||||||
|
|
||||||
|
if (!active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Allocate the array on first use */
|
||||||
|
if (!ntp_ts_map.timestamps) {
|
||||||
|
ntp_ts_map.timestamps = ARR_CreateInstance(sizeof (NtpTimestamps));
|
||||||
|
ARR_SetSize(ntp_ts_map.timestamps, ntp_ts_map.max_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
rx = ntp64_to_int64(rx_ts);
|
||||||
|
|
||||||
|
if (rx == 0ULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Disable the RX timestamp if it already exists to avoid responding
|
||||||
|
with a wrong TX timestamp */
|
||||||
|
if (find_ntp_rx_ts(rx, &index)) {
|
||||||
|
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(index <= ntp_ts_map.size);
|
||||||
|
|
||||||
|
if (index == ntp_ts_map.size) {
|
||||||
|
/* Increase the size or drop the oldest timestamp to make room for
|
||||||
|
the new timestamp */
|
||||||
|
index = push_ntp_tss(index);
|
||||||
|
} else {
|
||||||
|
/* Trim timestamps in distant future after backward step */
|
||||||
|
while (index < ntp_ts_map.size &&
|
||||||
|
get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - rx > NTPTS_FUTURE_LIMIT)
|
||||||
|
ntp_ts_map.size--;
|
||||||
|
|
||||||
|
/* Insert the timestamp if it is close to the latest timestamp.
|
||||||
|
Otherwise, replace the closest older or the oldest timestamp. */
|
||||||
|
if (index + NTPTS_INSERT_LIMIT >= ntp_ts_map.size) {
|
||||||
|
index = push_ntp_tss(index);
|
||||||
|
for (i = ntp_ts_map.size - 1; i > index; i--)
|
||||||
|
*get_ntp_tss(i) = *get_ntp_tss(i - 1);
|
||||||
|
} else {
|
||||||
|
if (index > 0)
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ntp_ts_map.cached_index = index;
|
||||||
|
ntp_ts_map.cached_rx_ts = rx;
|
||||||
|
|
||||||
|
tss = get_ntp_tss(index);
|
||||||
|
tss->rx_ts = rx;
|
||||||
|
tss->flags = 0;
|
||||||
|
tss->slew_epoch = ntp_ts_map.slew_epoch;
|
||||||
|
set_ntp_tx(tss, rx_ts, tx_ts, tx_src);
|
||||||
|
|
||||||
|
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
|
||||||
|
index, ntp_ts_map.first, ntp_ts_map.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||||
|
double doffset, LCL_ChangeType change_type, void *anything)
|
||||||
|
{
|
||||||
|
/* Drop all timestamps on unknown step */
|
||||||
|
if (change_type == LCL_ChangeUnknownStep) {
|
||||||
|
ntp_ts_map.size = 0;
|
||||||
|
ntp_ts_map.cached_rx_ts = 0ULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ntp_ts_map.slew_epoch++;
|
||||||
|
ntp_ts_map.slew_offset = doffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
|
||||||
|
{
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
if (!ntp_ts_map.timestamps)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* If the RX timestamp was captured before the last correction of the clock,
|
||||||
|
remove the adjustment from the TX timestamp */
|
||||||
|
if ((uint16_t)(get_ntp_tss(index)->slew_epoch + 1U) == ntp_ts_map.slew_epoch)
|
||||||
|
UTI_AddDoubleToTimespec(tx_ts, ntp_ts_map.slew_offset, tx_ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src)
|
||||||
|
{
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
if (!ntp_ts_map.timestamps)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||||
|
return;
|
||||||
|
|
||||||
|
set_ntp_tx(get_ntp_tss(index), rx_ts, tx_ts, tx_src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source *tx_src)
|
||||||
|
{
|
||||||
|
NtpTimestamps *tss;
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
if (!ntp_ts_map.timestamps)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tss = get_ntp_tss(index);
|
||||||
|
|
||||||
|
if (tss->flags & NTPTS_DISABLED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
get_ntp_tx(tss, tx_ts, tx_src);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
CLG_DisableNtpTimestamps(NTP_int64 *rx_ts)
|
||||||
|
{
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
if (!ntp_ts_map.timestamps)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
|
||||||
|
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
|
||||||
|
|
||||||
|
/* This assumes the function is called only to prevent multiple
|
||||||
|
interleaved responses to the same timestamp */
|
||||||
|
total_ntp_interleaved_hits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CLG_GetNumberOfIndices(void)
|
CLG_GetNumberOfIndices(void)
|
||||||
{
|
{
|
||||||
@@ -717,4 +1108,15 @@ CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
|
|||||||
report->cmd_drops = total_drops[CLG_CMDMON];
|
report->cmd_drops = total_drops[CLG_CMDMON];
|
||||||
report->log_drops = total_record_drops;
|
report->log_drops = total_record_drops;
|
||||||
report->ntp_auth_hits = total_ntp_auth_hits;
|
report->ntp_auth_hits = total_ntp_auth_hits;
|
||||||
|
report->ntp_interleaved_hits = total_ntp_interleaved_hits;
|
||||||
|
report->ntp_timestamps = ntp_ts_map.size;
|
||||||
|
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
|
||||||
|
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
|
||||||
|
get_ntp_tss(0)->rx_ts) >> 32 : 0;
|
||||||
|
report->ntp_daemon_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_DAEMON];
|
||||||
|
report->ntp_daemon_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_DAEMON];
|
||||||
|
report->ntp_kernel_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_KERNEL];
|
||||||
|
report->ntp_kernel_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_KERNEL];
|
||||||
|
report->ntp_hw_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_HARDWARE];
|
||||||
|
report->ntp_hw_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_HARDWARE];
|
||||||
}
|
}
|
||||||
|
|||||||
22
clientlog.h
22
clientlog.h
@@ -37,15 +37,31 @@ typedef enum {
|
|||||||
CLG_CMDMON,
|
CLG_CMDMON,
|
||||||
} CLG_Service;
|
} CLG_Service;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CLG_PASS = 0,
|
||||||
|
CLG_DROP,
|
||||||
|
CLG_KOD,
|
||||||
|
} CLG_Limit;
|
||||||
|
|
||||||
extern void CLG_Initialise(void);
|
extern void CLG_Initialise(void);
|
||||||
extern void CLG_Finalise(void);
|
extern void CLG_Finalise(void);
|
||||||
extern int CLG_GetClientIndex(IPAddr *client);
|
extern int CLG_GetClientIndex(IPAddr *client);
|
||||||
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
|
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
|
||||||
extern int CLG_LimitServiceRate(CLG_Service service, int index);
|
extern CLG_Limit CLG_LimitServiceRate(CLG_Service service, int index);
|
||||||
extern void CLG_LogAuthNtpRequest(void);
|
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
|
||||||
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
|
NTP_Timestamp_Source tx_ts_src);
|
||||||
extern int CLG_GetNtpMinPoll(void);
|
extern int CLG_GetNtpMinPoll(void);
|
||||||
|
|
||||||
|
/* Functions to save and retrieve timestamps for server interleaved mode */
|
||||||
|
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src);
|
||||||
|
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
|
||||||
|
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source tx_src);
|
||||||
|
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
|
||||||
|
NTP_Timestamp_Source *tx_src);
|
||||||
|
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
|
||||||
|
|
||||||
/* And some reporting functions, for use by chronyc. */
|
/* And some reporting functions, for use by chronyc. */
|
||||||
|
|
||||||
extern int CLG_GetNumberOfIndices(void);
|
extern int CLG_GetNumberOfIndices(void);
|
||||||
|
|||||||
189
cmac_gnutls.c
Normal file
189
cmac_gnutls.c
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2021
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
CMAC using the GnuTLS library
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include <gnutls/crypto.h>
|
||||||
|
|
||||||
|
#include "cmac.h"
|
||||||
|
#include "hash.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
|
struct CMC_Instance_Record {
|
||||||
|
gnutls_mac_algorithm_t algorithm;
|
||||||
|
gnutls_hmac_hd_t mac;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int instance_counter = 0;
|
||||||
|
static int gnutls_initialised = 0;
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_gnutls(void)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (gnutls_initialised)
|
||||||
|
return;
|
||||||
|
|
||||||
|
r = gnutls_global_init();
|
||||||
|
if (r < 0)
|
||||||
|
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
|
||||||
|
|
||||||
|
DEBUG_LOG("Initialised");
|
||||||
|
gnutls_initialised = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
deinit_gnutls(void)
|
||||||
|
{
|
||||||
|
assert(gnutls_initialised);
|
||||||
|
gnutls_global_deinit();
|
||||||
|
gnutls_initialised = 0;
|
||||||
|
DEBUG_LOG("Deinitialised");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static gnutls_mac_algorithm_t
|
||||||
|
get_mac_algorithm(CMC_Algorithm algorithm)
|
||||||
|
{
|
||||||
|
switch (algorithm) {
|
||||||
|
case CMC_AES128:
|
||||||
|
return GNUTLS_MAC_AES_CMAC_128;
|
||||||
|
case CMC_AES256:
|
||||||
|
return GNUTLS_MAC_AES_CMAC_256;
|
||||||
|
default:
|
||||||
|
return GNUTLS_MAC_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CMC_GetKeyLength(CMC_Algorithm algorithm)
|
||||||
|
{
|
||||||
|
gnutls_mac_algorithm_t malgo = get_mac_algorithm(algorithm);
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (malgo == GNUTLS_MAC_UNKNOWN)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = gnutls_hmac_get_key_size(malgo);
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
CMC_Instance
|
||||||
|
CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
|
||||||
|
{
|
||||||
|
gnutls_hmac_hd_t handle;
|
||||||
|
CMC_Instance inst;
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (instance_counter == 0)
|
||||||
|
init_gnutls();
|
||||||
|
|
||||||
|
if (length <= 0 || length != CMC_GetKeyLength(algorithm))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
r = gnutls_hmac_init(&handle, get_mac_algorithm(algorithm), key, length);
|
||||||
|
if (r < 0) {
|
||||||
|
DEBUG_LOG("Could not initialise %s : %s", "mac", gnutls_strerror(r));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst = MallocNew(struct CMC_Instance_Record);
|
||||||
|
inst->algorithm = get_mac_algorithm(algorithm);
|
||||||
|
inst->mac = handle;
|
||||||
|
|
||||||
|
instance_counter++;
|
||||||
|
|
||||||
|
return inst;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (instance_counter == 0)
|
||||||
|
deinit_gnutls();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
|
||||||
|
{
|
||||||
|
unsigned char buf[MAX_HASH_LENGTH];
|
||||||
|
int hash_len;
|
||||||
|
|
||||||
|
if (in_len < 0 || out_len < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hash_len = gnutls_hmac_get_len(inst->algorithm);
|
||||||
|
|
||||||
|
if (out_len > hash_len)
|
||||||
|
out_len = hash_len;
|
||||||
|
|
||||||
|
if (hash_len > sizeof (buf))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (gnutls_hmac(inst->mac, in, in_len) < 0) {
|
||||||
|
/* Reset the state */
|
||||||
|
gnutls_hmac_output(inst->mac, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_hmac_output(inst->mac, buf);
|
||||||
|
memcpy(out, buf, out_len);
|
||||||
|
|
||||||
|
return out_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
CMC_DestroyInstance(CMC_Instance inst)
|
||||||
|
{
|
||||||
|
gnutls_hmac_deinit(inst->mac, NULL);
|
||||||
|
Free(inst);
|
||||||
|
|
||||||
|
instance_counter--;
|
||||||
|
if (instance_counter == 0)
|
||||||
|
deinit_gnutls();
|
||||||
|
}
|
||||||
778
cmdmon.c
778
cmdmon.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2020
|
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2025
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -68,84 +68,6 @@ static int bound_sock_fd4;
|
|||||||
/* Flag indicating whether this module has been initialised or not */
|
/* Flag indicating whether this module has been initialised or not */
|
||||||
static int initialised = 0;
|
static int initialised = 0;
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
/* Array of permission levels for command types */
|
|
||||||
|
|
||||||
static const char permissions[] = {
|
|
||||||
PERMIT_OPEN, /* NULL */
|
|
||||||
PERMIT_AUTH, /* ONLINE */
|
|
||||||
PERMIT_AUTH, /* OFFLINE */
|
|
||||||
PERMIT_AUTH, /* BURST */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MINPOLL */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MAXPOLL */
|
|
||||||
PERMIT_AUTH, /* DUMP */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MAXDELAY */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MAXDELAYRATIO */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MAXUPDATESKEW */
|
|
||||||
PERMIT_OPEN, /* LOGON */
|
|
||||||
PERMIT_AUTH, /* SETTIME */
|
|
||||||
PERMIT_AUTH, /* LOCAL */
|
|
||||||
PERMIT_AUTH, /* MANUAL */
|
|
||||||
PERMIT_OPEN, /* N_SOURCES */
|
|
||||||
PERMIT_OPEN, /* SOURCE_DATA */
|
|
||||||
PERMIT_AUTH, /* REKEY */
|
|
||||||
PERMIT_AUTH, /* ALLOW */
|
|
||||||
PERMIT_AUTH, /* ALLOWALL */
|
|
||||||
PERMIT_AUTH, /* DENY */
|
|
||||||
PERMIT_AUTH, /* DENYALL */
|
|
||||||
PERMIT_AUTH, /* CMDALLOW */
|
|
||||||
PERMIT_AUTH, /* CMDALLOWALL */
|
|
||||||
PERMIT_AUTH, /* CMDDENY */
|
|
||||||
PERMIT_AUTH, /* CMDDENYALL */
|
|
||||||
PERMIT_AUTH, /* ACCHECK */
|
|
||||||
PERMIT_AUTH, /* CMDACCHECK */
|
|
||||||
PERMIT_AUTH, /* ADD_SERVER */
|
|
||||||
PERMIT_AUTH, /* ADD_PEER */
|
|
||||||
PERMIT_AUTH, /* DEL_SOURCE */
|
|
||||||
PERMIT_AUTH, /* WRITERTC */
|
|
||||||
PERMIT_AUTH, /* DFREQ */
|
|
||||||
PERMIT_AUTH, /* DOFFSET */
|
|
||||||
PERMIT_OPEN, /* TRACKING */
|
|
||||||
PERMIT_OPEN, /* SOURCESTATS */
|
|
||||||
PERMIT_OPEN, /* RTCREPORT */
|
|
||||||
PERMIT_AUTH, /* TRIMRTC */
|
|
||||||
PERMIT_AUTH, /* CYCLELOGS */
|
|
||||||
PERMIT_AUTH, /* SUBNETS_ACCESSED */
|
|
||||||
PERMIT_AUTH, /* CLIENT_ACCESSES (by subnet) */
|
|
||||||
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX */
|
|
||||||
PERMIT_OPEN, /* MANUAL_LIST */
|
|
||||||
PERMIT_AUTH, /* MANUAL_DELETE */
|
|
||||||
PERMIT_AUTH, /* MAKESTEP */
|
|
||||||
PERMIT_OPEN, /* ACTIVITY */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MINSTRATUM */
|
|
||||||
PERMIT_AUTH, /* MODIFY_POLLTARGET */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MAXDELAYDEVRATIO */
|
|
||||||
PERMIT_AUTH, /* RESELECT */
|
|
||||||
PERMIT_AUTH, /* RESELECTDISTANCE */
|
|
||||||
PERMIT_AUTH, /* MODIFY_MAKESTEP */
|
|
||||||
PERMIT_OPEN, /* SMOOTHING */
|
|
||||||
PERMIT_AUTH, /* SMOOTHTIME */
|
|
||||||
PERMIT_AUTH, /* REFRESH */
|
|
||||||
PERMIT_AUTH, /* SERVER_STATS */
|
|
||||||
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX2 */
|
|
||||||
PERMIT_AUTH, /* LOCAL2 */
|
|
||||||
PERMIT_AUTH, /* NTP_DATA */
|
|
||||||
PERMIT_AUTH, /* ADD_SERVER2 */
|
|
||||||
PERMIT_AUTH, /* ADD_PEER2 */
|
|
||||||
PERMIT_AUTH, /* ADD_SERVER3 */
|
|
||||||
PERMIT_AUTH, /* ADD_PEER3 */
|
|
||||||
PERMIT_AUTH, /* SHUTDOWN */
|
|
||||||
PERMIT_AUTH, /* ONOFFLINE */
|
|
||||||
PERMIT_AUTH, /* ADD_SOURCE */
|
|
||||||
PERMIT_OPEN, /* NTP_SOURCE_NAME */
|
|
||||||
PERMIT_AUTH, /* RESET_SOURCES */
|
|
||||||
PERMIT_AUTH, /* AUTH_DATA */
|
|
||||||
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
|
|
||||||
PERMIT_AUTH, /* SELECT_DATA */
|
|
||||||
PERMIT_AUTH, /* RELOAD_SOURCES */
|
|
||||||
PERMIT_AUTH, /* DOFFSET2 */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
/* This authorisation table is used for checking whether particular
|
/* This authorisation table is used for checking whether particular
|
||||||
@@ -224,19 +146,17 @@ do_size_checks(void)
|
|||||||
request.command = htons(i);
|
request.command = htons(i);
|
||||||
request_length = PKL_CommandLength(&request);
|
request_length = PKL_CommandLength(&request);
|
||||||
padding_length = PKL_CommandPaddingLength(&request);
|
padding_length = PKL_CommandPaddingLength(&request);
|
||||||
if (padding_length > MAX_PADDING_LENGTH || padding_length > request_length ||
|
BRIEF_ASSERT(padding_length <= MAX_PADDING_LENGTH && padding_length <= request_length &&
|
||||||
request_length > sizeof (CMD_Request) ||
|
request_length <= sizeof (CMD_Request) &&
|
||||||
(request_length && request_length < offsetof(CMD_Request, data)))
|
(request_length == 0 || request_length >= offsetof(CMD_Request, data)));
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 1; i < N_REPLY_TYPES; i++) {
|
for (i = 1; i < N_REPLY_TYPES; i++) {
|
||||||
reply.reply = htons(i);
|
reply.reply = htons(i);
|
||||||
reply.status = STT_SUCCESS;
|
reply.status = STT_SUCCESS;
|
||||||
reply_length = PKL_ReplyLength(&reply);
|
reply_length = PKL_ReplyLength(&reply);
|
||||||
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
|
BRIEF_ASSERT((reply_length == 0 || reply_length >= offsetof(CMD_Reply, data)) &&
|
||||||
reply_length > sizeof (CMD_Reply))
|
reply_length <= sizeof (CMD_Reply));
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +166,6 @@ void
|
|||||||
CAM_Initialise(void)
|
CAM_Initialise(void)
|
||||||
{
|
{
|
||||||
assert(!initialised);
|
assert(!initialised);
|
||||||
assert(sizeof (permissions) / sizeof (permissions[0]) == N_REQUEST_TYPES);
|
|
||||||
do_size_checks();
|
do_size_checks();
|
||||||
|
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
@@ -321,7 +240,8 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
|
|||||||
|
|
||||||
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
||||||
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
||||||
if (message->local_addr.ip.family == IPADDR_INET4 && (sock_fd != sock_fd4 || bound_sock_fd4))
|
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
|
||||||
|
(sock_fd != sock_fd4 || bound_sock_fd4))
|
||||||
message->local_addr.ip.family = IPADDR_UNSPEC;
|
message->local_addr.ip.family = IPADDR_UNSPEC;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -528,7 +448,10 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
if (ntohl(rx_message->data.local.on_off)) {
|
if (ntohl(rx_message->data.local.on_off)) {
|
||||||
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
|
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
|
||||||
UTI_FloatNetworkToHost(rx_message->data.local.distance),
|
UTI_FloatNetworkToHost(rx_message->data.local.distance),
|
||||||
ntohl(rx_message->data.local.orphan));
|
ntohl(rx_message->data.local.orphan),
|
||||||
|
UTI_FloatNetworkToHost(rx_message->data.local.activate),
|
||||||
|
UTI_FloatNetworkToHost(rx_message->data.local.wait_synced),
|
||||||
|
UTI_FloatNetworkToHost(rx_message->data.local.wait_unsynced));
|
||||||
} else {
|
} else {
|
||||||
REF_DisableLocal();
|
REF_DisableLocal();
|
||||||
}
|
}
|
||||||
@@ -702,14 +625,26 @@ handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
convert_addsrc_select_options(int flags)
|
||||||
|
{
|
||||||
|
return (flags & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
|
||||||
|
(flags & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
|
||||||
|
(flags & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
|
||||||
|
(flags & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||||
{
|
{
|
||||||
NTP_Source_Type type;
|
NTP_Source_Type type;
|
||||||
SourceParameters params;
|
SourceParameters params;
|
||||||
|
int family, pool, port;
|
||||||
NSR_Status status;
|
NSR_Status status;
|
||||||
|
uint32_t flags;
|
||||||
char *name;
|
char *name;
|
||||||
int pool, port;
|
|
||||||
|
|
||||||
switch (ntohl(rx_message->data.ntp_source.type)) {
|
switch (ntohl(rx_message->data.ntp_source.type)) {
|
||||||
case REQ_ADDSRC_SERVER:
|
case REQ_ADDSRC_SERVER:
|
||||||
@@ -737,6 +672,10 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags = ntohl(rx_message->data.ntp_source.flags);
|
||||||
|
|
||||||
|
family = flags & REQ_ADDSRC_IPV4 ? IPADDR_INET4 :
|
||||||
|
flags & REQ_ADDSRC_IPV6 ? IPADDR_INET6 : IPADDR_UNSPEC;
|
||||||
port = ntohl(rx_message->data.ntp_source.port);
|
port = ntohl(rx_message->data.ntp_source.port);
|
||||||
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
|
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
|
||||||
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
|
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
|
||||||
@@ -747,6 +686,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
|
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
|
||||||
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
|
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
|
||||||
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
|
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
|
||||||
|
params.max_unreach = ntohl(rx_message->data.ntp_source.max_unreach);
|
||||||
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
|
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
|
||||||
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
|
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
|
||||||
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
|
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
|
||||||
@@ -756,25 +696,25 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
|
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
|
||||||
params.max_delay_dev_ratio =
|
params.max_delay_dev_ratio =
|
||||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
|
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
|
||||||
|
params.max_delay_quant =
|
||||||
|
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
|
||||||
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
|
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
|
||||||
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
|
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
|
||||||
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
|
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
|
||||||
|
|
||||||
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
|
params.connectivity = flags & REQ_ADDSRC_ONLINE ? SRC_ONLINE : SRC_OFFLINE;
|
||||||
SRC_ONLINE : SRC_OFFLINE;
|
params.auto_offline = !!(flags & REQ_ADDSRC_AUTOOFFLINE);
|
||||||
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
|
params.iburst = !!(flags & REQ_ADDSRC_IBURST);
|
||||||
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
|
params.interleaved = !!(flags & REQ_ADDSRC_INTERLEAVED);
|
||||||
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
|
params.burst = !!(flags & REQ_ADDSRC_BURST);
|
||||||
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
|
params.nts = !!(flags & REQ_ADDSRC_NTS);
|
||||||
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
|
params.copy = !!(flags & REQ_ADDSRC_COPY);
|
||||||
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
|
params.ext_fields = (flags & REQ_ADDSRC_EF_EXP_MONO_ROOT ? NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
|
||||||
params.sel_options =
|
(flags & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
|
NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
|
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
|
|
||||||
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
|
|
||||||
|
|
||||||
status = NSR_AddSourceByName(name, port, pool, type, ¶ms, NULL);
|
status = NSR_AddSourceByName(name, family, port, pool, type, ¶ms, NULL);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case NSR_Success:
|
case NSR_Success:
|
||||||
break;
|
break;
|
||||||
@@ -792,6 +732,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
tx_message->status = htons(STT_INVALIDNAME);
|
tx_message->status = htons(STT_INVALIDNAME);
|
||||||
break;
|
break;
|
||||||
case NSR_InvalidAF:
|
case NSR_InvalidAF:
|
||||||
|
tx_message->status = htons(STT_INVALIDAF);
|
||||||
|
break;
|
||||||
case NSR_NoSuchSource:
|
case NSR_NoSuchSource:
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
@@ -1164,15 +1106,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
RPT_ServerStatsReport report;
|
RPT_ServerStatsReport report;
|
||||||
|
|
||||||
CLG_GetServerStatsReport(&report);
|
CLG_GetServerStatsReport(&report);
|
||||||
tx_message->reply = htons(RPY_SERVER_STATS2);
|
tx_message->reply = htons(RPY_SERVER_STATS4);
|
||||||
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
|
tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
|
||||||
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
|
tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
|
||||||
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
|
tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
|
||||||
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
|
tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
|
||||||
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
|
tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
|
||||||
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
|
tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
|
||||||
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
|
tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
|
||||||
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
|
tx_message->data.server_stats.ntp_auth_hits =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_auth_hits);
|
||||||
|
tx_message->data.server_stats.ntp_interleaved_hits =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_interleaved_hits);
|
||||||
|
tx_message->data.server_stats.ntp_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_span_seconds =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_span_seconds);
|
||||||
|
tx_message->data.server_stats.ntp_daemon_rx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_daemon_rx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_daemon_tx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_daemon_tx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_kernel_rx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_kernel_rx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_kernel_tx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_kernel_tx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_hw_rx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_hw_rx_timestamps);
|
||||||
|
tx_message->data.server_stats.ntp_hw_tx_timestamps =
|
||||||
|
UTI_Integer64HostToNetwork(report.ntp_hw_tx_timestamps);
|
||||||
|
memset(tx_message->data.server_stats.reserved, 0xff,
|
||||||
|
sizeof (tx_message->data.server_stats.reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1189,7 +1152,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tx_message->reply = htons(RPY_NTP_DATA);
|
tx_message->reply = htons(RPY_NTP_DATA2);
|
||||||
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
|
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
|
||||||
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
|
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
|
||||||
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
|
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
|
||||||
@@ -1216,6 +1179,11 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
|
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
|
||||||
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
|
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
|
||||||
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
|
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
|
||||||
|
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
|
||||||
|
tx_message->data.ntp_data.total_kernel_tx_ts = htonl(report.total_kernel_tx_ts);
|
||||||
|
tx_message->data.ntp_data.total_kernel_rx_ts = htonl(report.total_kernel_rx_ts);
|
||||||
|
tx_message->data.ntp_data.total_hw_tx_ts = htonl(report.total_hw_tx_ts);
|
||||||
|
tx_message->data.ntp_data.total_hw_rx_ts = htonl(report.total_hw_rx_ts);
|
||||||
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
|
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1319,7 +1287,7 @@ handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static uint16_t
|
static uint16_t
|
||||||
convert_select_options(int options)
|
convert_sd_sel_options(int options)
|
||||||
{
|
{
|
||||||
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
|
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
|
||||||
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
|
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
|
||||||
@@ -1346,26 +1314,286 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
|||||||
tx_message->data.select_data.state_char = report.state_char;
|
tx_message->data.select_data.state_char = report.state_char;
|
||||||
tx_message->data.select_data.authentication = report.authentication;
|
tx_message->data.select_data.authentication = report.authentication;
|
||||||
tx_message->data.select_data.leap = report.leap;
|
tx_message->data.select_data.leap = report.leap;
|
||||||
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
|
tx_message->data.select_data.conf_options = htons(convert_sd_sel_options(report.conf_options));
|
||||||
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
|
tx_message->data.select_data.eff_options = htons(convert_sd_sel_options(report.eff_options));
|
||||||
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
|
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
|
||||||
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
|
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
|
||||||
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
|
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
|
||||||
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
|
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||||
|
{
|
||||||
|
int mask, options;
|
||||||
|
uint32_t ref_id;
|
||||||
|
IPAddr ip_addr;
|
||||||
|
|
||||||
|
UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
|
||||||
|
ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
|
||||||
|
mask = ntohl(rx_message->data.modify_select_opts.mask);
|
||||||
|
options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
|
||||||
|
|
||||||
|
if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
|
||||||
|
tx_message->status = htons(STT_NOSUCHSOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||||
|
{
|
||||||
|
uint32_t ref_id;
|
||||||
|
IPAddr ip_addr;
|
||||||
|
double offset;
|
||||||
|
|
||||||
|
UTI_IPNetworkToHost(&rx_message->data.modify_offset.address, &ip_addr);
|
||||||
|
ref_id = ntohl(rx_message->data.modify_offset.ref_id);
|
||||||
|
offset = UTI_FloatNetworkToHost(rx_message->data.modify_offset.new_offset);
|
||||||
|
|
||||||
|
if ((ip_addr.family != IPADDR_UNSPEC && !NSR_ModifyOffset(&ip_addr, offset)) ||
|
||||||
|
(ip_addr.family == IPADDR_UNSPEC && !RCL_ModifyOffset(ref_id, offset)))
|
||||||
|
tx_message->status = htons(STT_NOSUCHSOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_readwrite_commands(int command, CMD_Request *request, CMD_Reply *reply)
|
||||||
|
{
|
||||||
|
switch (command) {
|
||||||
|
case REQ_ADD_SOURCE:
|
||||||
|
handle_add_source(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_ALLOW:
|
||||||
|
handle_allowdeny(request, reply, 1, 0);
|
||||||
|
break;
|
||||||
|
case REQ_ALLOWALL:
|
||||||
|
handle_allowdeny(request, reply, 1, 1);
|
||||||
|
break;
|
||||||
|
case REQ_BURST:
|
||||||
|
handle_burst(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_CMDALLOW:
|
||||||
|
handle_cmdallowdeny(request, reply, 1, 0);
|
||||||
|
break;
|
||||||
|
case REQ_CMDALLOWALL:
|
||||||
|
handle_cmdallowdeny(request, reply, 1, 1);
|
||||||
|
break;
|
||||||
|
case REQ_CMDDENY:
|
||||||
|
handle_cmdallowdeny(request, reply, 0, 0);
|
||||||
|
break;
|
||||||
|
case REQ_CMDDENYALL:
|
||||||
|
handle_cmdallowdeny(request, reply, 0, 1);
|
||||||
|
break;
|
||||||
|
case REQ_CYCLELOGS:
|
||||||
|
handle_cyclelogs(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_DEL_SOURCE:
|
||||||
|
handle_del_source(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_DENY:
|
||||||
|
handle_allowdeny(request, reply, 0, 0);
|
||||||
|
break;
|
||||||
|
case REQ_DENYALL:
|
||||||
|
handle_allowdeny(request, reply, 0, 1);
|
||||||
|
break;
|
||||||
|
case REQ_DFREQ:
|
||||||
|
handle_dfreq(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_DOFFSET2:
|
||||||
|
handle_doffset(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_DUMP:
|
||||||
|
handle_dump(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_LOCAL3:
|
||||||
|
handle_local(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MAKESTEP:
|
||||||
|
handle_make_step(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MANUAL:
|
||||||
|
handle_manual(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MANUAL_DELETE:
|
||||||
|
handle_manual_delete(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MAKESTEP:
|
||||||
|
handle_modify_makestep(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MAXDELAY:
|
||||||
|
handle_modify_maxdelay(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MAXDELAYDEVRATIO:
|
||||||
|
handle_modify_maxdelaydevratio(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MAXDELAYRATIO:
|
||||||
|
handle_modify_maxdelayratio(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MAXPOLL:
|
||||||
|
handle_modify_maxpoll(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MAXUPDATESKEW:
|
||||||
|
handle_modify_maxupdateskew(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MINPOLL:
|
||||||
|
handle_modify_minpoll(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_MINSTRATUM:
|
||||||
|
handle_modify_minstratum(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_OFFSET:
|
||||||
|
handle_modify_offset(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_POLLTARGET:
|
||||||
|
handle_modify_polltarget(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MODIFY_SELECTOPTS:
|
||||||
|
handle_modify_selectopts(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_OFFLINE:
|
||||||
|
handle_offline(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_ONLINE:
|
||||||
|
handle_online(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_ONOFFLINE:
|
||||||
|
handle_onoffline(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_REFRESH:
|
||||||
|
handle_refresh(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_REKEY:
|
||||||
|
handle_rekey(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_RELOAD_SOURCES:
|
||||||
|
handle_reload_sources(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_RESELECT:
|
||||||
|
handle_reselect(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_RESELECTDISTANCE:
|
||||||
|
handle_reselect_distance(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_RESET_SOURCES:
|
||||||
|
handle_reset_sources(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SETTIME:
|
||||||
|
handle_settime(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SHUTDOWN:
|
||||||
|
handle_shutdown(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SMOOTHTIME:
|
||||||
|
handle_smoothtime(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_TRIMRTC:
|
||||||
|
handle_trimrtc(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_WRITERTC:
|
||||||
|
handle_writertc(request, reply);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_readonly_commands(int command, int full_access, CMD_Request *request, CMD_Reply *reply)
|
||||||
|
{
|
||||||
|
ARR_Instance open_commands;
|
||||||
|
int i, allowed = 0;
|
||||||
|
|
||||||
|
if (full_access) {
|
||||||
|
allowed = 1;
|
||||||
|
} else {
|
||||||
|
open_commands = CNF_GetOpenCommands();
|
||||||
|
|
||||||
|
for (i = 0; i < ARR_GetSize(open_commands); i++) {
|
||||||
|
if (*(int *)ARR_GetElement(open_commands, i) == command) {
|
||||||
|
allowed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowed)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case REQ_ACCHECK:
|
||||||
|
handle_accheck(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_ACTIVITY:
|
||||||
|
handle_activity(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_AUTH_DATA:
|
||||||
|
handle_auth_data(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_CLIENT_ACCESSES_BY_INDEX3:
|
||||||
|
handle_client_accesses_by_index(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_CMDACCHECK:
|
||||||
|
handle_cmdaccheck(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_MANUAL_LIST:
|
||||||
|
handle_manual_list(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_NTP_DATA:
|
||||||
|
handle_ntp_data(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_NTP_SOURCE_NAME:
|
||||||
|
handle_ntp_source_name(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_N_SOURCES:
|
||||||
|
handle_n_sources(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_RTCREPORT:
|
||||||
|
handle_rtcreport(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SELECT_DATA:
|
||||||
|
handle_select_data(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SERVER_STATS:
|
||||||
|
handle_server_stats(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SMOOTHING:
|
||||||
|
handle_smoothing(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SOURCESTATS:
|
||||||
|
handle_sourcestats(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_SOURCE_DATA:
|
||||||
|
handle_source_data(request, reply);
|
||||||
|
break;
|
||||||
|
case REQ_TRACKING:
|
||||||
|
handle_tracking(request, reply);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Read a packet and process it */
|
/* Read a packet and process it */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
read_from_cmd_socket(int sock_fd, int event, void *anything)
|
read_from_cmd_socket(int sock_fd, int event, void *anything)
|
||||||
{
|
{
|
||||||
|
int read_length, expected_length, localhost, log_index, full_access, handled;
|
||||||
SCK_Message *sck_message;
|
SCK_Message *sck_message;
|
||||||
CMD_Request rx_message;
|
CMD_Request rx_message;
|
||||||
CMD_Reply tx_message;
|
CMD_Reply tx_message;
|
||||||
IPAddr loopback_addr, remote_ip;
|
IPAddr loopback_addr, remote_ip;
|
||||||
int read_length, expected_length;
|
|
||||||
int localhost, allowed, log_index;
|
|
||||||
uint16_t rx_command;
|
uint16_t rx_command;
|
||||||
struct timespec now, cooked_now;
|
struct timespec now, cooked_now;
|
||||||
|
|
||||||
@@ -1378,11 +1606,9 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
|||||||
/* Get current time cheaply */
|
/* Get current time cheaply */
|
||||||
SCH_GetLastEventTime(&cooked_now, NULL, &now);
|
SCH_GetLastEventTime(&cooked_now, NULL, &now);
|
||||||
|
|
||||||
/* Check if it's from localhost (127.0.0.1, ::1, or Unix domain),
|
/* Check if the request came from the Unix domain socket, or network and
|
||||||
or an authorised address */
|
whether the address is allowed (127.0.0.1 and ::1 is always allowed) */
|
||||||
switch (sck_message->addr_type) {
|
if ((sock_fd == sock_fd4 || sock_fd == sock_fd6) && sck_message->addr_type == SCK_ADDR_IP) {
|
||||||
case SCK_ADDR_IP:
|
|
||||||
assert(sock_fd == sock_fd4 || sock_fd == sock_fd6);
|
|
||||||
remote_ip = sck_message->remote_addr.ip.ip_addr;
|
remote_ip = sck_message->remote_addr.ip.ip_addr;
|
||||||
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
|
SCK_GetLoopbackIPAddress(remote_ip.family, &loopback_addr);
|
||||||
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
|
localhost = UTI_CompareIPs(&remote_ip, &loopback_addr, NULL) == 0;
|
||||||
@@ -1393,16 +1619,13 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(remote_ip.family != IPADDR_UNSPEC);
|
full_access = 0;
|
||||||
|
} else if (sock_fd == sock_fdu && sck_message->addr_type == SCK_ADDR_UNIX) {
|
||||||
break;
|
|
||||||
case SCK_ADDR_UNIX:
|
|
||||||
assert(sock_fd == sock_fdu);
|
|
||||||
remote_ip.family = IPADDR_UNSPEC;
|
remote_ip.family = IPADDR_UNSPEC;
|
||||||
localhost = 1;
|
localhost = 1;
|
||||||
break;
|
full_access = 1;
|
||||||
default:
|
} else {
|
||||||
DEBUG_LOG("Unexpected address type");
|
DEBUG_LOG("Unexpected socket/address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1428,7 +1651,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
|||||||
|
|
||||||
/* Don't reply to all requests from hosts other than localhost if the rate
|
/* Don't reply to all requests from hosts other than localhost if the rate
|
||||||
is excessive */
|
is excessive */
|
||||||
if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
|
if (!localhost && log_index >= 0 &&
|
||||||
|
CLG_LimitServiceRate(CLG_CMDMON, log_index) != CLG_PASS) {
|
||||||
DEBUG_LOG("Command packet discarded to limit response rate");
|
DEBUG_LOG("Command packet discarded to limit response rate");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1478,285 +1702,20 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
|
|||||||
|
|
||||||
/* OK, we have a valid message. Now dispatch on message type and process it. */
|
/* OK, we have a valid message. Now dispatch on message type and process it. */
|
||||||
|
|
||||||
if (rx_command >= N_REQUEST_TYPES) {
|
LOG_SetContext(LOGC_Command);
|
||||||
/* This should be already handled */
|
|
||||||
assert(0);
|
|
||||||
} else {
|
|
||||||
/* Check level of authority required to issue the command. All commands
|
|
||||||
from the Unix domain socket (which is accessible only by the root and
|
|
||||||
chrony user/group) are allowed. */
|
|
||||||
if (remote_ip.family == IPADDR_UNSPEC) {
|
|
||||||
assert(sock_fd == sock_fdu);
|
|
||||||
allowed = 1;
|
|
||||||
} else {
|
|
||||||
switch (permissions[rx_command]) {
|
|
||||||
case PERMIT_AUTH:
|
|
||||||
allowed = 0;
|
|
||||||
break;
|
|
||||||
case PERMIT_LOCAL:
|
|
||||||
allowed = localhost;
|
|
||||||
break;
|
|
||||||
case PERMIT_OPEN:
|
|
||||||
allowed = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
allowed = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowed) {
|
if (full_access)
|
||||||
switch(rx_command) {
|
handled = handle_readwrite_commands(rx_command, &rx_message, &tx_message);
|
||||||
case REQ_NULL:
|
else
|
||||||
/* Do nothing */
|
handled = 0;
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_DUMP:
|
if (!handled)
|
||||||
handle_dump(&rx_message, &tx_message);
|
handled = handle_readonly_commands(rx_command, full_access, &rx_message, &tx_message);
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ONLINE:
|
if (!handled)
|
||||||
handle_online(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_OFFLINE:
|
|
||||||
handle_offline(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_BURST:
|
|
||||||
handle_burst(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MINPOLL:
|
|
||||||
handle_modify_minpoll(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MAXPOLL:
|
|
||||||
handle_modify_maxpoll(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MAXDELAY:
|
|
||||||
handle_modify_maxdelay(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MAXDELAYRATIO:
|
|
||||||
handle_modify_maxdelayratio(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MAXDELAYDEVRATIO:
|
|
||||||
handle_modify_maxdelaydevratio(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MAXUPDATESKEW:
|
|
||||||
handle_modify_maxupdateskew(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MAKESTEP:
|
|
||||||
handle_modify_makestep(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_LOGON:
|
|
||||||
/* Authentication is no longer supported, log-on always fails */
|
|
||||||
tx_message.status = htons(STT_FAILED);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SETTIME:
|
|
||||||
handle_settime(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_LOCAL2:
|
|
||||||
handle_local(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MANUAL:
|
|
||||||
handle_manual(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_N_SOURCES:
|
|
||||||
handle_n_sources(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SOURCE_DATA:
|
|
||||||
handle_source_data(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_REKEY:
|
|
||||||
handle_rekey(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ALLOW:
|
|
||||||
handle_allowdeny(&rx_message, &tx_message, 1, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ALLOWALL:
|
|
||||||
handle_allowdeny(&rx_message, &tx_message, 1, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_DENY:
|
|
||||||
handle_allowdeny(&rx_message, &tx_message, 0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_DENYALL:
|
|
||||||
handle_allowdeny(&rx_message, &tx_message, 0, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CMDALLOW:
|
|
||||||
handle_cmdallowdeny(&rx_message, &tx_message, 1, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CMDALLOWALL:
|
|
||||||
handle_cmdallowdeny(&rx_message, &tx_message, 1, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CMDDENY:
|
|
||||||
handle_cmdallowdeny(&rx_message, &tx_message, 0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CMDDENYALL:
|
|
||||||
handle_cmdallowdeny(&rx_message, &tx_message, 0, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ACCHECK:
|
|
||||||
handle_accheck(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CMDACCHECK:
|
|
||||||
handle_cmdaccheck(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ADD_SOURCE:
|
|
||||||
handle_add_source(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_DEL_SOURCE:
|
|
||||||
handle_del_source(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_WRITERTC:
|
|
||||||
handle_writertc(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_DFREQ:
|
|
||||||
handle_dfreq(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_DOFFSET2:
|
|
||||||
handle_doffset(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_TRACKING:
|
|
||||||
handle_tracking(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SMOOTHING:
|
|
||||||
handle_smoothing(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SMOOTHTIME:
|
|
||||||
handle_smoothtime(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SOURCESTATS:
|
|
||||||
handle_sourcestats(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_RTCREPORT:
|
|
||||||
handle_rtcreport(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_TRIMRTC:
|
|
||||||
handle_trimrtc(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CYCLELOGS:
|
|
||||||
handle_cyclelogs(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_CLIENT_ACCESSES_BY_INDEX3:
|
|
||||||
handle_client_accesses_by_index(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MANUAL_LIST:
|
|
||||||
handle_manual_list(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MANUAL_DELETE:
|
|
||||||
handle_manual_delete(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MAKESTEP:
|
|
||||||
handle_make_step(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ACTIVITY:
|
|
||||||
handle_activity(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_RESELECTDISTANCE:
|
|
||||||
handle_reselect_distance(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_RESELECT:
|
|
||||||
handle_reselect(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_MINSTRATUM:
|
|
||||||
handle_modify_minstratum(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_MODIFY_POLLTARGET:
|
|
||||||
handle_modify_polltarget(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_REFRESH:
|
|
||||||
handle_refresh(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SERVER_STATS:
|
|
||||||
handle_server_stats(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_NTP_DATA:
|
|
||||||
handle_ntp_data(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SHUTDOWN:
|
|
||||||
handle_shutdown(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_ONOFFLINE:
|
|
||||||
handle_onoffline(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_NTP_SOURCE_NAME:
|
|
||||||
handle_ntp_source_name(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_RESET_SOURCES:
|
|
||||||
handle_reset_sources(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_AUTH_DATA:
|
|
||||||
handle_auth_data(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_SELECT_DATA:
|
|
||||||
handle_select_data(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REQ_RELOAD_SOURCES:
|
|
||||||
handle_reload_sources(&rx_message, &tx_message);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DEBUG_LOG("Unhandled command %d", rx_command);
|
|
||||||
tx_message.status = htons(STT_FAILED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tx_message.status = htons(STT_UNAUTH);
|
tx_message.status = htons(STT_UNAUTH);
|
||||||
}
|
|
||||||
}
|
LOG_UnsetContext(LOGC_Command);
|
||||||
|
|
||||||
/* Transmit the response */
|
/* Transmit the response */
|
||||||
transmit_reply(sock_fd, read_length, sck_message);
|
transmit_reply(sock_fd, read_length, sck_message);
|
||||||
@@ -1786,6 +1745,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
|
|||||||
if (status == ADF_BADSUBNET) {
|
if (status == ADF_BADSUBNET) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (status == ADF_SUCCESS) {
|
} else if (status == ADF_SUCCESS) {
|
||||||
|
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
|
||||||
|
allow ? "Allowed" : "Denied", all ? " all" : "", "command",
|
||||||
|
UTI_IPSubnetToString(ip_addr, subnet_bits));
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
258
cmdparse.c
258
cmdparse.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2013-2014, 2016
|
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021, 2025
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -39,12 +39,19 @@
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
#define SSCANF_IN_RANGE(s, f, x, n, min, max) \
|
||||||
|
(sscanf((s), (f), (x), (n)) == 1 && *(x) >= (min) && *(x) <= (max))
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
CPS_Status
|
||||||
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||||
{
|
{
|
||||||
char *hostname, *cmd;
|
char *hostname, *cmd;
|
||||||
int n;
|
uint32_t ef_type;
|
||||||
|
int n, sel_option;
|
||||||
|
|
||||||
|
src->family = IPADDR_UNSPEC;
|
||||||
src->port = SRC_DEFAULT_PORT;
|
src->port = SRC_DEFAULT_PORT;
|
||||||
src->params.minpoll = SRC_DEFAULT_MINPOLL;
|
src->params.minpoll = SRC_DEFAULT_MINPOLL;
|
||||||
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
|
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
|
||||||
@@ -59,17 +66,20 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
|
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
|
||||||
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
|
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||||
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
|
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
|
||||||
|
src->params.max_unreach = SRC_DEFAULT_MAXUNREACH;
|
||||||
src->params.filter_length = 0;
|
src->params.filter_length = 0;
|
||||||
src->params.interleaved = 0;
|
src->params.interleaved = 0;
|
||||||
src->params.sel_options = 0;
|
src->params.sel_options = 0;
|
||||||
src->params.nts = 0;
|
src->params.nts = 0;
|
||||||
src->params.nts_port = SRC_DEFAULT_NTSPORT;
|
src->params.nts_port = SRC_DEFAULT_NTSPORT;
|
||||||
src->params.copy = 0;
|
src->params.copy = 0;
|
||||||
|
src->params.ext_fields = 0;
|
||||||
src->params.authkey = INACTIVE_AUTHKEY;
|
src->params.authkey = INACTIVE_AUTHKEY;
|
||||||
src->params.cert_set = SRC_DEFAULT_CERTSET;
|
src->params.cert_set = SRC_DEFAULT_CERTSET;
|
||||||
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
|
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
|
||||||
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
|
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
|
||||||
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
|
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
|
||||||
|
src->params.max_delay_quant = 0.0;
|
||||||
src->params.min_delay = 0.0;
|
src->params.min_delay = 0.0;
|
||||||
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
|
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
|
||||||
src->params.offset = 0.0;
|
src->params.offset = 0.0;
|
||||||
@@ -78,7 +88,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
line = CPS_SplitWord(line);
|
line = CPS_SplitWord(line);
|
||||||
|
|
||||||
if (!*hostname)
|
if (!*hostname)
|
||||||
return 0;
|
return CPS_MissingArgument;
|
||||||
|
|
||||||
src->name = hostname;
|
src->name = hostname;
|
||||||
|
|
||||||
@@ -98,121 +108,249 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
|||||||
src->params.iburst = 1;
|
src->params.iburst = 1;
|
||||||
} else if (!strcasecmp(cmd, "offline")) {
|
} else if (!strcasecmp(cmd, "offline")) {
|
||||||
src->params.connectivity = SRC_OFFLINE;
|
src->params.connectivity = SRC_OFFLINE;
|
||||||
} else if (!strcasecmp(cmd, "noselect")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_NOSELECT;
|
|
||||||
} else if (!strcasecmp(cmd, "prefer")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_PREFER;
|
|
||||||
} else if (!strcasecmp(cmd, "require")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_REQUIRE;
|
|
||||||
} else if (!strcasecmp(cmd, "trust")) {
|
|
||||||
src->params.sel_options |= SRC_SELECT_TRUST;
|
|
||||||
} else if (!strcasecmp(cmd, "certset")) {
|
} else if (!strcasecmp(cmd, "certset")) {
|
||||||
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
|
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "key")) {
|
} else if (!strcasecmp(cmd, "key")) {
|
||||||
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
|
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
|
||||||
src->params.authkey == INACTIVE_AUTHKEY)
|
src->params.authkey == INACTIVE_AUTHKEY)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "asymmetry")) {
|
} else if (!strcasecmp(cmd, "asymmetry")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "extfield")) {
|
||||||
|
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
|
||||||
|
return CPS_InvalidValue;
|
||||||
|
switch (ef_type) {
|
||||||
|
case NTP_EF_EXP_MONO_ROOT:
|
||||||
|
src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
|
||||||
|
break;
|
||||||
|
case NTP_EF_EXP_NET_CORRECTION:
|
||||||
|
src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return CPS_InvalidValue;
|
||||||
|
}
|
||||||
} else if (!strcasecmp(cmd, "filter")) {
|
} else if (!strcasecmp(cmd, "filter")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.filter_length, &n, 0, INT_MAX))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "ipv4")) {
|
||||||
|
src->family = IPADDR_INET4;
|
||||||
|
} else if (!strcasecmp(cmd, "ipv6")) {
|
||||||
|
src->family = IPADDR_INET6;
|
||||||
} else if (!strcasecmp(cmd, "maxdelay")) {
|
} else if (!strcasecmp(cmd, "maxdelay")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "maxdelayratio")) {
|
} else if (!strcasecmp(cmd, "maxdelayratio")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
|
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "maxdelayquant")) {
|
||||||
|
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
|
||||||
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "maxpoll")) {
|
} else if (!strcasecmp(cmd, "maxpoll")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.maxpoll, &n, -32, 32))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "maxsamples")) {
|
} else if (!strcasecmp(cmd, "maxsamples")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.max_samples, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_samples, &n, 0, INT_MAX))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "maxsources")) {
|
} else if (!strcasecmp(cmd, "maxsources")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_sources, &n, 1, INT_MAX))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "maxunreach")) {
|
||||||
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.max_unreach, &n, 0, INT_MAX))
|
||||||
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "mindelay")) {
|
} else if (!strcasecmp(cmd, "mindelay")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.min_delay, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "minpoll")) {
|
} else if (!strcasecmp(cmd, "minpoll")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
|
if (sscanf(line, "%d%n", &src->params.minpoll, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "minsamples")) {
|
} else if (!strcasecmp(cmd, "minsamples")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.min_samples, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_samples, &n, 0, INT_MAX))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "minstratum")) {
|
} else if (!strcasecmp(cmd, "minstratum")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.min_stratum, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.min_stratum, &n, 0, NTP_MAX_STRATUM))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "nts")) {
|
} else if (!strcasecmp(cmd, "nts")) {
|
||||||
src->params.nts = 1;
|
src->params.nts = 1;
|
||||||
} else if (!strcasecmp(cmd, "ntsport")) {
|
} else if (!strcasecmp(cmd, "ntsport")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.nts_port, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.nts_port, &n, 0, 65535))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "offset")) {
|
} else if (!strcasecmp(cmd, "offset")) {
|
||||||
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
|
if (sscanf(line, "%lf%n", &src->params.offset, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "port")) {
|
} else if (!strcasecmp(cmd, "port")) {
|
||||||
if (sscanf(line, "%d%n", &src->port, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->port, &n, 0, 65535))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "polltarget")) {
|
} else if (!strcasecmp(cmd, "polltarget")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.poll_target, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.poll_target, &n, 1, INT_MAX))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "presend")) {
|
} else if (!strcasecmp(cmd, "presend")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.presend_minpoll, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.presend_minpoll, &n, -32, 32))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "version")) {
|
} else if (!strcasecmp(cmd, "version")) {
|
||||||
if (sscanf(line, "%d%n", &src->params.version, &n) != 1)
|
if (!SSCANF_IN_RANGE(line, "%d%n", &src->params.version, &n, 1, NTP_VERSION))
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
} else if (!strcasecmp(cmd, "xleave")) {
|
} else if (!strcasecmp(cmd, "xleave")) {
|
||||||
src->params.interleaved = 1;
|
src->params.interleaved = 1;
|
||||||
|
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
|
||||||
|
src->params.sel_options |= sel_option;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return CPS_InvalidOption;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return CPS_Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
|
CPS_GetSelectOption(char *option)
|
||||||
|
{
|
||||||
|
if (!strcasecmp(option, "noselect")) {
|
||||||
|
return SRC_SELECT_NOSELECT;
|
||||||
|
} else if (!strcasecmp(option, "prefer")) {
|
||||||
|
return SRC_SELECT_PREFER;
|
||||||
|
} else if (!strcasecmp(option, "require")) {
|
||||||
|
return SRC_SELECT_REQUIRE;
|
||||||
|
} else if (!strcasecmp(option, "trust")) {
|
||||||
|
return SRC_SELECT_TRUST;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
|
||||||
|
{
|
||||||
|
char *p, *net, *slash;
|
||||||
|
uint32_t a, b, c;
|
||||||
|
int bits, len, n;
|
||||||
|
|
||||||
|
p = CPS_SplitWord(line);
|
||||||
|
|
||||||
|
if (strcmp(line, "all") == 0) {
|
||||||
|
*all = 1;
|
||||||
|
net = p;
|
||||||
|
p = CPS_SplitWord(p);
|
||||||
|
} else {
|
||||||
|
*all = 0;
|
||||||
|
net = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure there are no other arguments */
|
||||||
|
if (*p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* No specified address or network means all IPv4 and IPv6 addresses */
|
||||||
|
if (!*net) {
|
||||||
|
ip->family = IPADDR_UNSPEC;
|
||||||
|
*subnet_bits = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
slash = strchr(net, '/');
|
||||||
|
if (slash) {
|
||||||
|
if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
|
||||||
|
return 0;
|
||||||
|
*slash = '\0';
|
||||||
|
} else {
|
||||||
|
bits = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UTI_StringToIP(net, ip)) {
|
||||||
|
if (bits >= 0)
|
||||||
|
*subnet_bits = bits;
|
||||||
|
else
|
||||||
|
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
|
||||||
|
numbers. This is different than the numbers-and-dots notation accepted
|
||||||
|
by inet_aton()! */
|
||||||
|
|
||||||
|
a = b = c = 0;
|
||||||
|
n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
|
||||||
|
|
||||||
|
if (n > 0 && !net[len]) {
|
||||||
|
if (a > 255 || b > 255 || c > 255)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ip->family = IPADDR_INET4;
|
||||||
|
ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
|
||||||
|
|
||||||
|
if (bits >= 0)
|
||||||
|
*subnet_bits = bits;
|
||||||
|
else
|
||||||
|
*subnet_bits = n * 8;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The last possibility is a hostname */
|
||||||
|
if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
|
||||||
|
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
CPS_Status
|
||||||
|
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate,
|
||||||
|
double *wait_synced, double *wait_unsynced)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
|
|
||||||
*stratum = 10;
|
*stratum = 10;
|
||||||
*distance = 1.0;
|
*distance = 1.0;
|
||||||
|
*activate = 0.0;
|
||||||
*orphan = 0;
|
*orphan = 0;
|
||||||
|
*wait_synced = 0;
|
||||||
|
*wait_unsynced = -1.0;
|
||||||
|
|
||||||
while (*line) {
|
while (*line) {
|
||||||
cmd = line;
|
cmd = line;
|
||||||
line = CPS_SplitWord(line);
|
line = CPS_SplitWord(line);
|
||||||
|
|
||||||
if (!strcasecmp(cmd, "stratum")) {
|
if (!strcasecmp(cmd, "stratum")) {
|
||||||
if (sscanf(line, "%d%n", stratum, &n) != 1 ||
|
if (!SSCANF_IN_RANGE(line, "%d%n", stratum, &n, 1, NTP_MAX_STRATUM - 1))
|
||||||
*stratum >= NTP_MAX_STRATUM || *stratum <= 0)
|
return CPS_InvalidValue;
|
||||||
return 0;
|
|
||||||
} else if (!strcasecmp(cmd, "orphan")) {
|
} else if (!strcasecmp(cmd, "orphan")) {
|
||||||
*orphan = 1;
|
*orphan = 1;
|
||||||
n = 0;
|
n = 0;
|
||||||
} else if (!strcasecmp(cmd, "distance")) {
|
} else if (!strcasecmp(cmd, "distance")) {
|
||||||
if (sscanf(line, "%lf%n", distance, &n) != 1)
|
if (sscanf(line, "%lf%n", distance, &n) != 1)
|
||||||
return 0;
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "activate")) {
|
||||||
|
if (sscanf(line, "%lf%n", activate, &n) != 1)
|
||||||
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "waitsynced")) {
|
||||||
|
if (sscanf(line, "%lf%n", wait_synced, &n) != 1)
|
||||||
|
return CPS_InvalidValue;
|
||||||
|
} else if (!strcasecmp(cmd, "waitunsynced")) {
|
||||||
|
if (sscanf(line, "%lf%n", wait_unsynced, &n) != 1)
|
||||||
|
return CPS_InvalidValue;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return CPS_InvalidOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
line += n;
|
line += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
if (*wait_unsynced < 0.0)
|
||||||
|
*wait_unsynced = *orphan ? 300 : 0.0;
|
||||||
|
|
||||||
|
return CPS_Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -301,3 +439,19 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CPS_ParseRefid(char *line, uint32_t *ref_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
|
||||||
|
if (i >= 4)
|
||||||
|
return 0;
|
||||||
|
*ref_id |= (uint32_t)line[i] << (24 - i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|||||||
22
cmdparse.h
22
cmdparse.h
@@ -30,17 +30,32 @@
|
|||||||
#include "srcparams.h"
|
#include "srcparams.h"
|
||||||
#include "addressing.h"
|
#include "addressing.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CPS_Success,
|
||||||
|
CPS_InvalidValue,
|
||||||
|
CPS_InvalidOption,
|
||||||
|
CPS_MissingArgument,
|
||||||
|
} CPS_Status;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
|
int family;
|
||||||
int port;
|
int port;
|
||||||
SourceParameters params;
|
SourceParameters params;
|
||||||
} CPS_NTP_Source;
|
} CPS_NTP_Source;
|
||||||
|
|
||||||
/* Parse a command to add an NTP server or peer */
|
/* Parse a command to add an NTP server or peer */
|
||||||
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
|
extern CPS_Status CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
|
||||||
|
|
||||||
|
/* Get an NTP/refclock select option */
|
||||||
|
extern int CPS_GetSelectOption(char *option);
|
||||||
|
|
||||||
|
/* Parse a command to allow/deny access */
|
||||||
|
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
|
||||||
|
|
||||||
/* Parse a command to enable local reference */
|
/* Parse a command to enable local reference */
|
||||||
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
|
extern CPS_Status CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance,
|
||||||
|
double *activate, double *wait_synced, double *wait_unsynced);
|
||||||
|
|
||||||
/* Remove extra white-space and comments */
|
/* Remove extra white-space and comments */
|
||||||
extern void CPS_NormalizeLine(char *line);
|
extern void CPS_NormalizeLine(char *line);
|
||||||
@@ -51,4 +66,7 @@ extern char *CPS_SplitWord(char *line);
|
|||||||
/* Parse a key from keyfile */
|
/* Parse a key from keyfile */
|
||||||
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
|
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
|
||||||
|
|
||||||
|
/* Parse a refclock reference ID (returns number of characters) */
|
||||||
|
extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
|
||||||
|
|
||||||
#endif /* GOT_CMDPARSE_H */
|
#endif /* GOT_CMDPARSE_H */
|
||||||
|
|||||||
22
conf.h
22
conf.h
@@ -29,6 +29,7 @@
|
|||||||
#define GOT_CONF_H
|
#define GOT_CONF_H
|
||||||
|
|
||||||
#include "addressing.h"
|
#include "addressing.h"
|
||||||
|
#include "array.h"
|
||||||
#include "reference.h"
|
#include "reference.h"
|
||||||
#include "sources.h"
|
#include "sources.h"
|
||||||
|
|
||||||
@@ -44,6 +45,8 @@ extern void CNF_ParseLine(const char *filename, int number, char *line);
|
|||||||
|
|
||||||
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
|
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
|
||||||
|
|
||||||
|
extern void CNF_CheckReadOnlyAccess(void);
|
||||||
|
|
||||||
extern void CNF_AddInitSources(void);
|
extern void CNF_AddInitSources(void);
|
||||||
extern void CNF_AddSources(void);
|
extern void CNF_AddSources(void);
|
||||||
extern void CNF_AddBroadcasts(void);
|
extern void CNF_AddBroadcasts(void);
|
||||||
@@ -53,11 +56,12 @@ extern void CNF_ReloadSources(void);
|
|||||||
|
|
||||||
extern int CNF_GetAcquisitionPort(void);
|
extern int CNF_GetAcquisitionPort(void);
|
||||||
extern int CNF_GetNTPPort(void);
|
extern int CNF_GetNTPPort(void);
|
||||||
extern char *CNF_GetDriftFile(void);
|
extern char *CNF_GetDriftFile(int *interval);
|
||||||
extern char *CNF_GetLogDir(void);
|
extern char *CNF_GetLogDir(void);
|
||||||
extern char *CNF_GetDumpDir(void);
|
extern char *CNF_GetDumpDir(void);
|
||||||
extern int CNF_GetLogBanner(void);
|
extern int CNF_GetLogBanner(void);
|
||||||
extern int CNF_GetLogMeasurements(int *raw);
|
extern int CNF_GetLogMeasurements(int *raw);
|
||||||
|
extern int CNF_GetLogSelection(void);
|
||||||
extern int CNF_GetLogStatistics(void);
|
extern int CNF_GetLogStatistics(void);
|
||||||
extern int CNF_GetLogTracking(void);
|
extern int CNF_GetLogTracking(void);
|
||||||
extern int CNF_GetLogRtc(void);
|
extern int CNF_GetLogRtc(void);
|
||||||
@@ -66,6 +70,7 @@ extern int CNF_GetLogTempComp(void);
|
|||||||
extern char *CNF_GetKeysFile(void);
|
extern char *CNF_GetKeysFile(void);
|
||||||
extern char *CNF_GetRtcFile(void);
|
extern char *CNF_GetRtcFile(void);
|
||||||
extern int CNF_GetManualEnabled(void);
|
extern int CNF_GetManualEnabled(void);
|
||||||
|
extern ARR_Instance CNF_GetOpenCommands(void);
|
||||||
extern int CNF_GetCommandPort(void);
|
extern int CNF_GetCommandPort(void);
|
||||||
extern int CNF_GetRtcOnUtc(void);
|
extern int CNF_GetRtcOnUtc(void);
|
||||||
extern int CNF_GetRtcSync(void);
|
extern int CNF_GetRtcSync(void);
|
||||||
@@ -88,6 +93,7 @@ extern char *CNF_GetNtpSigndSocket(void);
|
|||||||
extern char *CNF_GetPidFile(void);
|
extern char *CNF_GetPidFile(void);
|
||||||
extern REF_LeapMode CNF_GetLeapSecMode(void);
|
extern REF_LeapMode CNF_GetLeapSecMode(void);
|
||||||
extern char *CNF_GetLeapSecTimezone(void);
|
extern char *CNF_GetLeapSecTimezone(void);
|
||||||
|
extern char *CNF_GetLeapSecList(void);
|
||||||
|
|
||||||
/* Value returned in ppm, as read from file */
|
/* Value returned in ppm, as read from file */
|
||||||
extern double CNF_GetMaxUpdateSkew(void);
|
extern double CNF_GetMaxUpdateSkew(void);
|
||||||
@@ -104,14 +110,15 @@ extern double CNF_GetReselectDistance(void);
|
|||||||
extern double CNF_GetStratumWeight(void);
|
extern double CNF_GetStratumWeight(void);
|
||||||
extern double CNF_GetCombineLimit(void);
|
extern double CNF_GetCombineLimit(void);
|
||||||
|
|
||||||
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance);
|
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate,
|
||||||
|
double *wait_synced, double *wait_unsynced);
|
||||||
|
|
||||||
extern void CNF_SetupAccessRestrictions(void);
|
extern void CNF_SetupAccessRestrictions(void);
|
||||||
|
|
||||||
extern int CNF_GetSchedPriority(void);
|
extern int CNF_GetSchedPriority(void);
|
||||||
extern int CNF_GetLockMemory(void);
|
extern int CNF_GetLockMemory(void);
|
||||||
|
|
||||||
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
|
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod);
|
||||||
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
|
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
|
||||||
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
|
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
|
||||||
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
|
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
|
||||||
@@ -134,12 +141,14 @@ typedef enum {
|
|||||||
CNF_HWTS_RXFILTER_ANY,
|
CNF_HWTS_RXFILTER_ANY,
|
||||||
CNF_HWTS_RXFILTER_NONE,
|
CNF_HWTS_RXFILTER_NONE,
|
||||||
CNF_HWTS_RXFILTER_NTP,
|
CNF_HWTS_RXFILTER_NTP,
|
||||||
|
CNF_HWTS_RXFILTER_PTP,
|
||||||
CNF_HWTS_RXFILTER_ALL,
|
CNF_HWTS_RXFILTER_ALL,
|
||||||
} CNF_HwTs_RxFilter;
|
} CNF_HwTs_RxFilter;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
int minpoll;
|
int minpoll;
|
||||||
|
int maxpoll;
|
||||||
int min_samples;
|
int min_samples;
|
||||||
int max_samples;
|
int max_samples;
|
||||||
int nocrossts;
|
int nocrossts;
|
||||||
@@ -150,7 +159,14 @@ typedef struct {
|
|||||||
} CNF_HwTsInterface;
|
} CNF_HwTsInterface;
|
||||||
|
|
||||||
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
|
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
|
||||||
|
extern double CNF_GetHwTsTimeout(void);
|
||||||
|
|
||||||
|
extern int CNF_GetPtpPort(void);
|
||||||
|
extern int CNF_GetPtpDomain(void);
|
||||||
|
|
||||||
|
extern int CNF_GetRefresh(void);
|
||||||
|
|
||||||
|
extern ARR_Instance CNF_GetNtsAeads(void);
|
||||||
extern char *CNF_GetNtsDumpDir(void);
|
extern char *CNF_GetNtsDumpDir(void);
|
||||||
extern char *CNF_GetNtsNtpServer(void);
|
extern char *CNF_GetNtsNtpServer(void);
|
||||||
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
|
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
|
||||||
|
|||||||
186
configure
vendored
186
configure
vendored
@@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
# Copyright (C) Richard P. Curnow 1997-2003
|
# Copyright (C) Richard P. Curnow 1997-2003
|
||||||
# Copyright (C) Bryan Christianson 2016
|
# Copyright (C) Bryan Christianson 2016
|
||||||
# Copyright (C) Miroslav Lichvar 2009, 2012-2020
|
# Copyright (C) Miroslav Lichvar 2009, 2012-2022
|
||||||
# Copyright (C) Stefan R. Filipek 2019
|
# Copyright (C) Stefan R. Filipek 2019
|
||||||
#
|
#
|
||||||
# =======================================================================
|
# =======================================================================
|
||||||
@@ -111,12 +111,11 @@ For better control, use the options below.
|
|||||||
--without-editline Don't use editline even if it is available
|
--without-editline Don't use editline even if it is available
|
||||||
--disable-sechash Disable support for hashes other than MD5
|
--disable-sechash Disable support for hashes other than MD5
|
||||||
--without-nettle Don't use nettle even if it is available
|
--without-nettle Don't use nettle even if it is available
|
||||||
|
--without-gnutls Don't use gnutls even if it is available
|
||||||
--without-nss Don't use NSS even if it is available
|
--without-nss Don't use NSS even if it is available
|
||||||
--without-tomcrypt Don't use libtomcrypt even if it is available
|
--without-tomcrypt Don't use libtomcrypt even if it is available
|
||||||
--disable-nts Disable NTS support
|
--disable-nts Disable NTS support
|
||||||
--without-gnutls Don't use gnutls even if it is available
|
|
||||||
--disable-cmdmon Disable command and monitoring support
|
--disable-cmdmon Disable command and monitoring support
|
||||||
--disable-ntp Disable NTP support
|
|
||||||
--disable-refclock Disable reference clock support
|
--disable-refclock Disable reference clock support
|
||||||
--disable-phc Disable PHC refclock driver
|
--disable-phc Disable PHC refclock driver
|
||||||
--disable-pps Disable PPS refclock driver
|
--disable-pps Disable PPS refclock driver
|
||||||
@@ -126,14 +125,15 @@ For better control, use the options below.
|
|||||||
--without-libcap Don't use libcap even if it is available
|
--without-libcap Don't use libcap even if it is available
|
||||||
--enable-scfilter Enable support for system call filtering
|
--enable-scfilter Enable support for system call filtering
|
||||||
--without-seccomp Don't use seccomp even if it is available
|
--without-seccomp Don't use seccomp even if it is available
|
||||||
--disable-asyncdns Disable asynchronous name resolving
|
|
||||||
--disable-forcednsretry Don't retry on permanent DNS error
|
--disable-forcednsretry Don't retry on permanent DNS error
|
||||||
|
--without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
|
||||||
--without-clock-gettime Don't use clock_gettime() even if it is available
|
--without-clock-gettime Don't use clock_gettime() even if it is available
|
||||||
--disable-timestamping Disable support for SW/HW timestamping
|
--disable-timestamping Disable support for SW/HW timestamping
|
||||||
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
|
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
|
||||||
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
|
--with-ntp-era=SECONDS Specify earliest assumed NTP time in seconds
|
||||||
since 1970-01-01 [50*365 days ago]
|
since 1970-01-01 [50*365 days ago]
|
||||||
--with-user=USER Specify default chronyd user [root]
|
--with-user=USER Specify default chronyd user [root]
|
||||||
|
--with-chronyc-user=USER Specify default chronyc user [root]
|
||||||
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
|
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
|
||||||
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
|
--with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
|
||||||
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
|
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
|
||||||
@@ -218,7 +218,6 @@ EXTRA_CLI_OBJECTS=""
|
|||||||
|
|
||||||
feat_debug=0
|
feat_debug=0
|
||||||
feat_cmdmon=1
|
feat_cmdmon=1
|
||||||
feat_ntp=1
|
|
||||||
feat_refclock=1
|
feat_refclock=1
|
||||||
feat_readline=1
|
feat_readline=1
|
||||||
try_editline=1
|
try_editline=1
|
||||||
@@ -242,16 +241,17 @@ try_phc=0
|
|||||||
feat_pps=1
|
feat_pps=1
|
||||||
try_setsched=0
|
try_setsched=0
|
||||||
try_lockmem=0
|
try_lockmem=0
|
||||||
feat_asyncdns=1
|
|
||||||
feat_forcednsretry=1
|
feat_forcednsretry=1
|
||||||
|
try_aes_gcm_siv=1
|
||||||
try_clock_gettime=1
|
try_clock_gettime=1
|
||||||
|
try_arc4random=1
|
||||||
try_recvmmsg=1
|
try_recvmmsg=1
|
||||||
feat_timestamping=1
|
feat_timestamping=1
|
||||||
try_timestamping=0
|
try_timestamping=0
|
||||||
feat_ntp_signd=0
|
feat_ntp_signd=0
|
||||||
ntp_era_split=""
|
ntp_era_split=""
|
||||||
use_pthread=0
|
|
||||||
default_user="root"
|
default_user="root"
|
||||||
|
default_chronyc_user="root"
|
||||||
default_hwclockfile=""
|
default_hwclockfile=""
|
||||||
default_pidfile="/var/run/chrony/chronyd.pid"
|
default_pidfile="/var/run/chrony/chronyd.pid"
|
||||||
default_rtcdevice="/dev/rtc"
|
default_rtcdevice="/dev/rtc"
|
||||||
@@ -305,9 +305,6 @@ do
|
|||||||
--disable-cmdmon)
|
--disable-cmdmon)
|
||||||
feat_cmdmon=0
|
feat_cmdmon=0
|
||||||
;;
|
;;
|
||||||
--disable-ntp)
|
|
||||||
feat_ntp=0
|
|
||||||
;;
|
|
||||||
--disable-refclock)
|
--disable-refclock)
|
||||||
feat_refclock=0
|
feat_refclock=0
|
||||||
;;
|
;;
|
||||||
@@ -338,12 +335,12 @@ do
|
|||||||
--without-seccomp)
|
--without-seccomp)
|
||||||
try_seccomp=0
|
try_seccomp=0
|
||||||
;;
|
;;
|
||||||
--disable-asyncdns)
|
|
||||||
feat_asyncdns=0
|
|
||||||
;;
|
|
||||||
--disable-forcednsretry)
|
--disable-forcednsretry)
|
||||||
feat_forcednsretry=0
|
feat_forcednsretry=0
|
||||||
;;
|
;;
|
||||||
|
--without-aes-gcm-siv)
|
||||||
|
try_aes_gcm_siv=0
|
||||||
|
;;
|
||||||
--without-clock-gettime)
|
--without-clock-gettime)
|
||||||
try_clock_gettime=0
|
try_clock_gettime=0
|
||||||
;;
|
;;
|
||||||
@@ -359,6 +356,9 @@ do
|
|||||||
--with-user=* )
|
--with-user=* )
|
||||||
default_user=`echo $option | sed -e 's/^.*=//;'`
|
default_user=`echo $option | sed -e 's/^.*=//;'`
|
||||||
;;
|
;;
|
||||||
|
--with-chronyc-user=* )
|
||||||
|
default_chronyc_user=`echo $option | sed -e 's/^.*=//;'`
|
||||||
|
;;
|
||||||
--with-hwclockfile=* )
|
--with-hwclockfile=* )
|
||||||
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
|
default_hwclockfile=`echo $option | sed -e 's/^.*=//;'`
|
||||||
;;
|
;;
|
||||||
@@ -421,6 +421,7 @@ case $OPERATINGSYSTEM in
|
|||||||
try_setsched=1
|
try_setsched=1
|
||||||
try_lockmem=1
|
try_lockmem=1
|
||||||
try_phc=1
|
try_phc=1
|
||||||
|
try_arc4random=0
|
||||||
add_def LINUX
|
add_def LINUX
|
||||||
echo "Configuring for " $SYSTEM
|
echo "Configuring for " $SYSTEM
|
||||||
;;
|
;;
|
||||||
@@ -467,7 +468,7 @@ case $OPERATINGSYSTEM in
|
|||||||
;;
|
;;
|
||||||
SunOS)
|
SunOS)
|
||||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
||||||
LIBS="$LIBS -lsocket -lnsl -lresolv"
|
LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
|
||||||
try_setsched=1
|
try_setsched=1
|
||||||
try_lockmem=1
|
try_lockmem=1
|
||||||
add_def SOLARIS
|
add_def SOLARIS
|
||||||
@@ -479,7 +480,7 @@ case $OPERATINGSYSTEM in
|
|||||||
add_def FEAT_PRIVDROP
|
add_def FEAT_PRIVDROP
|
||||||
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
|
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
|
||||||
fi
|
fi
|
||||||
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
|
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
|
||||||
;;
|
;;
|
||||||
* )
|
* )
|
||||||
echo "error: $SYSTEM is not supported (yet?)"
|
echo "error: $SYSTEM is not supported (yet?)"
|
||||||
@@ -497,27 +498,15 @@ if [ $feat_cmdmon = "1" ]; then
|
|||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS cmdmon.o manual.o pktlength.o"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $feat_ntp = "1" ]; then
|
|
||||||
add_def FEAT_NTP
|
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_auth.o ntp_core.o ntp_ext.o ntp_io.o ntp_sources.o"
|
|
||||||
if [ $feat_ntp_signd = "1" ]; then
|
if [ $feat_ntp_signd = "1" ]; then
|
||||||
add_def FEAT_SIGND
|
add_def FEAT_SIGND
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS ntp_signd.o"
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
feat_asyncdns=0
|
|
||||||
feat_timestamping=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$feat_cmdmon" = "1" ] || [ $feat_ntp = "1" ]; then
|
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS addrfilt.o clientlog.o keys.o nameserv.o"
|
|
||||||
else
|
|
||||||
feat_ipv6=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $feat_refclock = "1" ]; then
|
if [ $feat_refclock = "1" ]; then
|
||||||
add_def FEAT_REFCLOCK
|
add_def FEAT_REFCLOCK
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o refclock_shm.o refclock_sock.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS refclock.o refclock_phc.o refclock_pps.o \
|
||||||
|
refclock_rtc.o refclock_shm.o refclock_sock.o"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MYCC="$CC"
|
MYCC="$CC"
|
||||||
@@ -563,6 +552,13 @@ if [ "x$MYCFLAGS" = "x" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TESTCFLAGS="-fwrapv"
|
||||||
|
if test_code '-fwrapv' '' "$TESTCFLAGS" '' ''; then
|
||||||
|
GETDATE_CFLAGS="-fwrapv"
|
||||||
|
else
|
||||||
|
GETDATE_CFLAGS=""
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
|
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
|
||||||
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
|
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
|
||||||
fi
|
fi
|
||||||
@@ -670,13 +666,15 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $try_clock_gettime = "1" ]; then
|
if [ $try_clock_gettime = "1" ]; then
|
||||||
if test_code 'clock_gettime()' 'time.h' '' '' \
|
if test_code 'clock_gettime()' 'time.h' '' '' '
|
||||||
'clock_gettime(CLOCK_REALTIME, NULL);'
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CLOCK_GETTIME
|
add_def HAVE_CLOCK_GETTIME
|
||||||
else
|
else
|
||||||
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
|
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' '
|
||||||
'clock_gettime(CLOCK_REALTIME, NULL);'
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CLOCK_GETTIME
|
add_def HAVE_CLOCK_GETTIME
|
||||||
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
||||||
@@ -691,22 +689,28 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $feat_asyncdns = "1" ] && \
|
if test_code 'pthread' 'pthread.h' '-pthread' '' '
|
||||||
test_code 'pthread' 'pthread.h' '-pthread' '' '
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
|
return (int)pthread_create(&thread, NULL, (void *)1, NULL);'
|
||||||
then
|
then
|
||||||
add_def FEAT_ASYNCDNS
|
|
||||||
add_def USE_PTHREAD_ASYNCDNS
|
add_def USE_PTHREAD_ASYNCDNS
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nameserv_async.o"
|
MYCFLAGS="$MYCFLAGS -pthread"
|
||||||
use_pthread=1
|
else
|
||||||
|
echo "error: pthread_create() not found"
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
|
if [ $try_arc4random = "1" ] && \
|
||||||
|
test_code 'arc4random_buf()' 'stdlib.h' '' '' '
|
||||||
|
char c;
|
||||||
|
arc4random_buf(&c, 1);'
|
||||||
|
then
|
||||||
add_def HAVE_ARC4RANDOM
|
add_def HAVE_ARC4RANDOM
|
||||||
else
|
else
|
||||||
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
|
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' '
|
||||||
'return getrandom(NULL, 256, 0);'; then
|
char c;
|
||||||
|
return getrandom(&c, 1, 0);'
|
||||||
|
then
|
||||||
add_def HAVE_GETRANDOM
|
add_def HAVE_GETRANDOM
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -799,12 +803,10 @@ if [ $feat_scfilter = "1" ] && [ $try_seccomp = "1" ] && \
|
|||||||
'seccomp_init(SCMP_ACT_KILL);'
|
'seccomp_init(SCMP_ACT_KILL);'
|
||||||
then
|
then
|
||||||
add_def FEAT_SCFILTER
|
add_def FEAT_SCFILTER
|
||||||
if [ $feat_ntp = "1" ]; then
|
|
||||||
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
|
# NAME2IPADDRESS shouldn't be enabled together with a privops operation
|
||||||
# used by the main thread as the helper process works on one request at
|
# used by the main thread as the helper process works on one request at
|
||||||
# a time and the async resolver would block the main thread
|
# a time and the async resolver would block the main thread
|
||||||
priv_ops="NAME2IPADDRESS RELOADDNS"
|
priv_ops="NAME2IPADDRESS RELOADDNS"
|
||||||
fi
|
|
||||||
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
|
EXTRA_LIBS="$EXTRA_LIBS -lseccomp"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -843,7 +845,6 @@ if [ $try_setsched = "1" ] && \
|
|||||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
|
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched);'
|
||||||
then
|
then
|
||||||
add_def HAVE_PTHREAD_SETSCHEDPARAM
|
add_def HAVE_PTHREAD_SETSCHEDPARAM
|
||||||
use_pthread=1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $try_lockmem = "1" ] && \
|
if [ $try_lockmem = "1" ] && \
|
||||||
@@ -897,15 +898,46 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
|
|||||||
HASH_OBJ="hash_nettle.o"
|
HASH_OBJ="hash_nettle.o"
|
||||||
HASH_LINK="$test_link"
|
HASH_LINK="$test_link"
|
||||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||||
|
add_def HAVE_NETTLE
|
||||||
add_def FEAT_SECHASH
|
add_def FEAT_SECHASH
|
||||||
|
|
||||||
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
|
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
|
||||||
'cmac128_update(NULL, NULL, NULL, 0, NULL);'
|
'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
|
||||||
then
|
then
|
||||||
add_def HAVE_CMAC
|
add_def HAVE_CMAC
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
|
||||||
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o"
|
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_nettle.o"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test_code 'nettle_memeql_sec()' 'nettle/memops.h' \
|
||||||
|
"$test_cflags" "$test_link" \
|
||||||
|
'return nettle_memeql_sec("", "", 0);'
|
||||||
|
then
|
||||||
|
add_def HAVE_NETTLE_MEMEQL
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
|
||||||
|
test_cflags="`pkg_config --cflags gnutls`"
|
||||||
|
test_link="`pkg_config --libs gnutls`"
|
||||||
|
if test_code 'gnutls' 'gnutls/crypto.h' \
|
||||||
|
"$test_cflags" "$test_link" '
|
||||||
|
return gnutls_hash((void *)1, (void *)2, 1);'
|
||||||
|
then
|
||||||
|
HASH_OBJ="hash_gnutls.o"
|
||||||
|
HASH_LINK="$test_link"
|
||||||
|
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||||
|
add_def HAVE_GNUTLS
|
||||||
|
add_def FEAT_SECHASH
|
||||||
|
|
||||||
|
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
|
||||||
|
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
|
||||||
|
then
|
||||||
|
add_def HAVE_CMAC
|
||||||
|
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
|
||||||
|
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -925,7 +957,7 @@ fi
|
|||||||
|
|
||||||
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
|
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
|
||||||
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
|
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
|
||||||
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
|
'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
|
||||||
then
|
then
|
||||||
HASH_OBJ="hash_tomcrypt.o"
|
HASH_OBJ="hash_tomcrypt.o"
|
||||||
HASH_LINK="-ltomcrypt"
|
HASH_LINK="-ltomcrypt"
|
||||||
@@ -938,41 +970,58 @@ EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
|
|||||||
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
|
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
|
||||||
LIBS="$LIBS $HASH_LINK"
|
LIBS="$LIBS $HASH_LINK"
|
||||||
|
|
||||||
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
if [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||||
|
if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
|
||||||
|
test_cflags=""
|
||||||
|
test_link=""
|
||||||
|
else
|
||||||
test_cflags="`pkg_config --cflags gnutls`"
|
test_cflags="`pkg_config --cflags gnutls`"
|
||||||
test_link="`pkg_config --libs gnutls`"
|
test_link="`pkg_config --libs gnutls`"
|
||||||
if test_code 'gnutls' 'gnutls/gnutls.h' \
|
fi
|
||||||
"$test_cflags" "$test_link" '
|
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
|
||||||
return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
|
"$test_cflags" "$test_link $LIBS" '
|
||||||
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
|
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
|
||||||
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
|
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
|
||||||
|
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
|
||||||
then
|
then
|
||||||
if test_code 'SIV in nettle' \
|
if [ $try_nettle = "1" ] && test_code 'AES-SIV-CMAC in nettle' \
|
||||||
'nettle/siv-cmac.h' "" "$LIBS" \
|
'nettle/siv-cmac.h' "" "$LIBS" \
|
||||||
'siv_cmac_aes128_set_key(NULL, NULL);'
|
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||||
add_def HAVE_SIV
|
add_def HAVE_SIV
|
||||||
add_def HAVE_NETTLE_SIV_CMAC
|
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
|
||||||
|
'nettle/siv-gcm.h' "" "$LIBS" \
|
||||||
|
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
|
||||||
|
(void *)4);'
|
||||||
|
then
|
||||||
|
add_def HAVE_NETTLE_SIV_GCM
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
if test_code 'SIV in gnutls' 'gnutls/gnutls.h' \
|
if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
|
||||||
"$test_cflags" "$test_link" '
|
"$test_cflags" "$test_link $LIBS" '
|
||||||
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
|
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
|
||||||
add_def HAVE_SIV
|
add_def HAVE_SIV
|
||||||
else
|
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in gnutls' \
|
||||||
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
|
'gnutls/crypto.h' "$test_cflags" "$test_link $LIBS" '
|
||||||
'aes128_set_encrypt_key(NULL, NULL);'
|
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV_GCM,
|
||||||
|
(void *)2);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
add_def HAVE_GNUTLS_SIV_GCM
|
||||||
add_def HAVE_SIV
|
fi
|
||||||
|
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
|
||||||
|
"$test_cflags" "$test_link $LIBS" '
|
||||||
|
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
|
||||||
|
then
|
||||||
|
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if grep '#define HAVE_SIV' config.h > /dev/null; then
|
if grep '#define HAVE_SIV' config.h > /dev/null; then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o tls_gnutls.o"
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
|
||||||
LIBS="$LIBS $test_link"
|
LIBS="$LIBS $test_link"
|
||||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||||
@@ -981,10 +1030,6 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $use_pthread = "1" ]; then
|
|
||||||
MYCFLAGS="$MYCFLAGS -pthread"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SYSCONFDIR=/etc
|
SYSCONFDIR=/etc
|
||||||
if [ "x$SETSYSCONFDIR" != "x" ]; then
|
if [ "x$SETSYSCONFDIR" != "x" ]; then
|
||||||
SYSCONFDIR=$SETSYSCONFDIR
|
SYSCONFDIR=$SETSYSCONFDIR
|
||||||
@@ -1045,12 +1090,13 @@ add_def DEFAULT_HWCLOCK_FILE "\"$default_hwclockfile\""
|
|||||||
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
|
add_def DEFAULT_PID_FILE "\"$default_pidfile\""
|
||||||
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
|
add_def DEFAULT_RTC_DEVICE "\"$default_rtcdevice\""
|
||||||
add_def DEFAULT_USER "\"$default_user\""
|
add_def DEFAULT_USER "\"$default_user\""
|
||||||
|
add_def DEFAULT_CHRONYC_USER "\"$default_chronyc_user\""
|
||||||
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
|
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
|
||||||
add_def MAIL_PROGRAM "\"$mail_program\""
|
add_def MAIL_PROGRAM "\"$mail_program\""
|
||||||
|
|
||||||
common_features="`get_features SECHASH IPV6 DEBUG`"
|
common_features="`get_features SECHASH IPV6 DEBUG`"
|
||||||
chronyc_features="`get_features READLINE`"
|
chronyc_features="`get_features READLINE`"
|
||||||
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS NTS`"
|
chronyd_features="`get_features CMDMON REFCLOCK RTC PRIVDROP SCFILTER SIGND NTS`"
|
||||||
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
|
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
|
||||||
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
|
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
|
||||||
echo "Features : $chronyd_features $chronyc_features $common_features"
|
echo "Features : $chronyd_features $chronyc_features $common_features"
|
||||||
@@ -1072,6 +1118,7 @@ do
|
|||||||
s%@CFLAGS@%${MYCFLAGS}%;\
|
s%@CFLAGS@%${MYCFLAGS}%;\
|
||||||
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
|
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
|
||||||
s%@LDFLAGS@%${MYLDFLAGS}%;\
|
s%@LDFLAGS@%${MYLDFLAGS}%;\
|
||||||
|
s%@GETDATE_CFLAGS@%${GETDATE_CFLAGS}%;\
|
||||||
s%@LIBS@%${LIBS}%;\
|
s%@LIBS@%${LIBS}%;\
|
||||||
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
|
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
|
||||||
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
|
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\
|
||||||
@@ -1087,6 +1134,7 @@ do
|
|||||||
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
|
s%@DEFAULT_PID_FILE@%${default_pidfile}%;\
|
||||||
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
|
s%@DEFAULT_RTC_DEVICE@%${default_rtcdevice}%;\
|
||||||
s%@DEFAULT_USER@%${default_user}%;\
|
s%@DEFAULT_USER@%${default_user}%;\
|
||||||
|
s%@DEFAULT_CHRONYC_USER@%${default_chronyc_user}%;\
|
||||||
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
|
s%@CHRONY_VERSION@%${CHRONY_VERSION}%;" \
|
||||||
< ${f}.in > $f
|
< ${f}.in > $f
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ Support files
|
|||||||
Dates and sizes may differ
|
Dates and sizes may differ
|
||||||
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
|
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
|
||||||
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
|
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
|
||||||
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist
|
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.chrony-project.chronyc.plist
|
||||||
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist
|
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.chrony-project.chronyd.plist
|
||||||
|
|
||||||
If you have used chrony support directories other than those suggested, you
|
If you have used chrony support directories other than those suggested, you
|
||||||
will need to edit each file and make the appropriate changes.
|
will need to edit each file and make the appropriate changes.
|
||||||
@@ -83,21 +83,21 @@ sudo chmod +x /usr/local/bin/chronylogrotate.sh
|
|||||||
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
|
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
|
||||||
|
|
||||||
|
|
||||||
2. org.tuxfamily.chronyc.plist
|
2. org.chrony-project.chronyc.plist
|
||||||
This file is the launchd plist that runs logrotation each day. You may
|
This file is the launchd plist that runs logrotation each day. You may
|
||||||
wish to edit this file to change the time of day at which the rotation
|
wish to edit this file to change the time of day at which the rotation
|
||||||
will run, currently 04:05 am
|
will run, currently 04:05 am
|
||||||
|
|
||||||
sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons
|
sudo cp org.chrony-project.chronyc.plist /Library/LaunchDaemons
|
||||||
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
|
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyc.plist
|
||||||
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
|
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyc.plist
|
||||||
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
|
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyc.plist
|
||||||
|
|
||||||
|
|
||||||
3. org.tuxfamily.chronyd.plist
|
3. org.chrony-project.chronyd.plist
|
||||||
This file is the launchd plist that runs chronyd when the Macintosh starts.
|
This file is the launchd plist that runs chronyd when the Macintosh starts.
|
||||||
|
|
||||||
sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons
|
sudo cp org.chrony-project.chronyd.plist /Library/LaunchDaemons
|
||||||
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
|
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyd.plist
|
||||||
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
|
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyd.plist
|
||||||
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
|
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyd.plist
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>org.tuxfamily.logrotate</string>
|
<string>org.chrony-project.logrotate</string>
|
||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>org.tuxfamily.chronyd</string>
|
<string>org.chrony-project.chronyd</string>
|
||||||
<key>Program</key>
|
<key>Program</key>
|
||||||
<string>/usr/local/sbin/chronyd</string>
|
<string>/usr/local/sbin/chronyd</string>
|
||||||
<key>ProgramArguments</key>
|
<key>ProgramArguments</key>
|
||||||
@@ -17,6 +17,7 @@ CHRONYRUNDIR = @CHRONYRUNDIR@
|
|||||||
CHRONYVARDIR = @CHRONYVARDIR@
|
CHRONYVARDIR = @CHRONYVARDIR@
|
||||||
CHRONY_VERSION = @CHRONY_VERSION@
|
CHRONY_VERSION = @CHRONY_VERSION@
|
||||||
DEFAULT_USER = @DEFAULT_USER@
|
DEFAULT_USER = @DEFAULT_USER@
|
||||||
|
DEFAULT_CHRONYC_USER = @DEFAULT_CHRONYC_USER@
|
||||||
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
|
DEFAULT_HWCLOCK_FILE = @DEFAULT_HWCLOCK_FILE@
|
||||||
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
|
DEFAULT_PID_FILE = @DEFAULT_PID_FILE@
|
||||||
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
|
DEFAULT_RTC_DEVICE = @DEFAULT_RTC_DEVICE@
|
||||||
@@ -29,6 +30,7 @@ SED_COMMANDS = "s%\@SYSCONFDIR\@%$(SYSCONFDIR)%g;\
|
|||||||
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
|
s%\@DEFAULT_PID_FILE\@%$(DEFAULT_PID_FILE)%g;\
|
||||||
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
|
s%\@DEFAULT_RTC_DEVICE\@%$(DEFAULT_RTC_DEVICE)%g;\
|
||||||
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
|
s%\@DEFAULT_USER\@%$(DEFAULT_USER)%g;\
|
||||||
|
s%\@DEFAULT_CHRONYC_USER\@%$(DEFAULT_CHRONYC_USER)%g;\
|
||||||
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
|
s%\@CHRONYRUNDIR\@%$(CHRONYRUNDIR)%g;\
|
||||||
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
|
s%\@CHRONYVARDIR\@%$(CHRONYVARDIR)%g;"
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
270
doc/chronyc.adoc
270
doc/chronyc.adoc
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// Copyright (C) Richard P. Curnow 1997-2003
|
// Copyright (C) Richard P. Curnow 1997-2003
|
||||||
// Copyright (C) Stephen Wadeley 2016
|
// Copyright (C) Stephen Wadeley 2016
|
||||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020
|
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2025
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -50,7 +50,7 @@ running under a non-root user), it will try to connect to 127.0.0.1 and then
|
|||||||
::1.
|
::1.
|
||||||
|
|
||||||
Only the following monitoring commands, which do not affect the behaviour of
|
Only the following monitoring commands, which do not affect the behaviour of
|
||||||
*chronyd*, are allowed from the network: *activity*, *manual list*,
|
*chronyd*, are allowed from the network by default: *activity*, *manual list*,
|
||||||
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
|
*rtcdata*, *smoothing*, *sourcename*, *sources*, *sourcestats*, *tracking*,
|
||||||
*waitsync*. The
|
*waitsync*. The
|
||||||
set of hosts from which *chronyd* will accept these commands can be configured
|
set of hosts from which *chronyd* will accept these commands can be configured
|
||||||
@@ -58,7 +58,10 @@ with the <<chrony.conf.adoc#cmdallow,*cmdallow*>> directive in the *chronyd*'s
|
|||||||
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
|
configuration file or the <<cmdallow,*cmdallow*>> command in *chronyc*. By
|
||||||
default, the commands are accepted only from localhost (127.0.0.1 or ::1).
|
default, the commands are accepted only from localhost (127.0.0.1 or ::1).
|
||||||
|
|
||||||
All other commands are allowed only through the Unix domain socket. When sent
|
Other monitoring commands can be enabled for network access by the
|
||||||
|
<<chrony.conf.adoc#opencommands,*opencommands*>> directive. Monitoring commands
|
||||||
|
with disabled network access and commands that affect the behaviour of
|
||||||
|
*chronyd* are allowed only through the Unix domain socket. If they are sent
|
||||||
over the network, *chronyd* will respond with a '`Not authorised`' error, even
|
over the network, *chronyd* will respond with a '`Not authorised`' error, even
|
||||||
if it is from localhost.
|
if it is from localhost.
|
||||||
|
|
||||||
@@ -89,6 +92,10 @@ format. Reverse DNS lookups will be disabled, time will be printed as number of
|
|||||||
seconds since the epoch, and values in seconds will not be converted to other
|
seconds since the epoch, and values in seconds will not be converted to other
|
||||||
units.
|
units.
|
||||||
|
|
||||||
|
*-e*::
|
||||||
|
With this option each *chronyc* response will end with a line containing a
|
||||||
|
single dot.
|
||||||
|
|
||||||
*-d*::
|
*-d*::
|
||||||
This option enables printing of debugging messages if *chronyc* was compiled
|
This option enables printing of debugging messages if *chronyc* was compiled
|
||||||
with debugging support.
|
with debugging support.
|
||||||
@@ -99,18 +106,28 @@ With this option multiple commands can be specified. Each argument will be
|
|||||||
interpreted as a whole command.
|
interpreted as a whole command.
|
||||||
|
|
||||||
*-h* _host_::
|
*-h* _host_::
|
||||||
This option allows the user to specify which host (or comma-separated list of
|
This option specifies the host to be contacted by *chronyc*. It can be
|
||||||
addresses) running the *chronyd* program is to be contacted. This allows for
|
specified with a hostname, IP address, or path to the local Unix domain socket.
|
||||||
remote monitoring, without having to connect over SSH to the other host first.
|
Multiple values can be specified as a comma-separated list to provide a
|
||||||
|
fallback.
|
||||||
+
|
+
|
||||||
The default is to contact *chronyd* running on the same host where
|
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
|
||||||
*chronyc* is being run.
|
where *chronyc* is being run. First, it tries to connect to the Unix domain
|
||||||
|
socket and if that fails (e.g. due to running under a non-root user), it
|
||||||
|
will try to connect to 127.0.0.1 and then ::1.
|
||||||
|
|
||||||
*-p* _port_::
|
*-p* _port_::
|
||||||
This option allows the user to specify the UDP port number which the target
|
This option allows the user to specify the UDP port number which the target
|
||||||
*chronyd* is using for its monitoring connections. This defaults to 323; there
|
*chronyd* is using for its monitoring connections. This defaults to 323; there
|
||||||
would rarely be a need to change this.
|
would rarely be a need to change this.
|
||||||
|
|
||||||
|
*-u* _user_::
|
||||||
|
This option sets the name of the user to which *chronyc* will switch when it is
|
||||||
|
started under the root user in order to drop its privileges. *chronyc* will not
|
||||||
|
try to change its identity if the option is set to _root_, or the effective
|
||||||
|
user ID of the started process is not 0. The compiled-in default value is
|
||||||
|
_@DEFAULT_CHRONYC_USER@_.
|
||||||
|
|
||||||
*-f* _file_::
|
*-f* _file_::
|
||||||
This option is ignored and is provided only for compatibility.
|
This option is ignored and is provided only for compatibility.
|
||||||
|
|
||||||
@@ -137,7 +154,7 @@ The *tracking* command displays parameters about the system's clock
|
|||||||
performance. An example of the output is shown below.
|
performance. An example of the output is shown below.
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
Reference ID : CB00710F (foo.example.net)
|
Reference ID : CB00710F (ntp1.example.net)
|
||||||
Stratum : 3
|
Stratum : 3
|
||||||
Ref time (UTC) : Fri Jan 27 09:49:17 2017
|
Ref time (UTC) : Fri Jan 27 09:49:17 2017
|
||||||
System time : 0.000006523 seconds slow of NTP time
|
System time : 0.000006523 seconds slow of NTP time
|
||||||
@@ -171,21 +188,25 @@ with an IPv4 address.
|
|||||||
*Stratum*:::
|
*Stratum*:::
|
||||||
The stratum indicates how many hops away from a computer with an attached
|
The stratum indicates how many hops away from a computer with an attached
|
||||||
reference clock we are. Such a computer is a stratum-1 computer, so the
|
reference clock we are. Such a computer is a stratum-1 computer, so the
|
||||||
computer in the example is two hops away (i.e. _foo.example.net_ is a
|
computer in the example is two hops away (i.e. _ntp1.example.net_ is a
|
||||||
stratum-2 and is synchronised from a stratum-1).
|
stratum-2 and is synchronised from a stratum-1).
|
||||||
*Ref time*:::
|
*Ref time*:::
|
||||||
This is the time (UTC) at which the last measurement from the reference
|
This is the time (UTC) at which the last measurement from the reference
|
||||||
source was processed.
|
source was processed.
|
||||||
*System time*:::
|
*System time*:::
|
||||||
In normal operation, *chronyd* by default never steps the system clock, because
|
This is the current offset between the NTP clock and system clock. The NTP
|
||||||
any jump in the time can have adverse consequences for certain application
|
clock is a software (virtual) clock maintained by *chronyd*, which is
|
||||||
programs. Instead, any error in the system clock is corrected by slightly
|
synchronised to the configured time sources and provides time to NTP clients.
|
||||||
speeding up or slowing down the system clock until the error has been removed,
|
The system clock is synchronised to the NTP clock. To avoid steps in the
|
||||||
and then returning to the system clock's normal speed. A consequence of this is
|
system time, which might have adverse consequences for certain applications,
|
||||||
that there will be a period when the system clock (as read by other programs)
|
the system clock is normally corrected only by speeding up or slowing down (up
|
||||||
will be different from *chronyd*'s estimate of the current true time (which it
|
to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
|
||||||
reports to NTP clients when it is operating as a server). The value reported
|
directive). If the offset is too large, this correction will take a very long
|
||||||
on this line is the difference due to this effect.
|
time. A step can be forced by the <<makestep,*makestep*>> command, or the
|
||||||
|
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
|
||||||
|
+
|
||||||
|
Note that all other offsets reported by *chronyc* and most offsets in the log
|
||||||
|
files are relative to the NTP clock, not the system clock.
|
||||||
*Last offset*:::
|
*Last offset*:::
|
||||||
This is the estimated local offset on the last clock update. A positive value
|
This is the estimated local offset on the last clock update. A positive value
|
||||||
indicates the local time (as previously estimated true time) was ahead of the
|
indicates the local time (as previously estimated true time) was ahead of the
|
||||||
@@ -310,8 +331,8 @@ extra caption lines are shown as a reminder of the meanings of the columns.
|
|||||||
MS Name/IP address Stratum Poll Reach LastRx Last sample
|
MS Name/IP address Stratum Poll Reach LastRx Last sample
|
||||||
===============================================================================
|
===============================================================================
|
||||||
#* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns
|
#* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns
|
||||||
^? foo.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
|
^? ntp1.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
|
||||||
^+ bar.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
|
^+ ntp2.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The columns are as follows:
|
The columns are as follows:
|
||||||
@@ -353,9 +374,12 @@ a measurement is being made every 64 seconds. *chronyd* automatically varies
|
|||||||
the polling rate in response to prevailing conditions.
|
the polling rate in response to prevailing conditions.
|
||||||
*Reach*:::
|
*Reach*:::
|
||||||
This shows the source's reachability register printed as an octal number. The
|
This shows the source's reachability register printed as an octal number. The
|
||||||
register has 8 bits and is updated on every received or missed packet from
|
register has 8 bits. It is shifted to left by one bit with each poll and it is
|
||||||
the source. A value of 377 indicates that a valid reply was received for all
|
updated by 1 when a valid NTP response, or just a sample in case of a reference
|
||||||
from the last eight transmissions.
|
clock, is received from the source. A value of 377 indicates that a valid
|
||||||
|
response or sample was received for all of the last 8 polls. Note that samples
|
||||||
|
can be dropped if they are not considered good enough for synchronisation, but
|
||||||
|
the reachability register will still have 1s for their polls.
|
||||||
*LastRx*:::
|
*LastRx*:::
|
||||||
This column shows how long ago the last good sample (which is shown in the next
|
This column shows how long ago the last good sample (which is shown in the next
|
||||||
column) was received from the source. Measurements that failed some tests are
|
column) was received from the source. Measurements that failed some tests are
|
||||||
@@ -368,9 +392,9 @@ offset. This can be suffixed by _ns_ (indicating nanoseconds), _us_
|
|||||||
(indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating
|
(indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating
|
||||||
seconds). The number to the left of the square brackets shows the original
|
seconds). The number to the left of the square brackets shows the original
|
||||||
measurement, adjusted to allow for any slews applied to the local clock
|
measurement, adjusted to allow for any slews applied to the local clock
|
||||||
since. The number following the _+/-_ indicator shows the margin of error in
|
since. Positive offsets indicate that the local clock is ahead of the source.
|
||||||
the measurement. Positive offsets indicate that the local clock is ahead of
|
The number following the _+/-_ indicator shows the margin of error in the
|
||||||
the source.
|
measurement (NTP root distance).
|
||||||
|
|
||||||
[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
|
[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
|
||||||
The *sourcestats* command displays information about the drift rate and offset
|
The *sourcestats* command displays information about the drift rate and offset
|
||||||
@@ -389,7 +413,7 @@ An example report is:
|
|||||||
----
|
----
|
||||||
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
||||||
===============================================================================
|
===============================================================================
|
||||||
foo.example.net 11 5 46m -0.001 0.045 1us 25us
|
ntp1.example.net 11 5 46m -0.001 0.045 1us 25us
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The columns are as follows:
|
The columns are as follows:
|
||||||
@@ -433,9 +457,9 @@ An example of the output is shown below.
|
|||||||
----
|
----
|
||||||
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||||
=======================================================================
|
=======================================================================
|
||||||
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
|
D ntp1.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
|
||||||
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us N
|
* ntp2.example.net N ----- ----- 0 1.0 -6846us +7305us N
|
||||||
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us N
|
+ ntp3.example.net N ----- ----- 10 1.0 -7381us +7355us N
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The columns are as follows:
|
The columns are as follows:
|
||||||
@@ -449,12 +473,14 @@ The following states indicate the source is not considered selectable for
|
|||||||
synchronisation:
|
synchronisation:
|
||||||
* _N_ - has the *noselect* option.
|
* _N_ - has the *noselect* option.
|
||||||
* _M_ - does not have enough measurements.
|
* _M_ - does not have enough measurements.
|
||||||
|
* _s_ - is not synchronised.
|
||||||
* _d_ - has a root distance larger than the maximum distance (configured by the
|
* _d_ - has a root distance larger than the maximum distance (configured by the
|
||||||
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
|
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
|
||||||
* _~_ - has a jitter larger than the maximum jitter (configured by the
|
* _~_ - has a jitter larger than the maximum jitter (configured by the
|
||||||
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
|
<<chrony.conf.adoc#maxjitter,*maxjitter*>> directive).
|
||||||
* _w_ - waits for other sources to get out of the _M_ state.
|
* _w_ - waits for other sources to get out of the _M_ state.
|
||||||
* _S_ - has older measurements than other sources.
|
* _S_ - has only measurements older than reachable sources, or is unreachable
|
||||||
|
for too many polls (configured by the *maxunreach* option).
|
||||||
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
|
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
|
||||||
the <<chrony.conf.adoc#local,*local*>> directive).
|
the <<chrony.conf.adoc#local,*local*>> directive).
|
||||||
* _T_ - does not fully agree with sources that have the *trust* option.
|
* _T_ - does not fully agree with sources that have the *trust* option.
|
||||||
@@ -480,7 +506,7 @@ local clock:
|
|||||||
This column shows the name or IP address of the source if it is an NTP server,
|
This column shows the name or IP address of the source if it is an NTP server,
|
||||||
or the reference ID if it is a reference clock.
|
or the reference ID if it is a reference clock.
|
||||||
*Auth*:::
|
*Auth*:::
|
||||||
This column indicites whether an authentication mechanism is enabled for the
|
This column indicates whether an authentication mechanism is enabled for the
|
||||||
source. _Y_ means yes and _N_ means no.
|
source. _Y_ means yes and _N_ means no.
|
||||||
*COpts*:::
|
*COpts*:::
|
||||||
This column displays the configured selection options of the source.
|
This column displays the configured selection options of the source.
|
||||||
@@ -492,8 +518,8 @@ This column displays the configured selection options of the source.
|
|||||||
This column displays the current effective selection options of the source,
|
This column displays the current effective selection options of the source,
|
||||||
which can be different from the configured options due to the authentication
|
which can be different from the configured options due to the authentication
|
||||||
selection mode (configured by the
|
selection mode (configured by the
|
||||||
<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
|
<<chrony.conf.adoc#authselectmode,*authselectmode*>> directive). The symbols
|
||||||
same as in the *COpts* column.
|
are the same as in the *COpts* column.
|
||||||
*Last*:::
|
*Last*:::
|
||||||
This column displays how long ago was the last measurement of the source made
|
This column displays how long ago was the last measurement of the source made
|
||||||
when the selection was performed.
|
when the selection was performed.
|
||||||
@@ -514,6 +540,23 @@ This column displays the current leap status of the source.
|
|||||||
* _-_ indicates that a leap second will be deleted at the end of the month.
|
* _-_ indicates that a leap second will be deleted at the end of the month.
|
||||||
* _?_ indicates the unknown status (i.e. no valid measurement was made).
|
* _?_ indicates the unknown status (i.e. no valid measurement was made).
|
||||||
|
|
||||||
|
[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
|
||||||
|
The *selectopts* command modifies the configured selection options of an NTP
|
||||||
|
source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
|
||||||
|
unknown addresses), or a reference clock specified by reference ID as a string.
|
||||||
|
+
|
||||||
|
The selection options can be added with the *+* symbol or removed with the *-*
|
||||||
|
symbol. The *selectdata* command can be used to verify the configuration. The
|
||||||
|
modified options will be applied in the next source selection, e.g. when a new
|
||||||
|
measurement is made, or the *reselect* command is executed.
|
||||||
|
+
|
||||||
|
An example of using this command is shown below.
|
||||||
|
+
|
||||||
|
----
|
||||||
|
selectopts 1.2.3.4 -noselect +prefer
|
||||||
|
selectopts GPS +trust
|
||||||
|
----
|
||||||
|
|
||||||
[[reselect]]*reselect*::
|
[[reselect]]*reselect*::
|
||||||
To avoid excessive switching between sources, *chronyd* can stay synchronised
|
To avoid excessive switching between sources, *chronyd* can stay synchronised
|
||||||
to a source even when it is not currently the best one among the available
|
to a source even when it is not currently the best one among the available
|
||||||
@@ -527,6 +570,13 @@ The *reselectdist* command sets the reselection distance. It is equivalent to
|
|||||||
the <<chrony.conf.adoc#reselectdist,*reselectdist*>> directive in the
|
the <<chrony.conf.adoc#reselectdist,*reselectdist*>> directive in the
|
||||||
configuration file.
|
configuration file.
|
||||||
|
|
||||||
|
[[offset]]*offset* _address|refid_ _offset_::
|
||||||
|
The *offset* command modifies the offset correction of an NTP source specified
|
||||||
|
by IP address (or the _ID#XXXXXXXXXX_ identifier used for unknown addresses),
|
||||||
|
or a reference clock specified by reference ID as a string. It is equivalent to
|
||||||
|
the *offset* option in the <<chrony.conf.adoc#server,*server*>> or
|
||||||
|
<<chrony.conf.adoc#refclock,*refclock*>> directive respectively.
|
||||||
|
|
||||||
=== NTP sources
|
=== NTP sources
|
||||||
|
|
||||||
[[activity]]*activity*::
|
[[activity]]*activity*::
|
||||||
@@ -563,9 +613,9 @@ shown below.
|
|||||||
----
|
----
|
||||||
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
||||||
=========================================================================
|
=========================================================================
|
||||||
foo.example.net NTS 1 15 256 135m 0 0 8 100
|
ntp1.example.net NTS 1 15 256 135m 0 0 8 100
|
||||||
bar.example.net SK 30 13 128 - 0 0 0 0
|
ntp2.example.net SK 30 13 128 - 0 0 0 0
|
||||||
baz.example.net - 0 0 0 - 0 0 0 0
|
ntp3.example.net - 0 0 0 - 0 0 0 0
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The columns are as follows:
|
The columns are as follows:
|
||||||
@@ -602,6 +652,7 @@ be reported:
|
|||||||
* 13: AES128
|
* 13: AES128
|
||||||
* 14: AES256
|
* 14: AES256
|
||||||
* 15: AEAD-AES-SIV-CMAC-256
|
* 15: AEAD-AES-SIV-CMAC-256
|
||||||
|
* 30: AEAD-AES-128-GCM-SIV
|
||||||
*KLen*:::
|
*KLen*:::
|
||||||
This column shows the length of the key in bits.
|
This column shows the length of the key in bits.
|
||||||
*Last*:::
|
*Last*:::
|
||||||
@@ -658,6 +709,11 @@ RX timestamping : Kernel
|
|||||||
Total TX : 24
|
Total TX : 24
|
||||||
Total RX : 24
|
Total RX : 24
|
||||||
Total valid RX : 24
|
Total valid RX : 24
|
||||||
|
Total good RX : 22
|
||||||
|
Total kernel TX : 24
|
||||||
|
Total kernel RX : 24
|
||||||
|
Total HW TX : 0
|
||||||
|
Total HW RX : 0
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The fields are explained as follows:
|
The fields are explained as follows:
|
||||||
@@ -695,7 +751,8 @@ packets sent to the source is more variable than the delay of packets sent
|
|||||||
from the source back.
|
from the source back.
|
||||||
*NTP tests*:::
|
*NTP tests*:::
|
||||||
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
|
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
|
||||||
delay, delay ratio, delay dev ratio, and synchronisation loop.
|
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
|
||||||
|
loop.
|
||||||
*Interleaved*:::
|
*Interleaved*:::
|
||||||
This shows if the response was in the interleaved mode.
|
This shows if the response was in the interleaved mode.
|
||||||
*Authenticated*:::
|
*Authenticated*:::
|
||||||
@@ -710,7 +767,22 @@ The number of packets sent to the source.
|
|||||||
*Total RX*:::
|
*Total RX*:::
|
||||||
The number of all packets received from the source.
|
The number of all packets received from the source.
|
||||||
*Total valid RX*:::
|
*Total valid RX*:::
|
||||||
The number of valid packets received from the source.
|
The number of packets which passed the first two groups of NTP tests.
|
||||||
|
*Total good RX*:::
|
||||||
|
The number of packets which passed all three groups of NTP tests, i.e. the NTP
|
||||||
|
measurement was accepted.
|
||||||
|
*Total kernel TX*:::
|
||||||
|
The number of packets sent to the source for which a timestamp was captured by
|
||||||
|
the kernel.
|
||||||
|
*Total kernel RX*:::
|
||||||
|
The number of packets received from the source for which a timestamp was
|
||||||
|
captured by the kernel.
|
||||||
|
*Total HW TX*:::
|
||||||
|
The number of packets sent to the source for which a timestamp was captured by
|
||||||
|
the NIC.
|
||||||
|
*Total HW RX*:::
|
||||||
|
The number of packets received from the source for which a timestamp was
|
||||||
|
captured by the NIC.
|
||||||
|
|
||||||
[[add_peer]]*add peer* _name_ [_option_]...::
|
[[add_peer]]*add peer* _name_ [_option_]...::
|
||||||
The *add peer* command allows a new NTP peer to be added whilst
|
The *add peer* command allows a new NTP peer to be added whilst
|
||||||
@@ -723,7 +795,7 @@ parameters and options is identical to that for the
|
|||||||
An example of using this command is shown below.
|
An example of using this command is shown below.
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
add peer foo.example.net minpoll 6 maxpoll 10 key 25
|
add peer ntp1.example.net minpoll 6 maxpoll 10 key 25
|
||||||
----
|
----
|
||||||
|
|
||||||
[[add_pool]]*add pool* _name_ [_option_]...::
|
[[add_pool]]*add pool* _name_ [_option_]...::
|
||||||
@@ -737,7 +809,7 @@ directive in the configuration file.
|
|||||||
An example of using this command is shown below:
|
An example of using this command is shown below:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
add pool foo.example.net maxsources 3 iburst
|
add pool ntp1.example.net maxsources 3 iburst
|
||||||
----
|
----
|
||||||
|
|
||||||
[[add_server]]*add server* _name_ [_option_]...::
|
[[add_server]]*add server* _name_ [_option_]...::
|
||||||
@@ -751,7 +823,7 @@ directive in the configuration file.
|
|||||||
An example of using this command is shown below:
|
An example of using this command is shown below:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
add server foo.example.net minpoll 6 maxpoll 10 key 25
|
add server ntp1.example.net minpoll 6 maxpoll 10 key 25
|
||||||
----
|
----
|
||||||
|
|
||||||
[[delete]]*delete* _address_::
|
[[delete]]*delete* _address_::
|
||||||
@@ -827,7 +899,7 @@ IPv6 addresses have first 48 bits equal to _2001:db8:789a_.
|
|||||||
Example of the three-argument form of the command is:
|
Example of the three-argument form of the command is:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
burst 2/10 foo.example.net
|
burst 2/10 ntp1.example.net
|
||||||
----
|
----
|
||||||
|
|
||||||
[[maxdelay]]*maxdelay* _address_ _delay_::
|
[[maxdelay]]*maxdelay* _address_ _delay_::
|
||||||
@@ -893,7 +965,7 @@ uses an IP address or a hostname. These forms are illustrated below.
|
|||||||
offline
|
offline
|
||||||
offline 255.255.255.0/1.2.3.0
|
offline 255.255.255.0/1.2.3.0
|
||||||
offline 2001:db8:789a::/48
|
offline 2001:db8:789a::/48
|
||||||
offline foo.example.net
|
offline ntp1.example.net
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
The second form means that the *offline* command is to be applied to any source
|
The second form means that the *offline* command is to be applied to any source
|
||||||
@@ -935,17 +1007,26 @@ current set of sources. It is equivalent to the *polltarget* option in the
|
|||||||
|
|
||||||
[[refresh]]*refresh*::
|
[[refresh]]*refresh*::
|
||||||
The *refresh* command can be used to force *chronyd* to resolve the names of
|
The *refresh* command can be used to force *chronyd* to resolve the names of
|
||||||
configured sources to IP addresses again, e.g. after suspending and resuming
|
configured NTP sources to IP addresses again and replace any addresses missing
|
||||||
the machine in a different network.
|
in the list of resolved addresses.
|
||||||
+
|
+
|
||||||
Sources that stop responding will be replaced with newly resolved addresses
|
Sources that stop responding are replaced with newly resolved addresses
|
||||||
automatically after 8 polling intervals, but this command can still be useful
|
automatically after 8 polling intervals. This command can be used to replace
|
||||||
to replace them immediately and not wait until they are marked as unreachable.
|
them immediately, e.g. after suspending and resuming the machine in a different
|
||||||
|
network.
|
||||||
|
+
|
||||||
|
Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
|
||||||
|
addresses are included in a single DNS response (e.g. pool.ntp.org), this
|
||||||
|
command might replace the addresses even if they are still in the pool.
|
||||||
|
|
||||||
[[reload]]*reload* *sources*::
|
[[reload]]*reload* *sources*::
|
||||||
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
|
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
|
||||||
from the directories specified by the
|
from the directories specified by the
|
||||||
<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive.
|
<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive.
|
||||||
|
+
|
||||||
|
Note that modified sources (e.g. specified with a new option) are not modified
|
||||||
|
in memory. They are removed and added again, which causes them to lose old
|
||||||
|
measurements and reset the selection state.
|
||||||
|
|
||||||
[[sourcename]]*sourcename* _address_::
|
[[sourcename]]*sourcename* _address_::
|
||||||
The *sourcename* command prints the original hostname or address that was
|
The *sourcename* command prints the original hostname or address that was
|
||||||
@@ -1054,7 +1135,7 @@ particular host.
|
|||||||
Examples of use, showing a named host and a numeric IP address, are as follows:
|
Examples of use, showing a named host and a numeric IP address, are as follows:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
accheck foo.example.net
|
accheck ntp1.example.net
|
||||||
accheck 1.2.3.4
|
accheck 1.2.3.4
|
||||||
accheck 2001:db8::1
|
accheck 2001:db8::1
|
||||||
----
|
----
|
||||||
@@ -1081,7 +1162,7 @@ An example of the output is:
|
|||||||
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
|
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
|
||||||
===============================================================================
|
===============================================================================
|
||||||
localhost 2 0 2 - 133 15 0 -1 7
|
localhost 2 0 2 - 133 15 0 -1 7
|
||||||
foo.example.net 12 0 6 - 23 0 0 - -
|
ntp1.example.net 12 0 6 - 23 0 0 - -
|
||||||
----
|
----
|
||||||
+
|
+
|
||||||
Each row shows the data for a single host. Only hosts that have passed the host
|
Each row shows the data for a single host. Only hosts that have passed the host
|
||||||
@@ -1107,13 +1188,9 @@ The columns are as follows:
|
|||||||
received/accepted.
|
received/accepted.
|
||||||
|
|
||||||
[[serverstats]]*serverstats*::
|
[[serverstats]]*serverstats*::
|
||||||
The *serverstats* command displays how many valid NTP and command requests, and
|
The *serverstats* command displays NTP and command server statistics.
|
||||||
NTS-KE connections, *chronyd* operating as a server received from clients, and
|
+
|
||||||
how many of them were dropped due to rate limiting. It also displays how many
|
An example of the output is shown below.
|
||||||
client log records were dropped due to the memory limit configured by the
|
|
||||||
<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive and how many of
|
|
||||||
the NTP requests (from those which were not dropped) were authenticated. An
|
|
||||||
example of the output is shown below.
|
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
NTP packets received : 1598
|
NTP packets received : 1598
|
||||||
@@ -1124,7 +1201,67 @@ Client log records dropped : 0
|
|||||||
NTS-KE connections accepted: 3
|
NTS-KE connections accepted: 3
|
||||||
NTS-KE connections dropped : 0
|
NTS-KE connections dropped : 0
|
||||||
Authenticated NTP packets : 189
|
Authenticated NTP packets : 189
|
||||||
|
Interleaved NTP packets : 43
|
||||||
|
NTP timestamps held : 44
|
||||||
|
NTP timestamp span : 120
|
||||||
|
NTP daemon RX timestamps : 0
|
||||||
|
NTP daemon TX timestamps : 1537
|
||||||
|
NTP kernel RX timestamps : 1590
|
||||||
|
NTP kernel TX timestamps : 43
|
||||||
|
NTP hardware RX timestamps : 0
|
||||||
|
NTP hardware TX timestamps : 0
|
||||||
----
|
----
|
||||||
|
+
|
||||||
|
The fields have the following meaning:
|
||||||
|
+
|
||||||
|
*NTP packets received*:::
|
||||||
|
The number of valid NTP requests received by the server.
|
||||||
|
*NTP packets dropped*:::
|
||||||
|
The number of NTP requests dropped by the server due to rate limiting
|
||||||
|
(configured by the <<chrony.conf.adoc#ratelimit,*ratelimit*>> directive).
|
||||||
|
*Command packets received*:::
|
||||||
|
The number of command requests received by the server.
|
||||||
|
*Command packets dropped*:::
|
||||||
|
The number of command requests dropped by the server due to rate limiting
|
||||||
|
(configured by the <<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directive).
|
||||||
|
*Client log records dropped*:::
|
||||||
|
The number of client log records dropped by the server to limit the memory use
|
||||||
|
(configured by the <<chrony.conf.adoc#clientloglimit,*clientloglimit*>>
|
||||||
|
directive).
|
||||||
|
*NTS-KE connections accepted*:::
|
||||||
|
The number of NTS-KE connections accepted by the server.
|
||||||
|
*NTS-KE connections dropped*:::
|
||||||
|
The number of NTS-KE connections dropped by the server due to rate limiting
|
||||||
|
(configured by the <<chrony.conf.adoc#ntsratelimit,*ntsratelimit*>> directive).
|
||||||
|
*Authenticated NTP packets*:::
|
||||||
|
The number of received NTP requests that were authenticated (with a symmetric
|
||||||
|
key or NTS).
|
||||||
|
*Interleaved NTP packets*:::
|
||||||
|
The number of received NTP requests that were detected to be in the interleaved
|
||||||
|
mode.
|
||||||
|
*NTP timestamps held*:::
|
||||||
|
The number of pairs of receive and transmit timestamps that the server is
|
||||||
|
currently holding in memory for clients using the interleaved mode.
|
||||||
|
*NTP timestamp span*:::
|
||||||
|
The interval (in seconds) covered by the currently held NTP timestamps.
|
||||||
|
*NTP daemon RX timestamps*:::
|
||||||
|
The number of NTP responses which included a receive timestamp captured by the
|
||||||
|
daemon.
|
||||||
|
*NTP daemon TX timestamps*:::
|
||||||
|
The number of NTP responses which included a transmit timestamp captured by the
|
||||||
|
daemon.
|
||||||
|
*NTP kernel RX timestamps*:::
|
||||||
|
The number of NTP responses which included a receive timestamp captured by the
|
||||||
|
kernel.
|
||||||
|
*NTP kernel TX timestamps*:::
|
||||||
|
The number of NTP responses (in the interleaved mode) which included a transmit
|
||||||
|
timestamp captured by the kernel.
|
||||||
|
*NTP hardware RX timestamps*:::
|
||||||
|
The number of NTP responses which included a receive timestamp captured by the
|
||||||
|
NIC.
|
||||||
|
*NTP hardware TX timestamps*:::
|
||||||
|
The number of NTP responses (in the interleaved mode) which included a transmit
|
||||||
|
timestamp captured by the NIC.
|
||||||
|
|
||||||
[[allow]]*allow* [*all*] [_subnet_]::
|
[[allow]]*allow* [*all*] [_subnet_]::
|
||||||
The effect of the allow command is identical to the
|
The effect of the allow command is identical to the
|
||||||
@@ -1163,8 +1300,8 @@ deny all
|
|||||||
*local* *off*::
|
*local* *off*::
|
||||||
The *local* command allows *chronyd* to be told that it is to appear as a
|
The *local* command allows *chronyd* to be told that it is to appear as a
|
||||||
reference source, even if it is not itself properly synchronised to an external
|
reference source, even if it is not itself properly synchronised to an external
|
||||||
source. (This can be used on isolated networks, to allow one computer to be a
|
source. This can be used on isolated networks, to allow a computer to be the
|
||||||
master time server with the other computers slaving to it.)
|
primary time server for other computers.
|
||||||
+
|
+
|
||||||
The first form enables the local reference mode on the host. The syntax is
|
The first form enables the local reference mode on the host. The syntax is
|
||||||
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
|
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
|
||||||
@@ -1225,7 +1362,7 @@ used to check whether monitoring access is permitted from a named host.
|
|||||||
Examples of use are as follows:
|
Examples of use are as follows:
|
||||||
+
|
+
|
||||||
----
|
----
|
||||||
cmdaccheck foo.example.net
|
cmdaccheck ntp1.example.net
|
||||||
cmdaccheck 1.2.3.4
|
cmdaccheck 1.2.3.4
|
||||||
cmdaccheck 2001:db8::1
|
cmdaccheck 2001:db8::1
|
||||||
----
|
----
|
||||||
@@ -1338,6 +1475,13 @@ purged. An example of how to do this is shown below.
|
|||||||
# chronyc cyclelogs
|
# chronyc cyclelogs
|
||||||
# rm /var/log/chrony/measurements1.log
|
# rm /var/log/chrony/measurements1.log
|
||||||
----
|
----
|
||||||
|
+
|
||||||
|
Note that log files enabled by the *log* directive are opened when the first
|
||||||
|
entry is made. The message log file specified by the *chronyd*'s *-l* option is
|
||||||
|
opened early on start and closed on the *cyclelogs* command only if opening of
|
||||||
|
the new file succeeds to avoid losing messages. If *chronyd* is configured to
|
||||||
|
drop root privileges and the directory containing the log file is not writable
|
||||||
|
for the specified user, *chronyd* will not be able to open the file again.
|
||||||
|
|
||||||
[[dump]]*dump*::
|
[[dump]]*dump*::
|
||||||
The *dump* command causes *chronyd* to write its current history of
|
The *dump* command causes *chronyd* to write its current history of
|
||||||
@@ -1452,7 +1596,7 @@ The *help* command displays a summary of the commands and their arguments.
|
|||||||
== BUGS
|
== BUGS
|
||||||
|
|
||||||
For instructions on how to report bugs, please visit
|
For instructions on how to report bugs, please visit
|
||||||
https://chrony.tuxfamily.org/.
|
https://chrony-project.org/.
|
||||||
|
|
||||||
== AUTHORS
|
== AUTHORS
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ terminal.
|
|||||||
|
|
||||||
*-L* _level_::
|
*-L* _level_::
|
||||||
This option specifies the minimum severity level of messages to be written to
|
This option specifies the minimum severity level of messages to be written to
|
||||||
the log file, syslog, or terminal. The following levels can be specified:
|
the log file, syslog, or terminal. The following levels can be specified: -1
|
||||||
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
|
(debug, if compiled with enabled support for debugging), 0 (informational), 1
|
||||||
default value is 0.
|
(warning), 2 (non-fatal error), and 3 (fatal error). The default value is 0.
|
||||||
|
|
||||||
*-p*::
|
*-p*::
|
||||||
When run in this mode, *chronyd* will print the configuration and exit. It will
|
When run in this mode, *chronyd* will print the configuration and exit. It will
|
||||||
@@ -88,8 +88,10 @@ will not detach from the terminal.
|
|||||||
|
|
||||||
*-Q*::
|
*-Q*::
|
||||||
This option is similar to the *-q* option, except it only prints the offset
|
This option is similar to the *-q* option, except it only prints the offset
|
||||||
without making any corrections of the clock and it allows *chronyd* to be
|
without making any corrections of the clock and disables server ports to allow
|
||||||
started without root privileges.
|
*chronyd* to be started without root privileges, assuming the configuration
|
||||||
|
does not have any directives which would require them (e.g. *refclock*,
|
||||||
|
*hwtimestamp*, *rtcfile*, etc).
|
||||||
|
|
||||||
*-r*::
|
*-r*::
|
||||||
This option will try to reload and then delete files containing sample
|
This option will try to reload and then delete files containing sample
|
||||||
@@ -100,7 +102,7 @@ directive in the configuration file. This option is useful if you want to stop
|
|||||||
and restart *chronyd* briefly for any reason, e.g. to install a new version.
|
and restart *chronyd* briefly for any reason, e.g. to install a new version.
|
||||||
However, it should be used only on systems where the kernel can maintain clock
|
However, it should be used only on systems where the kernel can maintain clock
|
||||||
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
|
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
|
||||||
Solaris, and macOS 10.13 or later).
|
illumos, and macOS 10.13 or later).
|
||||||
|
|
||||||
*-R*::
|
*-R*::
|
||||||
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
|
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
|
||||||
@@ -141,7 +143,7 @@ after start in order to drop root privileges. It overrides the
|
|||||||
_@DEFAULT_USER@_.
|
_@DEFAULT_USER@_.
|
||||||
+
|
+
|
||||||
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
||||||
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
|
On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
|
||||||
The child process retains root privileges, but can only perform a very limited
|
The child process retains root privileges, but can only perform a very limited
|
||||||
range of privileged system calls on behalf of the parent.
|
range of privileged system calls on behalf of the parent.
|
||||||
|
|
||||||
@@ -156,29 +158,40 @@ not recommended when the configuration is not known, or at least limited to
|
|||||||
specific directives.
|
specific directives.
|
||||||
|
|
||||||
*-F* _level_::
|
*-F* _level_::
|
||||||
This option configures a system call filter when *chronyd* is compiled with
|
This option configures system call filters loaded by *chronyd* processes if it
|
||||||
support for the Linux secure computing (seccomp) facility. In level 1 the
|
was compiled with support for the Linux secure computing (seccomp) facility.
|
||||||
process is killed when a forbidden system call is made, in level -1 the SIGSYS
|
Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
|
||||||
signal is thrown instead and in level 0 the filter is disabled. The default
|
levels 1 and 2, *chronyd* will be killed if it makes a system call which is
|
||||||
value is 0.
|
blocked by the filters. The level can be specified as a negative number to
|
||||||
|
trigger the SIGSYS signal instead of SIGKILL, which can be useful for
|
||||||
|
debugging. The default value is 0.
|
||||||
+
|
+
|
||||||
It is recommended to enable the filter only when it is known to work on the
|
At level 1, the filters allow only selected system calls that are normally
|
||||||
version of the system where *chrony* is installed as the filter needs to allow
|
expected to be made by *chronyd*. Other system calls are blocked. This level is
|
||||||
also system calls made from libraries that *chronyd* is using (e.g. libc) and
|
recommended only if it is known to work on the version of the system where
|
||||||
different versions or implementations of the libraries might make different
|
*chrony* is installed. The filters need to allow also system calls made by
|
||||||
system calls. If the filter is missing some system call, *chronyd* could be
|
libraries that *chronyd* is using (e.g. libc), but different versions or
|
||||||
killed even in normal operation.
|
implementations of the libraries might make different system calls. If the
|
||||||
|
filters are missing a system call, *chronyd* could be killed even in normal
|
||||||
|
operation.
|
||||||
|
+
|
||||||
|
At level 2, the filters block only a small number of specific system calls
|
||||||
|
(e.g. fork and exec). This approach should avoid false positives, but the
|
||||||
|
protection of the system against a compromised *chronyd* process is much more
|
||||||
|
limited.
|
||||||
|
+
|
||||||
|
The filters cannot be enabled with the *mailonchange* directive.
|
||||||
|
|
||||||
*-P* _priority_::
|
*-P* _priority_::
|
||||||
On Linux, this option will select the SCHED_FIFO real-time scheduler at the
|
On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
|
||||||
specified priority (which must be between 0 and 100). On macOS, this option
|
real-time scheduler at the specified priority (which must be between 0 and
|
||||||
must have either a value of 0 to disable the thread time
|
100). On macOS, this option must have either a value of 0 to disable the thread
|
||||||
constraint policy or 1 for the policy to be enabled. Other systems do not
|
time constraint policy or 1 for the policy to be enabled. Other systems do not
|
||||||
support this option. The default value is 0.
|
support this option. The default value is 0.
|
||||||
|
|
||||||
*-m*::
|
*-m*::
|
||||||
This option will lock *chronyd* into RAM so that it will never be paged out.
|
This option will lock *chronyd* into RAM so that it will never be paged out.
|
||||||
This mode is only supported on Linux.
|
This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
|
||||||
|
|
||||||
*-x*::
|
*-x*::
|
||||||
This option disables the control of the system clock. *chronyd* will not try to
|
This option disables the control of the system clock. *chronyd* will not try to
|
||||||
@@ -193,6 +206,25 @@ With this option *chronyd* will print version number to the terminal and exit.
|
|||||||
*-h*, *--help*::
|
*-h*, *--help*::
|
||||||
With this option *chronyd* will print a help message to the terminal and exit.
|
With this option *chronyd* will print a help message to the terminal and exit.
|
||||||
|
|
||||||
|
== ENVIRONMENT VARIABLES
|
||||||
|
|
||||||
|
*LISTEN_FDS*::
|
||||||
|
On Linux systems, the systemd service manager may pass file descriptors for
|
||||||
|
pre-initialised sockets to *chronyd*. The service manager allocates and binds
|
||||||
|
the file descriptors, and passes a copy to each spawned instance of the
|
||||||
|
service. This allows for zero-downtime service restarts as the sockets buffer
|
||||||
|
client requests until the service is able to handle them. The service manager
|
||||||
|
sets the LISTEN_FDS environment variable to the number of passed file
|
||||||
|
descriptors.
|
||||||
|
|
||||||
|
*NOTIFY_SOCKET*::
|
||||||
|
The systemd service manager sets this variable for services of the *notify*
|
||||||
|
type. *chronyd* sends a message to this socket when it it is fully initialised
|
||||||
|
and ready to accept commands (e.g. from *chronyc*), with the clock already set
|
||||||
|
if using the *-s* option or *initstepslew* directive. It is an alternative to
|
||||||
|
the *forking* service type, which does not need the PID file. *chronyd* needs
|
||||||
|
to be started with the *-n* or *-d* option to not fork.
|
||||||
|
|
||||||
== FILES
|
== FILES
|
||||||
|
|
||||||
_@SYSCONFDIR@/chrony.conf_
|
_@SYSCONFDIR@/chrony.conf_
|
||||||
@@ -204,7 +236,7 @@ _@SYSCONFDIR@/chrony.conf_
|
|||||||
== BUGS
|
== BUGS
|
||||||
|
|
||||||
For instructions on how to report bugs, please visit
|
For instructions on how to report bugs, please visit
|
||||||
https://chrony.tuxfamily.org/.
|
https://chrony-project.org/.
|
||||||
|
|
||||||
== AUTHORS
|
== AUTHORS
|
||||||
|
|
||||||
|
|||||||
74
doc/contributing.adoc
Normal file
74
doc/contributing.adoc
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// This file is part of chrony
|
||||||
|
//
|
||||||
|
// Copyright (C) Miroslav Lichvar 2024
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
= Contributing
|
||||||
|
|
||||||
|
== Patches
|
||||||
|
|
||||||
|
The source code of `chrony` is maintained in a git repository at
|
||||||
|
https://gitlab.com/chrony/chrony. Patches can be submitted to the `chrony-dev`
|
||||||
|
mailing list, or as a merge request on gitlab. Before spending a lot of time
|
||||||
|
implementing a new major feature, it is recommended to ask on the mailing list
|
||||||
|
for comments about its design and whether such feature fits the goals of the
|
||||||
|
project.
|
||||||
|
|
||||||
|
Each commit should be a self-contained logical change, which does not break
|
||||||
|
the build or tests. New functionality and fixed bugs should be covered by a new
|
||||||
|
test or an extended existing test in the test suite. The test can be included
|
||||||
|
in the same commit or added as a separate commit. The same rule applies to
|
||||||
|
documentation. All command-line options, configuration directives, and
|
||||||
|
`chronyc` commands should be documented.
|
||||||
|
|
||||||
|
The most important tests can be executed by running `make check` or `make
|
||||||
|
quickcheck`. The unit and system tests run on all supported systems. The system
|
||||||
|
tests require root privileges. The simulation tests run only on Linux and
|
||||||
|
require https://gitlab.com/chrony/clknetsim[clknetsim] to be compiled in the
|
||||||
|
directory containing the tests, but they are executed with a merge request on
|
||||||
|
gitlab.
|
||||||
|
|
||||||
|
The commit message should explain any non-trivial changes, e.g. what problem is
|
||||||
|
the commit solving and how. The commit subject (first line of the message)
|
||||||
|
should be written in an imperative form, prefixed with the component name if it
|
||||||
|
is not a more general change, starting in lower case, and no period at the end.
|
||||||
|
See the git log for examples.
|
||||||
|
|
||||||
|
Simpler code is better. Less code is better. Security is a top priority.
|
||||||
|
|
||||||
|
Assertions should catch only bugs in the `chrony` code. Unexpected values in
|
||||||
|
external input (e.g. anything received from network) must be handled correctly
|
||||||
|
without crashing and memory corruption. Fuzzing support is available at
|
||||||
|
https://gitlab.com/chrony/chrony-fuzz. The fuzzing coverage is checked by the
|
||||||
|
project maintainer before each release.
|
||||||
|
|
||||||
|
The code should mostly be self-documenting. Comments should explain the
|
||||||
|
less obvious things.
|
||||||
|
|
||||||
|
== Coding style
|
||||||
|
|
||||||
|
The code uses two spaces for indentation. No tabs. The line length should
|
||||||
|
normally not exceed 95 characters. Too much indentation indicates the code will
|
||||||
|
not be very readable.
|
||||||
|
|
||||||
|
Function names are in an imperative form. Names of static functions use
|
||||||
|
lowercase characters and underscores. Public functions, structures, typedefs
|
||||||
|
are in CamelCase with a prefix specific to the module (e.g. LCL - local, NCR
|
||||||
|
- NTP core, NKS - NTS-KE server, SST - sourcestats).
|
||||||
|
|
||||||
|
Function names are not followed by space, but keywords of the language (e.g.
|
||||||
|
`if`, `for`, `while`, `sizeof`) are followed by space.
|
||||||
|
|
||||||
|
Have a look at the existing code to get a better idea what is expected.
|
||||||
391
doc/faq.adoc
391
doc/faq.adoc
@@ -1,7 +1,8 @@
|
|||||||
// This file is part of chrony
|
// This file is part of chrony
|
||||||
//
|
//
|
||||||
// Copyright (C) Richard P. Curnow 1997-2003
|
// Copyright (C) Richard P. Curnow 1997-2003
|
||||||
// Copyright (C) Miroslav Lichvar 2014-2016, 2020
|
// Copyright (C) Luke Valenta 2023
|
||||||
|
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2024
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify
|
// 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
|
// it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -40,9 +41,36 @@ on an isolated network with no hardware reference clocks in sight, `chrony`
|
|||||||
will probably work better for you.
|
will probably work better for you.
|
||||||
|
|
||||||
For a more detailed comparison of features and performance, see the
|
For a more detailed comparison of features and performance, see the
|
||||||
https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
|
https://chrony-project.org/comparison.html[comparison page] on the `chrony`
|
||||||
website.
|
website.
|
||||||
|
|
||||||
|
=== Should I prefer `chrony` over `timesyncd` if I do not need to run a server?
|
||||||
|
|
||||||
|
Generally, yes.
|
||||||
|
|
||||||
|
`systemd-timesyncd` is a very simple NTP client included in the `systemd`
|
||||||
|
suite. It lacks almost all features of `chrony` and other advanced client
|
||||||
|
implementations listed on the
|
||||||
|
https://chrony-project.org/comparison.html[comparison page]. One of its main
|
||||||
|
limitations is that it cannot poll multiple servers at the same time and detect
|
||||||
|
servers having incorrect time (falsetickers in the NTP terminology). It should
|
||||||
|
be used only with trusted reliable servers, ideally in local network.
|
||||||
|
|
||||||
|
Using `timesyncd` with `pool.ntp.org` is problematic. The pool is very
|
||||||
|
robust as a whole, but the individual servers run by volunteers cannot be
|
||||||
|
relied on. Occasionally, servers drift away or make a step to distant past or
|
||||||
|
future due to misconfiguration, problematic implementation, and other bugs
|
||||||
|
(e.g. in firmware of a GPS receiver). The pool monitoring system detects such
|
||||||
|
servers and quickly removes them from the pool DNS, but clients like
|
||||||
|
`timesyncd` cannot recover from that. They follow the server as long as it
|
||||||
|
claims to be synchronised. They need to be restarted in order to get a new
|
||||||
|
address from the pool DNS.
|
||||||
|
|
||||||
|
Note that the complexity of NTP and clock synchronisation is on the client
|
||||||
|
side. The amount of code in `chrony` specific to NTP server is very small and
|
||||||
|
it is disabled by default. If it was removed, it would not significantly reduce
|
||||||
|
the amount of memory or storage needed.
|
||||||
|
|
||||||
== Configuration issues
|
== Configuration issues
|
||||||
|
|
||||||
=== What is the minimum recommended configuration for an NTP client?
|
=== What is the minimum recommended configuration for an NTP client?
|
||||||
@@ -137,6 +165,13 @@ versions or implementations of the libraries might make different system calls.
|
|||||||
If the filter is missing some system call, `chronyd` could be killed even in
|
If the filter is missing some system call, `chronyd` could be killed even in
|
||||||
normal operation.
|
normal operation.
|
||||||
|
|
||||||
|
The impact of potential security issues in `chronyc` can be reduced by running
|
||||||
|
`chronyc` under the _chrony_ user instead of root, or another unprivileged user
|
||||||
|
if access to the Unix domain socket is not needed. Since version 4.8, `chronyc`
|
||||||
|
drops root privileges automatically if it is started with the `-u` option
|
||||||
|
specifying the _chrony_ user, or the name was specified to be the compiled-in
|
||||||
|
default by the `--with-chronyc-user` option of the configure script.
|
||||||
|
|
||||||
=== How can I make the system clock more secure?
|
=== How can I make the system clock more secure?
|
||||||
|
|
||||||
An NTP client synchronising the system clock to an NTP server is susceptible to
|
An NTP client synchronising the system clock to an NTP server is susceptible to
|
||||||
@@ -232,17 +267,17 @@ authenticated servers should be configured as trusted and required to not allow
|
|||||||
the unauthenticated servers to override the authenticated servers in the source
|
the unauthenticated servers to override the authenticated servers in the source
|
||||||
selection. Since `chrony` version 4.0, the selection options are enabled in
|
selection. Since `chrony` version 4.0, the selection options are enabled in
|
||||||
such a case automatically. This behaviour can be disabled or modified by the
|
such a case automatically. This behaviour can be disabled or modified by the
|
||||||
`authselmode` directive.
|
`authselectmode` directive.
|
||||||
|
|
||||||
An example of a client configuration limiting the impact of the attacks could
|
An example of a client configuration limiting the impact of the attacks could
|
||||||
be
|
be
|
||||||
|
|
||||||
----
|
----
|
||||||
server foo.example.net iburst nts maxdelay 0.1
|
server ntp1.example.net iburst nts maxdelay 0.1
|
||||||
server bar.example.net iburst nts maxdelay 0.2
|
server ntp2.example.net iburst nts maxdelay 0.2
|
||||||
server baz.example.net iburst nts maxdelay 0.05
|
server ntp3.example.net iburst nts maxdelay 0.05
|
||||||
server qux.example.net iburst nts maxdelay 0.1
|
server ntp4.example.net iburst nts maxdelay 0.1
|
||||||
server quux.example.net iburst nts maxdelay 0.1
|
server ntp5.example.net iburst nts maxdelay 0.1
|
||||||
minsources 3
|
minsources 3
|
||||||
maxchange 100 0 0
|
maxchange 100 0 0
|
||||||
makestep 0.001 1
|
makestep 0.001 1
|
||||||
@@ -291,7 +326,7 @@ An example of the directive for an NTP server on the Internet that you are
|
|||||||
allowed to poll frequently could be
|
allowed to poll frequently could be
|
||||||
|
|
||||||
----
|
----
|
||||||
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
|
server ntp.example.net minpoll 4 maxpoll 6 polltarget 16
|
||||||
----
|
----
|
||||||
|
|
||||||
An example using shorter polling intervals with a server located in the same
|
An example using shorter polling intervals with a server located in the same
|
||||||
@@ -310,10 +345,10 @@ with local NTP server
|
|||||||
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
|
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
|
||||||
----
|
----
|
||||||
|
|
||||||
If your server supports the interleaved mode (e.g. it is running `chronyd`),
|
If your server supports the interleaved mode (RFC 9769), e.g. it is running
|
||||||
the `xleave` option should be added to the `server` directive to enable the
|
`chronyd` version 3.0 or later, the `xleave` option should be added to the
|
||||||
server to provide the client with more accurate transmit timestamps (kernel or
|
`server` directive to enable the server to provide the client with more
|
||||||
preferably hardware). For example:
|
accurate transmit timestamps (kernel or preferably hardware). For example:
|
||||||
|
|
||||||
----
|
----
|
||||||
server ntp.local minpoll 2 maxpoll 4 xleave
|
server ntp.local minpoll 2 maxpoll 4 xleave
|
||||||
@@ -345,6 +380,36 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
|
|||||||
hwtimestamp eth0 minpoll -6
|
hwtimestamp eth0 minpoll -6
|
||||||
----
|
----
|
||||||
|
|
||||||
|
Since `chrony` version 4.3, the minimum `minpoll` is -7 and a filter using a
|
||||||
|
long-term estimate of a delay quantile can be enabled by the `maxdelayquant`
|
||||||
|
option to replace the default `maxdelaydevratio` filter, which is sensitive to
|
||||||
|
outliers corrupting the minimum delay. For example:
|
||||||
|
|
||||||
|
----
|
||||||
|
server ntp.local minpoll -7 maxpoll -7 filter 31 maxdelayquant 0.3 xleave
|
||||||
|
----
|
||||||
|
|
||||||
|
Since version 4.2, `chronyd` supports an NTPv4
|
||||||
|
extension field containing an additional timestamp to enable frequency transfer
|
||||||
|
and significantly improve stability of synchronisation. It can be enabled by
|
||||||
|
the `extfield F323` option. For example:
|
||||||
|
|
||||||
|
----
|
||||||
|
server ntp.local minpoll 0 maxpoll 0 xleave extfield F323
|
||||||
|
----
|
||||||
|
|
||||||
|
Since version 4.5, `chronyd` can apply corrections from PTP one-step end-to-end
|
||||||
|
transparent clocks (e.g. network switches) to significantly improve accuracy of
|
||||||
|
synchronisation in local networks. It requires the PTP transport to be enabled
|
||||||
|
by the `ptpport` directive, HW timestamping, and the `extfield F324` option.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
----
|
||||||
|
server ntp.local minpoll -4 maxpoll -4 xleave extfield F323 extfield F324 port 319
|
||||||
|
ptpport 319
|
||||||
|
hwtimestamp eth0 minpoll -4
|
||||||
|
----
|
||||||
|
|
||||||
=== Does `chronyd` have an ntpdate mode?
|
=== Does `chronyd` have an ntpdate mode?
|
||||||
|
|
||||||
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
|
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
|
||||||
@@ -452,6 +517,59 @@ pidfile /var/run/chronyd-server1.pid
|
|||||||
driftfile /var/lib/chrony/drift-server1
|
driftfile /var/lib/chrony/drift-server1
|
||||||
----
|
----
|
||||||
|
|
||||||
|
=== How can `chronyd` be configured to minimise downtime during restarts?
|
||||||
|
|
||||||
|
The `dumpdir` directive in _chrony.conf_ provides `chronyd` a location to save
|
||||||
|
a measurement history of the sources it uses when the service exits. The `-r`
|
||||||
|
option then enables `chronyd` to load state from the dump files, reducing the
|
||||||
|
synchronisation time after a restart.
|
||||||
|
|
||||||
|
Similarly, the `ntsdumpdir` directive provides a location for `chronyd` to save
|
||||||
|
NTS cookies received from the server to avoid making a NTS-KE request when
|
||||||
|
`chronyd` is started. When operating as an NTS server, `chronyd` also saves
|
||||||
|
cookies keys to this directory to allow clients to continue to use the old keys
|
||||||
|
after a server restart for a more seamless experience.
|
||||||
|
|
||||||
|
On Linux systems,
|
||||||
|
https://www.freedesktop.org/software/systemd/man/latest/sd_listen_fds.html[systemd
|
||||||
|
socket activation] provides a mechanism to reuse server sockets across
|
||||||
|
`chronyd` restarts, so that client requests will be buffered until the service
|
||||||
|
is again able to handle the requests. This allows for zero-downtime service
|
||||||
|
restarts, simplified dependency logic at boot, and on-demand service spawning
|
||||||
|
(for instance, for separated server `chronyd` instances run with the `-x`
|
||||||
|
flag).
|
||||||
|
|
||||||
|
Socket activation is supported since `chrony` version 4.5.
|
||||||
|
The service manager (systemd) creates sockets and
|
||||||
|
passes file descriptors to them to the process via the `LISTEN_FDS` environment
|
||||||
|
variable. Before opening new sockets, `chronyd` first checks for and attempts
|
||||||
|
to reuse matching sockets passed from the service manager. For instance, if an
|
||||||
|
IPv4 datagram socket bound on `bindaddress` and `port` is available, it will be
|
||||||
|
used by the NTP server to accept incoming IPv4 requests.
|
||||||
|
|
||||||
|
An example systemd socket unit is below, where `chronyd` is configured with
|
||||||
|
`bindaddress 0.0.0.0`, `bindaddress ::`, `port 123`, and `ntsport 4460`.
|
||||||
|
|
||||||
|
----
|
||||||
|
[Unit]
|
||||||
|
Description=chronyd server sockets
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
Service=chronyd.service
|
||||||
|
# IPv4 NTP server
|
||||||
|
ListenDatagram=0.0.0.0:123
|
||||||
|
# IPv6 NTP server
|
||||||
|
ListenDatagram=[::]:123
|
||||||
|
# IPv4 NTS-KE server
|
||||||
|
ListenStream=0.0.0.0:4460
|
||||||
|
# IPv6 NTS-KE server
|
||||||
|
ListenStream=[::]:4460
|
||||||
|
BindIPv6Only=ipv6-only
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
||||||
|
----
|
||||||
|
|
||||||
=== Should be a leap smear enabled on NTP server?
|
=== Should be a leap smear enabled on NTP server?
|
||||||
|
|
||||||
With the `smoothtime` and `leapsecmode` directives it is possible to enable a
|
With the `smoothtime` and `leapsecmode` directives it is possible to enable a
|
||||||
@@ -466,9 +584,76 @@ identically configured leap-smearing servers. Note that some clients can get
|
|||||||
leap seconds from other sources (e.g. with the `leapsectz` directive in
|
leap seconds from other sources (e.g. with the `leapsectz` directive in
|
||||||
`chrony`) and they will not work correctly with a leap smearing server.
|
`chrony`) and they will not work correctly with a leap smearing server.
|
||||||
|
|
||||||
|
=== How should `chronyd` be configured with `gpsd`?
|
||||||
|
|
||||||
|
A GPS or other GNSS receiver can be used as a reference clock with `gpsd`. It
|
||||||
|
can work as one or two separate time sources for each connected receiver. The
|
||||||
|
first time source is based on timestamping of messages sent by the receiver.
|
||||||
|
Typically, it is accurate to milliseconds. The other source is much more
|
||||||
|
accurate. It is timestamping a pulse-per-second (PPS) signal, usually connected
|
||||||
|
to a serial port (e.g. DCD pin) or GPIO pin.
|
||||||
|
|
||||||
|
If the PPS signal is connected to the serial port which is receiving messages
|
||||||
|
from the GPS/GNSS receiver, `gpsd` should detect and use it automatically. If
|
||||||
|
it is connected to a GPIO pin, or another serial port, the PPS device needs to
|
||||||
|
be specified on the command line as an additional data source. On Linux, the
|
||||||
|
`ldattach` utility can be used to create a PPS device for a serial device.
|
||||||
|
|
||||||
|
The PPS-based time source provided by `gpsd` is available as a `SHM 1`
|
||||||
|
refclock, or other odd number if `gpsd` is configured with multiple receivers,
|
||||||
|
and also as `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
|
||||||
|
serial device (e.g. ttyS0).
|
||||||
|
|
||||||
|
The message-based time source is available as a `SHM 0` refclock (or other even
|
||||||
|
number) and since `gpsd` version 3.25 also as
|
||||||
|
`SOCK /var/run/chrony.clk.DEV.sock` where `DEV` is the name of the serial
|
||||||
|
device.
|
||||||
|
|
||||||
|
The SOCK refclocks should be preferred over SHM for better security
|
||||||
|
(the shared memory segment needs to be created by `chronyd` or `gpsd` with an
|
||||||
|
expected owner and permissions before an untrusted application or user has a
|
||||||
|
chance to create its own in order to feed `chronyd` with false measurements).
|
||||||
|
`gpsd` needs to be started after `chronyd` in order to connect to the socket.
|
||||||
|
|
||||||
|
With `chronyd` and `gpsd` both supporting PPS, there are two different
|
||||||
|
recommended configurations:
|
||||||
|
|
||||||
|
----
|
||||||
|
# First option
|
||||||
|
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
|
||||||
|
|
||||||
|
# Second option
|
||||||
|
refclock PPS /dev/pps0 lock NMEA refid GPS
|
||||||
|
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid NMEA noselect
|
||||||
|
----
|
||||||
|
|
||||||
|
They both have some advantages:
|
||||||
|
|
||||||
|
* `SOCK` can be more accurate than `PPS` if `gpsd` corrects for the
|
||||||
|
sawtooth error provided by the receiver in serial data
|
||||||
|
* `PPS` can be used with higher PPS rates (specified by the `rate` option),
|
||||||
|
but it requires a second refclock or another time source to pair pulses
|
||||||
|
with seconds, and the `SOCK` offset needs to be specified
|
||||||
|
<<using-pps-refclock,correctly>> to compensate for the message delay, while
|
||||||
|
`gpsd` can apply HW-specific information
|
||||||
|
|
||||||
|
If the PPS signal is not available, or cannot be used for some reason, the only
|
||||||
|
option is the message-based timing
|
||||||
|
|
||||||
|
----
|
||||||
|
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid GPS
|
||||||
|
----
|
||||||
|
|
||||||
|
or the SHM equivalent if using `gpsd` version before 3.25
|
||||||
|
|
||||||
|
----
|
||||||
|
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
|
||||||
|
----
|
||||||
|
|
||||||
=== Does `chrony` support PTP?
|
=== Does `chrony` support PTP?
|
||||||
|
|
||||||
No, the Precision Time Protocol (PTP) is not supported and there are no plans
|
No, the Precision Time Protocol (PTP) is not supported as a protocol for
|
||||||
|
synchronisation of clocks and there are no plans
|
||||||
to support it. It is a complex protocol, which shares some issues with the
|
to support it. It is a complex protocol, which shares some issues with the
|
||||||
NTP broadcast mode. One of the main differences between NTP and PTP is that PTP
|
NTP broadcast mode. One of the main differences between NTP and PTP is that PTP
|
||||||
was designed to be easily supported in hardware (e.g. network switches and
|
was designed to be easily supported in hardware (e.g. network switches and
|
||||||
@@ -483,6 +668,53 @@ packets (enabled by the `hwtimestamp` directive) if the NIC can timestamp other
|
|||||||
packets than PTP, which is usually the case at least for transmitted packets.
|
packets than PTP, which is usually the case at least for transmitted packets.
|
||||||
The `ethtool -T` command can be used to verify the timestamping support.
|
The `ethtool -T` command can be used to verify the timestamping support.
|
||||||
|
|
||||||
|
As an experimental feature added in version 4.2, `chrony` can use PTP as a
|
||||||
|
transport for NTP messages (NTP over PTP) to enable hardware timestamping on
|
||||||
|
hardware which can timestamp PTP packets only. It can be enabled by the
|
||||||
|
`ptpport` directive. Since version 4.5, `chrony` can also apply corrections
|
||||||
|
provided by PTP one-step end-to-end transparent clocks to reach the accuracy of
|
||||||
|
ordinary PTP clocks. The application of PTP corrections can be enabled by the
|
||||||
|
`extfield F324` option.
|
||||||
|
|
||||||
|
=== How can I avoid using wrong PHC refclock?
|
||||||
|
|
||||||
|
If your system has multiple PHC devices, normally named by `udev` as
|
||||||
|
_/dev/ptp0_, _/dev/ptp1_, and so on, their order can change randomly across
|
||||||
|
reboots depending on the order of initialisation of their drivers. If a PHC
|
||||||
|
refclock is specified by this name, `chronyd` could be using a wrong refclock
|
||||||
|
after reboot. To prevent that, you can configure `udev` to create a stable
|
||||||
|
symlink for `chronyd` with a rule like this (e.g. written to
|
||||||
|
_/etc/udev/rules.d/80-phc.rules_):
|
||||||
|
|
||||||
|
----
|
||||||
|
KERNEL=="ptp[0-9]*", DEVPATH=="/devices/pci0000:00/0000:00:01.2/0000:02:00.0/ptp/*", SYMLINK+="ptp-i350-1"
|
||||||
|
----
|
||||||
|
|
||||||
|
You can get the full _DEVPATH_ of an existing PHC device with the `udevadm
|
||||||
|
info` command. You will need to execute the `udevadm trigger` command, or
|
||||||
|
reboot the system, for these changes to take effect.
|
||||||
|
|
||||||
|
=== Why are client log records dropped before reaching `clientloglimit`?
|
||||||
|
|
||||||
|
The number of dropped client log records reported by the `serverstats` command
|
||||||
|
can be increasing before the number of clients reported by the `clients` command
|
||||||
|
reaches the maximum value corresponding to the memory limit set by the
|
||||||
|
`clientloglimit` directive.
|
||||||
|
|
||||||
|
This is due to the design of the data structure keeping the client records. It
|
||||||
|
is a hash table which can store only up to 16 colliding addresses per slot. If
|
||||||
|
a slot has more collisions and the table already has the maximum size, the
|
||||||
|
oldest record will be dropped and replaced by the new client.
|
||||||
|
|
||||||
|
Note that the size of the table is always a power of two and it can only grow.
|
||||||
|
The limit set by the `clientloglimit` directive takes into account that two
|
||||||
|
copies of the table exist when it is being resized. This means the actual
|
||||||
|
memory usage reported by `top` and other utilities can be significantly smaller
|
||||||
|
than the limit even when the maximum number of records is used.
|
||||||
|
|
||||||
|
The absolute maximum number of client records kept at the same time is
|
||||||
|
16777216.
|
||||||
|
|
||||||
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
||||||
|
|
||||||
They were removed in version 2.2. Authentication is no longer supported in the
|
They were removed in version 2.2. Authentication is no longer supported in the
|
||||||
@@ -502,8 +734,9 @@ following questions.
|
|||||||
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
|
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
|
||||||
is zero, it means `chronyd` did not get any valid responses from the NTP server
|
is zero, it means `chronyd` did not get any valid responses from the NTP server
|
||||||
you are trying to use. If there is a firewall between you and the server, the
|
you are trying to use. If there is a firewall between you and the server, the
|
||||||
packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
|
requests sent to the UDP port 123 of the server or responses sent back from
|
||||||
if you are getting any responses from the server.
|
the port might be blocked. Try using a tool like `wireshark` or `tcpdump` to
|
||||||
|
see if you are getting any responses from the server.
|
||||||
|
|
||||||
When `chronyd` is receiving responses from the servers, the output of the
|
When `chronyd` is receiving responses from the servers, the output of the
|
||||||
`sources` command issued few minutes after `chronyd` start might look like
|
`sources` command issued few minutes after `chronyd` start might look like
|
||||||
@@ -512,9 +745,9 @@ this:
|
|||||||
----
|
----
|
||||||
MS Name/IP address Stratum Poll Reach LastRx Last sample
|
MS Name/IP address Stratum Poll Reach LastRx Last sample
|
||||||
===============================================================================
|
===============================================================================
|
||||||
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
|
^* ntp1.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
|
||||||
^- bar.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
|
^- ntp2.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
|
||||||
^+ baz.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
|
^+ ntp3.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Are NTP servers specified with the `offline` option?
|
=== Are NTP servers specified with the `offline` option?
|
||||||
@@ -546,6 +779,17 @@ print all sources, even those that do not have a known address yet, with their
|
|||||||
names as they were specified in the configuration. This can be useful to verify
|
names as they were specified in the configuration. This can be useful to verify
|
||||||
that the names specified in the configuration are used as expected.
|
that the names specified in the configuration are used as expected.
|
||||||
|
|
||||||
|
When DNSSEC is enabled, it will not work until the time is synchronized, as it
|
||||||
|
requires validating a signature timestamp and its expiration date, so if the
|
||||||
|
system time is too far in the future or the past DNSSEC validation will fail and
|
||||||
|
`chronyd` will be unable to resolve the address of the NTP server. In such cases,
|
||||||
|
if hostnames are the only options and bare IP addresses cannot be used, DNSSEC
|
||||||
|
can be disabled for `chronyd` using resolver-specific mechanisms, if available,
|
||||||
|
although of course that means losing the protection afforded by DNSSEC.
|
||||||
|
For example, when using systemd-resolved, the `SYSTEMD_NSS_RESOLVE_VALIDATE=0`
|
||||||
|
environment variable can be set, for example in the `chronyd` systemd unit via
|
||||||
|
`Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0`.
|
||||||
|
|
||||||
=== Is `chronyd` allowed to step the system clock?
|
=== Is `chronyd` allowed to step the system clock?
|
||||||
|
|
||||||
By default, `chronyd` adjusts the clock gradually by slowing it down or
|
By default, `chronyd` adjusts the clock gradually by slowing it down or
|
||||||
@@ -584,9 +828,9 @@ successful:
|
|||||||
# chronyc -N authdata
|
# chronyc -N authdata
|
||||||
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
|
||||||
=========================================================================
|
=========================================================================
|
||||||
foo.example.net NTS 1 15 256 33m 0 0 8 100
|
ntp1.example.net NTS 1 15 256 33m 0 0 8 100
|
||||||
bar.example.net NTS 1 15 256 33m 0 0 8 100
|
ntp2.example.net NTS 1 15 256 33m 0 0 8 100
|
||||||
baz.example.net NTS 1 15 256 33m 0 0 8 100
|
ntp3.example.net NTS 1 15 256 33m 0 0 8 100
|
||||||
----
|
----
|
||||||
|
|
||||||
The KeyID, Type, and KLen columns should have non-zero values. If they are
|
The KeyID, Type, and KLen columns should have non-zero values. If they are
|
||||||
@@ -609,6 +853,18 @@ was not shut down for too long and the server's certificate was not renewed too
|
|||||||
close to its expiration, it should be sufficient for the time checks to
|
close to its expiration, it should be sufficient for the time checks to
|
||||||
succeed.
|
succeed.
|
||||||
|
|
||||||
|
If you run your own server, you can use a self-signed certificate covering
|
||||||
|
all dates where the client can start (e.g. years 1970-2100). The certificate
|
||||||
|
needs to be installed on the client and specified with the `ntstrustedcerts`
|
||||||
|
directive. The server can have multiple names and certificates. To avoid
|
||||||
|
trusting a certificate for too long, a new certificate can be added to the
|
||||||
|
server periodically (e.g. once per year) and the client can have the server
|
||||||
|
name and trusted certificate updated automatically (e.g. using a package
|
||||||
|
repository, or a cron script downloading the files directly from the server
|
||||||
|
over HTTPS). A client that was shut down for years will still be able to
|
||||||
|
synchronise its clock and perform the update as long as the server keeps
|
||||||
|
the old certificate.
|
||||||
|
|
||||||
As a last resort, you can disable the time checks by the `nocerttimecheck`
|
As a last resort, you can disable the time checks by the `nocerttimecheck`
|
||||||
directive. This has some important security implications. To reduce the
|
directive. This has some important security implications. To reduce the
|
||||||
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
|
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
|
||||||
@@ -648,7 +904,9 @@ measurements from both sources.
|
|||||||
|
|
||||||
If the first source was significantly better than the second source, it can
|
If the first source was significantly better than the second source, it can
|
||||||
take many hours before the second source is selected, depending on its polling
|
take many hours before the second source is selected, depending on its polling
|
||||||
interval. You can force a faster reselection by increasing the clock error rate
|
interval. You can force a faster reselection by reducing the maximum number of
|
||||||
|
polls the source can still be selected when unreachable (`maxunreach` option
|
||||||
|
supported since `chrony` version 4.8), increasing the clock error rate
|
||||||
(`maxclockerror` directive), shortening the polling interval (`maxpoll`
|
(`maxclockerror` directive), shortening the polling interval (`maxpoll`
|
||||||
option), or reducing the number of samples (`maxsamples` option).
|
option), or reducing the number of samples (`maxsamples` option).
|
||||||
|
|
||||||
@@ -677,12 +935,14 @@ frequently, you can effectively disable the test by setting the
|
|||||||
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
|
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
|
||||||
recovery by increasing the clock error rate with the `maxclockerror` directive.
|
recovery by increasing the clock error rate with the `maxclockerror` directive.
|
||||||
|
|
||||||
|
[[using-pps-refclock]]
|
||||||
=== Using a PPS reference clock?
|
=== Using a PPS reference clock?
|
||||||
|
|
||||||
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
||||||
determine which second of UTC corresponds to each pulse. If it is another
|
determine which second of UTC corresponds to each pulse. If it is another
|
||||||
reference clock specified with the `lock` option in the `refclock` directive,
|
reference clock specified with the `lock` option in the `refclock` directive,
|
||||||
the offset between the two reference clocks must be smaller than 0.2 seconds in
|
the offset between the two reference clocks must be smaller than 0.4 seconds
|
||||||
|
(0.2 seconds with `chrony` versions before 4.1) in
|
||||||
order for the PPS reference clock to work. With NMEA reference clocks it is
|
order for the PPS reference clock to work. With NMEA reference clocks it is
|
||||||
common to have a larger offset. It needs to be corrected with the `offset`
|
common to have a larger offset. It needs to be corrected with the `offset`
|
||||||
option.
|
option.
|
||||||
@@ -696,12 +956,12 @@ Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
|||||||
==============================================================================
|
==============================================================================
|
||||||
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
|
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
|
||||||
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
|
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
|
||||||
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
|
ntp1.example.net 7 3 200 -2.991 16.141 -107us 492us
|
||||||
----
|
----
|
||||||
|
|
||||||
the offset of the NMEA source would need to be increased by about 0.504
|
the offset of the NMEA source would need to be increased by about 0.504
|
||||||
seconds. It does not have to be very accurate. As long as the offset of the
|
seconds. It does not have to be very accurate. As long as the offset of the
|
||||||
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
|
NMEA reference clock stays below the limit, the PPS reference clock should be
|
||||||
able to determine the seconds corresponding to the pulses and allow the samples
|
able to determine the seconds corresponding to the pulses and allow the samples
|
||||||
to be used for synchronisation.
|
to be used for synchronisation.
|
||||||
|
|
||||||
@@ -757,6 +1017,10 @@ in parentheses) on the `Reference ID` line.
|
|||||||
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
|
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
|
||||||
(`chronyc` side).
|
(`chronyc` side).
|
||||||
|
|
||||||
|
Note that this protocol is not compatible with the mode 6 or mode 7 protocol
|
||||||
|
supported by `ntpd`, i.e. the `ntpq` or `ntpdc` utility cannot be used to
|
||||||
|
monitor `chronyd`, and `chronyc` cannot be used to monitor `ntpd`.
|
||||||
|
|
||||||
== Real-time clock issues
|
== Real-time clock issues
|
||||||
|
|
||||||
=== What is the real-time clock (RTC)?
|
=== What is the real-time clock (RTC)?
|
||||||
@@ -883,6 +1147,81 @@ timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
|
|||||||
synchronisation of its own clock, which will cause the other computer to
|
synchronisation of its own clock, which will cause the other computer to
|
||||||
measure a significant offset.
|
measure a significant offset.
|
||||||
|
|
||||||
|
== Operation
|
||||||
|
|
||||||
|
=== What clocks does `chronyd` use?
|
||||||
|
|
||||||
|
There are several different clocks used by `chronyd`:
|
||||||
|
|
||||||
|
* *System clock:* software clock maintained by the kernel. It is the main clock
|
||||||
|
used by applications running on the computer. It is synchronised by `chronyd`
|
||||||
|
to its NTP clock, unless started with the *-x* option.
|
||||||
|
* *NTP clock:* software clock (virtual) based on the system clock and internal
|
||||||
|
to `chronyd`. It keeps the best estimate of the true time according to the
|
||||||
|
configured time sources, which is served to NTP clients unless time smoothing
|
||||||
|
is enabled by the *smoothtime* directive. The *System time* value in the
|
||||||
|
`tracking` report is the current offset between the system and NTP clock.
|
||||||
|
* *Real-time clock (RTC):* hardware clock keeping time even when the
|
||||||
|
computer is turned off. It is used by the kernel to initialise the system
|
||||||
|
clock on boot and also by `chronyd` to compensate for its measured drift if
|
||||||
|
configured with the `rtcfile` directive and started with the `-s` option.
|
||||||
|
The clock can be kept accurate only by stepping enabled by the `rtcsync` or
|
||||||
|
`rtcautotrim` directive.
|
||||||
|
* *Reference clock:* hardware clock used as a time source. It is specified by
|
||||||
|
the `refclock` directive.
|
||||||
|
* *NIC clock (also known as PTP hardware clock):* hardware clock timestamping
|
||||||
|
packets received and transmitted by a network device specified by the
|
||||||
|
*hwtimestamp* directive. The clock is expected to be running free. It is not
|
||||||
|
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
|
||||||
|
order to convert the hardware timestamps.
|
||||||
|
|
||||||
|
=== How accurate is my system clock?
|
||||||
|
|
||||||
|
`chronyd` does not know how accurate really is the clock it is synchronizing.
|
||||||
|
Even if the measured offset of the clock is stable to nanoseconds, it could be
|
||||||
|
off by milliseconds due to asymmetric network delay, e.g. caused by asymmetric
|
||||||
|
routing or queuing delays in network switches. NTP provides root delay and root
|
||||||
|
dispersion to enable clients to estimate the maximum error of their clock.
|
||||||
|
|
||||||
|
Root delay measures the sum of round-trip times between all NTP servers on the
|
||||||
|
path from the client to the primary time source (e.g. a GPS receiver). Half of
|
||||||
|
the root delay is the maximum error due to asymmetric delays, assuming one
|
||||||
|
direction (e.g. from the client to the server) has a zero delay and the other
|
||||||
|
direction (from the server to the client) takes all of the measured delay. The
|
||||||
|
root delay also covers timestamping errors if the server implementation and
|
||||||
|
hardware meet the NTP requirement for transmit timestamps to never be late and
|
||||||
|
receive timestamps to never be early.
|
||||||
|
|
||||||
|
If you have additional information about the hardware and network between the
|
||||||
|
client and primary time source, you could modify the root delay to get a better
|
||||||
|
estimate of the maximum error. For example, from the physical distance of the
|
||||||
|
server and signal propagation speed in the cables a minimum symmetric
|
||||||
|
round-trip delay can be calculated and subtracted from the root delay measured
|
||||||
|
by NTP.
|
||||||
|
|
||||||
|
Root dispersion estimates errors due to instability of clocks and NTP
|
||||||
|
measurements. `chronyd` adjusts the rate at which root dispersion grows between
|
||||||
|
updates of the clock according to the stability of its NTP measurements. The
|
||||||
|
minimum rate is set by the the `maxclockerror` directive. By default it is 1
|
||||||
|
ppm (1 microsecond per second).
|
||||||
|
|
||||||
|
The estimated maximum error of the NTP clock is the sum of the root dispersion
|
||||||
|
and half of the root delay. This value is called root distance. The current
|
||||||
|
values of root dispersion and delay are included in the `tracking` report.
|
||||||
|
|
||||||
|
The estimated maximum error of the system clock, which is synchronized to the
|
||||||
|
NTP clock, is the sum of the root distance and remaining correction of the
|
||||||
|
system clock provided as `System time` in the `tracking` report. A maximum
|
||||||
|
value of this estimate between updates of the clock is included in the
|
||||||
|
`tracking` log.
|
||||||
|
|
||||||
|
Note that the resolution of the root delay and root dispersion fields in NTP
|
||||||
|
messages is about 15 microseconds and `chronyd` rounds the values up, i.e. the
|
||||||
|
minimum root distance an NTP client can normally observe is about 22.5
|
||||||
|
microseconds. An NTP extension field containing root delay and dispersion in a
|
||||||
|
better resolution of about 4 nanoseconds can be enabled by the `extfield F323`
|
||||||
|
option.
|
||||||
|
|
||||||
== Operating systems
|
== Operating systems
|
||||||
|
|
||||||
=== Does `chrony` support Windows?
|
=== Does `chrony` support Windows?
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ The following libraries with their development files, and programs, are needed
|
|||||||
to enable optional features:
|
to enable optional features:
|
||||||
|
|
||||||
* pkg-config: detection of development libraries
|
* pkg-config: detection of development libraries
|
||||||
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
|
* Nettle, GnuTLS, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
|
||||||
* libcap: dropping root privileges on Linux (`DROPROOT`)
|
* libcap: dropping root privileges on Linux (`DROPROOT`)
|
||||||
* libseccomp: system call filter on Linux (`SCFILTER`)
|
* libseccomp: system call filter on Linux (`SCFILTER`)
|
||||||
* GnuTLS and Nettle: Network Time Security (`NTS`)
|
* GnuTLS and Nettle: Network Time Security (`NTS`)
|
||||||
@@ -85,13 +85,6 @@ will be built with support for dropping root privileges. On other systems no
|
|||||||
extra library is needed. The default user which `chronyd` should run as can be
|
extra library is needed. The default user which `chronyd` should run as can be
|
||||||
specified with the `--with-user` option of the `configure` script.
|
specified with the `--with-user` option of the `configure` script.
|
||||||
|
|
||||||
If development files for the POSIX threads library are available, `chronyd`
|
|
||||||
will be built with support for asynchronous resolving of hostnames specified in
|
|
||||||
the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
|
|
||||||
a server to respond to client requests when resolving a hostname. If you don't
|
|
||||||
want to enable the support, specify the `--disable-asyncdns` flag to
|
|
||||||
`configure`.
|
|
||||||
|
|
||||||
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
|
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
|
||||||
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
|
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
|
||||||
https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
|
https://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
|
||||||
|
|||||||
@@ -16,5 +16,31 @@ TimeoutStartSec=180
|
|||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
StandardOutput=null
|
StandardOutput=null
|
||||||
|
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
DevicePolicy=closed
|
||||||
|
DynamicUser=yes
|
||||||
|
IPAddressAllow=localhost
|
||||||
|
IPAddressDeny=any
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateUsers=yes
|
||||||
|
ProtectClock=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectHostname=yes
|
||||||
|
ProtectKernelLogs=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProtectSystem=strict
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=@system-service
|
||||||
|
SystemCallFilter=~@privileged @resources
|
||||||
|
UMask=0777
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Use public NTP servers from the pool.ntp.org project.
|
# Use four public NTP servers from the pool.ntp.org project.
|
||||||
pool pool.ntp.org iburst
|
pool pool.ntp.org iburst
|
||||||
|
|
||||||
# Record the rate at which the system clock gains/losses time.
|
# Record the rate at which the system clock gains/losses time.
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Use public servers from the pool.ntp.org project.
|
# Note: The general recommendation for an NTP client is to have at least
|
||||||
# Please consider joining the pool (https://www.pool.ntp.org/join.html).
|
# three NTP servers to be able to detect one server providing incorrect
|
||||||
|
# time (falseticker).
|
||||||
|
|
||||||
|
# Use four public NTP servers from the pool.ntp.org project. If this
|
||||||
|
# host has a static public IP address, please consider joining the pool:
|
||||||
|
# https://www.ntppool.org/join.html
|
||||||
pool pool.ntp.org iburst
|
pool pool.ntp.org iburst
|
||||||
|
|
||||||
# Record the rate at which the system clock gains/losses time.
|
# Record the rate at which the system clock gains/losses time.
|
||||||
@@ -37,8 +42,8 @@ ntsdumpdir /var/lib/chrony
|
|||||||
# Insert/delete leap seconds by slewing instead of stepping.
|
# Insert/delete leap seconds by slewing instead of stepping.
|
||||||
#leapsecmode slew
|
#leapsecmode slew
|
||||||
|
|
||||||
# Get TAI-UTC offset and leap seconds from the system tz database.
|
# Set the TAI-UTC offset of the system clock.
|
||||||
#leapsectz right/UTC
|
#leapseclist /usr/share/zoneinfo/leap-seconds.list
|
||||||
|
|
||||||
# Specify directory for log files.
|
# Specify directory for log files.
|
||||||
logdir /var/log/chrony
|
logdir /var/log/chrony
|
||||||
|
|||||||
@@ -21,15 +21,17 @@
|
|||||||
#######################################################################
|
#######################################################################
|
||||||
### SPECIFY YOUR NTP SERVERS
|
### SPECIFY YOUR NTP SERVERS
|
||||||
# Most computers using chrony will send measurement requests to one or
|
# Most computers using chrony will send measurement requests to one or
|
||||||
# more 'NTP servers'. You will probably find that your Internet Service
|
# more NTP servers. The general recommendation is to have at least
|
||||||
|
# three NTP servers to be able to detect one server providing incorrect
|
||||||
|
# time (falseticker). You will probably find that your Internet Service
|
||||||
# Provider or company have one or more NTP servers that you can specify.
|
# Provider or company have one or more NTP servers that you can specify.
|
||||||
# Failing that, there are a lot of public NTP servers. There is a list
|
# Failing that, there are a lot of public NTP servers. There is a list
|
||||||
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or
|
# you can access at https://support.ntp.org/bin/view/Servers/WebHome or
|
||||||
# you can use servers from the pool.ntp.org project.
|
# you can use servers from the pool.ntp.org project.
|
||||||
|
|
||||||
! server foo.example.net iburst
|
! server ntp1.example.net iburst
|
||||||
! server bar.example.net iburst
|
! server ntp2.example.net iburst
|
||||||
! server baz.example.net iburst
|
! server ntp3.example.net iburst
|
||||||
|
|
||||||
! pool pool.ntp.org iburst
|
! pool pool.ntp.org iburst
|
||||||
|
|
||||||
@@ -99,8 +101,8 @@ ntsdumpdir /var/lib/chrony
|
|||||||
# and edit the following lines to specify the locations of the certificate and
|
# and edit the following lines to specify the locations of the certificate and
|
||||||
# key.
|
# key.
|
||||||
|
|
||||||
! ntsservercert /etc/.../foo.example.net.crt
|
! ntsservercert /etc/.../nts-server.crt
|
||||||
! ntsserverkey /etc/.../foo.example.net.key
|
! ntsserverkey /etc/.../nts-server.key
|
||||||
|
|
||||||
# chronyd can save the measurement history for the servers to files when
|
# chronyd can save the measurement history for the servers to files when
|
||||||
# it exits. This is useful in 2 situations:
|
# it exits. This is useful in 2 situations:
|
||||||
@@ -126,11 +128,11 @@ ntsdumpdir /var/lib/chrony
|
|||||||
|
|
||||||
! pidfile /var/run/chrony/chronyd.pid
|
! pidfile /var/run/chrony/chronyd.pid
|
||||||
|
|
||||||
# If the system timezone database is kept up to date and includes the
|
# The system timezone database usually comes with a list of leap seconds and
|
||||||
# right/UTC timezone, chronyd can use it to determine the current
|
# corresponding TAI-UTC offsets. chronyd can use it to set the offset of the
|
||||||
# TAI-UTC offset and when will the next leap second occur.
|
# system TAI clock and have an additional source of leap seconds.
|
||||||
|
|
||||||
! leapsectz right/UTC
|
! leapseclist /usr/share/zoneinfo/leap-seconds.list
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
### INITIAL CLOCK CORRECTION
|
### INITIAL CLOCK CORRECTION
|
||||||
@@ -238,7 +240,7 @@ ntsdumpdir /var/lib/chrony
|
|||||||
# several people, you need to set up a mailing list or sendmail alias
|
# several people, you need to set up a mailing list or sendmail alias
|
||||||
# for them and use the address of that.)
|
# for them and use the address of that.)
|
||||||
|
|
||||||
! mailonchange wibble@foo.example.net 0.5
|
! mailonchange wibble@example.net 0.5
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
### COMMAND ACCESS
|
### COMMAND ACCESS
|
||||||
|
|||||||
@@ -11,3 +11,5 @@
|
|||||||
#1 MD5 AVeryLongAndRandomPassword
|
#1 MD5 AVeryLongAndRandomPassword
|
||||||
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
|
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
|
||||||
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
|
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
|
||||||
|
#4 AES128 HEX:2DA837C4B6573748CA692B8C828E4891
|
||||||
|
#5 AES256 HEX:2666B8099BFF2D5BA20876121788ED24D2BE59111B8FFB562F0F56AE6EC7246E
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# This is a NetworkManager dispatcher script for chronyd to update
|
# This is a NetworkManager dispatcher script for chronyd to update
|
||||||
# its NTP sources passed from DHCP options. Note that this script is
|
# its NTP sources with servers from DHCP options passed by NetworkManager
|
||||||
# specific to NetworkManager-dispatcher due to use of the
|
# in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
|
||||||
# DHCP4_NTP_SERVERS environment variable.
|
|
||||||
|
|
||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
|
|
||||||
@@ -10,17 +9,23 @@ interface=$1
|
|||||||
action=$2
|
action=$2
|
||||||
|
|
||||||
chronyc=/usr/bin/chronyc
|
chronyc=/usr/bin/chronyc
|
||||||
default_server_options=iburst
|
server_options=iburst
|
||||||
server_dir=/var/run/chrony-dhcp
|
server_dir=/var/run/chrony-dhcp
|
||||||
|
|
||||||
dhcp_server_file=$server_dir/$interface.sources
|
dhcp_server_file=$server_dir/$interface.sources
|
||||||
# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager.
|
dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
|
||||||
nm_dhcp_servers=$DHCP4_NTP_SERVERS
|
|
||||||
|
|
||||||
add_servers_from_dhcp() {
|
add_servers_from_dhcp() {
|
||||||
rm -f "$dhcp_server_file"
|
rm -f "$dhcp_server_file"
|
||||||
for server in $nm_dhcp_servers; do
|
for server in $dhcp_ntp_servers; do
|
||||||
echo "server $server $default_server_options" >> "$dhcp_server_file"
|
# Check for invalid characters (from the DHCPv6 NTP FQDN suboption)
|
||||||
|
len1=$(printf '%s' "$server" | wc -c)
|
||||||
|
len2=$(printf '%s' "$server" | tr -d -c 'A-Za-z0-9:.-' | wc -c)
|
||||||
|
if [ "$len1" -ne "$len2" ] || [ "$len2" -lt 1 ] || [ "$len2" -gt 255 ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'server %s %s\n' "$server" "$server_options" >> "$dhcp_server_file"
|
||||||
done
|
done
|
||||||
$chronyc reload sources > /dev/null 2>&1 || :
|
$chronyc reload sources > /dev/null 2>&1 || :
|
||||||
}
|
}
|
||||||
@@ -34,10 +39,11 @@ clear_servers_from_dhcp() {
|
|||||||
|
|
||||||
mkdir -p $server_dir
|
mkdir -p $server_dir
|
||||||
|
|
||||||
if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then
|
case "$action" in
|
||||||
add_servers_from_dhcp
|
up|dhcp4-change|dhcp6-change)
|
||||||
elif [ "$action" = "down" ]; then
|
add_servers_from_dhcp;;
|
||||||
clear_servers_from_dhcp
|
down)
|
||||||
fi
|
clear_servers_from_dhcp;;
|
||||||
|
esac
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -7,8 +7,20 @@ export LC_ALL=C
|
|||||||
|
|
||||||
chronyc=/usr/bin/chronyc
|
chronyc=/usr/bin/chronyc
|
||||||
|
|
||||||
# For NetworkManager consider only up/down events
|
# For NetworkManager consider only selected events
|
||||||
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
|
if [ $# -ge 2 ]; then
|
||||||
|
case "$2" in
|
||||||
|
up|down|connectivity-change)
|
||||||
|
;;
|
||||||
|
dhcp4-change|dhcp6-change)
|
||||||
|
# Actions "up" and "connectivity-change" in some cases do not
|
||||||
|
# guarantee that the interface has a route (e.g. a bond).
|
||||||
|
# dhcp(x)-change handles at least cases that use DHCP.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
exit 0;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
|
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
|
||||||
|
|
||||||
|
|||||||
59
examples/chronyd-restricted.service
Normal file
59
examples/chronyd-restricted.service
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# This is a more restricted version of the chronyd service intended for
|
||||||
|
# minimal NTP/NTS client configurations. The daemon is started without root
|
||||||
|
# privileges and is allowed to write only to its own runtime, state, and log
|
||||||
|
# directories. It cannot bind to privileged ports in order to operate as an
|
||||||
|
# NTP server, or provide monitoring access over IPv4/IPv6. It cannot use
|
||||||
|
# reference clocks, HW timestamping, RTC tracking, and other features.
|
||||||
|
[Unit]
|
||||||
|
Description=NTP client (restricted)
|
||||||
|
Documentation=man:chronyd(8) man:chrony.conf(5)
|
||||||
|
After=chronyd.service ntpdate.service sntp.service ntpd.service
|
||||||
|
Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
|
||||||
|
ConditionCapability=CAP_SYS_TIME
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=notify
|
||||||
|
PIDFile=/run/chrony/chronyd.pid
|
||||||
|
Environment="OPTIONS="
|
||||||
|
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||||
|
ExecStart=/usr/sbin/chronyd -n -U $OPTIONS
|
||||||
|
|
||||||
|
User=chrony
|
||||||
|
LogsDirectory=chrony
|
||||||
|
LogsDirectoryMode=0750
|
||||||
|
RuntimeDirectory=chrony
|
||||||
|
RuntimeDirectoryMode=0750
|
||||||
|
RuntimeDirectoryPreserve=restart
|
||||||
|
StateDirectory=chrony
|
||||||
|
StateDirectoryMode=0750
|
||||||
|
|
||||||
|
AmbientCapabilities=CAP_SYS_TIME
|
||||||
|
CapabilityBoundingSet=CAP_SYS_TIME
|
||||||
|
DevicePolicy=closed
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateDevices=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
# This breaks adjtimex()
|
||||||
|
#PrivateUsers=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
|
ProtectHome=yes
|
||||||
|
ProtectHostname=yes
|
||||||
|
ProtectKernelLogs=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProtectSystem=strict
|
||||||
|
RemoveIPC=yes
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictRealtime=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io
|
||||||
|
SystemCallFilter=~@reboot @resources @swap
|
||||||
|
UMask=0077
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -6,13 +6,44 @@ Conflicts=ntpd.service systemd-timesyncd.service
|
|||||||
ConditionCapability=CAP_SYS_TIME
|
ConditionCapability=CAP_SYS_TIME
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=forking
|
Type=notify
|
||||||
PIDFile=/run/chrony/chronyd.pid
|
PIDFile=/run/chrony/chronyd.pid
|
||||||
|
Environment="OPTIONS="
|
||||||
EnvironmentFile=-/etc/sysconfig/chronyd
|
EnvironmentFile=-/etc/sysconfig/chronyd
|
||||||
ExecStart=/usr/sbin/chronyd $OPTIONS
|
ExecStart=/usr/sbin/chronyd -n $OPTIONS
|
||||||
|
|
||||||
|
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
|
||||||
|
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
|
||||||
|
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_MKNOD CAP_SYS_ADMIN
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_PACCT
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_PTRACE CAP_SYS_RAWIO CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
|
||||||
|
DeviceAllow=char-pps rw
|
||||||
|
DeviceAllow=char-ptp rw
|
||||||
|
DeviceAllow=char-rtc rw
|
||||||
|
DevicePolicy=closed
|
||||||
|
LockPersonality=yes
|
||||||
|
MemoryDenyWriteExecute=yes
|
||||||
|
NoNewPrivileges=yes
|
||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
|
ProtectControlGroups=yes
|
||||||
ProtectHome=yes
|
ProtectHome=yes
|
||||||
ProtectSystem=full
|
ProtectHostname=yes
|
||||||
|
ProtectKernelLogs=yes
|
||||||
|
ProtectKernelModules=yes
|
||||||
|
ProtectKernelTunables=yes
|
||||||
|
ProtectProc=invisible
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/run /var/lib/chrony -/var/log
|
||||||
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||||
|
RestrictNamespaces=yes
|
||||||
|
RestrictSUIDSGID=yes
|
||||||
|
SystemCallArchitectures=native
|
||||||
|
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @swap
|
||||||
|
|
||||||
|
# Adjust restrictions for /usr/sbin/sendmail (mailonchange directive)
|
||||||
|
NoNewPrivileges=no
|
||||||
|
ReadWritePaths=-/var/spool
|
||||||
|
RestrictAddressFamilies=AF_NETLINK
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
20
getdate.y
20
getdate.y
@@ -448,9 +448,9 @@ o_merid : /* NULL */
|
|||||||
the same signature as the function definition does. */
|
the same signature as the function definition does. */
|
||||||
#include "getdate.h"
|
#include "getdate.h"
|
||||||
|
|
||||||
extern struct tm *gmtime ();
|
extern struct tm *gmtime (const time_t *timep);
|
||||||
extern struct tm *localtime ();
|
extern struct tm *localtime (const time_t *timep);
|
||||||
extern time_t mktime ();
|
extern time_t mktime (struct tm *tm);
|
||||||
|
|
||||||
/* Month and day table. */
|
/* Month and day table. */
|
||||||
static TABLE const MonthDayTable[] = {
|
static TABLE const MonthDayTable[] = {
|
||||||
@@ -641,16 +641,13 @@ static TABLE const MilitaryTable[] = {
|
|||||||
|
|
||||||
/* ARGSUSED */
|
/* ARGSUSED */
|
||||||
static int
|
static int
|
||||||
yyerror (s)
|
yyerror (char *s ATTRIBUTE_UNUSED)
|
||||||
char *s ATTRIBUTE_UNUSED;
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ToHour (Hours, Meridian)
|
ToHour (int Hours, MERIDIAN Meridian)
|
||||||
int Hours;
|
|
||||||
MERIDIAN Meridian;
|
|
||||||
{
|
{
|
||||||
switch (Meridian)
|
switch (Meridian)
|
||||||
{
|
{
|
||||||
@@ -677,8 +674,7 @@ ToHour (Hours, Meridian)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ToYear (Year)
|
ToYear (int Year)
|
||||||
int Year;
|
|
||||||
{
|
{
|
||||||
if (Year < 0)
|
if (Year < 0)
|
||||||
Year = -Year;
|
Year = -Year;
|
||||||
@@ -694,8 +690,7 @@ ToYear (Year)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
LookupWord (buff)
|
LookupWord (char *buff)
|
||||||
char *buff;
|
|
||||||
{
|
{
|
||||||
register char *p;
|
register char *p;
|
||||||
register char *q;
|
register char *q;
|
||||||
@@ -912,6 +907,7 @@ get_date (const char *p, const time_t *now)
|
|||||||
yyHour = tmp->tm_hour;
|
yyHour = tmp->tm_hour;
|
||||||
yyMinutes = tmp->tm_min;
|
yyMinutes = tmp->tm_min;
|
||||||
yySeconds = tmp->tm_sec;
|
yySeconds = tmp->tm_sec;
|
||||||
|
memset(&tm, 0, sizeof (tm));
|
||||||
tm.tm_isdst = tmp->tm_isdst;
|
tm.tm_isdst = tmp->tm_isdst;
|
||||||
yyMeridian = MER24;
|
yyMeridian = MER24;
|
||||||
yyRelSeconds = 0;
|
yyRelSeconds = 0;
|
||||||
|
|||||||
1
hash.h
1
hash.h
@@ -44,6 +44,7 @@ typedef enum {
|
|||||||
HSH_SHA3_512 = 9,
|
HSH_SHA3_512 = 9,
|
||||||
HSH_TIGER = 10,
|
HSH_TIGER = 10,
|
||||||
HSH_WHIRLPOOL = 11,
|
HSH_WHIRLPOOL = 11,
|
||||||
|
HSH_MD5_NONCRYPTO = 10000, /* For NTPv4 reference ID */
|
||||||
} HSH_Algorithm;
|
} HSH_Algorithm;
|
||||||
|
|
||||||
extern int HSH_GetHashId(HSH_Algorithm algorithm);
|
extern int HSH_GetHashId(HSH_Algorithm algorithm);
|
||||||
|
|||||||
145
hash_gnutls.c
Normal file
145
hash_gnutls.c
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2021
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Crypto hashing using the GnuTLS library
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include <gnutls/crypto.h>
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
struct hash {
|
||||||
|
const HSH_Algorithm algorithm;
|
||||||
|
const gnutls_digest_algorithm_t type;
|
||||||
|
gnutls_hash_hd_t handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct hash hashes[] = {
|
||||||
|
{ HSH_MD5_NONCRYPTO, GNUTLS_DIG_MD5, NULL },
|
||||||
|
{ HSH_MD5, GNUTLS_DIG_MD5, NULL },
|
||||||
|
{ HSH_SHA1, GNUTLS_DIG_SHA1, NULL },
|
||||||
|
{ HSH_SHA256, GNUTLS_DIG_SHA256, NULL },
|
||||||
|
{ HSH_SHA384, GNUTLS_DIG_SHA384, NULL },
|
||||||
|
{ HSH_SHA512, GNUTLS_DIG_SHA512, NULL },
|
||||||
|
{ HSH_SHA3_224, GNUTLS_DIG_SHA3_224, NULL },
|
||||||
|
{ HSH_SHA3_256, GNUTLS_DIG_SHA3_256, NULL },
|
||||||
|
{ HSH_SHA3_384, GNUTLS_DIG_SHA3_384, NULL },
|
||||||
|
{ HSH_SHA3_512, GNUTLS_DIG_SHA3_512, NULL },
|
||||||
|
{ 0, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gnutls_initialised = 0;
|
||||||
|
|
||||||
|
int
|
||||||
|
HSH_GetHashId(HSH_Algorithm algorithm)
|
||||||
|
{
|
||||||
|
int id, r;
|
||||||
|
|
||||||
|
if (!gnutls_initialised) {
|
||||||
|
r = gnutls_global_init();
|
||||||
|
if (r < 0)
|
||||||
|
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
|
||||||
|
gnutls_initialised = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id = 0; hashes[id].algorithm != 0; id++) {
|
||||||
|
if (hashes[id].algorithm == algorithm)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashes[id].algorithm == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (hashes[id].handle)
|
||||||
|
return id;
|
||||||
|
|
||||||
|
if (algorithm == HSH_MD5_NONCRYPTO)
|
||||||
|
GNUTLS_FIPS140_SET_LAX_MODE();
|
||||||
|
|
||||||
|
r = gnutls_hash_init(&hashes[id].handle, hashes[id].type);
|
||||||
|
|
||||||
|
if (algorithm == HSH_MD5_NONCRYPTO)
|
||||||
|
GNUTLS_FIPS140_SET_STRICT_MODE();
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
DEBUG_LOG("Could not initialise %s : %s", "hash", gnutls_strerror(r));
|
||||||
|
hashes[id].handle = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
|
||||||
|
unsigned char *out, int out_len)
|
||||||
|
{
|
||||||
|
unsigned char buf[MAX_HASH_LENGTH];
|
||||||
|
gnutls_hash_hd_t handle;
|
||||||
|
int hash_len;
|
||||||
|
|
||||||
|
if (in1_len < 0 || in2_len < 0 || out_len < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
handle = hashes[id].handle;
|
||||||
|
hash_len = gnutls_hash_get_len(hashes[id].type);
|
||||||
|
|
||||||
|
if (out_len > hash_len)
|
||||||
|
out_len = hash_len;
|
||||||
|
|
||||||
|
if (hash_len > sizeof (buf))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (gnutls_hash(handle, in1, in1_len) < 0 ||
|
||||||
|
(in2 && gnutls_hash(handle, in2, in2_len) < 0)) {
|
||||||
|
/* Reset the state */
|
||||||
|
gnutls_hash_output(handle, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_hash_output(handle, buf);
|
||||||
|
memcpy(out, buf, out_len);
|
||||||
|
|
||||||
|
return out_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
HSH_Finalise(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!gnutls_initialised)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; hashes[i].algorithm != 0; i++) {
|
||||||
|
if (hashes[i].handle)
|
||||||
|
gnutls_hash_deinit(hashes[i].handle, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
gnutls_global_deinit();
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ int
|
|||||||
HSH_GetHashId(HSH_Algorithm algorithm)
|
HSH_GetHashId(HSH_Algorithm algorithm)
|
||||||
{
|
{
|
||||||
/* only MD5 is supported */
|
/* only MD5 is supported */
|
||||||
if (algorithm != HSH_MD5)
|
if (algorithm != HSH_MD5 && algorithm != HSH_MD5_NONCRYPTO)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ HSH_GetHashId(HSH_Algorithm algorithm)
|
|||||||
{
|
{
|
||||||
int id, nid;
|
int id, nid;
|
||||||
|
|
||||||
|
if (algorithm == HSH_MD5_NONCRYPTO)
|
||||||
|
algorithm = HSH_MD5;
|
||||||
|
|
||||||
for (id = 0; hashes[id].algorithm != 0; id++) {
|
for (id = 0; hashes[id].algorithm != 0; id++) {
|
||||||
if (hashes[id].algorithm == algorithm)
|
if (hashes[id].algorithm == algorithm)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ HSH_GetHashId(HSH_Algorithm algorithm)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (algorithm == HSH_MD5_NONCRYPTO)
|
||||||
|
algorithm = HSH_MD5;
|
||||||
|
|
||||||
for (i = 0; hashes[i].algorithm != 0; i++) {
|
for (i = 0; hashes[i].algorithm != 0; i++) {
|
||||||
if (hashes[i].algorithm == algorithm)
|
if (hashes[i].algorithm == algorithm)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ HSH_GetHashId(HSH_Algorithm algorithm)
|
|||||||
{
|
{
|
||||||
int i, h;
|
int i, h;
|
||||||
|
|
||||||
|
if (algorithm == HSH_MD5_NONCRYPTO)
|
||||||
|
algorithm = HSH_MD5;
|
||||||
|
|
||||||
for (i = 0; hashes[i].algorithm != 0; i++) {
|
for (i = 0; hashes[i].algorithm != 0; i++) {
|
||||||
if (hashes[i].algorithm == algorithm)
|
if (hashes[i].algorithm == algorithm)
|
||||||
break;
|
break;
|
||||||
|
|||||||
121
hwclock.c
121
hwclock.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
* Copyright (C) Miroslav Lichvar 2016-2018, 2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "local.h"
|
#include "local.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
#include "quantiles.h"
|
||||||
#include "regress.h"
|
#include "regress.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -43,6 +44,14 @@
|
|||||||
/* Maximum acceptable frequency offset of the clock */
|
/* Maximum acceptable frequency offset of the clock */
|
||||||
#define MAX_FREQ_OFFSET (2.0 / 3.0)
|
#define MAX_FREQ_OFFSET (2.0 / 3.0)
|
||||||
|
|
||||||
|
/* Quantiles for filtering readings by delay */
|
||||||
|
#define DELAY_QUANT_MIN_K 1
|
||||||
|
#define DELAY_QUANT_MAX_K 2
|
||||||
|
#define DELAY_QUANT_Q 10
|
||||||
|
#define DELAY_QUANT_REPEAT 7
|
||||||
|
#define DELAY_QUANT_LARGE_STEP_DELAY 1000
|
||||||
|
#define DELAY_QUANT_MIN_STEP 1.0e-9
|
||||||
|
|
||||||
struct HCL_Instance_Record {
|
struct HCL_Instance_Record {
|
||||||
/* HW and local reference timestamp */
|
/* HW and local reference timestamp */
|
||||||
struct timespec hw_ref;
|
struct timespec hw_ref;
|
||||||
@@ -64,12 +73,18 @@ struct HCL_Instance_Record {
|
|||||||
/* Minimum interval between samples */
|
/* Minimum interval between samples */
|
||||||
double min_separation;
|
double min_separation;
|
||||||
|
|
||||||
|
/* Expected precision of readings */
|
||||||
|
double precision;
|
||||||
|
|
||||||
/* Flag indicating the offset and frequency values are valid */
|
/* Flag indicating the offset and frequency values are valid */
|
||||||
int valid_coefs;
|
int valid_coefs;
|
||||||
|
|
||||||
/* Estimated offset and frequency of HW clock relative to local clock */
|
/* Estimated offset and frequency of HW clock relative to local clock */
|
||||||
double offset;
|
double offset;
|
||||||
double frequency;
|
double frequency;
|
||||||
|
|
||||||
|
/* Estimated quantiles of reading delay */
|
||||||
|
QNT_Instance delay_quants;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -92,7 +107,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
HCL_Instance
|
HCL_Instance
|
||||||
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
HCL_CreateInstance(int min_samples, int max_samples, double min_separation, double precision)
|
||||||
{
|
{
|
||||||
HCL_Instance clock;
|
HCL_Instance clock;
|
||||||
|
|
||||||
@@ -110,6 +125,11 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
|||||||
clock->n_samples = 0;
|
clock->n_samples = 0;
|
||||||
clock->valid_coefs = 0;
|
clock->valid_coefs = 0;
|
||||||
clock->min_separation = min_separation;
|
clock->min_separation = min_separation;
|
||||||
|
clock->precision = precision;
|
||||||
|
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
|
||||||
|
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
|
||||||
|
DELAY_QUANT_LARGE_STEP_DELAY,
|
||||||
|
DELAY_QUANT_MIN_STEP);
|
||||||
|
|
||||||
LCL_AddParameterChangeHandler(handle_slew, clock);
|
LCL_AddParameterChangeHandler(handle_slew, clock);
|
||||||
|
|
||||||
@@ -121,6 +141,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
|||||||
void HCL_DestroyInstance(HCL_Instance clock)
|
void HCL_DestroyInstance(HCL_Instance clock)
|
||||||
{
|
{
|
||||||
LCL_RemoveParameterChangeHandler(handle_slew, clock);
|
LCL_RemoveParameterChangeHandler(handle_slew, clock);
|
||||||
|
QNT_DestroyInstance(clock->delay_quants);
|
||||||
Free(clock->y_data);
|
Free(clock->y_data);
|
||||||
Free(clock->x_data);
|
Free(clock->x_data);
|
||||||
Free(clock);
|
Free(clock);
|
||||||
@@ -140,6 +161,102 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||||
|
struct timespec *hw_ts, struct timespec *local_ts, double *err,
|
||||||
|
int *quality)
|
||||||
|
{
|
||||||
|
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
|
||||||
|
double delay_sum, hw_sum, local_sum, local_prec, freq;
|
||||||
|
int i, min_reading, combined;
|
||||||
|
struct timespec ts1, ts2;
|
||||||
|
|
||||||
|
if (n_readings < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Work out the current correction multiplier needed to get cooked delays */
|
||||||
|
LCL_CookTime(&tss[0][0], &ts1, NULL);
|
||||||
|
LCL_CookTime(&tss[n_readings - 1][2], &ts2, NULL);
|
||||||
|
if (UTI_CompareTimespecs(&tss[0][0], &tss[n_readings - 1][2]) < 0)
|
||||||
|
freq = UTI_DiffTimespecsToDouble(&ts1, &ts2) /
|
||||||
|
UTI_DiffTimespecsToDouble(&tss[0][0], &tss[n_readings - 1][2]);
|
||||||
|
else
|
||||||
|
freq = 1.0;
|
||||||
|
|
||||||
|
for (i = 0; i < n_readings; i++) {
|
||||||
|
delay = freq * UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
|
||||||
|
|
||||||
|
if (delay < 0.0) {
|
||||||
|
/* Step in the middle of a reading? */
|
||||||
|
DEBUG_LOG("Bad reading delay=%e", delay);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 0 || min_delay > delay) {
|
||||||
|
min_delay = delay;
|
||||||
|
min_reading = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNT_Accumulate(clock->delay_quants, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_prec = LCL_GetSysPrecisionAsQuantum();
|
||||||
|
|
||||||
|
low_delay = QNT_GetQuantile(clock->delay_quants, QNT_GetMinK(clock->delay_quants)) -
|
||||||
|
QNT_GetMinStep(clock->delay_quants) / 2.0;
|
||||||
|
high_delay = QNT_GetQuantile(clock->delay_quants, QNT_GetMaxK(clock->delay_quants)) +
|
||||||
|
QNT_GetMinStep(clock->delay_quants) / 2.0;
|
||||||
|
low_delay = MIN(low_delay, high_delay);
|
||||||
|
high_delay = MAX(high_delay, low_delay + local_prec);
|
||||||
|
|
||||||
|
/* Combine readings with delay in the expected interval */
|
||||||
|
for (i = combined = 0, delay_sum = hw_sum = local_sum = 0.0; i < n_readings; i++) {
|
||||||
|
raw_delay = UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
|
||||||
|
delay = freq * raw_delay;
|
||||||
|
|
||||||
|
if (delay < low_delay || delay > high_delay)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
delay_sum += delay;
|
||||||
|
hw_sum += UTI_DiffTimespecsToDouble(&tss[i][1], &tss[0][1]);
|
||||||
|
local_sum += UTI_DiffTimespecsToDouble(&tss[i][0], &tss[0][0]) + raw_delay / 2.0;
|
||||||
|
combined++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Combined %d readings lo=%e hi=%e", combined, low_delay, high_delay);
|
||||||
|
|
||||||
|
if (combined > 0) {
|
||||||
|
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
|
||||||
|
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
|
||||||
|
*err = MAX(delay_sum / combined / 2.0, clock->precision);
|
||||||
|
*quality = 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indicate acceptable quality of the reading with minimum delay if its
|
||||||
|
interval does not contain the current offset predicted from previous
|
||||||
|
samples, or a new sample is needed to get the tracking working */
|
||||||
|
|
||||||
|
*hw_ts = tss[min_reading][1];
|
||||||
|
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
|
||||||
|
*err = MAX(min_delay / 2.0, clock->precision);
|
||||||
|
|
||||||
|
pred_err = 0.0;
|
||||||
|
LCL_CookTime(local_ts, &ts1, NULL);
|
||||||
|
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
|
||||||
|
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
|
||||||
|
*quality = 1;
|
||||||
|
} else {
|
||||||
|
*quality = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Min-delay reading err=%e prerr=%e ql=%d", *err, pred_err, *quality);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||||
struct timespec *local_ts, double err)
|
struct timespec *local_ts, double err)
|
||||||
|
|||||||
11
hwclock.h
11
hwclock.h
@@ -30,7 +30,7 @@ typedef struct HCL_Instance_Record *HCL_Instance;
|
|||||||
|
|
||||||
/* Create a new HW clock instance */
|
/* Create a new HW clock instance */
|
||||||
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
|
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
|
||||||
double min_separation);
|
double min_separation, double precision);
|
||||||
|
|
||||||
/* Destroy a HW clock instance */
|
/* Destroy a HW clock instance */
|
||||||
extern void HCL_DestroyInstance(HCL_Instance clock);
|
extern void HCL_DestroyInstance(HCL_Instance clock);
|
||||||
@@ -38,6 +38,15 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
|
|||||||
/* Check if a new sample should be accumulated at this time */
|
/* Check if a new sample should be accumulated at this time */
|
||||||
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
|
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
|
||||||
|
|
||||||
|
/* Process new readings of the HW clock in the form of (sys, hw, sys) triplets
|
||||||
|
and produce a sample which can be accumulated by HCL_AccumulateSample().
|
||||||
|
Indicate the quality of the sample relative to already processed samples as
|
||||||
|
a value of 0, 1, or 2, where a sample of quality 0 should normally be
|
||||||
|
dropped. */
|
||||||
|
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||||
|
struct timespec *hw_ts, struct timespec *local_ts, double *err,
|
||||||
|
int *quality);
|
||||||
|
|
||||||
/* Accumulate a new sample */
|
/* Accumulate a new sample */
|
||||||
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||||
struct timespec *local_ts, double err);
|
struct timespec *local_ts, double err);
|
||||||
|
|||||||
10
keys.c
10
keys.c
@@ -182,6 +182,9 @@ KEY_Reload(void)
|
|||||||
if (!key_file)
|
if (!key_file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!UTI_CheckFilePermissions(key_file, 0771))
|
||||||
|
;
|
||||||
|
|
||||||
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
|
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
|
||||||
if (!in) {
|
if (!in) {
|
||||||
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
|
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
|
||||||
@@ -255,14 +258,13 @@ KEY_Reload(void)
|
|||||||
more careful! */
|
more careful! */
|
||||||
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
|
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
|
||||||
|
|
||||||
|
LOG(LOGS_INFO, "Loaded %u symmetric keys", ARR_GetSize(keys));
|
||||||
|
|
||||||
/* Check for duplicates */
|
/* Check for duplicates */
|
||||||
for (i = 1; i < ARR_GetSize(keys); i++) {
|
for (i = 1; i < ARR_GetSize(keys); i++) {
|
||||||
if (get_key(i - 1)->id == get_key(i)->id)
|
if (get_key(i - 1)->id == get_key(i)->id)
|
||||||
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
|
LOG(LOGS_WARN, "Detected duplicate key %"PRIu32, get_key(i - 1)->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Erase any passwords from stack */
|
|
||||||
memset(line, 0, sizeof (line));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -400,7 +402,7 @@ check_auth(Key *key, const void *data, int data_len,
|
|||||||
|
|
||||||
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
|
hash_len = generate_auth(key, data, data_len, buf, sizeof (buf));
|
||||||
|
|
||||||
return MIN(hash_len, trunc_len) == auth_len && !memcmp(buf, auth, auth_len);
|
return MIN(hash_len, trunc_len) == auth_len && UTI_IsMemoryEqual(buf, auth, auth_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|||||||
272
leapdb.c
Normal file
272
leapdb.c
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
|
||||||
|
* Copyright (C) Patrick Oppenlander 2023, 2024
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
This module provides leap second information. */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
#include "leapdb.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
/* Source of leap second data */
|
||||||
|
enum {
|
||||||
|
SRC_NONE,
|
||||||
|
SRC_TIMEZONE,
|
||||||
|
SRC_LIST,
|
||||||
|
} leap_src;
|
||||||
|
|
||||||
|
/* Offset between leap-seconds.list timestamp epoch and Unix epoch.
|
||||||
|
leap-seconds.list epoch is 1 Jan 1900, 00:00:00 */
|
||||||
|
#define LEAP_SEC_LIST_OFFSET 2208988800
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static NTP_Leap
|
||||||
|
get_tz_leap(time_t when, int *tai_offset)
|
||||||
|
{
|
||||||
|
struct tm stm, *tm;
|
||||||
|
time_t t;
|
||||||
|
char *tz_env, tz_orig[128];
|
||||||
|
NTP_Leap tz_leap = LEAP_Normal;
|
||||||
|
|
||||||
|
tm = gmtime(&when);
|
||||||
|
if (!tm)
|
||||||
|
return tz_leap;
|
||||||
|
|
||||||
|
stm = *tm;
|
||||||
|
|
||||||
|
/* Temporarily switch to the timezone containing leap seconds */
|
||||||
|
tz_env = getenv("TZ");
|
||||||
|
if (tz_env) {
|
||||||
|
if (strlen(tz_env) >= sizeof (tz_orig))
|
||||||
|
return tz_leap;
|
||||||
|
strcpy(tz_orig, tz_env);
|
||||||
|
}
|
||||||
|
setenv("TZ", CNF_GetLeapSecTimezone(), 1);
|
||||||
|
tzset();
|
||||||
|
|
||||||
|
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
|
||||||
|
t = mktime(&stm);
|
||||||
|
if (t != -1)
|
||||||
|
*tai_offset = t - when + 10;
|
||||||
|
|
||||||
|
/* Set the time to 23:59:60 and see how it overflows in mktime() */
|
||||||
|
stm.tm_sec = 60;
|
||||||
|
stm.tm_min = 59;
|
||||||
|
stm.tm_hour = 23;
|
||||||
|
|
||||||
|
t = mktime(&stm);
|
||||||
|
|
||||||
|
if (tz_env)
|
||||||
|
setenv("TZ", tz_orig, 1);
|
||||||
|
else
|
||||||
|
unsetenv("TZ");
|
||||||
|
tzset();
|
||||||
|
|
||||||
|
if (t == -1)
|
||||||
|
return tz_leap;
|
||||||
|
|
||||||
|
if (stm.tm_sec == 60)
|
||||||
|
tz_leap = LEAP_InsertSecond;
|
||||||
|
else if (stm.tm_sec == 1)
|
||||||
|
tz_leap = LEAP_DeleteSecond;
|
||||||
|
|
||||||
|
return tz_leap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static NTP_Leap
|
||||||
|
get_list_leap(time_t when, int *tai_offset)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char line[1024];
|
||||||
|
NTP_Leap ret_leap = LEAP_Normal;
|
||||||
|
int ret_tai_offset = 0, prev_lsl_tai_offset = 10;
|
||||||
|
int64_t when1900, lsl_updated = 0, lsl_expiry = 0;
|
||||||
|
const char *leap_sec_list = CNF_GetLeapSecList();
|
||||||
|
|
||||||
|
if (!(f = UTI_OpenFile(NULL, leap_sec_list, NULL, 'r', 0))) {
|
||||||
|
LOG(LOGS_ERR, "Failed to open leap seconds list %s", leap_sec_list);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Leap second happens at midnight */
|
||||||
|
when = (when / (24 * 3600) + 1) * (24 * 3600);
|
||||||
|
|
||||||
|
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
|
||||||
|
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof line, f)) {
|
||||||
|
int64_t lsl_when;
|
||||||
|
int lsl_tai_offset;
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
/* Ignore blank lines */
|
||||||
|
for (p = line; *p && isspace(*p); ++p)
|
||||||
|
;
|
||||||
|
if (!*p)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (*line == '#') {
|
||||||
|
/* Update time line starts with #$ */
|
||||||
|
if (line[1] == '$' && sscanf(line + 2, "%"SCNd64, &lsl_updated) != 1)
|
||||||
|
goto error;
|
||||||
|
/* Expiration time line starts with #@ */
|
||||||
|
if (line[1] == '@' && sscanf(line + 2, "%"SCNd64, &lsl_expiry) != 1)
|
||||||
|
goto error;
|
||||||
|
/* Comment or a special comment we don't care about */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Leap entry */
|
||||||
|
if (sscanf(line, "%"SCNd64" %d", &lsl_when, &lsl_tai_offset) != 2)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (when1900 == lsl_when) {
|
||||||
|
if (lsl_tai_offset > prev_lsl_tai_offset)
|
||||||
|
ret_leap = LEAP_InsertSecond;
|
||||||
|
else if (lsl_tai_offset < prev_lsl_tai_offset)
|
||||||
|
ret_leap = LEAP_DeleteSecond;
|
||||||
|
/* When is rounded to the end of the day, so offset hasn't changed yet! */
|
||||||
|
ret_tai_offset = prev_lsl_tai_offset;
|
||||||
|
} else if (when1900 > lsl_when) {
|
||||||
|
ret_tai_offset = lsl_tai_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_lsl_tai_offset = lsl_tai_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the file looks sensible */
|
||||||
|
if (!feof(f) || !lsl_updated || !lsl_expiry)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (when1900 >= lsl_expiry)
|
||||||
|
LOG(LOGS_WARN, "Leap second list %s needs update", leap_sec_list);
|
||||||
|
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (f)
|
||||||
|
fclose(f);
|
||||||
|
LOG(LOGS_ERR, "Failed to parse leap seconds list %s", leap_sec_list);
|
||||||
|
return LEAP_Normal;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (f)
|
||||||
|
fclose(f);
|
||||||
|
*tai_offset = ret_tai_offset;
|
||||||
|
return ret_leap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_leap_source(NTP_Leap (*src)(time_t when, int *tai_offset))
|
||||||
|
{
|
||||||
|
int tai_offset = 0;
|
||||||
|
|
||||||
|
/* Check that the leap second source has good data for Jun 30 2012 and Dec 31 2012 */
|
||||||
|
if (src(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
|
||||||
|
src(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LDB_Initialise(void)
|
||||||
|
{
|
||||||
|
const char *leap_tzname, *leap_sec_list;
|
||||||
|
|
||||||
|
leap_tzname = CNF_GetLeapSecTimezone();
|
||||||
|
if (leap_tzname && !check_leap_source(get_tz_leap)) {
|
||||||
|
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
|
||||||
|
leap_tzname = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
leap_sec_list = CNF_GetLeapSecList();
|
||||||
|
if (leap_sec_list && !check_leap_source(get_list_leap)) {
|
||||||
|
LOG(LOGS_WARN, "Leap second list %s failed check, ignoring", leap_sec_list);
|
||||||
|
leap_sec_list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leap_sec_list) {
|
||||||
|
LOG(LOGS_INFO, "Using leap second list %s", leap_sec_list);
|
||||||
|
leap_src = SRC_LIST;
|
||||||
|
} else if (leap_tzname) {
|
||||||
|
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
|
||||||
|
leap_src = SRC_TIMEZONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
NTP_Leap
|
||||||
|
LDB_GetLeap(time_t when, int *tai_offset)
|
||||||
|
{
|
||||||
|
static time_t last_ldb_leap_check;
|
||||||
|
static NTP_Leap ldb_leap;
|
||||||
|
static int ldb_tai_offset;
|
||||||
|
|
||||||
|
/* Do this check at most twice a day */
|
||||||
|
when = when / (12 * 3600) * (12 * 3600);
|
||||||
|
if (last_ldb_leap_check == when)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
last_ldb_leap_check = when;
|
||||||
|
ldb_leap = LEAP_Normal;
|
||||||
|
ldb_tai_offset = 0;
|
||||||
|
|
||||||
|
switch (leap_src) {
|
||||||
|
case SRC_NONE:
|
||||||
|
break;
|
||||||
|
case SRC_TIMEZONE:
|
||||||
|
ldb_leap = get_tz_leap(when, &ldb_tai_offset);
|
||||||
|
break;
|
||||||
|
case SRC_LIST:
|
||||||
|
ldb_leap = get_list_leap(when, &ldb_tai_offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
*tai_offset = ldb_tai_offset;
|
||||||
|
return ldb_leap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LDB_Finalise(void)
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
}
|
||||||
37
leapdb.h
Normal file
37
leapdb.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Patrick Oppenlander 2024
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
This module provides leap second information.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_LEAPDB_H
|
||||||
|
#define GOT_LEAPDB_H
|
||||||
|
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
|
extern void LDB_Initialise(void);
|
||||||
|
extern NTP_Leap LDB_GetLeap(time_t when, int *tai_offset);
|
||||||
|
extern void LDB_Finalise(void);
|
||||||
|
|
||||||
|
#endif /* GOT_LEAPDB_H */
|
||||||
39
local.c
39
local.c
@@ -184,10 +184,8 @@ void
|
|||||||
LCL_Finalise(void)
|
LCL_Finalise(void)
|
||||||
{
|
{
|
||||||
/* Make sure all handlers have been removed */
|
/* Make sure all handlers have been removed */
|
||||||
if (change_list.next != &change_list)
|
BRIEF_ASSERT(change_list.next == &change_list);
|
||||||
assert(0);
|
BRIEF_ASSERT(dispersion_notify_list.next == &dispersion_notify_list);
|
||||||
if (dispersion_notify_list.next != &dispersion_notify_list)
|
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -225,9 +223,7 @@ LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything
|
|||||||
|
|
||||||
/* Check that the handler is not already registered */
|
/* Check that the handler is not already registered */
|
||||||
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
||||||
if (!(ptr->handler != handler || ptr->anything != anything)) {
|
BRIEF_ASSERT(ptr->handler != handler || ptr->anything != anything);
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_entry = MallocNew(ChangeListEntry);
|
new_entry = MallocNew(ChangeListEntry);
|
||||||
@@ -301,9 +297,7 @@ LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anythi
|
|||||||
|
|
||||||
/* Check that the handler is not already registered */
|
/* Check that the handler is not already registered */
|
||||||
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
|
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
|
||||||
if (!(ptr->handler != handler || ptr->anything != anything)) {
|
BRIEF_ASSERT(ptr->handler != handler || ptr->anything != anything);
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_entry = MallocNew(DispersionNotifyListEntry);
|
new_entry = MallocNew(DispersionNotifyListEntry);
|
||||||
@@ -563,6 +557,8 @@ void
|
|||||||
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
|
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
|
||||||
double offset, double dispersion)
|
double offset, double dispersion)
|
||||||
{
|
{
|
||||||
|
LCL_CancelOffsetCorrection();
|
||||||
|
|
||||||
/* Dispatch to all handlers */
|
/* Dispatch to all handlers */
|
||||||
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
||||||
|
|
||||||
@@ -628,6 +624,24 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
|
||||||
|
{
|
||||||
|
ChangeListEntry *first_handler;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
first_handler = change_list.next;
|
||||||
|
change_list.next = &change_list;
|
||||||
|
|
||||||
|
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
|
||||||
|
|
||||||
|
change_list.next = first_handler;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
lcl_InvokeDispersionNotifyHandlers(double dispersion)
|
lcl_InvokeDispersionNotifyHandlers(double dispersion)
|
||||||
{
|
{
|
||||||
@@ -681,8 +695,11 @@ LCL_MakeStep(void)
|
|||||||
|
|
||||||
/* Cancel remaining slew and make the step */
|
/* Cancel remaining slew and make the step */
|
||||||
LCL_AccumulateOffset(correction, 0.0);
|
LCL_AccumulateOffset(correction, 0.0);
|
||||||
if (!LCL_ApplyStepOffset(-correction))
|
if (!LCL_ApplyStepOffset(-correction)) {
|
||||||
|
/* Revert the correction */
|
||||||
|
LCL_AccumulateOffset(-correction, 0.0);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
|
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
|
||||||
|
|
||||||
|
|||||||
5
local.h
5
local.h
@@ -173,6 +173,11 @@ extern void LCL_NotifyLeap(int leap);
|
|||||||
a slew, in one easy step */
|
a slew, in one easy step */
|
||||||
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
|
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
|
||||||
|
|
||||||
|
/* Same as the routine above, except it does not call the registered
|
||||||
|
parameter change handlers */
|
||||||
|
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
|
||||||
|
double corr_rate);
|
||||||
|
|
||||||
/* Routine to read the system precision as a log to base 2 value. */
|
/* Routine to read the system precision as a log to base 2 value. */
|
||||||
extern int LCL_GetSysPrecisionAsLog(void);
|
extern int LCL_GetSysPrecisionAsLog(void);
|
||||||
|
|
||||||
|
|||||||
98
logging.c
98
logging.c
@@ -39,10 +39,14 @@
|
|||||||
/* This is used by DEBUG_LOG macro */
|
/* This is used by DEBUG_LOG macro */
|
||||||
LOG_Severity log_min_severity = LOGS_INFO;
|
LOG_Severity log_min_severity = LOGS_INFO;
|
||||||
|
|
||||||
|
/* Current logging contexts */
|
||||||
|
static LOG_Context log_contexts;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Flag indicating we have initialised */
|
/* Flag indicating we have initialised */
|
||||||
static int initialised = 0;
|
static int initialised = 0;
|
||||||
|
|
||||||
|
static char *file_log_path = NULL;
|
||||||
static FILE *file_log = NULL;
|
static FILE *file_log = NULL;
|
||||||
static int system_log = 0;
|
static int system_log = 0;
|
||||||
|
|
||||||
@@ -72,6 +76,8 @@ void
|
|||||||
LOG_Initialise(void)
|
LOG_Initialise(void)
|
||||||
{
|
{
|
||||||
debug_prefix = Strdup("");
|
debug_prefix = Strdup("");
|
||||||
|
log_contexts = 0;
|
||||||
|
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
LOG_OpenFileLog(NULL);
|
LOG_OpenFileLog(NULL);
|
||||||
}
|
}
|
||||||
@@ -85,8 +91,11 @@ LOG_Finalise(void)
|
|||||||
if (system_log)
|
if (system_log)
|
||||||
closelog();
|
closelog();
|
||||||
|
|
||||||
if (file_log)
|
if (file_log && file_log != stderr)
|
||||||
fclose(file_log);
|
fclose(file_log);
|
||||||
|
file_log = NULL;
|
||||||
|
Free(file_log_path);
|
||||||
|
file_log_path = NULL;
|
||||||
|
|
||||||
LOG_CycleLogFiles();
|
LOG_CycleLogFiles();
|
||||||
|
|
||||||
@@ -140,6 +149,7 @@ void LOG_Message(LOG_Severity severity,
|
|||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
|
|
||||||
assert(initialised);
|
assert(initialised);
|
||||||
|
severity = CLAMP(LOGS_DEBUG, severity, LOGS_FATAL);
|
||||||
|
|
||||||
if (!system_log && file_log && severity >= log_min_severity) {
|
if (!system_log && file_log && severity >= log_min_severity) {
|
||||||
/* Don't clutter up syslog with timestamps and internal debugging info */
|
/* Don't clutter up syslog with timestamps and internal debugging info */
|
||||||
@@ -150,8 +160,13 @@ void LOG_Message(LOG_Severity severity,
|
|||||||
fprintf(file_log, "%s ", buf);
|
fprintf(file_log, "%s ", buf);
|
||||||
}
|
}
|
||||||
#if DEBUG > 0
|
#if DEBUG > 0
|
||||||
if (log_min_severity <= LOGS_DEBUG)
|
if (log_min_severity <= LOGS_DEBUG) {
|
||||||
fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name);
|
/* Log severity to character mapping (debug, info, warn, err, fatal) */
|
||||||
|
const char severity_chars[LOGS_FATAL - LOGS_DEBUG + 1] = {'D', 'I', 'W', 'E', 'F'};
|
||||||
|
|
||||||
|
fprintf(file_log, "%c:%s%s:%d:(%s) ", severity_chars[severity - LOGS_DEBUG],
|
||||||
|
debug_prefix, filename, line_number, function_name);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +189,7 @@ void LOG_Message(LOG_Severity severity,
|
|||||||
/* Send the message also to the foreground process if it is
|
/* Send the message also to the foreground process if it is
|
||||||
still running, or stderr if it is still open */
|
still running, or stderr if it is still open */
|
||||||
if (parent_fd > 0) {
|
if (parent_fd > 0) {
|
||||||
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
|
if (!LOG_NotifyParent(buf))
|
||||||
; /* Not much we can do here */
|
; /* Not much we can do here */
|
||||||
} else if (system_log && parent_fd == 0) {
|
} else if (system_log && parent_fd == 0) {
|
||||||
system_log = 0;
|
system_log = 0;
|
||||||
@@ -189,27 +204,42 @@ void LOG_Message(LOG_Severity severity,
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
static FILE *
|
||||||
LOG_OpenFileLog(const char *log_file)
|
open_file_log(const char *log_file, char mode)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
|
||||||
if (log_file) {
|
if (log_file) {
|
||||||
f = UTI_OpenFile(NULL, log_file, NULL, 'A', 0640);
|
f = UTI_OpenFile(NULL, log_file, NULL, mode, 0640);
|
||||||
} else {
|
} else {
|
||||||
f = stderr;
|
f = stderr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable line buffering */
|
/* Enable line buffering */
|
||||||
|
if (f)
|
||||||
setvbuf(f, NULL, _IOLBF, BUFSIZ);
|
setvbuf(f, NULL, _IOLBF, BUFSIZ);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LOG_OpenFileLog(const char *log_file)
|
||||||
|
{
|
||||||
|
if (file_log_path)
|
||||||
|
Free(file_log_path);
|
||||||
|
if (log_file)
|
||||||
|
file_log_path = Strdup(log_file);
|
||||||
|
else
|
||||||
|
file_log_path = NULL;
|
||||||
|
|
||||||
if (file_log && file_log != stderr)
|
if (file_log && file_log != stderr)
|
||||||
fclose(file_log);
|
fclose(file_log);
|
||||||
|
|
||||||
file_log = f;
|
file_log = open_file_log(file_log_path, 'A');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -237,6 +267,30 @@ LOG_GetMinSeverity(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LOG_SetContext(LOG_Context context)
|
||||||
|
{
|
||||||
|
log_contexts |= context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
LOG_UnsetContext(LOG_Context context)
|
||||||
|
{
|
||||||
|
log_contexts &= ~context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
LOG_Severity
|
||||||
|
LOG_GetContextSeverity(LOG_Context contexts)
|
||||||
|
{
|
||||||
|
return log_contexts & contexts ? LOGS_INFO : LOGS_DEBUG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
LOG_SetDebugPrefix(const char *prefix)
|
LOG_SetDebugPrefix(const char *prefix)
|
||||||
{
|
{
|
||||||
@@ -256,6 +310,17 @@ LOG_SetParentFd(int fd)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
LOG_NotifyParent(const char *message)
|
||||||
|
{
|
||||||
|
if (parent_fd <= 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return write(parent_fd, message, strlen(message) + 1) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
LOG_CloseParentFd()
|
LOG_CloseParentFd()
|
||||||
{
|
{
|
||||||
@@ -339,14 +404,29 @@ LOG_FileWrite(LOG_FileID id, const char *format, ...)
|
|||||||
void
|
void
|
||||||
LOG_CycleLogFiles(void)
|
LOG_CycleLogFiles(void)
|
||||||
{
|
{
|
||||||
|
struct stat st;
|
||||||
LOG_FileID i;
|
LOG_FileID i;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
/* The log will be opened later when an entry is logged */
|
||||||
for (i = 0; i < n_filelogs; i++) {
|
for (i = 0; i < n_filelogs; i++) {
|
||||||
if (logfiles[i].file)
|
if (logfiles[i].file)
|
||||||
fclose(logfiles[i].file);
|
fclose(logfiles[i].file);
|
||||||
logfiles[i].file = NULL;
|
logfiles[i].file = NULL;
|
||||||
logfiles[i].writes = 0;
|
logfiles[i].writes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Try to open the log specified by the -l option, but only if nothing is
|
||||||
|
present at its path to avoid unnecessary error messages. Keep the
|
||||||
|
original file if that fails (the process might no longer have the
|
||||||
|
necessary privileges to write in the directory). */
|
||||||
|
if (file_log && file_log != stderr && stat(file_log_path, &st) < 0 && errno == ENOENT) {
|
||||||
|
f = open_file_log(file_log_path, 'a');
|
||||||
|
if (f) {
|
||||||
|
fclose(file_log);
|
||||||
|
file_log = f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|||||||
19
logging.h
19
logging.h
@@ -100,6 +100,20 @@ extern void LOG_SetMinSeverity(LOG_Severity severity);
|
|||||||
/* Get the minimum severity */
|
/* Get the minimum severity */
|
||||||
extern LOG_Severity LOG_GetMinSeverity(void);
|
extern LOG_Severity LOG_GetMinSeverity(void);
|
||||||
|
|
||||||
|
/* Flags for info messages that should be logged only in specific contexts */
|
||||||
|
typedef enum {
|
||||||
|
LOGC_Command = 1,
|
||||||
|
LOGC_SourceFile = 2,
|
||||||
|
} LOG_Context;
|
||||||
|
|
||||||
|
/* Modify current contexts */
|
||||||
|
extern void LOG_SetContext(LOG_Context context);
|
||||||
|
extern void LOG_UnsetContext(LOG_Context context);
|
||||||
|
|
||||||
|
/* Get severity depending on the current active contexts: INFO if they contain
|
||||||
|
at least one of the specified contexts, DEBUG otherwise */
|
||||||
|
extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
|
||||||
|
|
||||||
/* Set a prefix for debug messages */
|
/* Set a prefix for debug messages */
|
||||||
extern void LOG_SetDebugPrefix(const char *prefix);
|
extern void LOG_SetDebugPrefix(const char *prefix);
|
||||||
|
|
||||||
@@ -112,7 +126,10 @@ extern void LOG_OpenSystemLog(void);
|
|||||||
/* Stop using stderr and send fatal message to the foreground process */
|
/* Stop using stderr and send fatal message to the foreground process */
|
||||||
extern void LOG_SetParentFd(int fd);
|
extern void LOG_SetParentFd(int fd);
|
||||||
|
|
||||||
/* Close the pipe to the foreground process so it can exit */
|
/* Send a message to the foreground process */
|
||||||
|
extern int LOG_NotifyParent(const char *message);
|
||||||
|
|
||||||
|
/* Close the pipe to the foreground process */
|
||||||
extern void LOG_CloseParentFd(void);
|
extern void LOG_CloseParentFd(void);
|
||||||
|
|
||||||
/* File logging functions */
|
/* File logging functions */
|
||||||
|
|||||||
82
main.c
82
main.c
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
#include "leapdb.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
@@ -76,11 +77,18 @@ static REF_Mode ref_mode = REF_ModeNormal;
|
|||||||
static void
|
static void
|
||||||
do_platform_checks(void)
|
do_platform_checks(void)
|
||||||
{
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
/* Require at least 32-bit integers, two's complement representation and
|
/* Require at least 32-bit integers, two's complement representation and
|
||||||
the usual implementation of conversion of unsigned integers */
|
the usual implementation of conversion of unsigned integers */
|
||||||
assert(sizeof (int) >= 4);
|
assert(sizeof (int) >= 4);
|
||||||
assert(-1 == ~0);
|
assert(-1 == ~0);
|
||||||
assert((int32_t)4294967295U == (int32_t)-1);
|
assert((int32_t)4294967295U == (int32_t)-1);
|
||||||
|
|
||||||
|
/* Require time_t and tv_nsec in timespec to be signed */
|
||||||
|
ts.tv_sec = -1;
|
||||||
|
ts.tv_nsec = -1;
|
||||||
|
assert(ts.tv_sec < 0 && ts.tv_nsec < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -99,11 +107,40 @@ delete_pidfile(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
notify_system_manager(int start)
|
||||||
|
{
|
||||||
|
#ifdef LINUX
|
||||||
|
/* The systemd protocol is documented in the sd_notify(3) man page */
|
||||||
|
const char *message, *path = getenv("NOTIFY_SOCKET");
|
||||||
|
int sock_fd;
|
||||||
|
|
||||||
|
if (!path)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (path[0] != '/')
|
||||||
|
LOG_FATAL("Unsupported notification socket");
|
||||||
|
|
||||||
|
message = start ? "READY=1" : "STOPPING=1";
|
||||||
|
|
||||||
|
sock_fd = SCK_OpenUnixDatagramSocket(path, NULL, 0);
|
||||||
|
|
||||||
|
if (sock_fd < 0 || SCK_Send(sock_fd, message, strlen(message), 0) != strlen(message))
|
||||||
|
LOG_FATAL("Could not send notification to $NOTIFY_SOCKET");
|
||||||
|
|
||||||
|
SCK_CloseSocket(sock_fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
MAI_CleanupAndExit(void)
|
MAI_CleanupAndExit(void)
|
||||||
{
|
{
|
||||||
if (!initialised) exit(exit_status);
|
if (!initialised) exit(exit_status);
|
||||||
|
|
||||||
|
notify_system_manager(0);
|
||||||
|
|
||||||
LCL_CancelOffsetCorrection();
|
LCL_CancelOffsetCorrection();
|
||||||
SRC_DumpSources();
|
SRC_DumpSources();
|
||||||
|
|
||||||
@@ -127,6 +164,7 @@ MAI_CleanupAndExit(void)
|
|||||||
RCL_Finalise();
|
RCL_Finalise();
|
||||||
SRC_Finalise();
|
SRC_Finalise();
|
||||||
REF_Finalise();
|
REF_Finalise();
|
||||||
|
LDB_Finalise();
|
||||||
RTC_Finalise();
|
RTC_Finalise();
|
||||||
SYS_Finalise();
|
SYS_Finalise();
|
||||||
|
|
||||||
@@ -141,6 +179,8 @@ MAI_CleanupAndExit(void)
|
|||||||
HSH_Finalise();
|
HSH_Finalise();
|
||||||
LOG_Finalise();
|
LOG_Finalise();
|
||||||
|
|
||||||
|
UTI_ResetGetRandomFunctions();
|
||||||
|
|
||||||
exit(exit_status);
|
exit(exit_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +197,8 @@ signal_cleanup(int x)
|
|||||||
static void
|
static void
|
||||||
quit_timeout(void *arg)
|
quit_timeout(void *arg)
|
||||||
{
|
{
|
||||||
|
LOG(LOGS_INFO, "Timeout reached");
|
||||||
|
|
||||||
/* Return with non-zero status if the clock is not synchronised */
|
/* Return with non-zero status if the clock is not synchronised */
|
||||||
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
|
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
|
||||||
SCH_QuitProgram();
|
SCH_QuitProgram();
|
||||||
@@ -202,7 +244,12 @@ post_init_ntp_hook(void *anything)
|
|||||||
REF_SetMode(ref_mode);
|
REF_SetMode(ref_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close the pipe to the foreground process so it can exit */
|
notify_system_manager(1);
|
||||||
|
|
||||||
|
/* Send an empty message to the foreground process so it can exit.
|
||||||
|
If that fails, indicating the process was killed, exit too. */
|
||||||
|
if (!LOG_NotifyParent(""))
|
||||||
|
SCH_QuitProgram();
|
||||||
LOG_CloseParentFd();
|
LOG_CloseParentFd();
|
||||||
|
|
||||||
CNF_AddSources();
|
CNF_AddSources();
|
||||||
@@ -320,10 +367,16 @@ go_daemon(void)
|
|||||||
char message[1024];
|
char message[1024];
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
close(pipefd[1]);
|
/* Don't exit before the 'parent' */
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
r = read(pipefd[0], message, sizeof (message));
|
r = read(pipefd[0], message, sizeof (message));
|
||||||
if (r) {
|
|
||||||
if (r > 0) {
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
|
||||||
|
if (r != 1 || message[0] != '\0') {
|
||||||
|
if (r > 1) {
|
||||||
/* Print the error message from the child */
|
/* Print the error message from the child */
|
||||||
message[sizeof (message) - 1] = '\0';
|
message[sizeof (message) - 1] = '\0';
|
||||||
fprintf(stderr, "%s\n", message);
|
fprintf(stderr, "%s\n", message);
|
||||||
@@ -332,8 +385,6 @@ go_daemon(void)
|
|||||||
} else
|
} else
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
close(pipefd[0]);
|
|
||||||
|
|
||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
/* Do 2nd fork, as-per recommended practice for launching daemons. */
|
/* Do 2nd fork, as-per recommended practice for launching daemons. */
|
||||||
@@ -342,7 +393,10 @@ go_daemon(void)
|
|||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
LOG_FATAL("fork() failed : %s", strerror(errno));
|
LOG_FATAL("fork() failed : %s", strerror(errno));
|
||||||
} else if (pid > 0) {
|
} else if (pid > 0) {
|
||||||
exit(0); /* In the 'parent' */
|
/* In the 'parent' */
|
||||||
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
/* In the child we want to leave running as the daemon */
|
/* In the child we want to leave running as the daemon */
|
||||||
|
|
||||||
@@ -352,9 +406,9 @@ go_daemon(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Don't keep stdin/out/err from before. But don't close
|
/* Don't keep stdin/out/err from before. But don't close
|
||||||
the parent pipe yet. */
|
the parent pipe yet, or reusable file descriptors. */
|
||||||
for (fd=0; fd<1024; fd++) {
|
for (fd=0; fd<1024; fd++) {
|
||||||
if (fd != pipefd[1])
|
if (fd != pipefd[1] && !SCK_IsReusable(fd))
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,6 +598,9 @@ int main
|
|||||||
if (user_check && getuid() != 0)
|
if (user_check && getuid() != 0)
|
||||||
LOG_FATAL("Not superuser");
|
LOG_FATAL("Not superuser");
|
||||||
|
|
||||||
|
/* Initialise reusable file descriptors before fork */
|
||||||
|
SCK_PreInitialise();
|
||||||
|
|
||||||
/* Turn into a daemon */
|
/* Turn into a daemon */
|
||||||
if (!nofork) {
|
if (!nofork) {
|
||||||
go_daemon();
|
go_daemon();
|
||||||
@@ -626,12 +683,17 @@ int main
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Drop root privileges if the specified user has a non-zero UID */
|
/* Drop root privileges if the specified user has a non-zero UID */
|
||||||
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
|
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
|
||||||
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
|
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
|
||||||
|
|
||||||
|
/* Warn if missing read access or having write access to keys */
|
||||||
|
CNF_CheckReadOnlyAccess();
|
||||||
|
}
|
||||||
|
|
||||||
if (!geteuid())
|
if (!geteuid())
|
||||||
LOG(LOGS_WARN, "Running with root privileges");
|
LOG(LOGS_WARN, "Running with root privileges");
|
||||||
|
|
||||||
|
LDB_Initialise();
|
||||||
REF_Initialise();
|
REF_Initialise();
|
||||||
SST_Initialise();
|
SST_Initialise();
|
||||||
NSR_Initialise();
|
NSR_Initialise();
|
||||||
|
|||||||
15
md5.c
15
md5.c
@@ -117,8 +117,7 @@ inline UINT4 ROTATE_LEFT(UINT4 x, int n)
|
|||||||
/* The routine MD5Init initializes the message-digest context
|
/* The routine MD5Init initializes the message-digest context
|
||||||
mdContext. All fields are set to zero.
|
mdContext. All fields are set to zero.
|
||||||
*/
|
*/
|
||||||
void MD5Init (mdContext)
|
void MD5Init (MD5_CTX *mdContext)
|
||||||
MD5_CTX *mdContext;
|
|
||||||
{
|
{
|
||||||
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
|
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
|
||||||
|
|
||||||
@@ -134,10 +133,7 @@ MD5_CTX *mdContext;
|
|||||||
account for the presence of each of the characters inBuf[0..inLen-1]
|
account for the presence of each of the characters inBuf[0..inLen-1]
|
||||||
in the message whose digest is being computed.
|
in the message whose digest is being computed.
|
||||||
*/
|
*/
|
||||||
void MD5Update (mdContext, inBuf, inLen)
|
void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
|
||||||
MD5_CTX *mdContext;
|
|
||||||
unsigned const char *inBuf;
|
|
||||||
unsigned int inLen;
|
|
||||||
{
|
{
|
||||||
UINT4 in[16];
|
UINT4 in[16];
|
||||||
int mdi;
|
int mdi;
|
||||||
@@ -173,8 +169,7 @@ unsigned int inLen;
|
|||||||
ends with the desired message digest in mdContext->digest[0...15].
|
ends with the desired message digest in mdContext->digest[0...15].
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void MD5Final (mdContext)
|
void MD5Final (MD5_CTX *mdContext)
|
||||||
MD5_CTX *mdContext;
|
|
||||||
{
|
{
|
||||||
UINT4 in[16];
|
UINT4 in[16];
|
||||||
int mdi;
|
int mdi;
|
||||||
@@ -214,9 +209,7 @@ MD5_CTX *mdContext;
|
|||||||
|
|
||||||
/* Basic MD5 step. Transforms buf based on in.
|
/* Basic MD5 step. Transforms buf based on in.
|
||||||
*/
|
*/
|
||||||
static void Transform (buf, in)
|
static void Transform (UINT4 *buf, UINT4 *in)
|
||||||
UINT4 *buf;
|
|
||||||
UINT4 *in;
|
|
||||||
{
|
{
|
||||||
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
|
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
|
||||||
|
|
||||||
|
|||||||
7
memory.c
7
memory.c
@@ -47,8 +47,13 @@ Realloc(void *ptr, size_t size)
|
|||||||
{
|
{
|
||||||
void *r;
|
void *r;
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
Free(ptr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
r = realloc(ptr, size);
|
r = realloc(ptr, size);
|
||||||
if (!r && size)
|
if (!r)
|
||||||
LOG_FATAL("Could not allocate memory");
|
LOG_FATAL("Could not allocate memory");
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
43
ntp.h
43
ntp.h
@@ -113,6 +113,41 @@ typedef struct {
|
|||||||
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
|
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
|
||||||
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
|
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
|
||||||
|
|
||||||
|
/* Non-authentication extension fields and corresponding internal flags */
|
||||||
|
|
||||||
|
#define NTP_EF_EXP_MONO_ROOT 0xF323
|
||||||
|
#define NTP_EF_EXP_NET_CORRECTION 0xF324
|
||||||
|
|
||||||
|
#define NTP_EF_FLAG_EXP_MONO_ROOT 0x1
|
||||||
|
#define NTP_EF_FLAG_EXP_NET_CORRECTION 0x2
|
||||||
|
|
||||||
|
/* Pre-NTPv5 experimental extension field */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
NTP_int32 root_delay;
|
||||||
|
NTP_int32 root_dispersion;
|
||||||
|
NTP_int64 mono_receive_ts;
|
||||||
|
uint32_t mono_epoch;
|
||||||
|
} NTP_EFExpMonoRoot;
|
||||||
|
|
||||||
|
#define NTP_EF_EXP_MONO_ROOT_MAGIC 0xF5BEDD9AU
|
||||||
|
|
||||||
|
/* Experimental extension field to provide PTP corrections */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t magic;
|
||||||
|
NTP_int64 correction;
|
||||||
|
uint32_t reserved[3];
|
||||||
|
} NTP_EFExpNetCorrection;
|
||||||
|
|
||||||
|
#define NTP_EF_EXP_NET_CORRECTION_MAGIC 0x07AC2CEBU
|
||||||
|
|
||||||
|
/* Authentication extension fields */
|
||||||
|
|
||||||
|
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
|
||||||
|
#define NTP_EF_NTS_COOKIE 0x0204
|
||||||
|
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
|
||||||
|
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
|
||||||
|
|
||||||
/* Enumeration for authentication modes of NTP packets */
|
/* Enumeration for authentication modes of NTP packets */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NTP_AUTH_NONE = 0, /* No authentication */
|
NTP_AUTH_NONE = 0, /* No authentication */
|
||||||
@@ -130,6 +165,7 @@ typedef struct {
|
|||||||
NTP_Mode mode;
|
NTP_Mode mode;
|
||||||
|
|
||||||
int ext_fields;
|
int ext_fields;
|
||||||
|
int ext_field_flags;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
NTP_AuthMode mode;
|
NTP_AuthMode mode;
|
||||||
@@ -154,4 +190,11 @@ typedef struct {
|
|||||||
double root_dispersion;
|
double root_dispersion;
|
||||||
} NTP_Sample;
|
} NTP_Sample;
|
||||||
|
|
||||||
|
/* Possible sources of timestamps */
|
||||||
|
typedef enum {
|
||||||
|
NTP_TS_DAEMON = 0,
|
||||||
|
NTP_TS_KERNEL,
|
||||||
|
NTP_TS_HARDWARE
|
||||||
|
} NTP_Timestamp_Source;
|
||||||
|
|
||||||
#endif /* GOT_NTP_H */
|
#endif /* GOT_NTP_H */
|
||||||
|
|||||||
109
ntp_auth.c
109
ntp_auth.c
@@ -32,7 +32,6 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "ntp_auth.h"
|
#include "ntp_auth.h"
|
||||||
#include "ntp_ext.h"
|
|
||||||
#include "ntp_signd.h"
|
#include "ntp_signd.h"
|
||||||
#include "nts_ntp.h"
|
#include "nts_ntp.h"
|
||||||
#include "nts_ntp_client.h"
|
#include "nts_ntp_client.h"
|
||||||
@@ -105,19 +104,6 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
|
||||||
is_zero_data(unsigned char *data, int length)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < length; i++)
|
|
||||||
if (data[i] != 0)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static NAU_Instance
|
static NAU_Instance
|
||||||
create_instance(NTP_AuthMode mode)
|
create_instance(NTP_AuthMode mode)
|
||||||
{
|
{
|
||||||
@@ -247,101 +233,6 @@ NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketIn
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
|
||||||
NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
|
|
||||||
{
|
|
||||||
int parsed, remainder, ef_length, ef_type;
|
|
||||||
unsigned char *data;
|
|
||||||
|
|
||||||
data = (void *)packet;
|
|
||||||
parsed = NTP_HEADER_LENGTH;
|
|
||||||
remainder = info->length - parsed;
|
|
||||||
|
|
||||||
info->ext_fields = 0;
|
|
||||||
|
|
||||||
/* Check if this is a plain NTP packet with no extension fields or MAC */
|
|
||||||
if (remainder <= 0)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
assert(remainder % 4 == 0);
|
|
||||||
|
|
||||||
/* In NTPv3 and older packets don't have extension fields. Anything after
|
|
||||||
the header is assumed to be a MAC. */
|
|
||||||
if (info->version <= 3) {
|
|
||||||
info->auth.mode = NTP_AUTH_SYMMETRIC;
|
|
||||||
info->auth.mac.start = parsed;
|
|
||||||
info->auth.mac.length = remainder;
|
|
||||||
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
|
|
||||||
|
|
||||||
/* Check if it is an MS-SNTP authenticator field or extended authenticator
|
|
||||||
field with zeroes as digest */
|
|
||||||
if (info->version == 3 && info->auth.mac.key_id != 0) {
|
|
||||||
if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
|
|
||||||
info->auth.mode = NTP_AUTH_MSSNTP;
|
|
||||||
else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
|
|
||||||
info->auth.mode = NTP_AUTH_MSSNTP_EXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a crypto NAK */
|
|
||||||
if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) {
|
|
||||||
info->auth.mode = NTP_AUTH_SYMMETRIC;
|
|
||||||
info->auth.mac.start = parsed;
|
|
||||||
info->auth.mac.length = remainder;
|
|
||||||
info->auth.mac.key_id = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse the rest of the NTPv4 packet */
|
|
||||||
|
|
||||||
while (remainder > 0) {
|
|
||||||
/* Check if the remaining data is a MAC */
|
|
||||||
if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Check if this is a valid NTPv4 extension field and skip it */
|
|
||||||
if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) {
|
|
||||||
DEBUG_LOG("Invalid format");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(ef_length > 0 && ef_length % 4 == 0);
|
|
||||||
|
|
||||||
switch (ef_type) {
|
|
||||||
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
|
|
||||||
case NTP_EF_NTS_COOKIE:
|
|
||||||
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
|
|
||||||
case NTP_EF_NTS_AUTH_AND_EEF:
|
|
||||||
info->auth.mode = NTP_AUTH_NTS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
info->ext_fields++;
|
|
||||||
parsed += ef_length;
|
|
||||||
remainder = info->length - parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainder == 0) {
|
|
||||||
/* No MAC */
|
|
||||||
return 1;
|
|
||||||
} else if (remainder >= NTP_MIN_MAC_LENGTH) {
|
|
||||||
info->auth.mode = NTP_AUTH_SYMMETRIC;
|
|
||||||
info->auth.mac.start = parsed;
|
|
||||||
info->auth.mac.length = remainder;
|
|
||||||
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_LOG("Invalid format");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
int
|
int
|
||||||
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
|
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,9 +55,6 @@ extern int NAU_PrepareRequestAuth(NAU_Instance instance);
|
|||||||
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
|
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
|
||||||
NTP_PacketInfo *info);
|
NTP_PacketInfo *info);
|
||||||
|
|
||||||
/* Parse a request or response to detect the authentication mode */
|
|
||||||
extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info);
|
|
||||||
|
|
||||||
/* Verify that a request is authentic. If it is not authentic and a non-zero
|
/* Verify that a request is authentic. If it is not authentic and a non-zero
|
||||||
kod code is returned, a KoD response should be sent back. */
|
kod code is returned, a KoD response should be sent back. */
|
||||||
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
|
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);
|
||||||
|
|||||||
949
ntp_core.c
949
ntp_core.c
File diff suppressed because it is too large
Load Diff
10
ntp_core.h
10
ntp_core.h
@@ -38,16 +38,12 @@ typedef enum {
|
|||||||
NTP_SERVER, NTP_PEER
|
NTP_SERVER, NTP_PEER
|
||||||
} NTP_Source_Type;
|
} NTP_Source_Type;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NTP_TS_DAEMON = 0,
|
|
||||||
NTP_TS_KERNEL,
|
|
||||||
NTP_TS_HARDWARE
|
|
||||||
} NTP_Timestamp_Source;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
double err;
|
double err;
|
||||||
NTP_Timestamp_Source source;
|
NTP_Timestamp_Source source;
|
||||||
|
double rx_duration;
|
||||||
|
double net_correction;
|
||||||
} NTP_Local_Timestamp;
|
} NTP_Local_Timestamp;
|
||||||
|
|
||||||
/* This is a private data type used for storing the instance record for
|
/* This is a private data type used for storing the instance record for
|
||||||
@@ -117,6 +113,8 @@ extern void NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_d
|
|||||||
|
|
||||||
extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum);
|
extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum);
|
||||||
|
|
||||||
|
extern void NCR_ModifyOffset(NCR_Instance inst, double new_offset);
|
||||||
|
|
||||||
extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
|
extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
|
||||||
|
|
||||||
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
|
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
|
||||||
|
|||||||
187
ntp_io.c
187
ntp_io.c
@@ -4,7 +4,7 @@
|
|||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Timo Teras 2009
|
* Copyright (C) Timo Teras 2009
|
||||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020
|
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -30,9 +30,11 @@
|
|||||||
|
|
||||||
#include "sysincl.h"
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "memory.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "ntp_sources.h"
|
#include "ntp_sources.h"
|
||||||
|
#include "ptp.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
@@ -70,6 +72,16 @@ static int permanent_server_sockets;
|
|||||||
/* Flag indicating the server IPv4 socket is bound to an address */
|
/* Flag indicating the server IPv4 socket is bound to an address */
|
||||||
static int bound_server_sock_fd4;
|
static int bound_server_sock_fd4;
|
||||||
|
|
||||||
|
/* PTP event port, or 0 if disabled */
|
||||||
|
static int ptp_port;
|
||||||
|
|
||||||
|
/* Shared server/client sockets for NTP-over-PTP */
|
||||||
|
static int ptp_sock_fd4;
|
||||||
|
static int ptp_sock_fd6;
|
||||||
|
|
||||||
|
/* Buffer for transmitted NTP-over-PTP messages */
|
||||||
|
static PTP_NtpMessage *ptp_message;
|
||||||
|
|
||||||
/* Flag indicating that we have been initialised */
|
/* Flag indicating that we have been initialised */
|
||||||
static int initialised=0;
|
static int initialised=0;
|
||||||
|
|
||||||
@@ -114,8 +126,14 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
|
|||||||
dscp = CNF_GetNtpDscp();
|
dscp = CNF_GetNtpDscp();
|
||||||
if (dscp > 0 && dscp < 64) {
|
if (dscp > 0 && dscp < 64) {
|
||||||
#ifdef IP_TOS
|
#ifdef IP_TOS
|
||||||
|
if (family == IPADDR_INET4)
|
||||||
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
|
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
|
||||||
;
|
;
|
||||||
|
#endif
|
||||||
|
#if defined(FEAT_IPV6) && defined(IPV6_TCLASS)
|
||||||
|
if (family == IPADDR_INET6)
|
||||||
|
if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, dscp << 2))
|
||||||
|
;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,9 +169,6 @@ close_socket(int sock_fd)
|
|||||||
if (sock_fd == INVALID_SOCK_FD)
|
if (sock_fd == INVALID_SOCK_FD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
||||||
NIO_Linux_NotifySocketClosing(sock_fd);
|
|
||||||
#endif
|
|
||||||
SCH_RemoveFileHandler(sock_fd);
|
SCH_RemoveFileHandler(sock_fd);
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
}
|
}
|
||||||
@@ -221,6 +236,17 @@ NIO_Initialise(void)
|
|||||||
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
|
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
|
||||||
LOG_FATAL("Could not open NTP sockets");
|
LOG_FATAL("Could not open NTP sockets");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ptp_port = CNF_GetPtpPort();
|
||||||
|
ptp_sock_fd4 = INVALID_SOCK_FD;
|
||||||
|
ptp_sock_fd6 = INVALID_SOCK_FD;
|
||||||
|
ptp_message = NULL;
|
||||||
|
|
||||||
|
if (ptp_port > 0) {
|
||||||
|
ptp_sock_fd4 = open_socket(IPADDR_INET4, ptp_port, 0, NULL);
|
||||||
|
ptp_sock_fd6 = open_socket(IPADDR_INET6, ptp_port, 0, NULL);
|
||||||
|
ptp_message = MallocNew(PTP_NtpMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -238,6 +264,11 @@ NIO_Finalise(void)
|
|||||||
close_socket(server_sock_fd6);
|
close_socket(server_sock_fd6);
|
||||||
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
|
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
|
||||||
|
|
||||||
|
close_socket(ptp_sock_fd4);
|
||||||
|
close_socket(ptp_sock_fd6);
|
||||||
|
ptp_sock_fd4 = ptp_sock_fd6 = INVALID_SOCK_FD;
|
||||||
|
Free(ptp_message);
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||||
NIO_Linux_Finalise();
|
NIO_Linux_Finalise();
|
||||||
#endif
|
#endif
|
||||||
@@ -247,22 +278,38 @@ NIO_Finalise(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NIO_IsHwTsEnabled(void)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||||
|
return NIO_Linux_IsHwTsEnabled();
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
|
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
|
||||||
{
|
{
|
||||||
if (separate_client_sockets) {
|
|
||||||
return open_separate_client_socket(remote_addr);
|
|
||||||
} else {
|
|
||||||
switch (remote_addr->ip_addr.family) {
|
switch (remote_addr->ip_addr.family) {
|
||||||
case IPADDR_INET4:
|
case IPADDR_INET4:
|
||||||
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
||||||
|
return ptp_sock_fd4;
|
||||||
|
if (separate_client_sockets)
|
||||||
|
return open_separate_client_socket(remote_addr);
|
||||||
return client_sock_fd4;
|
return client_sock_fd4;
|
||||||
case IPADDR_INET6:
|
case IPADDR_INET6:
|
||||||
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
||||||
|
return ptp_sock_fd6;
|
||||||
|
if (separate_client_sockets)
|
||||||
|
return open_separate_client_socket(remote_addr);
|
||||||
return client_sock_fd6;
|
return client_sock_fd6;
|
||||||
default:
|
default:
|
||||||
return INVALID_SOCK_FD;
|
return INVALID_SOCK_FD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
@@ -271,6 +318,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
|
|||||||
{
|
{
|
||||||
switch (remote_addr->ip_addr.family) {
|
switch (remote_addr->ip_addr.family) {
|
||||||
case IPADDR_INET4:
|
case IPADDR_INET4:
|
||||||
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
||||||
|
return ptp_sock_fd4;
|
||||||
if (permanent_server_sockets)
|
if (permanent_server_sockets)
|
||||||
return server_sock_fd4;
|
return server_sock_fd4;
|
||||||
if (server_sock_fd4 == INVALID_SOCK_FD)
|
if (server_sock_fd4 == INVALID_SOCK_FD)
|
||||||
@@ -279,6 +328,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
|
|||||||
server_sock_ref4++;
|
server_sock_ref4++;
|
||||||
return server_sock_fd4;
|
return server_sock_fd4;
|
||||||
case IPADDR_INET6:
|
case IPADDR_INET6:
|
||||||
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
||||||
|
return ptp_sock_fd6;
|
||||||
if (permanent_server_sockets)
|
if (permanent_server_sockets)
|
||||||
return server_sock_fd6;
|
return server_sock_fd6;
|
||||||
if (server_sock_fd6 == INVALID_SOCK_FD)
|
if (server_sock_fd6 == INVALID_SOCK_FD)
|
||||||
@@ -293,9 +344,21 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_ptp_socket(int sock_fd)
|
||||||
|
{
|
||||||
|
return ptp_port > 0 && sock_fd != INVALID_SOCK_FD &&
|
||||||
|
(sock_fd == ptp_sock_fd4 || sock_fd == ptp_sock_fd6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
NIO_CloseClientSocket(int sock_fd)
|
NIO_CloseClientSocket(int sock_fd)
|
||||||
{
|
{
|
||||||
|
if (is_ptp_socket(sock_fd))
|
||||||
|
return;
|
||||||
|
|
||||||
if (separate_client_sockets)
|
if (separate_client_sockets)
|
||||||
close_socket(sock_fd);
|
close_socket(sock_fd);
|
||||||
}
|
}
|
||||||
@@ -305,7 +368,7 @@ NIO_CloseClientSocket(int sock_fd)
|
|||||||
void
|
void
|
||||||
NIO_CloseServerSocket(int sock_fd)
|
NIO_CloseServerSocket(int sock_fd)
|
||||||
{
|
{
|
||||||
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD)
|
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD || is_ptp_socket(sock_fd))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sock_fd == server_sock_fd4) {
|
if (sock_fd == server_sock_fd4) {
|
||||||
@@ -329,7 +392,7 @@ int
|
|||||||
NIO_IsServerSocket(int sock_fd)
|
NIO_IsServerSocket(int sock_fd)
|
||||||
{
|
{
|
||||||
return sock_fd != INVALID_SOCK_FD &&
|
return sock_fd != INVALID_SOCK_FD &&
|
||||||
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6);
|
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6 || is_ptp_socket(sock_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -337,7 +400,8 @@ NIO_IsServerSocket(int sock_fd)
|
|||||||
int
|
int
|
||||||
NIO_IsServerSocketOpen(void)
|
NIO_IsServerSocketOpen(void)
|
||||||
{
|
{
|
||||||
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD;
|
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD ||
|
||||||
|
ptp_sock_fd4 != INVALID_SOCK_FD || ptp_sock_fd6 != INVALID_SOCK_FD;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -367,6 +431,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
|
|||||||
|
|
||||||
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
|
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
|
||||||
local_ts.source = NTP_TS_DAEMON;
|
local_ts.source = NTP_TS_DAEMON;
|
||||||
|
local_ts.rx_duration = 0.0;
|
||||||
|
local_ts.net_correction = 0.0;
|
||||||
|
|
||||||
sched_ts = local_ts.ts;
|
sched_ts = local_ts.ts;
|
||||||
|
|
||||||
if (message->addr_type != SCK_ADDR_IP) {
|
if (message->addr_type != SCK_ADDR_IP) {
|
||||||
@@ -392,6 +459,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
|
|||||||
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
|
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
|
||||||
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
|
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
|
||||||
|
|
||||||
|
if (!NIO_UnwrapMessage(message, sock_fd, &local_ts.net_correction))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Just ignore the packet if it's not of a recognized length */
|
/* Just ignore the packet if it's not of a recognized length */
|
||||||
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
|
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
|
||||||
DEBUG_LOG("Unexpected length");
|
DEBUG_LOG("Unexpected length");
|
||||||
@@ -409,11 +479,6 @@ read_from_socket(int sock_fd, int event, void *anything)
|
|||||||
SCK_Message *messages;
|
SCK_Message *messages;
|
||||||
int i, received, flags = 0;
|
int i, received, flags = 0;
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
||||||
if (NIO_Linux_ProcessEvent(sock_fd, event))
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (event == SCH_FILE_EXCEPTION) {
|
if (event == SCH_FILE_EXCEPTION) {
|
||||||
#ifdef HAVE_LINUX_TIMESTAMPING
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
||||||
flags |= SCK_FLAG_MSG_ERRQUEUE;
|
flags |= SCK_FLAG_MSG_ERRQUEUE;
|
||||||
@@ -430,6 +495,91 @@ read_from_socket(int sock_fd, int event, void *anything)
|
|||||||
process_message(&messages[i], sock_fd, event);
|
process_message(&messages[i], sock_fd, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction)
|
||||||
|
{
|
||||||
|
double ptp_correction;
|
||||||
|
PTP_NtpMessage *msg;
|
||||||
|
|
||||||
|
if (!is_ptp_socket(sock_fd))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (message->length <= PTP_NTP_PREFIX_LENGTH) {
|
||||||
|
DEBUG_LOG("Unexpected length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = message->data;
|
||||||
|
|
||||||
|
if ((msg->header.type != PTP_TYPE_DELAY_REQ && msg->header.type != PTP_TYPE_SYNC) ||
|
||||||
|
(msg->header.version != PTP_VERSION_2 &&
|
||||||
|
(msg->header.version != PTP_VERSION_2_1 || msg->header.min_sdoid != 0)) ||
|
||||||
|
ntohs(msg->header.length) != message->length ||
|
||||||
|
msg->header.domain != CNF_GetPtpDomain() ||
|
||||||
|
ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
|
||||||
|
ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
|
||||||
|
ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
|
||||||
|
DEBUG_LOG("Unexpected PTP message");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
|
||||||
|
message->length -= PTP_NTP_PREFIX_LENGTH;
|
||||||
|
|
||||||
|
ptp_correction = UTI_Integer64NetworkToHost(*(Integer64 *)msg->header.correction) /
|
||||||
|
((1 << 16) * 1.0e9);
|
||||||
|
|
||||||
|
/* Use the correction only if the RX duration is known (i.e. HW timestamp) */
|
||||||
|
if (*net_correction > 0.0)
|
||||||
|
*net_correction += ptp_correction;
|
||||||
|
|
||||||
|
DEBUG_LOG("Unwrapped PTP->NTP len=%d corr=%.9f", message->length, ptp_correction);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
wrap_message(SCK_Message *message, int sock_fd)
|
||||||
|
{
|
||||||
|
static uint16_t sequence_id = 0;
|
||||||
|
|
||||||
|
assert(PTP_NTP_PREFIX_LENGTH == 48);
|
||||||
|
|
||||||
|
if (!is_ptp_socket(sock_fd))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!ptp_message)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (message->length < NTP_HEADER_LENGTH ||
|
||||||
|
message->length + PTP_NTP_PREFIX_LENGTH > sizeof (*ptp_message)) {
|
||||||
|
DEBUG_LOG("Unexpected length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
|
||||||
|
ptp_message->header.type = PTP_TYPE_DELAY_REQ;
|
||||||
|
ptp_message->header.version = PTP_VERSION_2;
|
||||||
|
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
|
||||||
|
ptp_message->header.domain = CNF_GetPtpDomain();
|
||||||
|
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
|
||||||
|
ptp_message->header.sequence_id = htons(sequence_id++);
|
||||||
|
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
|
||||||
|
ptp_message->tlv_header.length = htons(message->length);
|
||||||
|
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
|
||||||
|
|
||||||
|
message->data = ptp_message;
|
||||||
|
message->length += PTP_NTP_PREFIX_LENGTH;
|
||||||
|
|
||||||
|
DEBUG_LOG("Wrapped NTP->PTP len=%d", message->length - PTP_NTP_PREFIX_LENGTH);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Send a packet to remote address from local address */
|
/* Send a packet to remote address from local address */
|
||||||
|
|
||||||
@@ -451,6 +601,9 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
|||||||
message.data = packet;
|
message.data = packet;
|
||||||
message.length = length;
|
message.length = length;
|
||||||
|
|
||||||
|
if (!wrap_message(&message, local_addr->sock_fd))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Specify remote address if the socket is not connected */
|
/* Specify remote address if the socket is not connected */
|
||||||
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
|
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
|
||||||
message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
|
message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
|
||||||
@@ -467,7 +620,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
|||||||
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
||||||
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
||||||
if (message.local_addr.ip.family == IPADDR_INET4 &&
|
if (message.local_addr.ip.family == IPADDR_INET4 &&
|
||||||
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
|
(bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
|
||||||
message.local_addr.ip.family = IPADDR_UNSPEC;
|
message.local_addr.ip.family = IPADDR_UNSPEC;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
7
ntp_io.h
7
ntp_io.h
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "ntp.h"
|
#include "ntp.h"
|
||||||
#include "addressing.h"
|
#include "addressing.h"
|
||||||
|
#include "socket.h"
|
||||||
|
|
||||||
/* Function to initialise the module. */
|
/* Function to initialise the module. */
|
||||||
extern void NIO_Initialise(void);
|
extern void NIO_Initialise(void);
|
||||||
@@ -38,6 +39,9 @@ extern void NIO_Initialise(void);
|
|||||||
/* Function to finalise the module */
|
/* Function to finalise the module */
|
||||||
extern void NIO_Finalise(void);
|
extern void NIO_Finalise(void);
|
||||||
|
|
||||||
|
/* Function to check if HW timestamping is enabled on any interface */
|
||||||
|
extern int NIO_IsHwTsEnabled(void);
|
||||||
|
|
||||||
/* Function to obtain a socket for sending client packets */
|
/* Function to obtain a socket for sending client packets */
|
||||||
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr);
|
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr);
|
||||||
|
|
||||||
@@ -59,6 +63,9 @@ extern int NIO_IsServerSocketOpen(void);
|
|||||||
/* Function to check if client packets can be sent to a server */
|
/* Function to check if client packets can be sent to a server */
|
||||||
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
|
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
|
||||||
|
|
||||||
|
/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */
|
||||||
|
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction);
|
||||||
|
|
||||||
/* Function to transmit a packet */
|
/* Function to transmit a packet */
|
||||||
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||||
NTP_Local_Address *local_addr, int length, int process_tx);
|
NTP_Local_Address *local_addr, int length, int process_tx);
|
||||||
|
|||||||
258
ntp_io_linux.c
258
ntp_io_linux.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2016-2019
|
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2023
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "hwclock.h"
|
#include "hwclock.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
#include "ntp_io_linux.h"
|
#include "ntp_io_linux.h"
|
||||||
@@ -59,21 +60,22 @@ struct Interface {
|
|||||||
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
|
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
|
||||||
int l2_udp4_ntp_start;
|
int l2_udp4_ntp_start;
|
||||||
int l2_udp6_ntp_start;
|
int l2_udp6_ntp_start;
|
||||||
/* Precision of PHC readings */
|
|
||||||
double precision;
|
|
||||||
/* Compensation of errors in TX and RX timestamping */
|
/* Compensation of errors in TX and RX timestamping */
|
||||||
double tx_comp;
|
double tx_comp;
|
||||||
double rx_comp;
|
double rx_comp;
|
||||||
HCL_Instance clock;
|
HCL_Instance clock;
|
||||||
|
int maxpoll;
|
||||||
|
SCH_TimeoutID poll_timeout_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Number of PHC readings per HW clock sample */
|
/* Number of PHC readings per HW clock sample */
|
||||||
#define PHC_READINGS 10
|
#define PHC_READINGS 25
|
||||||
|
|
||||||
/* Minimum interval between PHC readings */
|
/* Minimum and maximum interval between PHC readings */
|
||||||
#define MIN_PHC_POLL -6
|
#define MIN_PHC_POLL -6
|
||||||
|
#define MAX_PHC_POLL 20
|
||||||
|
|
||||||
/* Maximum acceptable offset between HW and daemon/kernel timestamp */
|
/* Maximum acceptable offset between SW/HW and daemon timestamp */
|
||||||
#define MAX_TS_DELAY 1.0
|
#define MAX_TS_DELAY 1.0
|
||||||
|
|
||||||
/* Array of Interfaces */
|
/* Array of Interfaces */
|
||||||
@@ -86,19 +88,6 @@ static int ts_tx_flags;
|
|||||||
/* Flag indicating the socket options can't be changed in control messages */
|
/* Flag indicating the socket options can't be changed in control messages */
|
||||||
static int permanent_ts_options;
|
static int permanent_ts_options;
|
||||||
|
|
||||||
/* When sending client requests to a close and fast server, it is possible that
|
|
||||||
a response will be received before the HW transmit timestamp of the request
|
|
||||||
itself. To avoid processing of the response without the HW timestamp, we
|
|
||||||
monitor events returned by select() and suspend reading of packets from the
|
|
||||||
receive queue for up to 200 microseconds. As the requests are normally
|
|
||||||
separated by at least 200 milliseconds, it is sufficient to monitor and
|
|
||||||
suspend one socket at a time. */
|
|
||||||
static int monitored_socket;
|
|
||||||
static int suspended_socket;
|
|
||||||
static SCH_TimeoutID resume_timeout_id;
|
|
||||||
|
|
||||||
#define RESUME_TIMEOUT 200.0e-6
|
|
||||||
|
|
||||||
/* Unbound socket keeping the kernel RX timestamping permanently enabled
|
/* Unbound socket keeping the kernel RX timestamping permanently enabled
|
||||||
in order to avoid a race condition between receiving a server response
|
in order to avoid a race condition between receiving a server response
|
||||||
and the kernel actually starting to timestamp received packets after
|
and the kernel actually starting to timestamp received packets after
|
||||||
@@ -109,13 +98,17 @@ static int dummy_rxts_socket;
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void poll_phc(struct Interface *iface, struct timespec *now);
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_interface(CNF_HwTsInterface *conf_iface)
|
add_interface(CNF_HwTsInterface *conf_iface)
|
||||||
{
|
{
|
||||||
|
int sock_fd, if_index, minpoll, phc_fd, req_hwts_flags, rx_filter;
|
||||||
struct ethtool_ts_info ts_info;
|
struct ethtool_ts_info ts_info;
|
||||||
struct hwtstamp_config ts_config;
|
struct hwtstamp_config ts_config;
|
||||||
struct ifreq req;
|
struct ifreq req;
|
||||||
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
|
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
struct Interface *iface;
|
struct Interface *iface;
|
||||||
|
|
||||||
@@ -189,6 +182,14 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
|||||||
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case CNF_HWTS_RXFILTER_PTP:
|
||||||
|
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT))
|
||||||
|
rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
|
||||||
|
else if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_EVENT))
|
||||||
|
rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
||||||
|
else
|
||||||
|
rx_filter = HWTSTAMP_FILTER_NONE;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
rx_filter = HWTSTAMP_FILTER_ALL;
|
rx_filter = HWTSTAMP_FILTER_ALL;
|
||||||
break;
|
break;
|
||||||
@@ -219,7 +220,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
|||||||
|
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
|
|
||||||
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
|
phc_fd = SYS_Linux_OpenPHC(req.ifr_name, O_RDONLY);
|
||||||
if (phc_fd < 0)
|
if (phc_fd < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -236,12 +237,18 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
|||||||
iface->l2_udp4_ntp_start = 42;
|
iface->l2_udp4_ntp_start = 42;
|
||||||
iface->l2_udp6_ntp_start = 62;
|
iface->l2_udp6_ntp_start = 62;
|
||||||
|
|
||||||
iface->precision = conf_iface->precision;
|
|
||||||
iface->tx_comp = conf_iface->tx_comp;
|
iface->tx_comp = conf_iface->tx_comp;
|
||||||
iface->rx_comp = conf_iface->rx_comp;
|
iface->rx_comp = conf_iface->rx_comp;
|
||||||
|
|
||||||
|
minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
|
||||||
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
|
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
|
||||||
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
|
UTI_Log2ToDouble(minpoll), conf_iface->precision);
|
||||||
|
|
||||||
|
iface->maxpoll = CLAMP(minpoll, conf_iface->maxpoll, MAX_PHC_POLL);
|
||||||
|
|
||||||
|
/* Do not schedule the first poll timeout here! The argument (interface) can
|
||||||
|
move until all interfaces are added. Wait for the first HW timestamp. */
|
||||||
|
iface->poll_timeout_id = 0;
|
||||||
|
|
||||||
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
|
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
|
||||||
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
|
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
|
||||||
@@ -406,8 +413,6 @@ NIO_Linux_Initialise(void)
|
|||||||
/* Kernels before 4.7 ignore timestamping flags set in control messages */
|
/* Kernels before 4.7 ignore timestamping flags set in control messages */
|
||||||
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
|
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
|
||||||
|
|
||||||
monitored_socket = INVALID_SOCK_FD;
|
|
||||||
suspended_socket = INVALID_SOCK_FD;
|
|
||||||
dummy_rxts_socket = INVALID_SOCK_FD;
|
dummy_rxts_socket = INVALID_SOCK_FD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +429,7 @@ NIO_Linux_Finalise(void)
|
|||||||
|
|
||||||
for (i = 0; i < ARR_GetSize(interfaces); i++) {
|
for (i = 0; i < ARR_GetSize(interfaces); i++) {
|
||||||
iface = ARR_GetElement(interfaces, i);
|
iface = ARR_GetElement(interfaces, i);
|
||||||
|
SCH_RemoveTimeout(iface->poll_timeout_id);
|
||||||
HCL_DestroyInstance(iface->clock);
|
HCL_DestroyInstance(iface->clock);
|
||||||
close(iface->phc_fd);
|
close(iface->phc_fd);
|
||||||
}
|
}
|
||||||
@@ -433,6 +439,14 @@ NIO_Linux_Finalise(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NIO_Linux_IsHwTsEnabled(void)
|
||||||
|
{
|
||||||
|
return ARR_GetSize(interfaces) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
|
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
|
||||||
{
|
{
|
||||||
@@ -466,73 +480,6 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
|
||||||
resume_socket(int sock_fd)
|
|
||||||
{
|
|
||||||
if (monitored_socket == sock_fd)
|
|
||||||
monitored_socket = INVALID_SOCK_FD;
|
|
||||||
|
|
||||||
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
|
|
||||||
return;
|
|
||||||
|
|
||||||
suspended_socket = INVALID_SOCK_FD;
|
|
||||||
|
|
||||||
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
|
|
||||||
|
|
||||||
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
|
|
||||||
resume_timeout_id ? "before" : "on", sock_fd);
|
|
||||||
|
|
||||||
if (resume_timeout_id) {
|
|
||||||
SCH_RemoveTimeout(resume_timeout_id);
|
|
||||||
resume_timeout_id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
resume_timeout(void *arg)
|
|
||||||
{
|
|
||||||
resume_timeout_id = 0;
|
|
||||||
resume_socket(suspended_socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
suspend_socket(int sock_fd)
|
|
||||||
{
|
|
||||||
resume_socket(suspended_socket);
|
|
||||||
|
|
||||||
suspended_socket = sock_fd;
|
|
||||||
|
|
||||||
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
|
|
||||||
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
|
|
||||||
|
|
||||||
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
int
|
|
||||||
NIO_Linux_ProcessEvent(int sock_fd, int event)
|
|
||||||
{
|
|
||||||
if (sock_fd != monitored_socket)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (event == SCH_FILE_INPUT) {
|
|
||||||
suspend_socket(monitored_socket);
|
|
||||||
monitored_socket = INVALID_SOCK_FD;
|
|
||||||
|
|
||||||
/* Don't process the message yet */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static struct Interface *
|
static struct Interface *
|
||||||
get_interface(int if_index)
|
get_interface(int if_index)
|
||||||
{
|
{
|
||||||
@@ -552,26 +499,71 @@ get_interface(int if_index)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
poll_timeout(void *arg)
|
||||||
|
{
|
||||||
|
struct Interface *iface = arg;
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
iface->poll_timeout_id = 0;
|
||||||
|
|
||||||
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
poll_phc(iface, &now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
poll_phc(struct Interface *iface, struct timespec *now)
|
||||||
|
{
|
||||||
|
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
|
||||||
|
struct timespec phc_readings[PHC_READINGS][3];
|
||||||
|
double phc_err, local_err, interval;
|
||||||
|
int n_readings, quality;
|
||||||
|
|
||||||
|
if (!HCL_NeedsNewSample(iface->clock, now))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DEBUG_LOG("Polling PHC on %s%s",
|
||||||
|
iface->name, iface->poll_timeout_id != 0 ? " before timeout" : "");
|
||||||
|
|
||||||
|
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
|
||||||
|
&iface->phc_mode, PHC_READINGS, phc_readings);
|
||||||
|
|
||||||
|
/* Add timeout for the next poll in case no HW timestamp will be captured
|
||||||
|
between the minpoll and maxpoll. Separate reading of different PHCs to
|
||||||
|
avoid long intervals between handling I/O events. */
|
||||||
|
SCH_RemoveTimeout(iface->poll_timeout_id);
|
||||||
|
interval = UTI_Log2ToDouble(iface->maxpoll);
|
||||||
|
iface->poll_timeout_id = SCH_AddTimeoutInClass(interval, interval /
|
||||||
|
ARR_GetSize(interfaces) / 4, 0.1,
|
||||||
|
SCH_PhcPollClass, poll_timeout, iface);
|
||||||
|
|
||||||
|
if (n_readings <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
|
||||||
|
&sample_phc_ts, &sample_sys_ts, &phc_err, &quality) ||
|
||||||
|
quality <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
||||||
|
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, phc_err + local_err);
|
||||||
|
|
||||||
|
update_interface_speed(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
||||||
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
|
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
|
||||||
int l2_length)
|
int l2_length)
|
||||||
{
|
{
|
||||||
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
|
double rx_correction = 0.0, ts_delay, local_err;
|
||||||
double rx_correction, ts_delay, phc_err, local_err;
|
struct timespec ts;
|
||||||
|
|
||||||
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
|
poll_phc(iface, &local_ts->ts);
|
||||||
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
|
|
||||||
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
|
|
||||||
&phc_err))
|
|
||||||
return;
|
|
||||||
|
|
||||||
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
|
||||||
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
|
|
||||||
phc_err + local_err);
|
|
||||||
|
|
||||||
update_interface_speed(iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We need to transpose RX timestamps as hardware timestamps are normally
|
/* We need to transpose RX timestamps as hardware timestamps are normally
|
||||||
preamble timestamps and RX timestamps in NTP are supposed to be trailer
|
preamble timestamps and RX timestamps in NTP are supposed to be trailer
|
||||||
@@ -609,6 +601,32 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
|||||||
local_ts->ts = ts;
|
local_ts->ts = ts;
|
||||||
local_ts->err = local_err;
|
local_ts->err = local_err;
|
||||||
local_ts->source = NTP_TS_HARDWARE;
|
local_ts->source = NTP_TS_HARDWARE;
|
||||||
|
local_ts->rx_duration = rx_correction;
|
||||||
|
/* Network correction needs to include the RX duration to avoid
|
||||||
|
asymmetric correction with asymmetric link speeds */
|
||||||
|
local_ts->net_correction = rx_correction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
process_sw_timestamp(struct timespec *sw_ts, NTP_Local_Timestamp *local_ts)
|
||||||
|
{
|
||||||
|
double ts_delay, local_err;
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
LCL_CookTime(sw_ts, &ts, &local_err);
|
||||||
|
|
||||||
|
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
|
||||||
|
|
||||||
|
if (fabs(ts_delay) > MAX_TS_DELAY) {
|
||||||
|
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_ts->ts = ts;
|
||||||
|
local_ts->err = local_err;
|
||||||
|
local_ts->source = NTP_TS_KERNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -710,6 +728,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
|||||||
{
|
{
|
||||||
struct Interface *iface;
|
struct Interface *iface;
|
||||||
int is_tx, ts_if_index, l2_length;
|
int is_tx, ts_if_index, l2_length;
|
||||||
|
double c = 0.0;
|
||||||
|
|
||||||
is_tx = event == SCH_FILE_EXCEPTION;
|
is_tx = event == SCH_FILE_EXCEPTION;
|
||||||
iface = NULL;
|
iface = NULL;
|
||||||
@@ -727,17 +746,11 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
|||||||
} else {
|
} else {
|
||||||
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
|
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a HW transmit timestamp was received, resume processing
|
|
||||||
of non-error messages on this socket */
|
|
||||||
if (is_tx)
|
|
||||||
resume_socket(local_addr->sock_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
|
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
|
||||||
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
|
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
|
||||||
LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err);
|
process_sw_timestamp(&message->timestamp.kernel, local_ts);
|
||||||
local_ts->source = NTP_TS_KERNEL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the kernel is slow with enabling RX timestamping, open a dummy
|
/* If the kernel is slow with enabling RX timestamping, open a dummy
|
||||||
@@ -776,7 +789,10 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message->length < NTP_HEADER_LENGTH)
|
if (!NIO_UnwrapMessage(message, local_addr->sock_fd, &c))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
|
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
|
||||||
@@ -792,23 +808,9 @@ NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
|
|||||||
if (!ts_flags)
|
if (!ts_flags)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* If a HW transmit timestamp is requested on a client socket, monitor
|
|
||||||
events on the socket in order to avoid processing of a fast response
|
|
||||||
without the HW timestamp of the request */
|
|
||||||
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
|
|
||||||
monitored_socket = sock_fd;
|
|
||||||
|
|
||||||
/* Check if TX timestamping is disabled on this socket */
|
/* Check if TX timestamping is disabled on this socket */
|
||||||
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
|
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
message->timestamp.tx_flags = ts_tx_flags;
|
message->timestamp.tx_flags = ts_tx_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
void
|
|
||||||
NIO_Linux_NotifySocketClosing(int sock_fd)
|
|
||||||
{
|
|
||||||
resume_socket(sock_fd);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -33,15 +33,13 @@ extern void NIO_Linux_Initialise(void);
|
|||||||
|
|
||||||
extern void NIO_Linux_Finalise(void);
|
extern void NIO_Linux_Finalise(void);
|
||||||
|
|
||||||
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
|
extern int NIO_Linux_IsHwTsEnabled(void);
|
||||||
|
|
||||||
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
|
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
|
||||||
|
|
||||||
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
|
||||||
NTP_Local_Timestamp *local_ts, int event);
|
NTP_Local_Timestamp *local_ts, int event);
|
||||||
|
|
||||||
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
|
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
|
||||||
|
|
||||||
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
13
ntp_signd.c
13
ntp_signd.c
@@ -99,6 +99,9 @@ static int sock_fd;
|
|||||||
/* Flag indicating if the MS-SNTP authentication is enabled */
|
/* Flag indicating if the MS-SNTP authentication is enabled */
|
||||||
static int enabled;
|
static int enabled;
|
||||||
|
|
||||||
|
/* Flag limiting logging of connection error messages */
|
||||||
|
static int logged_connection_error;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void read_write_socket(int sock_fd, int event, void *anything);
|
static void read_write_socket(int sock_fd, int event, void *anything);
|
||||||
@@ -134,6 +137,14 @@ open_socket(void)
|
|||||||
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
|
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
|
||||||
if (sock_fd < 0) {
|
if (sock_fd < 0) {
|
||||||
sock_fd = INVALID_SOCK_FD;
|
sock_fd = INVALID_SOCK_FD;
|
||||||
|
|
||||||
|
/* Log an error only once before a successful exchange to avoid
|
||||||
|
flooding the system log */
|
||||||
|
if (!logged_connection_error) {
|
||||||
|
LOG(LOGS_ERR, "Could not connect to signd socket %s : %s", path, strerror(errno));
|
||||||
|
logged_connection_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +171,8 @@ process_response(SignInstance *inst)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logged_connection_error = 0;
|
||||||
|
|
||||||
/* Check if the file descriptor is still valid */
|
/* Check if the file descriptor is still valid */
|
||||||
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
|
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
|
||||||
DEBUG_LOG("Invalid NTP socket");
|
DEBUG_LOG("Invalid NTP socket");
|
||||||
|
|||||||
280
ntp_sources.c
280
ntp_sources.c
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020
|
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "sysincl.h"
|
#include "sysincl.h"
|
||||||
|
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
|
#include "conf.h"
|
||||||
#include "ntp_sources.h"
|
#include "ntp_sources.h"
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "ntp_io.h"
|
#include "ntp_io.h"
|
||||||
@@ -58,12 +59,17 @@ typedef struct {
|
|||||||
NCR_Instance data; /* Data for the protocol engine for this source */
|
NCR_Instance data; /* Data for the protocol engine for this source */
|
||||||
char *name; /* Name of the source as it was specified
|
char *name; /* Name of the source as it was specified
|
||||||
(may be an IP address) */
|
(may be an IP address) */
|
||||||
|
IPAddr resolved_addr; /* Address resolved from the name, which can be
|
||||||
|
different from remote_addr (e.g. NTS-KE) */
|
||||||
|
int family; /* IP family of acceptable resolved addresses
|
||||||
|
(IPADDR_UNSPEC if any) */
|
||||||
int pool_id; /* ID of the pool from which was this source
|
int pool_id; /* ID of the pool from which was this source
|
||||||
added or INVALID_POOL */
|
added or INVALID_POOL */
|
||||||
int tentative; /* Flag indicating there was no valid response
|
int tentative; /* Flag indicating there was no valid response
|
||||||
received from the source yet */
|
received from the source yet */
|
||||||
uint32_t conf_id; /* Configuration ID, which can be shared with
|
uint32_t conf_id; /* Configuration ID, which can be shared with
|
||||||
different sources in case of a pool */
|
different sources in case of a pool */
|
||||||
|
double last_resolving; /* Time of last name resolving (monotonic) */
|
||||||
} SourceRecord;
|
} SourceRecord;
|
||||||
|
|
||||||
/* Hash table of SourceRecord, its size is a power of two and it's never
|
/* Hash table of SourceRecord, its size is a power of two and it's never
|
||||||
@@ -94,8 +100,13 @@ struct UnresolvedSource {
|
|||||||
int pool_id;
|
int pool_id;
|
||||||
/* Name to be resolved */
|
/* Name to be resolved */
|
||||||
char *name;
|
char *name;
|
||||||
|
/* Address family to filter resolved addresses */
|
||||||
|
int family;
|
||||||
/* Flag indicating addresses should be used in a random order */
|
/* Flag indicating addresses should be used in a random order */
|
||||||
int random_order;
|
int random_order;
|
||||||
|
/* Flag indicating current address should be replaced only if it is
|
||||||
|
no longer returned by the resolver */
|
||||||
|
int refreshment;
|
||||||
/* Next unresolved source in the list */
|
/* Next unresolved source in the list */
|
||||||
struct UnresolvedSource *next;
|
struct UnresolvedSource *next;
|
||||||
};
|
};
|
||||||
@@ -103,7 +114,7 @@ struct UnresolvedSource {
|
|||||||
#define RESOLVE_INTERVAL_UNIT 7
|
#define RESOLVE_INTERVAL_UNIT 7
|
||||||
#define MIN_RESOLVE_INTERVAL 2
|
#define MIN_RESOLVE_INTERVAL 2
|
||||||
#define MAX_RESOLVE_INTERVAL 9
|
#define MAX_RESOLVE_INTERVAL 9
|
||||||
#define MIN_REPLACEMENT_INTERVAL 8
|
#define MAX_REPLACEMENT_INTERVAL 9
|
||||||
|
|
||||||
static struct UnresolvedSource *unresolved_sources = NULL;
|
static struct UnresolvedSource *unresolved_sources = NULL;
|
||||||
static int resolving_interval = 0;
|
static int resolving_interval = 0;
|
||||||
@@ -184,6 +195,7 @@ void
|
|||||||
NSR_Initialise(void)
|
NSR_Initialise(void)
|
||||||
{
|
{
|
||||||
n_sources = 0;
|
n_sources = 0;
|
||||||
|
resolving_id = 0;
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
|
|
||||||
records = ARR_CreateInstance(sizeof (SourceRecord));
|
records = ARR_CreateInstance(sizeof (SourceRecord));
|
||||||
@@ -206,8 +218,15 @@ NSR_Finalise(void)
|
|||||||
ARR_DestroyInstance(records);
|
ARR_DestroyInstance(records);
|
||||||
ARR_DestroyInstance(pools);
|
ARR_DestroyInstance(pools);
|
||||||
|
|
||||||
|
SCH_RemoveTimeout(resolving_id);
|
||||||
|
|
||||||
|
/* Leave the unresolved sources allocated if the async resolver is running
|
||||||
|
to avoid reading the name from freed memory. The handler will not be
|
||||||
|
called as the scheduler should no longer be running at this point. */
|
||||||
|
if (!resolving_source) {
|
||||||
while (unresolved_sources)
|
while (unresolved_sources)
|
||||||
remove_unresolved_source(unresolved_sources);
|
remove_unresolved_source(unresolved_sources);
|
||||||
|
}
|
||||||
|
|
||||||
initialised = 0;
|
initialised = 0;
|
||||||
}
|
}
|
||||||
@@ -317,9 +336,34 @@ rehash_records(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_source(SourceRecord *record, int addition, int once_per_pool)
|
||||||
|
{
|
||||||
|
int pool, log_addr;
|
||||||
|
char *ip_str;
|
||||||
|
|
||||||
|
if (once_per_pool && record->pool_id != INVALID_POOL) {
|
||||||
|
if (get_pool(record->pool_id)->sources > 1)
|
||||||
|
return;
|
||||||
|
pool = 1;
|
||||||
|
log_addr = 0;
|
||||||
|
} else {
|
||||||
|
ip_str = UTI_IPToString(&record->remote_addr->ip_addr);
|
||||||
|
pool = 0;
|
||||||
|
log_addr = strcmp(record->name, ip_str) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(LOG_GetContextSeverity(LOGC_Command | LOGC_SourceFile), "%s %s %s%s%s%s",
|
||||||
|
addition ? "Added" : "Removed", pool ? "pool" : "source",
|
||||||
|
log_addr ? ip_str : record->name,
|
||||||
|
log_addr ? " (" : "", log_addr ? record->name : "", log_addr ? ")" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
/* Procedure to add a new source */
|
/* Procedure to add a new source */
|
||||||
static NSR_Status
|
static NSR_Status
|
||||||
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
add_source(NTP_Remote_Address *remote_addr, char *name, int family, NTP_Source_Type type,
|
||||||
SourceParameters *params, int pool_id, uint32_t conf_id)
|
SourceParameters *params, int pool_id, uint32_t conf_id)
|
||||||
{
|
{
|
||||||
SourceRecord *record;
|
SourceRecord *record;
|
||||||
@@ -353,13 +397,15 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
|||||||
record_lock = 1;
|
record_lock = 1;
|
||||||
|
|
||||||
record = get_record(slot);
|
record = get_record(slot);
|
||||||
assert(!name || !UTI_IsStringIP(name));
|
|
||||||
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
|
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
|
||||||
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
|
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
|
||||||
record->remote_addr = NCR_GetRemoteAddress(record->data);
|
record->remote_addr = NCR_GetRemoteAddress(record->data);
|
||||||
|
record->resolved_addr = remote_addr->ip_addr;
|
||||||
|
record->family = family;
|
||||||
record->pool_id = pool_id;
|
record->pool_id = pool_id;
|
||||||
record->tentative = 1;
|
record->tentative = 1;
|
||||||
record->conf_id = conf_id;
|
record->conf_id = conf_id;
|
||||||
|
record->last_resolving = SCH_GetLastEventMonoTime();
|
||||||
|
|
||||||
record_lock = 0;
|
record_lock = 0;
|
||||||
|
|
||||||
@@ -372,6 +418,8 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
|||||||
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
|
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
|
||||||
NCR_StartInstance(record->data);
|
NCR_StartInstance(record->data);
|
||||||
|
|
||||||
|
log_source(record, 1, 1);
|
||||||
|
|
||||||
/* The new instance is allowed to change its address immediately */
|
/* The new instance is allowed to change its address immediately */
|
||||||
handle_saved_address_update();
|
handle_saved_address_update();
|
||||||
|
|
||||||
@@ -406,10 +454,11 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
|
|||||||
|
|
||||||
record = get_record(slot1);
|
record = get_record(slot1);
|
||||||
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
|
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
|
||||||
|
if (replacement)
|
||||||
|
record->resolved_addr = new_addr->ip_addr;
|
||||||
|
|
||||||
if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
|
BRIEF_ASSERT(record->remote_addr == NCR_GetRemoteAddress(record->data) &&
|
||||||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
|
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) == 0);
|
||||||
assert(0);
|
|
||||||
|
|
||||||
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
|
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
|
||||||
if (auto_start_sources)
|
if (auto_start_sources)
|
||||||
@@ -489,7 +538,21 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
|
|||||||
NTP_Remote_Address old_addr, new_addr;
|
NTP_Remote_Address old_addr, new_addr;
|
||||||
SourceRecord *record;
|
SourceRecord *record;
|
||||||
unsigned short first = 0;
|
unsigned short first = 0;
|
||||||
int i, j;
|
int i, j, slot;
|
||||||
|
|
||||||
|
/* Keep using the current address if it is being refreshed and it is
|
||||||
|
still included in the resolved addresses */
|
||||||
|
if (us->refreshment) {
|
||||||
|
assert(us->pool_id == INVALID_POOL);
|
||||||
|
|
||||||
|
for (i = 0; i < n_addrs; i++) {
|
||||||
|
if (find_slot2(&us->address, &slot) == 2 &&
|
||||||
|
UTI_CompareIPs(&get_record(slot)->resolved_addr, &ip_addrs[i], NULL) == 0) {
|
||||||
|
DEBUG_LOG("%s still fresh", UTI_IPToString(&us->address.ip_addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (us->random_order)
|
if (us->random_order)
|
||||||
UTI_GetRandomBytes(&first, sizeof (first));
|
UTI_GetRandomBytes(&first, sizeof (first));
|
||||||
@@ -499,6 +562,10 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
|
|||||||
|
|
||||||
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
|
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
|
||||||
|
|
||||||
|
/* Skip addresses not from the requested family */
|
||||||
|
if (us->family != IPADDR_UNSPEC && us->family != new_addr.ip_addr.family)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (us->pool_id != INVALID_POOL) {
|
if (us->pool_id != INVALID_POOL) {
|
||||||
/* In the pool resolving mode, try to replace a source from
|
/* In the pool resolving mode, try to replace a source from
|
||||||
the pool which does not have a real address yet */
|
the pool which does not have a real address yet */
|
||||||
@@ -576,13 +643,16 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
|
|||||||
next = us->next;
|
next = us->next;
|
||||||
|
|
||||||
/* Don't repeat the resolving if it (permanently) failed, it was a
|
/* Don't repeat the resolving if it (permanently) failed, it was a
|
||||||
replacement of a real address, or all addresses are already resolved */
|
replacement of a real address, a refreshment, or all addresses are
|
||||||
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
|
already resolved */
|
||||||
|
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) ||
|
||||||
|
us->refreshment || is_resolved(us))
|
||||||
remove_unresolved_source(us);
|
remove_unresolved_source(us);
|
||||||
|
|
||||||
/* If a restart was requested and this was the last source in the list,
|
/* If a restart was requested and this was the last source in the list,
|
||||||
start with the first source again (if there still is one) */
|
start with the first source again (if there still is one) */
|
||||||
if (!next && resolving_restart) {
|
if (!next && resolving_restart) {
|
||||||
|
DEBUG_LOG("Restarting");
|
||||||
next = unresolved_sources;
|
next = unresolved_sources;
|
||||||
resolving_restart = 0;
|
resolving_restart = 0;
|
||||||
}
|
}
|
||||||
@@ -647,11 +717,15 @@ static void
|
|||||||
append_unresolved_source(struct UnresolvedSource *us)
|
append_unresolved_source(struct UnresolvedSource *us)
|
||||||
{
|
{
|
||||||
struct UnresolvedSource **i;
|
struct UnresolvedSource **i;
|
||||||
|
int n;
|
||||||
|
|
||||||
for (i = &unresolved_sources; *i; i = &(*i)->next)
|
for (i = &unresolved_sources, n = 0; *i; i = &(*i)->next, n++)
|
||||||
;
|
;
|
||||||
*i = us;
|
*i = us;
|
||||||
us->next = NULL;
|
us->next = NULL;
|
||||||
|
|
||||||
|
DEBUG_LOG("Added unresolved source #%d pool_id=%d random=%d refresh=%d",
|
||||||
|
n + 1, us->pool_id, us->random_order, us->refreshment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -698,38 +772,57 @@ static int get_unused_pool_id(void)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
NSR_Status
|
static uint32_t
|
||||||
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
get_next_conf_id(uint32_t *conf_id)
|
||||||
SourceParameters *params, uint32_t *conf_id)
|
|
||||||
{
|
{
|
||||||
NSR_Status s;
|
SourceRecord *record;
|
||||||
|
unsigned int i;
|
||||||
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
|
|
||||||
if (s != NSR_Success)
|
|
||||||
return s;
|
|
||||||
|
|
||||||
|
again:
|
||||||
last_conf_id++;
|
last_conf_id++;
|
||||||
|
|
||||||
|
/* Make sure the ID is not already used (after 32-bit wraparound) */
|
||||||
|
for (i = 0; i < ARR_GetSize(records); i++) {
|
||||||
|
record = get_record(i);
|
||||||
|
if (record->remote_addr && record->conf_id == last_conf_id)
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
if (conf_id)
|
if (conf_id)
|
||||||
*conf_id = last_conf_id;
|
*conf_id = last_conf_id;
|
||||||
|
|
||||||
return s;
|
return last_conf_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
NSR_Status
|
NSR_Status
|
||||||
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||||
|
SourceParameters *params, uint32_t *conf_id)
|
||||||
|
{
|
||||||
|
return add_source(remote_addr, NULL, IPADDR_UNSPEC, type, params, INVALID_POOL,
|
||||||
|
get_next_conf_id(conf_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
NSR_Status
|
||||||
|
NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
|
||||||
SourceParameters *params, uint32_t *conf_id)
|
SourceParameters *params, uint32_t *conf_id)
|
||||||
{
|
{
|
||||||
struct UnresolvedSource *us;
|
struct UnresolvedSource *us;
|
||||||
struct SourcePool *sp;
|
struct SourcePool *sp;
|
||||||
NTP_Remote_Address remote_addr;
|
NTP_Remote_Address remote_addr;
|
||||||
int i, new_sources, pool_id;
|
int i, new_sources, pool_id;
|
||||||
|
uint32_t cid;
|
||||||
|
|
||||||
/* If the name is an IP address, add the source with the address directly */
|
/* If the name is an IP address, add the source with the address directly */
|
||||||
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
|
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
|
||||||
remote_addr.port = port;
|
remote_addr.port = port;
|
||||||
return NSR_AddSource(&remote_addr, type, params, conf_id);
|
if (family != IPADDR_UNSPEC && family != remote_addr.ip_addr.family)
|
||||||
|
return NSR_InvalidAF;
|
||||||
|
return add_source(&remote_addr, name, IPADDR_UNSPEC, type, params, INVALID_POOL,
|
||||||
|
get_next_conf_id(conf_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the name is at least printable and has no spaces */
|
/* Make sure the name is at least printable and has no spaces */
|
||||||
@@ -740,7 +833,9 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
|||||||
|
|
||||||
us = MallocNew(struct UnresolvedSource);
|
us = MallocNew(struct UnresolvedSource);
|
||||||
us->name = Strdup(name);
|
us->name = Strdup(name);
|
||||||
|
us->family = family;
|
||||||
us->random_order = 0;
|
us->random_order = 0;
|
||||||
|
us->refreshment = 0;
|
||||||
|
|
||||||
remote_addr.ip_addr.family = IPADDR_ID;
|
remote_addr.ip_addr.family = IPADDR_ID;
|
||||||
remote_addr.ip_addr.addr.id = ++last_address_id;
|
remote_addr.ip_addr.addr.id = ++last_address_id;
|
||||||
@@ -770,14 +865,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
|||||||
|
|
||||||
append_unresolved_source(us);
|
append_unresolved_source(us);
|
||||||
|
|
||||||
last_conf_id++;
|
cid = get_next_conf_id(conf_id);
|
||||||
if (conf_id)
|
|
||||||
*conf_id = last_conf_id;
|
|
||||||
|
|
||||||
for (i = 0; i < new_sources; i++) {
|
for (i = 0; i < new_sources; i++) {
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
remote_addr.ip_addr.addr.id = ++last_address_id;
|
remote_addr.ip_addr.addr.id = ++last_address_id;
|
||||||
if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
|
if (add_source(&remote_addr, name, family, type, params, us->pool_id, cid) != NSR_Success)
|
||||||
return NSR_TooManySources;
|
return NSR_TooManySources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,6 +879,31 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
const char *
|
||||||
|
NSR_StatusToString(NSR_Status status)
|
||||||
|
{
|
||||||
|
switch (status) {
|
||||||
|
case NSR_Success:
|
||||||
|
return "Success";
|
||||||
|
case NSR_NoSuchSource:
|
||||||
|
return "No such source";
|
||||||
|
case NSR_AlreadyInUse:
|
||||||
|
return "Already in use";
|
||||||
|
case NSR_TooManySources:
|
||||||
|
return "Too many sources";
|
||||||
|
case NSR_InvalidAF:
|
||||||
|
return "Invalid address";
|
||||||
|
case NSR_InvalidName:
|
||||||
|
return "Invalid name";
|
||||||
|
case NSR_UnresolvedName:
|
||||||
|
return "Unresolved name";
|
||||||
|
default:
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
|
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
|
||||||
{
|
{
|
||||||
@@ -881,6 +999,7 @@ NSR_RemoveSource(IPAddr *address)
|
|||||||
if (find_slot(address, &slot) == 0)
|
if (find_slot(address, &slot) == 0)
|
||||||
return NSR_NoSuchSource;
|
return NSR_NoSuchSource;
|
||||||
|
|
||||||
|
log_source(get_record(slot), 0, 0);
|
||||||
clean_source_record(get_record(slot));
|
clean_source_record(get_record(slot));
|
||||||
|
|
||||||
/* Rehash the table to make sure there are no broken probe sequences.
|
/* Rehash the table to make sure there are no broken probe sequences.
|
||||||
@@ -903,6 +1022,7 @@ NSR_RemoveSourcesById(uint32_t conf_id)
|
|||||||
record = get_record(i);
|
record = get_record(i);
|
||||||
if (!record->remote_addr || record->conf_id != conf_id)
|
if (!record->remote_addr || record->conf_id != conf_id)
|
||||||
continue;
|
continue;
|
||||||
|
log_source(record, 0, 1);
|
||||||
clean_source_record(record);
|
clean_source_record(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,24 +1050,31 @@ NSR_RemoveAllSources(void)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
resolve_source_replacement(SourceRecord *record)
|
resolve_source_replacement(SourceRecord *record, int refreshment)
|
||||||
{
|
{
|
||||||
struct UnresolvedSource *us;
|
struct UnresolvedSource *us;
|
||||||
|
|
||||||
DEBUG_LOG("trying to replace %s (%s)",
|
DEBUG_LOG("%s %s (%s)", refreshment ? "refreshing" : "trying to replace",
|
||||||
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
|
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
|
||||||
|
|
||||||
|
record->last_resolving = SCH_GetLastEventMonoTime();
|
||||||
|
|
||||||
us = MallocNew(struct UnresolvedSource);
|
us = MallocNew(struct UnresolvedSource);
|
||||||
us->name = Strdup(record->name);
|
us->name = Strdup(record->name);
|
||||||
/* If there never was a valid reply from this source (e.g. it was a bad
|
us->family = record->family;
|
||||||
replacement), ignore the order of addresses from the resolver to not get
|
/* Ignore the order of addresses from the resolver to not get
|
||||||
stuck to a pair of addresses if the order doesn't change, or a group of
|
stuck with a pair of unreachable or otherwise unusable servers
|
||||||
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
|
(e.g. falsetickers) in case the order doesn't change, or a group
|
||||||
us->random_order = record->tentative;
|
of servers if they are ordered by IP family */
|
||||||
|
us->random_order = 1;
|
||||||
|
us->refreshment = refreshment;
|
||||||
us->pool_id = INVALID_POOL;
|
us->pool_id = INVALID_POOL;
|
||||||
us->address = *record->remote_addr;
|
us->address = *record->remote_addr;
|
||||||
|
|
||||||
append_unresolved_source(us);
|
append_unresolved_source(us);
|
||||||
|
|
||||||
|
/* Don't restart resolving round if already running */
|
||||||
|
if (!resolving_source)
|
||||||
NSR_ResolveSources();
|
NSR_ResolveSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -956,11 +1083,11 @@ resolve_source_replacement(SourceRecord *record)
|
|||||||
void
|
void
|
||||||
NSR_HandleBadSource(IPAddr *address)
|
NSR_HandleBadSource(IPAddr *address)
|
||||||
{
|
{
|
||||||
static struct timespec last_replacement;
|
static double next_replacement = 0.0;
|
||||||
struct timespec now;
|
|
||||||
SourceRecord *record;
|
SourceRecord *record;
|
||||||
IPAddr ip_addr;
|
IPAddr ip_addr;
|
||||||
double diff;
|
uint32_t rnd;
|
||||||
|
double now;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
if (!find_slot(address, &slot))
|
if (!find_slot(address, &slot))
|
||||||
@@ -975,15 +1102,56 @@ NSR_HandleBadSource(IPAddr *address)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* Don't resolve names too frequently */
|
/* Don't resolve names too frequently */
|
||||||
SCH_GetLastEventTime(NULL, NULL, &now);
|
now = SCH_GetLastEventMonoTime();
|
||||||
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
|
if (now < next_replacement) {
|
||||||
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
|
|
||||||
DEBUG_LOG("replacement postponed");
|
DEBUG_LOG("replacement postponed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
last_replacement = now;
|
|
||||||
|
|
||||||
resolve_source_replacement(record);
|
UTI_GetRandomBytes(&rnd, sizeof (rnd));
|
||||||
|
next_replacement = now + ((double)rnd / (uint32_t)-1) *
|
||||||
|
(RESOLVE_INTERVAL_UNIT * (1 << MAX_REPLACEMENT_INTERVAL));
|
||||||
|
|
||||||
|
resolve_source_replacement(record, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_refresh_source(void)
|
||||||
|
{
|
||||||
|
static double last_refreshment = 0.0;
|
||||||
|
SourceRecord *record, *oldest_record;
|
||||||
|
int i, min_interval;
|
||||||
|
double now;
|
||||||
|
|
||||||
|
min_interval = CNF_GetRefresh();
|
||||||
|
|
||||||
|
now = SCH_GetLastEventMonoTime();
|
||||||
|
if (min_interval <= 0 || now < last_refreshment + min_interval)
|
||||||
|
return;
|
||||||
|
|
||||||
|
last_refreshment = now;
|
||||||
|
|
||||||
|
for (i = 0, oldest_record = NULL; i < ARR_GetSize(records); i++) {
|
||||||
|
record = get_record(i);
|
||||||
|
if (!record->remote_addr || UTI_IsStringIP(record->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!oldest_record || oldest_record->last_resolving > record->last_resolving)
|
||||||
|
oldest_record = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!oldest_record)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Check if the name wasn't already resolved in the last interval */
|
||||||
|
if (now < oldest_record->last_resolving + min_interval) {
|
||||||
|
last_refreshment = oldest_record->last_resolving;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_source_replacement(oldest_record, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -999,7 +1167,7 @@ NSR_RefreshAddresses(void)
|
|||||||
if (!record->remote_addr)
|
if (!record->remote_addr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
resolve_source_replacement(record);
|
resolve_source_replacement(record, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1100,8 +1268,10 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
|||||||
|
|
||||||
assert(initialised);
|
assert(initialised);
|
||||||
|
|
||||||
/* Must match IP address AND port number */
|
/* Avoid unnecessary lookup if the packet cannot be a response from our
|
||||||
if (find_slot2(remote_addr, &slot) == 2) {
|
source. Otherwise, it must match both IP address and port number. */
|
||||||
|
if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
|
||||||
|
find_slot2(remote_addr, &slot) == 2) {
|
||||||
record = get_record(slot);
|
record = get_record(slot);
|
||||||
|
|
||||||
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
|
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
|
||||||
@@ -1123,6 +1293,8 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
|||||||
remove_pool_sources(record->pool_id, 1, 0);
|
remove_pool_sources(record->pool_id, 1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybe_refresh_source();
|
||||||
} else {
|
} else {
|
||||||
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
|
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
|
||||||
}
|
}
|
||||||
@@ -1137,8 +1309,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
|||||||
SourceRecord *record;
|
SourceRecord *record;
|
||||||
int slot;
|
int slot;
|
||||||
|
|
||||||
/* Must match IP address AND port number */
|
/* Avoid unnecessary lookup if the packet cannot be a request to our
|
||||||
if (find_slot2(remote_addr, &slot) == 2) {
|
source. Otherwise, it must match both IP address and port number. */
|
||||||
|
if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
|
||||||
|
find_slot2(remote_addr, &slot) == 2) {
|
||||||
record = get_record(slot);
|
record = get_record(slot);
|
||||||
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
|
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
|
||||||
} else {
|
} else {
|
||||||
@@ -1296,6 +1470,20 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NSR_ModifyOffset(IPAddr *address, double new_offset)
|
||||||
|
{
|
||||||
|
int slot;
|
||||||
|
|
||||||
|
if (!find_slot(address, &slot))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
NCR_ModifyOffset(get_record(slot)->data, new_offset);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
|
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,11 +55,16 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
|
|||||||
|
|
||||||
/* Procedure to add a new server, peer source, or pool of servers specified by
|
/* Procedure to add a new server, peer source, or pool of servers specified by
|
||||||
name instead of address. The name is resolved in exponentially increasing
|
name instead of address. The name is resolved in exponentially increasing
|
||||||
intervals until it succeeds or fails with a non-temporary error. If the
|
intervals until it succeeds or fails with a non-temporary error. The
|
||||||
name is an address, it is equivalent to NSR_AddSource(). */
|
specified family filters resolved addresses. If the name is an address
|
||||||
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
and its family does not conflict with the specified family, it is equivalent
|
||||||
|
to NSR_AddSource(). */
|
||||||
|
extern NSR_Status NSR_AddSourceByName(char *name, int family, int port, int pool,
|
||||||
|
NTP_Source_Type type,
|
||||||
SourceParameters *params, uint32_t *conf_id);
|
SourceParameters *params, uint32_t *conf_id);
|
||||||
|
|
||||||
|
extern const char *NSR_StatusToString(NSR_Status status);
|
||||||
|
|
||||||
/* Function type for handlers to be called back when an attempt
|
/* Function type for handlers to be called back when an attempt
|
||||||
* (possibly unsuccessful) to resolve unresolved sources ends */
|
* (possibly unsuccessful) to resolve unresolved sources ends */
|
||||||
typedef void (*NSR_SourceResolvingEndHandler)(void);
|
typedef void (*NSR_SourceResolvingEndHandler)(void);
|
||||||
@@ -135,6 +140,8 @@ extern int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_rati
|
|||||||
|
|
||||||
extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum);
|
extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum);
|
||||||
|
|
||||||
|
extern int NSR_ModifyOffset(IPAddr *address, double new_offset);
|
||||||
|
|
||||||
extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
|
extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
|
||||||
|
|
||||||
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
|
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
|
||||||
|
|||||||
3
nts_ke.h
3
nts_ke.h
@@ -40,6 +40,7 @@
|
|||||||
#define NKE_RECORD_COOKIE 5
|
#define NKE_RECORD_COOKIE 5
|
||||||
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
|
#define NKE_RECORD_NTPV4_SERVER_NEGOTIATION 6
|
||||||
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
|
#define NKE_RECORD_NTPV4_PORT_NEGOTIATION 7
|
||||||
|
#define NKE_RECORD_COMPLIANT_128GCM_EXPORT 1024
|
||||||
|
|
||||||
#define NKE_NEXT_PROTOCOL_NTPV4 0
|
#define NKE_NEXT_PROTOCOL_NTPV4 0
|
||||||
|
|
||||||
@@ -49,8 +50,6 @@
|
|||||||
|
|
||||||
#define NKE_ALPN_NAME "ntske/1"
|
#define NKE_ALPN_NAME "ntske/1"
|
||||||
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
|
#define NKE_EXPORTER_LABEL "EXPORTER-network-time-security"
|
||||||
#define NKE_EXPORTER_CONTEXT_C2S "\x0\x0\x0\xf\x0"
|
|
||||||
#define NKE_EXPORTER_CONTEXT_S2C "\x0\x0\x0\xf\x1"
|
|
||||||
|
|
||||||
#define NKE_MAX_MESSAGE_LENGTH 16384
|
#define NKE_MAX_MESSAGE_LENGTH 16384
|
||||||
#define NKE_MAX_RECORD_BODY_LENGTH 256
|
#define NKE_MAX_RECORD_BODY_LENGTH 256
|
||||||
|
|||||||
103
nts_ke_client.c
103
nts_ke_client.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020
|
* Copyright (C) Miroslav Lichvar 2020-2021, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -50,7 +50,9 @@ struct NKC_Instance_Record {
|
|||||||
int got_response;
|
int got_response;
|
||||||
int resolving_name;
|
int resolving_name;
|
||||||
|
|
||||||
|
int compliant_128gcm;
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
|
NKE_Context alt_context;
|
||||||
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
NKE_Cookie cookies[NKE_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
|
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
|
||||||
@@ -98,22 +100,39 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
#define MAX_AEAD_ALGORITHMS 4
|
||||||
|
|
||||||
static int
|
static int
|
||||||
prepare_request(NKC_Instance inst)
|
prepare_request(NKC_Instance inst)
|
||||||
{
|
{
|
||||||
NKSN_Instance session = inst->session;
|
NKSN_Instance session = inst->session;
|
||||||
uint16_t datum;
|
uint16_t data[MAX_AEAD_ALGORITHMS];
|
||||||
|
int i, aead_algorithm, length;
|
||||||
|
|
||||||
NKSN_BeginMessage(session);
|
NKSN_BeginMessage(session);
|
||||||
|
|
||||||
datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
|
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
datum = htons(AEAD_AES_SIV_CMAC_256);
|
for (i = length = 0; i < ARR_GetSize(CNF_GetNtsAeads()) && length < MAX_AEAD_ALGORITHMS;
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
i++) {
|
||||||
|
aead_algorithm = *(int *)ARR_GetElement(CNF_GetNtsAeads(), i);
|
||||||
|
if (SIV_GetKeyLength(aead_algorithm) > 0)
|
||||||
|
data[length++] = htons(aead_algorithm);
|
||||||
|
}
|
||||||
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
|
||||||
|
length * sizeof (data[0])))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++) {
|
||||||
|
if (data[i] == htons(AEAD_AES_128_GCM_SIV)) {
|
||||||
|
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!NKSN_EndMessage(session))
|
if (!NKSN_EndMessage(session))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -127,11 +146,14 @@ process_response(NKC_Instance inst)
|
|||||||
{
|
{
|
||||||
int next_protocol = -1, aead_algorithm = -1, error = 0;
|
int next_protocol = -1, aead_algorithm = -1, error = 0;
|
||||||
int i, critical, type, length;
|
int i, critical, type, length;
|
||||||
uint16_t data[NKE_MAX_COOKIE_LENGTH / sizeof (uint16_t)];
|
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
|
||||||
|
|
||||||
assert(NKE_MAX_COOKIE_LENGTH % sizeof (uint16_t) == 0);
|
assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH);
|
||||||
|
assert(sizeof (data) % sizeof (uint16_t) == 0);
|
||||||
assert(sizeof (uint16_t) == 2);
|
assert(sizeof (uint16_t) == 2);
|
||||||
|
|
||||||
|
inst->compliant_128gcm = 0;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
|
||||||
inst->ntp_address.port = 0;
|
inst->ntp_address.port = 0;
|
||||||
@@ -141,6 +163,13 @@ process_response(NKC_Instance inst)
|
|||||||
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
|
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (length > sizeof (data)) {
|
||||||
|
DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical);
|
||||||
|
if (critical)
|
||||||
|
error = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case NKE_RECORD_NEXT_PROTOCOL:
|
case NKE_RECORD_NEXT_PROTOCOL:
|
||||||
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
|
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
|
||||||
@@ -151,13 +180,31 @@ process_response(NKC_Instance inst)
|
|||||||
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
|
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_AEAD_ALGORITHM:
|
case NKE_RECORD_AEAD_ALGORITHM:
|
||||||
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
|
if (length != 2) {
|
||||||
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
|
DEBUG_LOG("Unexpected AEAD algorithm");
|
||||||
error = 1;
|
error = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
aead_algorithm = AEAD_AES_SIV_CMAC_256;
|
for (i = 0; i < ARR_GetSize(CNF_GetNtsAeads()); i++) {
|
||||||
|
if (ntohs(data[0]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), i) &&
|
||||||
|
SIV_GetKeyLength(ntohs(data[0])) > 0) {
|
||||||
|
aead_algorithm = ntohs(data[0]);
|
||||||
inst->context.algorithm = aead_algorithm;
|
inst->context.algorithm = aead_algorithm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aead_algorithm < 0) {
|
||||||
|
DEBUG_LOG("Unexpected AEAD algorithm");
|
||||||
|
error = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
|
||||||
|
if (length != 0) {
|
||||||
|
DEBUG_LOG("Non-empty compliant-128gcm record");
|
||||||
|
error = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DEBUG_LOG("Compliant AES-128-GCM-SIV export");
|
||||||
|
inst->compliant_128gcm = 1;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_ERROR:
|
case NKE_RECORD_ERROR:
|
||||||
if (length == 2)
|
if (length == 2)
|
||||||
@@ -228,7 +275,7 @@ process_response(NKC_Instance inst)
|
|||||||
|
|
||||||
if (error || inst->num_cookies == 0 ||
|
if (error || inst->num_cookies == 0 ||
|
||||||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
|
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
|
||||||
aead_algorithm != AEAD_AES_SIV_CMAC_256)
|
aead_algorithm < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -239,6 +286,7 @@ process_response(NKC_Instance inst)
|
|||||||
static int
|
static int
|
||||||
handle_message(void *arg)
|
handle_message(void *arg)
|
||||||
{
|
{
|
||||||
|
SIV_Algorithm exporter_algorithm;
|
||||||
NKC_Instance inst = arg;
|
NKC_Instance inst = arg;
|
||||||
|
|
||||||
if (!process_response(inst)) {
|
if (!process_response(inst)) {
|
||||||
@@ -246,8 +294,25 @@ handle_message(void *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
|
exporter_algorithm = inst->context.algorithm;
|
||||||
&inst->context.c2s, &inst->context.s2c))
|
|
||||||
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
|
context incorrectly for compatibility with older chrony servers unless
|
||||||
|
the server confirmed support for the compliant context. Generate both
|
||||||
|
sets of keys in case the server uses the compliant context, but does not
|
||||||
|
support the negotiation record, assuming it will respond with an NTS NAK
|
||||||
|
to a request authenticated with the noncompliant key. */
|
||||||
|
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !inst->compliant_128gcm) {
|
||||||
|
inst->alt_context.algorithm = inst->context.algorithm;
|
||||||
|
if (!NKSN_GetKeys(inst->session, inst->alt_context.algorithm, exporter_algorithm,
|
||||||
|
NKE_NEXT_PROTOCOL_NTPV4, &inst->alt_context.c2s, &inst->alt_context.s2c))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NKSN_GetKeys(inst->session, inst->context.algorithm, exporter_algorithm,
|
||||||
|
NKE_NEXT_PROTOCOL_NTPV4, &inst->context.c2s, &inst->context.s2c))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (inst->server_name[0] != '\0') {
|
if (inst->server_name[0] != '\0') {
|
||||||
@@ -362,6 +427,13 @@ NKC_Start(NKC_Instance inst)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't try to connect if missing the algorithm which all servers
|
||||||
|
are required to support */
|
||||||
|
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) <= 0) {
|
||||||
|
LOG(LOGS_ERR, "Missing AES-SIV-CMAC-256");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Follow the bindacqaddress and bindacqdevice settings */
|
/* Follow the bindacqaddress and bindacqdevice settings */
|
||||||
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
|
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
|
||||||
local_addr.port = 0;
|
local_addr.port = 0;
|
||||||
@@ -405,7 +477,7 @@ NKC_IsActive(NKC_Instance inst)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
|
||||||
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address)
|
IPSockAddr *ntp_address)
|
||||||
{
|
{
|
||||||
@@ -415,6 +487,7 @@ NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
*context = inst->context;
|
*context = inst->context;
|
||||||
|
*alt_context = inst->alt_context;
|
||||||
|
|
||||||
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
|
for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
|
||||||
cookies[i] = inst->cookies[i];
|
cookies[i] = inst->cookies[i];
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ extern int NKC_Start(NKC_Instance inst);
|
|||||||
extern int NKC_IsActive(NKC_Instance inst);
|
extern int NKC_IsActive(NKC_Instance inst);
|
||||||
|
|
||||||
/* Get the NTS data if the session was successful */
|
/* Get the NTS data if the session was successful */
|
||||||
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
|
extern int NKC_GetNtsData(NKC_Instance inst, NKE_Context *context, NKE_Context *alt_context,
|
||||||
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
NKE_Cookie *cookies, int *num_cookies, int max_cookies,
|
||||||
IPSockAddr *ntp_address);
|
IPSockAddr *ntp_address);
|
||||||
|
|
||||||
|
|||||||
259
nts_ke_server.c
259
nts_ke_server.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020
|
* Copyright (C) Miroslav Lichvar 2020, 2022, 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -47,31 +47,33 @@
|
|||||||
|
|
||||||
#define SERVER_TIMEOUT 2.0
|
#define SERVER_TIMEOUT 2.0
|
||||||
|
|
||||||
#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
|
#define MAX_COOKIE_NONCE_LENGTH 16
|
||||||
#define SERVER_COOKIE_NONCE_LENGTH 16
|
|
||||||
|
|
||||||
#define KEY_ID_INDEX_BITS 2
|
#define KEY_ID_INDEX_BITS 2
|
||||||
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
|
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
|
||||||
#define FUTURE_KEYS 1
|
#define FUTURE_KEYS 1
|
||||||
|
|
||||||
#define DUMP_FILENAME "ntskeys"
|
#define DUMP_FILENAME "ntskeys"
|
||||||
#define DUMP_IDENTIFIER "NKS0\n"
|
#define DUMP_IDENTIFIER "NKS1\n"
|
||||||
|
#define OLD_DUMP_IDENTIFIER "NKS0\n"
|
||||||
|
|
||||||
#define INVALID_SOCK_FD (-7)
|
#define INVALID_SOCK_FD (-7)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t key_id;
|
uint32_t key_id;
|
||||||
unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
|
|
||||||
} ServerCookieHeader;
|
} ServerCookieHeader;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
unsigned char key[SIV_MAX_KEY_LENGTH];
|
unsigned char key[SIV_MAX_KEY_LENGTH];
|
||||||
|
SIV_Algorithm siv_algorithm;
|
||||||
SIV_Instance siv;
|
SIV_Instance siv;
|
||||||
|
int nonce_length;
|
||||||
} ServerKey;
|
} ServerKey;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t key_id;
|
uint32_t key_id;
|
||||||
|
uint32_t siv_algorithm;
|
||||||
unsigned char key[SIV_MAX_KEY_LENGTH];
|
unsigned char key[SIV_MAX_KEY_LENGTH];
|
||||||
IPAddr client_addr;
|
IPAddr client_addr;
|
||||||
uint16_t client_port;
|
uint16_t client_port;
|
||||||
@@ -148,12 +150,30 @@ handle_client(int sock_fd, IPSockAddr *addr)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_key_siv(ServerKey *key, SIV_Algorithm algorithm)
|
||||||
|
{
|
||||||
|
if (!key->siv || key->siv_algorithm != algorithm) {
|
||||||
|
if (key->siv)
|
||||||
|
SIV_DestroyInstance(key->siv);
|
||||||
|
key->siv_algorithm = algorithm;
|
||||||
|
key->siv = SIV_CreateInstance(algorithm);
|
||||||
|
key->nonce_length = MIN(SIV_GetMaxNonceLength(key->siv), MAX_COOKIE_NONCE_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key->siv || !SIV_SetKey(key->siv, key->key, SIV_GetKeyLength(key->siv_algorithm)))
|
||||||
|
LOG_FATAL("Could not set SIV key");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_helper_request(int fd, int event, void *arg)
|
handle_helper_request(int fd, int event, void *arg)
|
||||||
{
|
{
|
||||||
SCK_Message *message;
|
SCK_Message *message;
|
||||||
HelperRequest *req;
|
HelperRequest *req;
|
||||||
IPSockAddr client_addr;
|
IPSockAddr client_addr;
|
||||||
|
ServerKey *key;
|
||||||
int sock_fd;
|
int sock_fd;
|
||||||
|
|
||||||
/* Receive the helper request with the NTS-KE session socket.
|
/* Receive the helper request with the NTS-KE session socket.
|
||||||
@@ -181,16 +201,14 @@ handle_helper_request(int fd, int event, void *arg)
|
|||||||
req = message->data;
|
req = message->data;
|
||||||
|
|
||||||
/* Extract the current server key and client address from the request */
|
/* Extract the current server key and client address from the request */
|
||||||
server_keys[current_server_key].id = ntohl(req->key_id);
|
key = &server_keys[current_server_key];
|
||||||
assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key));
|
key->id = ntohl(req->key_id);
|
||||||
memcpy(server_keys[current_server_key].key, req->key,
|
assert(sizeof (key->key) == sizeof (req->key));
|
||||||
sizeof (server_keys[current_server_key].key));
|
memcpy(key->key, req->key, sizeof (key->key));
|
||||||
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
|
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
|
||||||
client_addr.port = ntohs(req->client_port);
|
client_addr.port = ntohs(req->client_port);
|
||||||
|
|
||||||
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
|
update_key_siv(key, ntohl(req->siv_algorithm));
|
||||||
SIV_GetKeyLength(SERVER_COOKIE_SIV)))
|
|
||||||
LOG_FATAL("Could not set SIV key");
|
|
||||||
|
|
||||||
if (!handle_client(sock_fd, &client_addr)) {
|
if (!handle_client(sock_fd, &client_addr)) {
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
@@ -224,7 +242,7 @@ accept_connection(int listening_fd, int event, void *arg)
|
|||||||
SCH_GetLastEventTime(&now, NULL, NULL);
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
|
||||||
log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
|
log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
|
||||||
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) {
|
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index) != CLG_PASS) {
|
||||||
DEBUG_LOG("Rejected connection from %s (%s)",
|
DEBUG_LOG("Rejected connection from %s (%s)",
|
||||||
UTI_IPSockAddrToString(&addr), "rate limit");
|
UTI_IPSockAddrToString(&addr), "rate limit");
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
@@ -240,6 +258,7 @@ accept_connection(int listening_fd, int event, void *arg)
|
|||||||
|
|
||||||
/* Include the current server key and client address in the request */
|
/* Include the current server key and client address in the request */
|
||||||
req.key_id = htonl(server_keys[current_server_key].id);
|
req.key_id = htonl(server_keys[current_server_key].id);
|
||||||
|
req.siv_algorithm = htonl(server_keys[current_server_key].siv_algorithm);
|
||||||
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
|
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
|
||||||
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
|
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
|
||||||
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
|
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
|
||||||
@@ -318,8 +337,10 @@ helper_signal(int x)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm)
|
prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_algorithm,
|
||||||
|
int compliant_128gcm)
|
||||||
{
|
{
|
||||||
|
SIV_Algorithm exporter_algorithm;
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
NKE_Cookie cookie;
|
NKE_Cookie cookie;
|
||||||
char *ntp_server;
|
char *ntp_server;
|
||||||
@@ -352,6 +373,11 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
|
|||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (aead_algorithm == AEAD_AES_128_GCM_SIV && compliant_128gcm) {
|
||||||
|
if (!NKSN_AddRecord(session, 0, NKE_RECORD_COMPLIANT_128GCM_EXPORT, NULL, 0))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (CNF_GetNTPPort() != NTP_PORT) {
|
if (CNF_GetNTPPort() != NTP_PORT) {
|
||||||
datum = htons(CNF_GetNTPPort());
|
datum = htons(CNF_GetNTPPort());
|
||||||
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
|
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NTPV4_PORT_NEGOTIATION, &datum, sizeof (datum)))
|
||||||
@@ -366,8 +392,16 @@ prepare_response(NKSN_Instance session, int error, int next_protocol, int aead_a
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.algorithm = aead_algorithm;
|
context.algorithm = aead_algorithm;
|
||||||
|
exporter_algorithm = aead_algorithm;
|
||||||
|
|
||||||
if (!NKSN_GetKeys(session, aead_algorithm, &context.c2s, &context.s2c))
|
/* With AES-128-GCM-SIV, set the algorithm ID in the RFC5705 key exporter
|
||||||
|
context incorrectly for compatibility with older chrony clients unless
|
||||||
|
the client requested the compliant context */
|
||||||
|
if (exporter_algorithm == AEAD_AES_128_GCM_SIV && !compliant_128gcm)
|
||||||
|
exporter_algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
|
||||||
|
if (!NKSN_GetKeys(session, aead_algorithm, exporter_algorithm,
|
||||||
|
NKE_NEXT_PROTOCOL_NTPV4, &context.c2s, &context.s2c))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
for (i = 0; i < NKE_MAX_COOKIES; i++) {
|
||||||
@@ -392,7 +426,8 @@ process_request(NKSN_Instance session)
|
|||||||
int next_protocol_records = 0, aead_algorithm_records = 0;
|
int next_protocol_records = 0, aead_algorithm_records = 0;
|
||||||
int next_protocol_values = 0, aead_algorithm_values = 0;
|
int next_protocol_values = 0, aead_algorithm_values = 0;
|
||||||
int next_protocol = -1, aead_algorithm = -1, error = -1;
|
int next_protocol = -1, aead_algorithm = -1, error = -1;
|
||||||
int i, critical, type, length;
|
int i, j, critical, type, length;
|
||||||
|
int compliant_128gcm = 0;
|
||||||
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
|
uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
|
||||||
|
|
||||||
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
|
assert(NKE_MAX_RECORD_BODY_LENGTH % sizeof (uint16_t) == 0);
|
||||||
@@ -427,9 +462,20 @@ process_request(NKSN_Instance session)
|
|||||||
|
|
||||||
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
|
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
|
||||||
aead_algorithm_values++;
|
aead_algorithm_values++;
|
||||||
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
|
/* Use the first enabled and supported algorithm */
|
||||||
aead_algorithm = AEAD_AES_SIV_CMAC_256;
|
for (j = 0; j < ARR_GetSize(CNF_GetNtsAeads()); j++) {
|
||||||
|
if (ntohs(data[i]) == *(int *)ARR_GetElement(CNF_GetNtsAeads(), j) &&
|
||||||
|
aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
|
||||||
|
aead_algorithm = ntohs(data[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NKE_RECORD_COMPLIANT_128GCM_EXPORT:
|
||||||
|
if (length != 0) {
|
||||||
|
error = NKE_ERROR_BAD_REQUEST;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
compliant_128gcm = 1;
|
||||||
break;
|
break;
|
||||||
case NKE_RECORD_ERROR:
|
case NKE_RECORD_ERROR:
|
||||||
case NKE_RECORD_WARNING:
|
case NKE_RECORD_WARNING:
|
||||||
@@ -449,7 +495,7 @@ process_request(NKSN_Instance session)
|
|||||||
error = NKE_ERROR_BAD_REQUEST;
|
error = NKE_ERROR_BAD_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!prepare_response(session, error, next_protocol, aead_algorithm))
|
if (!prepare_response(session, error, next_protocol, aead_algorithm, compliant_128gcm))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -470,28 +516,37 @@ handle_message(void *arg)
|
|||||||
static void
|
static void
|
||||||
generate_key(int index)
|
generate_key(int index)
|
||||||
{
|
{
|
||||||
|
SIV_Algorithm algorithm;
|
||||||
|
ServerKey *key;
|
||||||
int key_length;
|
int key_length;
|
||||||
|
|
||||||
if (index < 0 || index >= MAX_SERVER_KEYS)
|
BRIEF_ASSERT(index >= 0 && index < MAX_SERVER_KEYS);
|
||||||
assert(0);
|
|
||||||
|
|
||||||
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
|
/* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
|
||||||
if (key_length > sizeof (server_keys[index].key))
|
from ntsdumpdir use a different algorithm, responding to NTP requests
|
||||||
assert(0);
|
with cookies encrypted with those keys will not work if the new algorithm
|
||||||
|
produces longer cookies (i.e. response would be longer than request).
|
||||||
|
Switching from AES-SIV-CMAC-256 to AES-128-GCM-SIV is ok. */
|
||||||
|
algorithm = SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
|
||||||
|
AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256;
|
||||||
|
|
||||||
UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
|
key = &server_keys[index];
|
||||||
|
|
||||||
if (!server_keys[index].siv ||
|
key_length = SIV_GetKeyLength(algorithm);
|
||||||
!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
|
BRIEF_ASSERT(key_length <= sizeof (key->key));
|
||||||
LOG_FATAL("Could not set SIV key");
|
|
||||||
|
|
||||||
UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
|
UTI_GetRandomBytesUrandom(key->key, key_length);
|
||||||
|
if (key_length < sizeof (key->key))
|
||||||
|
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
|
||||||
|
UTI_GetRandomBytes(&key->id, sizeof (key->id));
|
||||||
|
|
||||||
/* Encode the index in the lowest bits of the ID */
|
/* Encode the index in the lowest bits of the ID */
|
||||||
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
|
key->id &= -1U << KEY_ID_INDEX_BITS;
|
||||||
server_keys[index].id |= index;
|
key->id |= index;
|
||||||
|
|
||||||
DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
|
update_key_siv(key, algorithm);
|
||||||
|
|
||||||
|
DEBUG_LOG("Generated key %08"PRIX32" (%d)", key->id, (int)key->siv_algorithm);
|
||||||
|
|
||||||
last_server_key_ts = SCH_GetLastEventMonoTime();
|
last_server_key_ts = SCH_GetLastEventMonoTime();
|
||||||
}
|
}
|
||||||
@@ -519,18 +574,19 @@ save_keys(void)
|
|||||||
if (!f)
|
if (!f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
|
|
||||||
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
|
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
|
||||||
|
|
||||||
if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0)
|
if (fprintf(f, "%s%.1f\n", DUMP_IDENTIFIER, last_key_age) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
||||||
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
|
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
|
||||||
|
key_length = SIV_GetKeyLength(server_keys[index].siv_algorithm);
|
||||||
|
|
||||||
if (key_length > sizeof (server_keys[index].key) ||
|
if (key_length > sizeof (server_keys[index].key) ||
|
||||||
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
|
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
|
||||||
fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0)
|
fprintf(f, "%08"PRIX32" %s %d\n", server_keys[index].id, buf,
|
||||||
|
(int)server_keys[index].siv_algorithm) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +601,7 @@ save_keys(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
DEBUG_LOG("Could not %s server keys", "save");
|
LOG(LOGS_ERR, "Could not %s %s", "save", "server NTS keys");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
|
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
|
||||||
@@ -554,17 +610,16 @@ error:
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
#define MAX_WORDS 2
|
#define MAX_WORDS 3
|
||||||
|
|
||||||
static int
|
static int
|
||||||
load_keys(void)
|
load_keys(void)
|
||||||
{
|
{
|
||||||
|
int i, index, key_length, algorithm = 0, old_ver;
|
||||||
char *dump_dir, line[1024], *words[MAX_WORDS];
|
char *dump_dir, line[1024], *words[MAX_WORDS];
|
||||||
unsigned char key[SIV_MAX_KEY_LENGTH];
|
ServerKey new_keys[MAX_SERVER_KEYS];
|
||||||
int i, index, key_length, algorithm;
|
|
||||||
double key_age;
|
double key_age;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
uint32_t id;
|
|
||||||
|
|
||||||
dump_dir = CNF_GetNtsDumpDir();
|
dump_dir = CNF_GetNtsDumpDir();
|
||||||
if (!dump_dir)
|
if (!dump_dir)
|
||||||
@@ -574,43 +629,58 @@ load_keys(void)
|
|||||||
if (!f)
|
if (!f)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
|
if (!fgets(line, sizeof (line), f) ||
|
||||||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
|
(strcmp(line, DUMP_IDENTIFIER) != 0 && strcmp(line, OLD_DUMP_IDENTIFIER) != 0))
|
||||||
sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
|
|
||||||
sscanf(words[1], "%lf", &key_age) != 1)
|
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
|
old_ver = strcmp(line, DUMP_IDENTIFIER) != 0;
|
||||||
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
|
|
||||||
|
if (!fgets(line, sizeof (line), f) ||
|
||||||
|
UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 1) ||
|
||||||
|
(old_ver && sscanf(words[0], "%d", &algorithm) != 1) ||
|
||||||
|
sscanf(words[old_ver ? 1 : 0], "%lf", &key_age) != 1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
|
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
|
||||||
if (UTI_SplitString(line, words, MAX_WORDS) != 2 ||
|
if (UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 3) ||
|
||||||
sscanf(words[0], "%"PRIX32, &id) != 1)
|
sscanf(words[0], "%"PRIX32, &new_keys[i].id) != 1 ||
|
||||||
|
(!old_ver && sscanf(words[2], "%d", &algorithm) != 1))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length)
|
new_keys[i].siv_algorithm = algorithm;
|
||||||
|
key_length = SIV_GetKeyLength(algorithm);
|
||||||
|
|
||||||
|
if ((i > 0 && (new_keys[i].id - new_keys[i - 1].id) % MAX_SERVER_KEYS != 1) ||
|
||||||
|
key_length <= 0 ||
|
||||||
|
UTI_HexToBytes(words[1], new_keys[i].key, sizeof (new_keys[i].key)) != key_length)
|
||||||
|
goto error;
|
||||||
|
memset(new_keys[i].key + key_length, 0, sizeof (new_keys[i].key) - key_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < MAX_SERVER_KEYS)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
index = id % MAX_SERVER_KEYS;
|
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
||||||
|
index = new_keys[i].id % MAX_SERVER_KEYS;
|
||||||
|
server_keys[index].id = new_keys[i].id;
|
||||||
|
memcpy(server_keys[index].key, new_keys[i].key, sizeof (server_keys[index].key));
|
||||||
|
|
||||||
server_keys[index].id = id;
|
update_key_siv(&server_keys[index], new_keys[i].siv_algorithm);
|
||||||
assert(sizeof (server_keys[index].key) == sizeof (key));
|
|
||||||
memcpy(server_keys[index].key, key, key_length);
|
|
||||||
|
|
||||||
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
|
DEBUG_LOG("Loaded key %08"PRIX32" (%d)",
|
||||||
LOG_FATAL("Could not set SIV key");
|
server_keys[index].id, (int)server_keys[index].siv_algorithm);
|
||||||
|
}
|
||||||
DEBUG_LOG("Loaded key %"PRIX32, id);
|
|
||||||
|
|
||||||
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
|
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
|
||||||
}
|
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
LOG(LOGS_INFO, "Loaded %s", "server NTS keys");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
DEBUG_LOG("Could not %s server keys", "load");
|
LOG(LOGS_ERR, "Could not %s %s", "load", "server NTS keys");
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -631,7 +701,7 @@ key_timeout(void *arg)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
run_helper(uid_t uid, gid_t gid, int scfilter_level)
|
run_helper(uid_t uid, gid_t gid, int scfilter_level, int sock_fd)
|
||||||
{
|
{
|
||||||
LOG_Severity log_severity;
|
LOG_Severity log_severity;
|
||||||
|
|
||||||
@@ -640,6 +710,8 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
|
|||||||
|
|
||||||
DEBUG_LOG("Helper started");
|
DEBUG_LOG("Helper started");
|
||||||
|
|
||||||
|
SCK_CloseReusableSockets();
|
||||||
|
|
||||||
/* Suppress a log message about disabled clock control */
|
/* Suppress a log message about disabled clock control */
|
||||||
log_severity = LOG_GetMinSeverity();
|
log_severity = LOG_GetMinSeverity();
|
||||||
LOG_SetMinSeverity(LOGS_ERR);
|
LOG_SetMinSeverity(LOGS_ERR);
|
||||||
@@ -656,10 +728,15 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
|
|||||||
if (scfilter_level != 0)
|
if (scfilter_level != 0)
|
||||||
SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
|
SYS_EnableSystemCallFilter(scfilter_level, SYS_NTSKE_HELPER);
|
||||||
|
|
||||||
|
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, handle_helper_request, NULL);
|
||||||
|
|
||||||
SCH_MainLoop();
|
SCH_MainLoop();
|
||||||
|
|
||||||
DEBUG_LOG("Helper exiting");
|
DEBUG_LOG("Helper exiting");
|
||||||
|
|
||||||
|
SCH_RemoveFileHandler(sock_fd);
|
||||||
|
close(sock_fd);
|
||||||
|
|
||||||
NKS_Finalise();
|
NKS_Finalise();
|
||||||
SCK_Finalise();
|
SCK_Finalise();
|
||||||
SYS_Finalise();
|
SYS_Finalise();
|
||||||
@@ -669,6 +746,8 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
|
|||||||
CNF_Finalise();
|
CNF_Finalise();
|
||||||
LOG_Finalise();
|
LOG_Finalise();
|
||||||
|
|
||||||
|
UTI_ResetGetRandomFunctions();
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,14 +789,15 @@ NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
|
|||||||
|
|
||||||
is_helper = 1;
|
is_helper = 1;
|
||||||
|
|
||||||
|
UTI_ResetGetRandomFunctions();
|
||||||
|
|
||||||
snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1);
|
snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1);
|
||||||
LOG_SetDebugPrefix(prefix);
|
LOG_SetDebugPrefix(prefix);
|
||||||
LOG_CloseParentFd();
|
LOG_CloseParentFd();
|
||||||
|
|
||||||
SCK_CloseSocket(sock_fd1);
|
SCK_CloseSocket(sock_fd1);
|
||||||
SCH_AddFileHandler(sock_fd2, SCH_FILE_INPUT, handle_helper_request, NULL);
|
|
||||||
|
|
||||||
run_helper(uid, gid, scfilter_level);
|
run_helper(uid, gid, scfilter_level, sock_fd2);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCK_CloseSocket(sock_fd2);
|
SCK_CloseSocket(sock_fd2);
|
||||||
@@ -755,7 +835,7 @@ NKS_Initialise(void)
|
|||||||
/* Generate random keys, even if they will be replaced by reloaded keys,
|
/* Generate random keys, even if they will be replaced by reloaded keys,
|
||||||
or unused (in the helper) */
|
or unused (in the helper) */
|
||||||
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
for (i = 0; i < MAX_SERVER_KEYS; i++) {
|
||||||
server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
|
server_keys[i].siv = NULL;
|
||||||
generate_key(i);
|
generate_key(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,6 +855,11 @@ NKS_Initialise(void)
|
|||||||
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
|
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
|
||||||
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
|
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Warn if keys are not saved, which can cause a flood of requests
|
||||||
|
after server restart */
|
||||||
|
if (!CNF_GetNtsDumpDir())
|
||||||
|
LOG(LOGS_WARN, "No ntsdumpdir to save server keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
initialised = 1;
|
initialised = 1;
|
||||||
@@ -848,7 +933,7 @@ NKS_ReloadKeys(void)
|
|||||||
int
|
int
|
||||||
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
||||||
{
|
{
|
||||||
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
||||||
int plaintext_length, tag_length;
|
int plaintext_length, tag_length;
|
||||||
ServerCookieHeader *header;
|
ServerCookieHeader *header;
|
||||||
ServerKey *key;
|
ServerKey *key;
|
||||||
@@ -858,14 +943,12 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The algorithm is hardcoded for now */
|
/* The AEAD ID is not encoded in the cookie. It is implied from the key
|
||||||
if (context->algorithm != AEAD_AES_SIV_CMAC_256) {
|
length (as long as only algorithms with different key lengths are
|
||||||
DEBUG_LOG("Unexpected SIV algorithm");
|
supported). */
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
|
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
|
||||||
context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) {
|
context->s2c.length != context->c2s.length) {
|
||||||
DEBUG_LOG("Invalid key length");
|
DEBUG_LOG("Invalid key length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -875,7 +958,10 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
header = (ServerCookieHeader *)cookie->cookie;
|
header = (ServerCookieHeader *)cookie->cookie;
|
||||||
|
|
||||||
header->key_id = htonl(key->id);
|
header->key_id = htonl(key->id);
|
||||||
UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
|
|
||||||
|
nonce = cookie->cookie + sizeof (*header);
|
||||||
|
BRIEF_ASSERT(key->nonce_length <= sizeof (cookie->cookie) - sizeof (*header));
|
||||||
|
UTI_GetRandomBytes(nonce, key->nonce_length);
|
||||||
|
|
||||||
plaintext_length = context->c2s.length + context->s2c.length;
|
plaintext_length = context->c2s.length + context->s2c.length;
|
||||||
assert(plaintext_length <= sizeof (plaintext));
|
assert(plaintext_length <= sizeof (plaintext));
|
||||||
@@ -883,11 +969,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
|
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
|
||||||
|
|
||||||
tag_length = SIV_GetTagLength(key->siv);
|
tag_length = SIV_GetTagLength(key->siv);
|
||||||
cookie->length = sizeof (*header) + plaintext_length + tag_length;
|
cookie->length = sizeof (*header) + key->nonce_length + plaintext_length + tag_length;
|
||||||
assert(cookie->length <= sizeof (cookie->cookie));
|
assert(cookie->length <= sizeof (cookie->cookie));
|
||||||
ciphertext = cookie->cookie + sizeof (*header);
|
ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
|
||||||
|
|
||||||
if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce),
|
if (!SIV_Encrypt(key->siv, nonce, key->nonce_length,
|
||||||
"", 0,
|
"", 0,
|
||||||
plaintext, plaintext_length,
|
plaintext, plaintext_length,
|
||||||
ciphertext, plaintext_length + tag_length)) {
|
ciphertext, plaintext_length + tag_length)) {
|
||||||
@@ -903,7 +989,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
|
|||||||
int
|
int
|
||||||
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
||||||
{
|
{
|
||||||
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
|
||||||
int ciphertext_length, plaintext_length, tag_length;
|
int ciphertext_length, plaintext_length, tag_length;
|
||||||
ServerCookieHeader *header;
|
ServerCookieHeader *header;
|
||||||
ServerKey *key;
|
ServerKey *key;
|
||||||
@@ -920,8 +1006,6 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
header = (ServerCookieHeader *)cookie->cookie;
|
header = (ServerCookieHeader *)cookie->cookie;
|
||||||
ciphertext = cookie->cookie + sizeof (*header);
|
|
||||||
ciphertext_length = cookie->length - sizeof (*header);
|
|
||||||
|
|
||||||
key_id = ntohl(header->key_id);
|
key_id = ntohl(header->key_id);
|
||||||
key = &server_keys[key_id % MAX_SERVER_KEYS];
|
key = &server_keys[key_id % MAX_SERVER_KEYS];
|
||||||
@@ -931,18 +1015,23 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tag_length = SIV_GetTagLength(key->siv);
|
tag_length = SIV_GetTagLength(key->siv);
|
||||||
if (tag_length >= ciphertext_length) {
|
|
||||||
|
if (cookie->length <= (int)sizeof (*header) + key->nonce_length + tag_length) {
|
||||||
DEBUG_LOG("Invalid cookie length");
|
DEBUG_LOG("Invalid cookie length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonce = cookie->cookie + sizeof (*header);
|
||||||
|
ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
|
||||||
|
ciphertext_length = cookie->length - sizeof (*header) - key->nonce_length;
|
||||||
plaintext_length = ciphertext_length - tag_length;
|
plaintext_length = ciphertext_length - tag_length;
|
||||||
|
|
||||||
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
|
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
|
||||||
DEBUG_LOG("Invalid cookie length");
|
DEBUG_LOG("Invalid cookie length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce),
|
if (!SIV_Decrypt(key->siv, nonce, key->nonce_length,
|
||||||
"", 0,
|
"", 0,
|
||||||
ciphertext, ciphertext_length,
|
ciphertext, ciphertext_length,
|
||||||
plaintext, plaintext_length)) {
|
plaintext, plaintext_length)) {
|
||||||
@@ -950,7 +1039,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Select a supported algorithm corresponding to the key length, avoiding
|
||||||
|
potentially slow SIV_GetKeyLength() */
|
||||||
|
switch (plaintext_length / 2) {
|
||||||
|
case 16:
|
||||||
|
context->algorithm = AEAD_AES_128_GCM_SIV;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
context->algorithm = AEAD_AES_SIV_CMAC_256;
|
context->algorithm = AEAD_AES_SIV_CMAC_256;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DEBUG_LOG("Unknown key length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
context->c2s.length = plaintext_length / 2;
|
context->c2s.length = plaintext_length / 2;
|
||||||
context->s2c.length = plaintext_length / 2;
|
context->s2c.length = plaintext_length / 2;
|
||||||
|
|||||||
345
nts_ke_session.c
345
nts_ke_session.c
@@ -2,7 +2,8 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020
|
* Copyright (C) Miroslav Lichvar 2020-2021
|
||||||
|
* Copyright (C) Anthony Brandon 2025
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -37,11 +38,9 @@
|
|||||||
#include "siv.h"
|
#include "siv.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
#include "tls.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <gnutls/gnutls.h>
|
|
||||||
#include <gnutls/x509.h>
|
|
||||||
|
|
||||||
#define INVALID_SOCK_FD (-8)
|
#define INVALID_SOCK_FD (-8)
|
||||||
|
|
||||||
struct RecordHeader {
|
struct RecordHeader {
|
||||||
@@ -75,7 +74,7 @@ struct NKSN_Instance_Record {
|
|||||||
KeState state;
|
KeState state;
|
||||||
int sock_fd;
|
int sock_fd;
|
||||||
char *label;
|
char *label;
|
||||||
gnutls_session_t tls_session;
|
TLS_Instance tls_session;
|
||||||
SCH_TimeoutID timeout_id;
|
SCH_TimeoutID timeout_id;
|
||||||
int retry_factor;
|
int retry_factor;
|
||||||
|
|
||||||
@@ -85,8 +84,6 @@ struct NKSN_Instance_Record {
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static gnutls_priority_t priority_cache;
|
|
||||||
|
|
||||||
static int credentials_counter = 0;
|
static int credentials_counter = 0;
|
||||||
|
|
||||||
static int clock_updates = 0;
|
static int clock_updates = 0;
|
||||||
@@ -206,71 +203,6 @@ check_message_format(struct Message *message, int eof)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static gnutls_session_t
|
|
||||||
create_tls_session(int server_mode, int sock_fd, const char *server_name,
|
|
||||||
gnutls_certificate_credentials_t credentials,
|
|
||||||
gnutls_priority_t priority)
|
|
||||||
{
|
|
||||||
unsigned char alpn_name[sizeof (NKE_ALPN_NAME)];
|
|
||||||
gnutls_session_t session;
|
|
||||||
gnutls_datum_t alpn;
|
|
||||||
unsigned int flags;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = gnutls_init(&session, GNUTLS_NONBLOCK | GNUTLS_NO_TICKETS |
|
|
||||||
(server_mode ? GNUTLS_SERVER : GNUTLS_CLIENT));
|
|
||||||
if (r < 0) {
|
|
||||||
LOG(LOGS_ERR, "Could not %s TLS session : %s", "create", gnutls_strerror(r));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!server_mode) {
|
|
||||||
assert(server_name);
|
|
||||||
|
|
||||||
if (!UTI_IsStringIP(server_name)) {
|
|
||||||
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = 0;
|
|
||||||
|
|
||||||
if (clock_updates < CNF_GetNoCertTimeCheck()) {
|
|
||||||
flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
|
|
||||||
DEBUG_LOG("Disabled time checks");
|
|
||||||
}
|
|
||||||
|
|
||||||
gnutls_session_set_verify_cert(session, server_name, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = gnutls_priority_set(session, priority);
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, credentials);
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
memcpy(alpn_name, NKE_ALPN_NAME, sizeof (alpn_name));
|
|
||||||
alpn.data = alpn_name;
|
|
||||||
alpn.size = sizeof (alpn_name) - 1;
|
|
||||||
|
|
||||||
r = gnutls_alpn_set_protocols(session, &alpn, 1, 0);
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
gnutls_transport_set_int(session, sock_fd);
|
|
||||||
|
|
||||||
return session;
|
|
||||||
|
|
||||||
error:
|
|
||||||
LOG(LOGS_ERR, "Could not %s TLS session : %s", "set", gnutls_strerror(r));
|
|
||||||
gnutls_deinit(session);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stop_session(NKSN_Instance inst)
|
stop_session(NKSN_Instance inst)
|
||||||
{
|
{
|
||||||
@@ -286,7 +218,7 @@ stop_session(NKSN_Instance inst)
|
|||||||
Free(inst->label);
|
Free(inst->label);
|
||||||
inst->label = NULL;
|
inst->label = NULL;
|
||||||
|
|
||||||
gnutls_deinit(inst->tls_session);
|
TLS_DestroyInstance(inst->tls_session);
|
||||||
inst->tls_session = NULL;
|
inst->tls_session = NULL;
|
||||||
|
|
||||||
SCH_RemoveTimeout(inst->timeout_id);
|
SCH_RemoveTimeout(inst->timeout_id);
|
||||||
@@ -308,21 +240,6 @@ session_timeout(void *arg)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
|
||||||
check_alpn(NKSN_Instance inst)
|
|
||||||
{
|
|
||||||
gnutls_datum_t alpn;
|
|
||||||
|
|
||||||
if (gnutls_alpn_get_selected_protocol(inst->tls_session, &alpn) < 0 ||
|
|
||||||
alpn.size != sizeof (NKE_ALPN_NAME) - 1 ||
|
|
||||||
memcmp(alpn.data, NKE_ALPN_NAME, sizeof (NKE_ALPN_NAME) - 1) != 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_input_output(NKSN_Instance inst, int output)
|
set_input_output(NKSN_Instance inst, int output)
|
||||||
{
|
{
|
||||||
@@ -364,6 +281,7 @@ static int
|
|||||||
handle_event(NKSN_Instance inst, int event)
|
handle_event(NKSN_Instance inst, int event)
|
||||||
{
|
{
|
||||||
struct Message *message = &inst->message;
|
struct Message *message = &inst->message;
|
||||||
|
TLS_Status s;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
|
DEBUG_LOG("Session event %d fd=%d state=%d", event, inst->sock_fd, (int)inst->state);
|
||||||
@@ -390,56 +308,28 @@ handle_event(NKSN_Instance inst, int event)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case KE_HANDSHAKE:
|
case KE_HANDSHAKE:
|
||||||
r = gnutls_handshake(inst->tls_session);
|
s = TLS_DoHandshake(inst->tls_session);
|
||||||
|
|
||||||
if (r < 0) {
|
|
||||||
if (gnutls_error_is_fatal(r)) {
|
|
||||||
gnutls_datum_t cert_error;
|
|
||||||
|
|
||||||
/* Get a description of verification errors */
|
|
||||||
if (r != GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR ||
|
|
||||||
gnutls_certificate_verification_status_print(
|
|
||||||
gnutls_session_get_verify_cert_status(inst->tls_session),
|
|
||||||
gnutls_certificate_type_get(inst->tls_session), &cert_error, 0) < 0)
|
|
||||||
cert_error.data = NULL;
|
|
||||||
|
|
||||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
|
||||||
"TLS handshake with %s failed : %s%s%s", inst->label, gnutls_strerror(r),
|
|
||||||
cert_error.data ? " " : "", cert_error.data ? (const char *)cert_error.data : "");
|
|
||||||
|
|
||||||
if (cert_error.data)
|
|
||||||
gnutls_free(cert_error.data);
|
|
||||||
|
|
||||||
|
switch (s) {
|
||||||
|
case TLS_SUCCESS:
|
||||||
|
break;
|
||||||
|
case TLS_AGAIN_OUTPUT:
|
||||||
|
case TLS_AGAIN_INPUT:
|
||||||
|
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
stop_session(inst);
|
stop_session(inst);
|
||||||
|
|
||||||
/* Increase the retry interval if the handshake did not fail due
|
/* Increase the retry interval if the handshake did not fail due
|
||||||
to the other end closing the connection */
|
to the other end closing the connection */
|
||||||
if (r != GNUTLS_E_PULL_ERROR && r != GNUTLS_E_PREMATURE_TERMINATION)
|
if (s != TLS_CLOSED)
|
||||||
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
|
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable output when the handshake is trying to receive data */
|
|
||||||
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
|
inst->retry_factor = NKE_RETRY_FACTOR2_TLS;
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
char *description = gnutls_session_get_desc(inst->tls_session);
|
|
||||||
DEBUG_LOG("Handshake with %s completed %s",
|
|
||||||
inst->label, description ? description : "");
|
|
||||||
gnutls_free(description);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!check_alpn(inst)) {
|
|
||||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR, "NTS-KE not supported by %s", inst->label);
|
|
||||||
stop_session(inst);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Client will send a request to the server */
|
/* Client will send a request to the server */
|
||||||
change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
|
change_state(inst, inst->server ? KE_RECEIVE : KE_SEND);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -448,15 +338,16 @@ handle_event(NKSN_Instance inst, int event)
|
|||||||
assert(inst->new_message && message->complete);
|
assert(inst->new_message && message->complete);
|
||||||
assert(message->length <= sizeof (message->data) && message->length > message->sent);
|
assert(message->length <= sizeof (message->data) && message->length > message->sent);
|
||||||
|
|
||||||
r = gnutls_record_send(inst->tls_session, &message->data[message->sent],
|
s = TLS_Send(inst->tls_session, &message->data[message->sent],
|
||||||
message->length - message->sent);
|
message->length - message->sent, &r);
|
||||||
|
|
||||||
if (r < 0) {
|
switch (s) {
|
||||||
if (gnutls_error_is_fatal(r)) {
|
case TLS_SUCCESS:
|
||||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
break;
|
||||||
"Could not send NTS-KE message to %s : %s", inst->label, gnutls_strerror(r));
|
case TLS_AGAIN_OUTPUT:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
stop_session(inst);
|
stop_session(inst);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,18 +371,16 @@ handle_event(NKSN_Instance inst, int event)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = gnutls_record_recv(inst->tls_session, &message->data[message->length],
|
s = TLS_Receive(inst->tls_session, &message->data[message->length],
|
||||||
sizeof (message->data) - message->length);
|
sizeof (message->data) - message->length, &r);
|
||||||
|
|
||||||
if (r < 0) {
|
switch (s) {
|
||||||
/* Handle a renegotiation request on both client and server as
|
case TLS_SUCCESS:
|
||||||
a protocol error */
|
break;
|
||||||
if (gnutls_error_is_fatal(r) || r == GNUTLS_E_REHANDSHAKE) {
|
case TLS_AGAIN_INPUT:
|
||||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
return 0;
|
||||||
"Could not receive NTS-KE message from %s : %s",
|
default:
|
||||||
inst->label, gnutls_strerror(r));
|
|
||||||
stop_session(inst);
|
stop_session(inst);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +388,7 @@ handle_event(NKSN_Instance inst, int event)
|
|||||||
|
|
||||||
message->length += r;
|
message->length += r;
|
||||||
|
|
||||||
} while (gnutls_record_check_pending(inst->tls_session) > 0);
|
} while (TLS_CheckPending(inst->tls_session));
|
||||||
|
|
||||||
if (!check_message_format(message, r == 0)) {
|
if (!check_message_format(message, r == 0)) {
|
||||||
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
LOG(inst->server ? LOGS_DEBUG : LOGS_ERR,
|
||||||
@@ -519,17 +408,17 @@ handle_event(NKSN_Instance inst, int event)
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
case KE_SHUTDOWN:
|
case KE_SHUTDOWN:
|
||||||
r = gnutls_bye(inst->tls_session, GNUTLS_SHUT_RDWR);
|
s = TLS_Shutdown(inst->tls_session);
|
||||||
|
|
||||||
if (r < 0) {
|
switch (s) {
|
||||||
if (gnutls_error_is_fatal(r)) {
|
case TLS_SUCCESS:
|
||||||
DEBUG_LOG("Shutdown with %s failed : %s", inst->label, gnutls_strerror(r));
|
break;
|
||||||
stop_session(inst);
|
case TLS_AGAIN_OUTPUT:
|
||||||
|
case TLS_AGAIN_INPUT:
|
||||||
|
set_input_output(inst, s == TLS_AGAIN_OUTPUT);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
default:
|
||||||
|
stop_session(inst);
|
||||||
/* Disable output when the TLS shutdown is trying to receive data */
|
|
||||||
set_input_output(inst, gnutls_record_get_direction(inst->tls_session));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,50 +481,37 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int gnutls_initialised = 0;
|
static int tls_initialised = 0;
|
||||||
|
|
||||||
static void
|
static int
|
||||||
init_gnutls(void)
|
init_tls(void)
|
||||||
{
|
{
|
||||||
int r;
|
if (tls_initialised)
|
||||||
|
return 1;
|
||||||
|
|
||||||
if (gnutls_initialised)
|
if (!TLS_Initialise(&get_time))
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
r = gnutls_global_init();
|
tls_initialised = 1;
|
||||||
if (r < 0)
|
|
||||||
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
|
|
||||||
|
|
||||||
/* Prepare a priority cache for server and client NTS-KE sessions
|
|
||||||
(the NTS specification requires TLS1.3 or later) */
|
|
||||||
r = gnutls_priority_init2(&priority_cache,
|
|
||||||
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
|
|
||||||
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
|
|
||||||
if (r < 0)
|
|
||||||
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
|
|
||||||
|
|
||||||
/* Use our clock instead of the system clock in certificate verification */
|
|
||||||
gnutls_global_set_time_function(get_time);
|
|
||||||
|
|
||||||
gnutls_initialised = 1;
|
|
||||||
DEBUG_LOG("Initialised");
|
DEBUG_LOG("Initialised");
|
||||||
|
|
||||||
LCL_AddParameterChangeHandler(handle_step, NULL);
|
LCL_AddParameterChangeHandler(handle_step, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
deinit_gnutls(void)
|
deinit_tls(void)
|
||||||
{
|
{
|
||||||
if (!gnutls_initialised || credentials_counter > 0)
|
if (!tls_initialised || credentials_counter > 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LCL_RemoveParameterChangeHandler(handle_step, NULL);
|
LCL_RemoveParameterChangeHandler(handle_step, NULL);
|
||||||
|
|
||||||
gnutls_priority_deinit(priority_cache);
|
TLS_Finalise();
|
||||||
gnutls_global_deinit();
|
tls_initialised = 0;
|
||||||
gnutls_initialised = 0;
|
|
||||||
DEBUG_LOG("Deinitialised");
|
DEBUG_LOG("Deinitialised");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,66 +522,21 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
|
|||||||
const char **trusted_certs, uint32_t *trusted_certs_ids,
|
const char **trusted_certs, uint32_t *trusted_certs_ids,
|
||||||
int n_trusted_certs, uint32_t trusted_cert_set)
|
int n_trusted_certs, uint32_t trusted_cert_set)
|
||||||
{
|
{
|
||||||
gnutls_certificate_credentials_t credentials = NULL;
|
TLS_Credentials credentials;
|
||||||
int i, r;
|
|
||||||
|
|
||||||
init_gnutls();
|
if (!init_tls())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
r = gnutls_certificate_allocate_credentials(&credentials);
|
credentials = TLS_CreateCredentials(certs, keys, n_certs_keys, trusted_certs,
|
||||||
if (r < 0)
|
trusted_certs_ids, n_trusted_certs, trusted_cert_set);
|
||||||
goto error;
|
if (!credentials) {
|
||||||
|
deinit_tls();
|
||||||
if (certs && keys) {
|
return NULL;
|
||||||
if (trusted_certs || trusted_certs_ids)
|
|
||||||
assert(0);
|
|
||||||
|
|
||||||
for (i = 0; i < n_certs_keys; i++) {
|
|
||||||
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
|
|
||||||
GNUTLS_X509_FMT_PEM);
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (certs || keys || n_certs_keys > 0)
|
|
||||||
assert(0);
|
|
||||||
|
|
||||||
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
|
|
||||||
r = gnutls_certificate_set_x509_system_trust(credentials);
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trusted_certs && trusted_certs_ids) {
|
|
||||||
for (i = 0; i < n_trusted_certs; i++) {
|
|
||||||
struct stat buf;
|
|
||||||
|
|
||||||
if (trusted_certs_ids[i] != trusted_cert_set)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
|
|
||||||
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
|
|
||||||
GNUTLS_X509_FMT_PEM);
|
|
||||||
else
|
|
||||||
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
|
|
||||||
GNUTLS_X509_FMT_PEM);
|
|
||||||
if (r < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
credentials_counter++;
|
credentials_counter++;
|
||||||
|
|
||||||
return (NKSN_Credentials)credentials;
|
return credentials;
|
||||||
|
|
||||||
error:
|
|
||||||
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
|
|
||||||
if (credentials)
|
|
||||||
gnutls_certificate_free_credentials(credentials);
|
|
||||||
deinit_gnutls();
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -730,9 +561,9 @@ NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
|
|||||||
void
|
void
|
||||||
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
|
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
|
||||||
{
|
{
|
||||||
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
|
TLS_DestroyCredentials(credentials);
|
||||||
credentials_counter--;
|
credentials_counter--;
|
||||||
deinit_gnutls();
|
deinit_tls();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -782,9 +613,9 @@ NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
|
|||||||
{
|
{
|
||||||
assert(inst->state == KE_STOPPED);
|
assert(inst->state == KE_STOPPED);
|
||||||
|
|
||||||
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
|
inst->tls_session = TLS_CreateInstance(inst->server, sock_fd, inst->server_name,
|
||||||
(gnutls_certificate_credentials_t)credentials,
|
label, NKE_ALPN_NAME, credentials,
|
||||||
priority_cache);
|
clock_updates < CNF_GetNoCertTimeCheck());
|
||||||
if (!inst->tls_session)
|
if (!inst->tls_session)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -868,23 +699,39 @@ NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c)
|
NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm, SIV_Algorithm exporter_algorithm,
|
||||||
|
int next_protocol, NKE_Key *c2s, NKE_Key *s2c)
|
||||||
{
|
{
|
||||||
int length = SIV_GetKeyLength(siv);
|
int length = SIV_GetKeyLength(algorithm);
|
||||||
|
struct {
|
||||||
|
uint16_t next_protocol;
|
||||||
|
uint16_t algorithm;
|
||||||
|
uint8_t is_s2c;
|
||||||
|
uint8_t _pad;
|
||||||
|
} context;
|
||||||
|
|
||||||
|
if (!inst->tls_session)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
|
if (length <= 0 || length > sizeof (c2s->key) || length > sizeof (s2c->key)) {
|
||||||
DEBUG_LOG("Invalid algorithm");
|
DEBUG_LOG("Invalid algorithm");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gnutls_prf_rfc5705(inst->tls_session,
|
assert(sizeof (context) == 6);
|
||||||
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
context.next_protocol = htons(next_protocol);
|
||||||
sizeof (NKE_EXPORTER_CONTEXT_C2S) - 1, NKE_EXPORTER_CONTEXT_C2S,
|
context.algorithm = htons(exporter_algorithm);
|
||||||
length, (char *)c2s->key) < 0 ||
|
|
||||||
gnutls_prf_rfc5705(inst->tls_session,
|
context.is_s2c = 0;
|
||||||
sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||||
sizeof (NKE_EXPORTER_CONTEXT_S2C) - 1, NKE_EXPORTER_CONTEXT_S2C,
|
sizeof (context) - 1, &context, length, c2s->key)) {
|
||||||
length, (char *)s2c->key) < 0) {
|
DEBUG_LOG("Could not export key");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.is_s2c = 1;
|
||||||
|
if (!TLS_ExportKey(inst->tls_session, sizeof (NKE_EXPORTER_LABEL) - 1, NKE_EXPORTER_LABEL,
|
||||||
|
sizeof (context) - 1, &context, length, s2c->key)) {
|
||||||
DEBUG_LOG("Could not export key");
|
DEBUG_LOG("Could not export key");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ extern int NKSN_EndMessage(NKSN_Instance inst);
|
|||||||
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
|
extern int NKSN_GetRecord(NKSN_Instance inst, int *critical, int *type, int *body_length,
|
||||||
void *body, int buffer_length);
|
void *body, int buffer_length);
|
||||||
|
|
||||||
/* Export NTS keys for a specified algorithm */
|
/* Export NTS keys for a specified algorithm (for compatibility reasons the
|
||||||
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm siv, NKE_Key *c2s, NKE_Key *s2c);
|
RFC5705 exporter context is allowed to have a different algorithm) */
|
||||||
|
extern int NKSN_GetKeys(NKSN_Instance inst, SIV_Algorithm algorithm,
|
||||||
|
SIV_Algorithm exporter_algorithm,
|
||||||
|
int next_protocol, NKE_Key *c2s, NKE_Key *s2c);
|
||||||
|
|
||||||
/* Check if the session has stopped */
|
/* Check if the session has stopped */
|
||||||
extern int NKSN_IsStopped(NKSN_Instance inst);
|
extern int NKSN_IsStopped(NKSN_Instance inst);
|
||||||
|
|||||||
@@ -27,11 +27,6 @@
|
|||||||
#ifndef GOT_NTS_NTP_H
|
#ifndef GOT_NTS_NTP_H
|
||||||
#define GOT_NTS_NTP_H
|
#define GOT_NTS_NTP_H
|
||||||
|
|
||||||
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
|
|
||||||
#define NTP_EF_NTS_COOKIE 0x0204
|
|
||||||
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
|
|
||||||
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
|
|
||||||
|
|
||||||
#define NTP_KOD_NTS_NAK 0x4e54534e
|
#define NTP_KOD_NTS_NAK 0x4e54534e
|
||||||
|
|
||||||
#define NTS_MIN_UNIQ_ID_LENGTH 32
|
#define NTS_MIN_UNIQ_ID_LENGTH 32
|
||||||
|
|||||||
@@ -61,23 +61,25 @@ get_padded_length(int length)
|
|||||||
|
|
||||||
int
|
int
|
||||||
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
const unsigned char *nonce, int nonce_length,
|
const unsigned char *nonce, int max_nonce_length,
|
||||||
const unsigned char *plaintext, int plaintext_length,
|
const unsigned char *plaintext, int plaintext_length,
|
||||||
int min_ef_length)
|
int min_ef_length)
|
||||||
{
|
{
|
||||||
int auth_length, ciphertext_length, assoc_length;
|
int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length;
|
||||||
int nonce_padding, ciphertext_padding, additional_padding;
|
int nonce_padding, ciphertext_padding, additional_padding;
|
||||||
unsigned char *ciphertext, *body;
|
unsigned char *ciphertext, *body;
|
||||||
struct AuthHeader *header;
|
struct AuthHeader *header;
|
||||||
|
|
||||||
assert(sizeof (*header) == 4);
|
assert(sizeof (*header) == 4);
|
||||||
|
|
||||||
if (nonce_length <= 0 || plaintext_length < 0) {
|
if (max_nonce_length <= 0 || plaintext_length < 0) {
|
||||||
DEBUG_LOG("Invalid nonce/plaintext length");
|
DEBUG_LOG("Invalid nonce/plaintext length");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
assoc_length = info->length;
|
assoc_length = info->length;
|
||||||
|
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
|
||||||
|
nonce_length = MIN(max_nonce_length, max_siv_nonce_length);
|
||||||
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
|
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
|
||||||
nonce_padding = get_padding_length(nonce_length);
|
nonce_padding = get_padding_length(nonce_length);
|
||||||
ciphertext_padding = get_padding_length(ciphertext_length);
|
ciphertext_padding = get_padding_length(ciphertext_length);
|
||||||
@@ -86,8 +88,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
|||||||
auth_length = sizeof (*header) + nonce_length + nonce_padding +
|
auth_length = sizeof (*header) + nonce_length + nonce_padding +
|
||||||
ciphertext_length + ciphertext_padding;
|
ciphertext_length + ciphertext_padding;
|
||||||
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
|
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
|
||||||
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
|
additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) -
|
||||||
additional_padding);
|
nonce_length - nonce_padding, additional_padding);
|
||||||
auth_length += additional_padding;
|
auth_length += additional_padding;
|
||||||
|
|
||||||
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
|
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
|
||||||
@@ -102,9 +104,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
|||||||
body = (unsigned char *)(header + 1);
|
body = (unsigned char *)(header + 1);
|
||||||
ciphertext = body + nonce_length + nonce_padding;
|
ciphertext = body + nonce_length + nonce_padding;
|
||||||
|
|
||||||
if ((unsigned char *)header + auth_length !=
|
BRIEF_ASSERT((unsigned char *)header + auth_length ==
|
||||||
ciphertext + ciphertext_length + ciphertext_padding + additional_padding)
|
ciphertext + ciphertext_length + ciphertext_padding + additional_padding);
|
||||||
assert(0);
|
|
||||||
|
|
||||||
memcpy(body, nonce, nonce_length);
|
memcpy(body, nonce, nonce_length);
|
||||||
memset(body + nonce_length, 0, nonce_padding);
|
memset(body + nonce_length, 0, nonce_padding);
|
||||||
@@ -113,6 +114,7 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
|||||||
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
|
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
|
||||||
DEBUG_LOG("SIV encrypt failed");
|
DEBUG_LOG("SIV encrypt failed");
|
||||||
info->length = assoc_length;
|
info->length = assoc_length;
|
||||||
|
info->ext_fields--;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +129,7 @@ int
|
|||||||
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
|
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
|
||||||
unsigned char *plaintext, int buffer_length, int *plaintext_length)
|
unsigned char *plaintext, int buffer_length, int *plaintext_length)
|
||||||
{
|
{
|
||||||
unsigned int siv_tag_length, nonce_length, ciphertext_length;
|
int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length;
|
||||||
unsigned char *nonce, *ciphertext;
|
unsigned char *nonce, *ciphertext;
|
||||||
int ef_type, ef_body_length;
|
int ef_type, ef_body_length;
|
||||||
void *ef_body;
|
void *ef_body;
|
||||||
@@ -155,6 +157,7 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
|
|||||||
nonce = (unsigned char *)(header + 1);
|
nonce = (unsigned char *)(header + 1);
|
||||||
ciphertext = nonce + get_padded_length(nonce_length);
|
ciphertext = nonce + get_padded_length(nonce_length);
|
||||||
|
|
||||||
|
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
|
||||||
siv_tag_length = SIV_GetTagLength(siv);
|
siv_tag_length = SIV_GetTagLength(siv);
|
||||||
|
|
||||||
if (nonce_length < 1 ||
|
if (nonce_length < 1 ||
|
||||||
@@ -164,8 +167,8 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ef_body_length < sizeof (*header) +
|
if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) +
|
||||||
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
|
get_padded_length(ciphertext_length) > ef_body_length) {
|
||||||
DEBUG_LOG("Missing padding");
|
DEBUG_LOG("Missing padding");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include "siv.h"
|
#include "siv.h"
|
||||||
|
|
||||||
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
const unsigned char *nonce, int nonce_length,
|
const unsigned char *nonce, int max_nonce_length,
|
||||||
const unsigned char *plaintext, int plaintext_length,
|
const unsigned char *plaintext, int plaintext_length,
|
||||||
int min_ef_length);
|
int min_ef_length);
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,9 @@
|
|||||||
/* Maximum length of all cookies to avoid IP fragmentation */
|
/* Maximum length of all cookies to avoid IP fragmentation */
|
||||||
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
|
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
|
||||||
|
|
||||||
|
/* Retry interval for NTS-KE start (which doesn't generate network traffic) */
|
||||||
|
#define RETRY_INTERVAL_KE_START 2.0
|
||||||
|
|
||||||
/* Magic string of files containing keys and cookies */
|
/* Magic string of files containing keys and cookies */
|
||||||
#define DUMP_IDENTIFIER "NNC0\n"
|
#define DUMP_IDENTIFIER "NNC0\n"
|
||||||
|
|
||||||
@@ -69,6 +72,7 @@ struct NNC_Instance_Record {
|
|||||||
double last_nke_success;
|
double last_nke_success;
|
||||||
|
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
|
NKE_Context alt_context;
|
||||||
unsigned int context_id;
|
unsigned int context_id;
|
||||||
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
@@ -102,6 +106,7 @@ reset_instance(NNC_Instance inst)
|
|||||||
inst->last_nke_success = 0.0;
|
inst->last_nke_success = 0.0;
|
||||||
|
|
||||||
memset(&inst->context, 0, sizeof (inst->context));
|
memset(&inst->context, 0, sizeof (inst->context));
|
||||||
|
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
|
||||||
inst->context_id = 0;
|
inst->context_id = 0;
|
||||||
memset(inst->cookies, 0, sizeof (inst->cookies));
|
memset(inst->cookies, 0, sizeof (inst->cookies));
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
@@ -162,6 +167,21 @@ check_cookies(NNC_Instance inst)
|
|||||||
if (inst->num_cookies > 0 &&
|
if (inst->num_cookies > 0 &&
|
||||||
((inst->nak_response && !inst->ok_response) ||
|
((inst->nak_response && !inst->ok_response) ||
|
||||||
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
|
SCH_GetLastEventMonoTime() - inst->last_nke_success > CNF_GetNtsRefresh())) {
|
||||||
|
|
||||||
|
/* Before dropping the cookies, check whether there is an alternate set of
|
||||||
|
keys available (exported with the compliant context for AES-128-GCM-SIV)
|
||||||
|
and the NAK was the only valid response after the last NTS-KE session,
|
||||||
|
indicating we use incorrect keys and switching to the other set of keys
|
||||||
|
for the following NTP requests might work */
|
||||||
|
if (inst->alt_context.algorithm != AEAD_SIV_INVALID &&
|
||||||
|
inst->alt_context.algorithm == inst->context.algorithm &&
|
||||||
|
inst->nke_attempts > 0 && inst->nak_response && !inst->ok_response) {
|
||||||
|
inst->context = inst->alt_context;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
|
DEBUG_LOG("Switched to compliant keys");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
DEBUG_LOG("Dropped cookies");
|
DEBUG_LOG("Dropped cookies");
|
||||||
}
|
}
|
||||||
@@ -203,10 +223,15 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_next_nke_attempt(NNC_Instance inst, double now)
|
update_next_nke_attempt(NNC_Instance inst, int failed_start, double now)
|
||||||
{
|
{
|
||||||
int factor, interval;
|
int factor, interval;
|
||||||
|
|
||||||
|
if (failed_start) {
|
||||||
|
inst->next_nke_attempt = now + RETRY_INTERVAL_KE_START;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!inst->nke)
|
if (!inst->nke)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -221,8 +246,8 @@ static int
|
|||||||
get_cookies(NNC_Instance inst)
|
get_cookies(NNC_Instance inst)
|
||||||
{
|
{
|
||||||
NTP_Remote_Address ntp_address;
|
NTP_Remote_Address ntp_address;
|
||||||
|
int got_data, failed_start = 0;
|
||||||
double now;
|
double now;
|
||||||
int got_data;
|
|
||||||
|
|
||||||
assert(inst->num_cookies == 0);
|
assert(inst->num_cookies == 0);
|
||||||
|
|
||||||
@@ -239,13 +264,12 @@ get_cookies(NNC_Instance inst)
|
|||||||
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
|
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
|
||||||
|
|
||||||
inst->nke_attempts++;
|
inst->nke_attempts++;
|
||||||
update_next_nke_attempt(inst, now);
|
|
||||||
|
|
||||||
if (!NKC_Start(inst->nke))
|
if (!NKC_Start(inst->nke))
|
||||||
return 0;
|
failed_start = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_next_nke_attempt(inst, now);
|
update_next_nke_attempt(inst, failed_start, now);
|
||||||
|
|
||||||
/* Wait until the session stops */
|
/* Wait until the session stops */
|
||||||
if (NKC_IsActive(inst->nke))
|
if (NKC_IsActive(inst->nke))
|
||||||
@@ -254,7 +278,7 @@ get_cookies(NNC_Instance inst)
|
|||||||
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
|
assert(sizeof (inst->cookies) / sizeof (inst->cookies[0]) == NTS_MAX_COOKIES);
|
||||||
|
|
||||||
/* Get the new keys, cookies and NTP address if the session was successful */
|
/* Get the new keys, cookies and NTP address if the session was successful */
|
||||||
got_data = NKC_GetNtsData(inst->nke, &inst->context,
|
got_data = NKC_GetNtsData(inst->nke, &inst->context, &inst->alt_context,
|
||||||
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
|
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
|
||||||
&ntp_address);
|
&ntp_address);
|
||||||
|
|
||||||
@@ -457,7 +481,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
|
|||||||
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
|
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
|
||||||
if (!NEF_ParseField(packet, info->length, parsed,
|
if (!NEF_ParseField(packet, info->length, parsed,
|
||||||
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
/* This is not expected as the packet already passed NAU_ParsePacket() */
|
/* This is not expected as the packet already passed parsing */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (ef_type) {
|
switch (ef_type) {
|
||||||
@@ -513,6 +537,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
|
|||||||
new NTS-KE session to be started as soon as the cookies run out. */
|
new NTS-KE session to be started as soon as the cookies run out. */
|
||||||
inst->nke_attempts = 0;
|
inst->nke_attempts = 0;
|
||||||
inst->next_nke_attempt = 0.0;
|
inst->next_nke_attempt = 0.0;
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -636,6 +661,7 @@ load_cookies(NNC_Instance inst)
|
|||||||
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
|
sscanf(words[0], "%u", &context_id) != 1 || sscanf(words[1], "%d", &algorithm) != 1)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
inst->alt_context.algorithm = AEAD_SIV_INVALID;
|
||||||
inst->context.algorithm = algorithm;
|
inst->context.algorithm = algorithm;
|
||||||
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
|
inst->context.s2c.length = UTI_HexToBytes(words[2], inst->context.s2c.key,
|
||||||
sizeof (inst->context.s2c.key));
|
sizeof (inst->context.s2c.key));
|
||||||
@@ -643,6 +669,7 @@ load_cookies(NNC_Instance inst)
|
|||||||
sizeof (inst->context.c2s.key));
|
sizeof (inst->context.c2s.key));
|
||||||
|
|
||||||
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
|
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
|
||||||
|
inst->context.s2c.length <= 0 ||
|
||||||
inst->context.c2s.length != inst->context.s2c.length)
|
inst->context.c2s.length != inst->context.s2c.length)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@@ -669,6 +696,8 @@ load_cookies(NNC_Instance inst)
|
|||||||
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
|
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
|
||||||
inst->context_id = context_id;
|
inst->context_id = context_id;
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
DEBUG_LOG("Loaded %d cookies for %s", i, filename);
|
DEBUG_LOG("Loaded %d cookies for %s", i, filename);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -677,6 +706,7 @@ error:
|
|||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
memset(&inst->context, 0, sizeof (inst->context));
|
memset(&inst->context, 0, sizeof (inst->context));
|
||||||
|
memset(&inst->alt_context, 0, sizeof (inst->alt_context));
|
||||||
inst->num_cookies = 0;
|
inst->num_cookies = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2020
|
* Copyright (C) Miroslav Lichvar 2020, 2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -41,13 +41,15 @@
|
|||||||
#include "siv.h"
|
#include "siv.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define SERVER_SIV AEAD_AES_SIV_CMAC_256
|
#define MAX_SERVER_SIVS 2
|
||||||
|
|
||||||
struct NtsServer {
|
struct NtsServer {
|
||||||
SIV_Instance siv;
|
SIV_Instance sivs[MAX_SERVER_SIVS];
|
||||||
|
SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS];
|
||||||
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
|
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
|
||||||
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
int num_cookies;
|
int num_cookies;
|
||||||
|
int siv_index;
|
||||||
NTP_int64 req_tx;
|
NTP_int64 req_tx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ void
|
|||||||
NNS_Initialise(void)
|
NNS_Initialise(void)
|
||||||
{
|
{
|
||||||
const char **certs, **keys;
|
const char **certs, **keys;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
|
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
|
||||||
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
|
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
|
||||||
@@ -68,9 +71,17 @@ NNS_Initialise(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
server = Malloc(sizeof (struct NtsServer));
|
server = Malloc(sizeof (struct NtsServer));
|
||||||
server->siv = SIV_CreateInstance(SERVER_SIV);
|
|
||||||
if (!server->siv)
|
server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
|
||||||
LOG_FATAL("Could not initialise SIV cipher");
|
server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV;
|
||||||
|
assert(MAX_SERVER_SIVS == 2);
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]);
|
||||||
|
|
||||||
|
/* AES-SIV-CMAC-256 is required on servers */
|
||||||
|
if (!server->sivs[0])
|
||||||
|
LOG_FATAL("Missing AES-SIV-CMAC-256");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -78,10 +89,15 @@ NNS_Initialise(void)
|
|||||||
void
|
void
|
||||||
NNS_Finalise(void)
|
NNS_Finalise(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!server)
|
if (!server)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SIV_DestroyInstance(server->siv);
|
for (i = 0; i < MAX_SERVER_SIVS; i++) {
|
||||||
|
if (server->sivs[i])
|
||||||
|
SIV_DestroyInstance(server->sivs[i]);
|
||||||
|
}
|
||||||
Free(server);
|
Free(server);
|
||||||
server = NULL;
|
server = NULL;
|
||||||
}
|
}
|
||||||
@@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
||||||
NKE_Context context;
|
NKE_Context context;
|
||||||
NKE_Cookie cookie;
|
NKE_Cookie cookie;
|
||||||
|
SIV_Instance siv;
|
||||||
void *ef_body;
|
void *ef_body;
|
||||||
|
|
||||||
*kod = 0;
|
*kod = 0;
|
||||||
@@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
server->num_cookies = 0;
|
server->num_cookies = 0;
|
||||||
|
server->siv_index = -1;
|
||||||
server->req_tx = packet->transmit_ts;
|
server->req_tx = packet->transmit_ts;
|
||||||
|
|
||||||
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
|
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
|
||||||
@@ -163,17 +181,22 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.algorithm != SERVER_SIV) {
|
/* Find the SIV instance needed for authentication */
|
||||||
|
for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++)
|
||||||
|
;
|
||||||
|
if (i == MAX_SERVER_SIVS || !server->sivs[i]) {
|
||||||
DEBUG_LOG("Unexpected SIV");
|
DEBUG_LOG("Unexpected SIV");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
server->siv_index = i;
|
||||||
|
siv = server->sivs[i];
|
||||||
|
|
||||||
if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) {
|
if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) {
|
||||||
DEBUG_LOG("Could not set C2S key");
|
DEBUG_LOG("Could not set C2S key");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
|
if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
|
||||||
plaintext, sizeof (plaintext), &plaintext_length)) {
|
plaintext, sizeof (plaintext), &plaintext_length)) {
|
||||||
*kod = NTP_KOD_NTS_NAK;
|
*kod = NTP_KOD_NTS_NAK;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -199,7 +222,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
|
if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) {
|
||||||
DEBUG_LOG("Could not set S2C key");
|
DEBUG_LOG("Could not set S2C key");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -236,13 +259,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
|||||||
|
|
||||||
/* Make sure this is a response to the request from the last call
|
/* Make sure this is a response to the request from the last call
|
||||||
of NNS_CheckRequestAuth() */
|
of NNS_CheckRequestAuth() */
|
||||||
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
|
BRIEF_ASSERT(UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) == 0);
|
||||||
assert(0);
|
|
||||||
|
|
||||||
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
|
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
|
||||||
if (!NEF_ParseField(request, req_info->length, parsed,
|
if (!NEF_ParseField(request, req_info->length, parsed,
|
||||||
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
/* This is not expected as the packet already passed NAU_ParsePacket() */
|
/* This is not expected as the packet already passed parsing */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
switch (ef_type) {
|
switch (ef_type) {
|
||||||
@@ -256,7 +278,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* NTS NAK response does not have any other fields */
|
/* NTS NAK response does not have any other fields */
|
||||||
if (kod)
|
if (kod == NTP_KOD_NTS_NAK)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
|
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
|
||||||
@@ -271,9 +293,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
|||||||
|
|
||||||
server->num_cookies = 0;
|
server->num_cookies = 0;
|
||||||
|
|
||||||
|
if (server->siv_index < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Generate an authenticator field which will make the length
|
/* Generate an authenticator field which will make the length
|
||||||
of the response equal to the length of the request */
|
of the response equal to the length of the request */
|
||||||
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
|
if (!NNA_GenerateAuthEF(response, res_info, server->sivs[server->siv_index],
|
||||||
server->nonce, sizeof (server->nonce),
|
server->nonce, sizeof (server->nonce),
|
||||||
plaintext, plaintext_length,
|
plaintext, plaintext_length,
|
||||||
req_info->length - res_info->length))
|
req_info->length - res_info->length))
|
||||||
|
|||||||
16
pktlength.c
16
pktlength.c
@@ -55,7 +55,7 @@ struct request_length {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct request_length request_lengths[] = {
|
static const struct request_length request_lengths[] = {
|
||||||
REQ_LENGTH_ENTRY(null, null), /* NULL */
|
{ 0, 0 }, /* NULL - not supported */
|
||||||
REQ_LENGTH_ENTRY(online, null), /* ONLINE */
|
REQ_LENGTH_ENTRY(online, null), /* ONLINE */
|
||||||
REQ_LENGTH_ENTRY(offline, null), /* OFFLINE */
|
REQ_LENGTH_ENTRY(offline, null), /* OFFLINE */
|
||||||
REQ_LENGTH_ENTRY(burst, null), /* BURST */
|
REQ_LENGTH_ENTRY(burst, null), /* BURST */
|
||||||
@@ -65,7 +65,7 @@ static const struct request_length request_lengths[] = {
|
|||||||
REQ_LENGTH_ENTRY(modify_maxdelay, null), /* MODIFY_MAXDELAY */
|
REQ_LENGTH_ENTRY(modify_maxdelay, null), /* MODIFY_MAXDELAY */
|
||||||
REQ_LENGTH_ENTRY(modify_maxdelayratio, null), /* MODIFY_MAXDELAYRATIO */
|
REQ_LENGTH_ENTRY(modify_maxdelayratio, null), /* MODIFY_MAXDELAYRATIO */
|
||||||
REQ_LENGTH_ENTRY(modify_maxupdateskew, null), /* MODIFY_MAXUPDATESKEW */
|
REQ_LENGTH_ENTRY(modify_maxupdateskew, null), /* MODIFY_MAXUPDATESKEW */
|
||||||
REQ_LENGTH_ENTRY(logon, null), /* LOGON */
|
{ 0, 0 }, /* LOGON - not supported */
|
||||||
REQ_LENGTH_ENTRY(settime, manual_timestamp), /* SETTIME */
|
REQ_LENGTH_ENTRY(settime, manual_timestamp), /* SETTIME */
|
||||||
{ 0, 0 }, /* LOCAL */
|
{ 0, 0 }, /* LOCAL */
|
||||||
REQ_LENGTH_ENTRY(manual, null), /* MANUAL */
|
REQ_LENGTH_ENTRY(manual, null), /* MANUAL */
|
||||||
@@ -111,7 +111,7 @@ static const struct request_length request_lengths[] = {
|
|||||||
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
|
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
|
||||||
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
|
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
|
||||||
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
|
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
|
||||||
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
|
{ 0, 0 }, /* LOCAL2 - not supported */
|
||||||
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
|
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
|
||||||
{ 0, 0 }, /* ADD_SERVER2 */
|
{ 0, 0 }, /* ADD_SERVER2 */
|
||||||
{ 0, 0 }, /* ADD_PEER2 */
|
{ 0, 0 }, /* ADD_PEER2 */
|
||||||
@@ -129,6 +129,9 @@ static const struct request_length request_lengths[] = {
|
|||||||
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
|
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
|
||||||
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
|
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
|
||||||
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
|
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
|
||||||
|
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
|
||||||
|
REQ_LENGTH_ENTRY(modify_offset, null), /* MODIFY_OFFSET */
|
||||||
|
REQ_LENGTH_ENTRY(local, null), /* LOCAL3 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint16_t reply_lengths[] = {
|
static const uint16_t reply_lengths[] = {
|
||||||
@@ -148,14 +151,17 @@ static const uint16_t reply_lengths[] = {
|
|||||||
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
|
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
|
||||||
0, /* SERVER_STATS - not supported */
|
0, /* SERVER_STATS - not supported */
|
||||||
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
|
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
|
||||||
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
|
0, /* NTP_DATA - not supported */
|
||||||
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
|
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
|
||||||
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
|
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
|
||||||
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
|
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
|
||||||
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
|
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
|
||||||
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
|
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
|
||||||
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS2 */
|
0, /* SERVER_STATS2 - not supported */
|
||||||
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
|
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
|
||||||
|
0, /* SERVER_STATS3 - not supported */
|
||||||
|
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
|
||||||
|
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA2 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|||||||
11
privops.c
11
privops.c
@@ -141,6 +141,7 @@ have_helper(void)
|
|||||||
/* ======================================================================= */
|
/* ======================================================================= */
|
||||||
|
|
||||||
/* HELPER - prepare fatal error for daemon */
|
/* HELPER - prepare fatal error for daemon */
|
||||||
|
FORMAT_ATTRIBUTE_PRINTF(2, 3)
|
||||||
static void
|
static void
|
||||||
res_fatal(PrvResponse *res, const char *fmt, ...)
|
res_fatal(PrvResponse *res, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@@ -255,7 +256,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
|
|||||||
|
|
||||||
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
|
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
|
||||||
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
|
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
|
||||||
ip_saddr.port != CNF_GetAcquisitionPort()) {
|
ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort()) {
|
||||||
SCK_CloseSocket(sock_fd);
|
SCK_CloseSocket(sock_fd);
|
||||||
res_fatal(res, "Invalid port %d", ip_saddr.port);
|
res_fatal(res, "Invalid port %d", ip_saddr.port);
|
||||||
return;
|
return;
|
||||||
@@ -546,9 +547,9 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
|
|||||||
PrvResponse res;
|
PrvResponse res;
|
||||||
|
|
||||||
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
|
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
|
||||||
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
|
BRIEF_ASSERT(ip_saddr.port == 0 || ip_saddr.port == CNF_GetNTPPort() ||
|
||||||
ip_saddr.port != CNF_GetAcquisitionPort())
|
ip_saddr.port == CNF_GetAcquisitionPort() ||
|
||||||
assert(0);
|
ip_saddr.port == CNF_GetPtpPort());
|
||||||
|
|
||||||
if (!have_helper())
|
if (!have_helper())
|
||||||
return bind(sock, address, address_len);
|
return bind(sock, address, address_len);
|
||||||
@@ -662,6 +663,8 @@ PRV_StartHelper(void)
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UTI_ResetGetRandomFunctions();
|
||||||
|
|
||||||
/* ignore signals, the process will exit on OP_QUIT request */
|
/* ignore signals, the process will exit on OP_QUIT request */
|
||||||
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
|
UTI_SetQuitSignalsHandler(SIG_IGN, 1);
|
||||||
|
|
||||||
|
|||||||
70
ptp.h
Normal file
70
ptp.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2021
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
This is the header file for the Precision Time Protocol (PTP).
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef GOT_PTP_H
|
||||||
|
#define GOT_PTP_H
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
|
#define PTP_VERSION_2 2
|
||||||
|
#define PTP_VERSION_2_1 (2 | 1 << 4)
|
||||||
|
#define PTP_TYPE_SYNC 0
|
||||||
|
#define PTP_TYPE_DELAY_REQ 1
|
||||||
|
#define PTP_FLAG_UNICAST (1 << (2 + 8))
|
||||||
|
#define PTP_TLV_NTP 0x2023
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t version;
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t domain;
|
||||||
|
uint8_t min_sdoid;
|
||||||
|
uint16_t flags;
|
||||||
|
uint8_t correction[8];
|
||||||
|
uint8_t msg_specific[4];
|
||||||
|
uint8_t port_id[10];
|
||||||
|
uint16_t sequence_id;
|
||||||
|
uint8_t control;
|
||||||
|
int8_t interval;
|
||||||
|
} PTP_Header;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t length;
|
||||||
|
} PTP_TlvHeader;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PTP_Header header;
|
||||||
|
uint8_t origin_ts[10];
|
||||||
|
PTP_TlvHeader tlv_header;
|
||||||
|
NTP_Packet ntp_msg;
|
||||||
|
} PTP_NtpMessage;
|
||||||
|
|
||||||
|
#define PTP_NTP_PREFIX_LENGTH (int)offsetof(PTP_NtpMessage, ntp_msg)
|
||||||
|
|
||||||
|
#endif
|
||||||
232
quantiles.c
Normal file
232
quantiles.c
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Estimation of quantiles using the Frugal-2U streaming algorithm
|
||||||
|
(https://arxiv.org/pdf/1407.1121v1.pdf)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "quantiles.h"
|
||||||
|
#include "regress.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/* Maximum number of repeated estimates for stabilisation */
|
||||||
|
#define MAX_REPEAT 64
|
||||||
|
|
||||||
|
struct Quantile {
|
||||||
|
double est;
|
||||||
|
double step;
|
||||||
|
int sign;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct QNT_Instance_Record {
|
||||||
|
struct Quantile *quants;
|
||||||
|
int n_quants;
|
||||||
|
int repeat;
|
||||||
|
int q;
|
||||||
|
int min_k;
|
||||||
|
double min_step;
|
||||||
|
double neg_step_limit;
|
||||||
|
int n_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
QNT_Instance
|
||||||
|
QNT_CreateInstance(int min_k, int max_k, int q, int repeat,
|
||||||
|
int large_step_delay, double min_step)
|
||||||
|
{
|
||||||
|
QNT_Instance inst;
|
||||||
|
long seed;
|
||||||
|
|
||||||
|
BRIEF_ASSERT(q >= 2 && min_k <= max_k && min_k >= 1 && max_k < q && repeat >= 1 &&
|
||||||
|
repeat <= MAX_REPEAT && min_step > 0.0 && large_step_delay >= 0);
|
||||||
|
|
||||||
|
inst = MallocNew(struct QNT_Instance_Record);
|
||||||
|
inst->n_quants = (max_k - min_k + 1) * repeat;
|
||||||
|
inst->quants = MallocArray(struct Quantile, inst->n_quants);
|
||||||
|
inst->repeat = repeat;
|
||||||
|
inst->q = q;
|
||||||
|
inst->min_k = min_k;
|
||||||
|
inst->min_step = min_step;
|
||||||
|
inst->neg_step_limit = -large_step_delay * min_step;
|
||||||
|
|
||||||
|
QNT_Reset(inst);
|
||||||
|
|
||||||
|
/* Seed the random number generator, which will not be isolated from
|
||||||
|
other instances and other random() users */
|
||||||
|
UTI_GetRandomBytes(&seed, sizeof (seed));
|
||||||
|
srandom(seed);
|
||||||
|
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
QNT_DestroyInstance(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
Free(inst->quants);
|
||||||
|
Free(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
QNT_Reset(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
inst->n_set = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < inst->n_quants; i++) {
|
||||||
|
inst->quants[i].est = 0.0;
|
||||||
|
inst->quants[i].step = inst->min_step;
|
||||||
|
inst->quants[i].sign = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
insert_initial_value(QNT_Instance inst, double value)
|
||||||
|
{
|
||||||
|
int i, j, r = inst->repeat;
|
||||||
|
|
||||||
|
BRIEF_ASSERT(inst->n_set * r < inst->n_quants);
|
||||||
|
|
||||||
|
/* Keep the initial estimates repeated and ordered */
|
||||||
|
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
|
||||||
|
for (j = 0; j < r; j++)
|
||||||
|
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < r; j++)
|
||||||
|
inst->quants[i * r + j].est = value;
|
||||||
|
inst->n_set++;
|
||||||
|
|
||||||
|
/* Duplicate the largest value in unset quantiles */
|
||||||
|
for (i = inst->n_set * r; i < inst->n_quants; i++)
|
||||||
|
inst->quants[i].est = inst->quants[i - 1].est;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_estimate(struct Quantile *quantile, double value, double p, double rand,
|
||||||
|
double min_step, double neg_step_limit)
|
||||||
|
{
|
||||||
|
if (value >= quantile->est) {
|
||||||
|
if (rand < (1.0 - p))
|
||||||
|
return;
|
||||||
|
quantile->step += quantile->sign > 0 ? min_step : -min_step;
|
||||||
|
quantile->est += quantile->step > min_step ? quantile->step : min_step;
|
||||||
|
if (quantile->est > value) {
|
||||||
|
quantile->step += value - quantile->est;
|
||||||
|
quantile->est = value + min_step / 4.0;
|
||||||
|
}
|
||||||
|
if (quantile->sign < 0 && quantile->step > min_step)
|
||||||
|
quantile->step = min_step;
|
||||||
|
quantile->sign = 1;
|
||||||
|
} else {
|
||||||
|
if (rand < p)
|
||||||
|
return;
|
||||||
|
quantile->step += quantile->sign < 0 ? min_step : -min_step;
|
||||||
|
quantile->est -= quantile->step > min_step ? quantile->step : min_step;
|
||||||
|
if (quantile->est < value) {
|
||||||
|
quantile->step += quantile->est - value;
|
||||||
|
quantile->est = value - min_step / 4.0;
|
||||||
|
}
|
||||||
|
if (quantile->sign > 0 && quantile->step > min_step)
|
||||||
|
quantile->step = min_step;
|
||||||
|
quantile->sign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quantile->step < neg_step_limit)
|
||||||
|
quantile->step = neg_step_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
QNT_Accumulate(QNT_Instance inst, double value)
|
||||||
|
{
|
||||||
|
double p, rand;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Initialise the estimates with first received values */
|
||||||
|
if (inst->n_set * inst->repeat < inst->n_quants) {
|
||||||
|
insert_initial_value(inst, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < inst->n_quants; i++) {
|
||||||
|
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
|
||||||
|
rand = (double)random() / ((1U << 31) - 1);
|
||||||
|
|
||||||
|
update_estimate(&inst->quants[i], value, p, rand, inst->min_step, inst->neg_step_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
QNT_GetMinK(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
return inst->min_k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
QNT_GetMaxK(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
return inst->min_k + (inst->n_quants / inst->repeat) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
double
|
||||||
|
QNT_GetMinStep(QNT_Instance inst)
|
||||||
|
{
|
||||||
|
return inst->min_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
double
|
||||||
|
QNT_GetQuantile(QNT_Instance inst, int k)
|
||||||
|
{
|
||||||
|
double estimates[MAX_REPEAT];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
BRIEF_ASSERT(k >= inst->min_k && (k - inst->min_k) * inst->repeat < inst->n_quants);
|
||||||
|
|
||||||
|
for (i = 0; i < inst->repeat; i++)
|
||||||
|
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
|
||||||
|
|
||||||
|
return RGR_FindMedian(estimates, inst->repeat);
|
||||||
|
}
|
||||||
44
quantiles.h
Normal file
44
quantiles.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Header file for estimation of quantiles.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_QUANTILES_H
|
||||||
|
#define GOT_QUANTILES_H
|
||||||
|
|
||||||
|
typedef struct QNT_Instance_Record *QNT_Instance;
|
||||||
|
|
||||||
|
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat,
|
||||||
|
int large_step_delay, double min_step);
|
||||||
|
extern void QNT_DestroyInstance(QNT_Instance inst);
|
||||||
|
|
||||||
|
extern void QNT_Reset(QNT_Instance inst);
|
||||||
|
extern void QNT_Accumulate(QNT_Instance inst, double value);
|
||||||
|
extern int QNT_GetMinK(QNT_Instance inst);
|
||||||
|
extern int QNT_GetMaxK(QNT_Instance inst);
|
||||||
|
extern double QNT_GetMinStep(QNT_Instance inst);
|
||||||
|
extern double QNT_GetQuantile(QNT_Instance inst, int k);
|
||||||
|
|
||||||
|
#endif
|
||||||
205
refclock.c
205
refclock.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
|
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -48,6 +48,7 @@ extern RefclockDriver RCL_SHM_driver;
|
|||||||
extern RefclockDriver RCL_SOCK_driver;
|
extern RefclockDriver RCL_SOCK_driver;
|
||||||
extern RefclockDriver RCL_PPS_driver;
|
extern RefclockDriver RCL_PPS_driver;
|
||||||
extern RefclockDriver RCL_PHC_driver;
|
extern RefclockDriver RCL_PHC_driver;
|
||||||
|
extern RefclockDriver RCL_RTC_driver;
|
||||||
|
|
||||||
struct FilterSample {
|
struct FilterSample {
|
||||||
double offset;
|
double offset;
|
||||||
@@ -55,21 +56,6 @@ struct FilterSample {
|
|||||||
struct timespec sample_time;
|
struct timespec sample_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MedianFilter {
|
|
||||||
int length;
|
|
||||||
int index;
|
|
||||||
int used;
|
|
||||||
int last;
|
|
||||||
int avg_var_n;
|
|
||||||
double avg_var;
|
|
||||||
double max_var;
|
|
||||||
struct FilterSample *samples;
|
|
||||||
int *selected;
|
|
||||||
double *x_data;
|
|
||||||
double *y_data;
|
|
||||||
double *w_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RCL_Instance_Record {
|
struct RCL_Instance_Record {
|
||||||
RefclockDriver *driver;
|
RefclockDriver *driver;
|
||||||
void *data;
|
void *data;
|
||||||
@@ -78,7 +64,9 @@ struct RCL_Instance_Record {
|
|||||||
int driver_poll;
|
int driver_poll;
|
||||||
int driver_polled;
|
int driver_polled;
|
||||||
int poll;
|
int poll;
|
||||||
|
int reached;
|
||||||
int leap_status;
|
int leap_status;
|
||||||
|
int local;
|
||||||
int pps_forced;
|
int pps_forced;
|
||||||
int pps_rate;
|
int pps_rate;
|
||||||
int pps_active;
|
int pps_active;
|
||||||
@@ -173,6 +161,8 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||||||
inst->driver = &RCL_PPS_driver;
|
inst->driver = &RCL_PPS_driver;
|
||||||
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
} else if (strcmp(params->driver_name, "PHC") == 0) {
|
||||||
inst->driver = &RCL_PHC_driver;
|
inst->driver = &RCL_PHC_driver;
|
||||||
|
} else if (strcmp(params->driver_name, "RTC") == 0) {
|
||||||
|
inst->driver = &RCL_RTC_driver;
|
||||||
} else {
|
} else {
|
||||||
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
LOG_FATAL("unknown refclock driver %s", params->driver_name);
|
||||||
}
|
}
|
||||||
@@ -180,8 +170,8 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||||||
if (!inst->driver->init && !inst->driver->poll)
|
if (!inst->driver->init && !inst->driver->poll)
|
||||||
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
|
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
|
||||||
|
|
||||||
if (params->tai && !CNF_GetLeapSecTimezone())
|
if (params->tai && !CNF_GetLeapSecList() && !CNF_GetLeapSecTimezone())
|
||||||
LOG_FATAL("refclock tai option requires leapsectz");
|
LOG_FATAL("refclock tai option requires leapseclist or leapsectz");
|
||||||
|
|
||||||
inst->data = NULL;
|
inst->data = NULL;
|
||||||
inst->driver_parameter = Strdup(params->driver_parameter);
|
inst->driver_parameter = Strdup(params->driver_parameter);
|
||||||
@@ -189,7 +179,9 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||||||
inst->driver_poll = params->driver_poll;
|
inst->driver_poll = params->driver_poll;
|
||||||
inst->poll = params->poll;
|
inst->poll = params->poll;
|
||||||
inst->driver_polled = 0;
|
inst->driver_polled = 0;
|
||||||
|
inst->reached = 0;
|
||||||
inst->leap_status = LEAP_Normal;
|
inst->leap_status = LEAP_Normal;
|
||||||
|
inst->local = params->local;
|
||||||
inst->pps_forced = params->pps_forced;
|
inst->pps_forced = params->pps_forced;
|
||||||
inst->pps_rate = params->pps_rate;
|
inst->pps_rate = params->pps_rate;
|
||||||
inst->pps_active = 0;
|
inst->pps_active = 0;
|
||||||
@@ -231,34 +223,28 @@ RCL_AddRefclock(RefclockParameters *params)
|
|||||||
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->driver->poll) {
|
if (inst->local) {
|
||||||
int max_samples;
|
inst->pps_forced = 1;
|
||||||
|
inst->lock_ref = inst->ref_id;
|
||||||
|
inst->leap_status = LEAP_Unsynchronised;
|
||||||
|
inst->max_lock_age = MAX(inst->max_lock_age, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst->driver->poll) {
|
||||||
if (inst->driver_poll > inst->poll)
|
if (inst->driver_poll > inst->poll)
|
||||||
inst->driver_poll = inst->poll;
|
inst->driver_poll = inst->poll;
|
||||||
|
|
||||||
max_samples = 1 << (inst->poll - inst->driver_poll);
|
|
||||||
if (max_samples < params->filter_length) {
|
|
||||||
if (max_samples < 4) {
|
|
||||||
LOG(LOGS_WARN, "Setting filter length for %s to %d",
|
|
||||||
UTI_RefidToString(inst->ref_id), max_samples);
|
|
||||||
}
|
|
||||||
params->filter_length = max_samples;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->driver->init && !inst->driver->init(inst))
|
if (inst->driver->init && !inst->driver->init(inst))
|
||||||
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
|
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
|
||||||
|
|
||||||
/* Require the filter to have at least 4 samples to produce a filtered
|
/* Don't require more than one sample per poll and combine 60% of the
|
||||||
sample, or be full for shorter lengths, and combine 60% of samples
|
samples closest to the median offset */
|
||||||
closest to the median */
|
inst->filter = SPF_CreateInstance(1, params->filter_length, params->max_dispersion, 0.6);
|
||||||
inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
|
|
||||||
params->max_dispersion, 0.6);
|
|
||||||
|
|
||||||
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
|
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, 0, params->sel_options,
|
||||||
NULL, params->min_samples, params->max_samples,
|
NULL, params->min_samples, params->max_samples,
|
||||||
0.0, 0.0);
|
0.0, 0.0, params->max_unreach);
|
||||||
|
|
||||||
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
|
DEBUG_LOG("refclock %s refid=%s poll=%d dpoll=%d filter=%d",
|
||||||
params->driver_name, UTI_RefidToString(inst->ref_id),
|
params->driver_name, UTI_RefidToString(inst->ref_id),
|
||||||
@@ -300,7 +286,7 @@ RCL_StartRefclocks(void)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lock_index == -1 || lock_index == i)
|
if (lock_index == -1 || (lock_index == i && !inst->local))
|
||||||
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
|
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +313,22 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
RCL_ModifyOffset(uint32_t ref_id, double offset)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARR_GetSize(refclocks); i++) {
|
||||||
|
RCL_Instance inst = get_refclock(i);
|
||||||
|
if (inst->ref_id == ref_id) {
|
||||||
|
inst->offset = offset;
|
||||||
|
LOG(LOGS_INFO, "Source %s new offset %f", UTI_RefidToString(ref_id), offset);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RCL_SetDriverData(RCL_Instance instance, void *data)
|
RCL_SetDriverData(RCL_Instance instance, void *data)
|
||||||
{
|
{
|
||||||
@@ -425,10 +427,21 @@ convert_tai_offset(struct timespec *sample_time, double *offset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
|
accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion,
|
||||||
|
int quality)
|
||||||
{
|
{
|
||||||
NTP_Sample sample;
|
NTP_Sample sample;
|
||||||
|
|
||||||
|
instance->reached++;
|
||||||
|
|
||||||
|
/* Don't accumulate the sample if the driver is suggesting it should be
|
||||||
|
dropped due to low quality. The only reason it went so far was to update
|
||||||
|
the reachability. */
|
||||||
|
if (quality < 1) {
|
||||||
|
DEBUG_LOG("refclock sample ignored quality=%d", quality);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
sample.time = *sample_time;
|
sample.time = *sample_time;
|
||||||
sample.offset = offset;
|
sample.offset = offset;
|
||||||
sample.peer_delay = instance->delay;
|
sample.peer_delay = instance->delay;
|
||||||
@@ -440,20 +453,24 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
|
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||||
|
struct timespec *ref_time, int leap, int quality)
|
||||||
{
|
{
|
||||||
double correction, dispersion;
|
double correction, dispersion, raw_offset, offset;
|
||||||
struct timespec cooked_time;
|
struct timespec cooked_time;
|
||||||
|
|
||||||
if (instance->pps_forced)
|
if (instance->pps_forced)
|
||||||
return RCL_AddPulse(instance, sample_time, -offset);
|
return RCL_AddPulse(instance, sample_time,
|
||||||
|
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec), quality);
|
||||||
|
|
||||||
|
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
|
||||||
|
|
||||||
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
||||||
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
|
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
|
||||||
dispersion += instance->precision;
|
dispersion += instance->precision;
|
||||||
|
|
||||||
/* Make sure the timestamp and offset provided by the driver are sane */
|
/* Make sure the timestamp and offset provided by the driver are sane */
|
||||||
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
|
if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
|
||||||
!valid_sample_time(instance, &cooked_time))
|
!valid_sample_time(instance, &cooked_time))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -468,18 +485,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate offset = raw_offset - correction + instance->offset
|
||||||
|
in parts to avoid loss of precision if there are large differences */
|
||||||
|
offset = ref_time->tv_sec - sample_time->tv_sec -
|
||||||
|
(time_t)correction + (time_t)instance->offset;
|
||||||
|
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
|
||||||
|
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
|
||||||
|
|
||||||
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
|
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
|
||||||
DEBUG_LOG("refclock sample ignored unknown TAI offset");
|
DEBUG_LOG("refclock sample ignored unknown TAI offset");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!accumulate_sample(instance, &cooked_time,
|
if (!accumulate_sample(instance, &cooked_time, offset, dispersion, quality))
|
||||||
offset - correction + instance->offset, dispersion))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
instance->pps_active = 0;
|
instance->pps_active = 0;
|
||||||
|
|
||||||
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
|
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
|
||||||
|
|
||||||
/* for logging purposes */
|
/* for logging purposes */
|
||||||
if (!instance->driver->poll)
|
if (!instance->driver->poll)
|
||||||
@@ -489,7 +512,7 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second, int quality)
|
||||||
{
|
{
|
||||||
double correction, dispersion;
|
double correction, dispersion;
|
||||||
struct timespec cooked_time;
|
struct timespec cooked_time;
|
||||||
@@ -501,7 +524,7 @@ RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second)
|
|||||||
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
|
if (!UTI_IsTimeOffsetSane(pulse_time, 0.0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction);
|
return RCL_AddCookedPulse(instance, &cooked_time, second, dispersion, correction, quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -527,7 +550,7 @@ check_pulse_edge(RCL_Instance instance, double offset, double distance)
|
|||||||
|
|
||||||
int
|
int
|
||||||
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||||
double second, double dispersion, double raw_correction)
|
double second, double dispersion, double raw_correction, int quality)
|
||||||
{
|
{
|
||||||
double offset;
|
double offset;
|
||||||
int rate;
|
int rate;
|
||||||
@@ -558,24 +581,35 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
|||||||
lock_refclock = get_refclock(instance->lock_ref);
|
lock_refclock = get_refclock(instance->lock_ref);
|
||||||
|
|
||||||
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
|
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
|
||||||
|
if (instance->local) {
|
||||||
|
/* Make the first sample in order to lock to itself */
|
||||||
|
ref_sample.time = *cooked_time;
|
||||||
|
ref_sample.offset = offset;
|
||||||
|
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
|
||||||
|
ref_sample.root_delay = ref_sample.root_dispersion = 0;
|
||||||
|
} else {
|
||||||
DEBUG_LOG("refclock pulse ignored no ref sample");
|
DEBUG_LOG("refclock pulse ignored no ref sample");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
|
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
|
||||||
|
|
||||||
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
|
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
|
||||||
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
|
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
|
||||||
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
|
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
|
||||||
sample_diff);
|
|
||||||
|
/* Restart the local mode */
|
||||||
|
if (instance->local) {
|
||||||
|
LOG(LOGS_WARN, "Local refclock lost lock");
|
||||||
|
SPF_DropSamples(instance->filter);
|
||||||
|
SRC_ResetInstance(instance->source);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Align the offset to the reference sample */
|
/* Align the offset to the reference sample */
|
||||||
if ((ref_sample.offset - offset) >= 0.0)
|
shift = round((ref_sample.offset - offset) * rate) / rate;
|
||||||
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
|
|
||||||
else
|
|
||||||
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
|
|
||||||
|
|
||||||
offset += shift;
|
offset += shift;
|
||||||
|
|
||||||
@@ -618,7 +652,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!accumulate_sample(instance, cooked_time, offset, dispersion))
|
if (!accumulate_sample(instance, cooked_time, offset, dispersion, quality))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
instance->leap_status = leap;
|
instance->leap_status = leap;
|
||||||
@@ -696,6 +730,52 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
|
||||||
|
{
|
||||||
|
double offset_sd, freq_sd, skew, root_delay, root_disp;
|
||||||
|
SST_Stats stats = SRC_GetSourcestats(inst->source);
|
||||||
|
|
||||||
|
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
|
||||||
|
UTI_ZeroTimespec(ref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
|
||||||
|
&skew, &root_delay, &root_disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
|
||||||
|
double prev_offset)
|
||||||
|
{
|
||||||
|
SST_Stats stats = SRC_GetSourcestats(inst->source);
|
||||||
|
double freq, dfreq, offset, doffset, elapsed;
|
||||||
|
struct timespec now, ref_time;
|
||||||
|
|
||||||
|
get_local_stats(inst, &ref_time, &freq, &offset);
|
||||||
|
|
||||||
|
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
|
||||||
|
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
|
||||||
|
doffset = offset - elapsed * prev_freq - prev_offset;
|
||||||
|
|
||||||
|
if (!REF_AdjustReference(doffset, dfreq))
|
||||||
|
return;
|
||||||
|
|
||||||
|
LCL_ReadCookedTime(&now, NULL);
|
||||||
|
SST_SlewSamples(stats, &now, dfreq, doffset);
|
||||||
|
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
|
||||||
|
|
||||||
|
/* Keep the offset close to zero to not lose precision */
|
||||||
|
if (fabs(offset) >= 1.0) {
|
||||||
|
SST_CorrectOffset(stats, -round(offset));
|
||||||
|
SPF_CorrectOffset(inst->filter, -round(offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
poll_timeout(void *arg)
|
poll_timeout(void *arg)
|
||||||
{
|
{
|
||||||
@@ -715,21 +795,32 @@ poll_timeout(void *arg)
|
|||||||
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
|
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
|
||||||
inst->driver_polled = 0;
|
inst->driver_polled = 0;
|
||||||
|
|
||||||
|
SRC_UpdateReachability(inst->source, inst->reached > 0);
|
||||||
|
inst->reached = 0;
|
||||||
|
|
||||||
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
||||||
|
double local_freq, local_offset;
|
||||||
|
struct timespec local_ref_time;
|
||||||
|
|
||||||
/* Handle special case when PPS is used with the local reference */
|
/* Handle special case when PPS is used with the local reference */
|
||||||
if (inst->pps_active && inst->lock_ref == -1)
|
if (inst->pps_active && inst->lock_ref == -1)
|
||||||
stratum = pps_stratum(inst, &sample.time);
|
stratum = pps_stratum(inst, &sample.time);
|
||||||
else
|
else
|
||||||
stratum = inst->stratum;
|
stratum = inst->stratum;
|
||||||
|
|
||||||
SRC_UpdateReachability(inst->source, 1);
|
if (inst->local) {
|
||||||
|
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
|
||||||
|
inst->leap_status = LEAP_Unsynchronised;
|
||||||
|
}
|
||||||
|
|
||||||
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
|
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
|
||||||
SRC_AccumulateSample(inst->source, &sample);
|
SRC_AccumulateSample(inst->source, &sample);
|
||||||
SRC_SelectSource(inst->source);
|
SRC_SelectSource(inst->source);
|
||||||
|
|
||||||
|
if (inst->local)
|
||||||
|
follow_local(inst, &local_ref_time, local_freq, local_offset);
|
||||||
|
|
||||||
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
||||||
} else {
|
|
||||||
SRC_UpdateReachability(inst->source, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
refclock.h
12
refclock.h
@@ -37,10 +37,12 @@ typedef struct {
|
|||||||
int driver_poll;
|
int driver_poll;
|
||||||
int poll;
|
int poll;
|
||||||
int filter_length;
|
int filter_length;
|
||||||
|
int local;
|
||||||
int pps_forced;
|
int pps_forced;
|
||||||
int pps_rate;
|
int pps_rate;
|
||||||
int min_samples;
|
int min_samples;
|
||||||
int max_samples;
|
int max_samples;
|
||||||
|
int max_unreach;
|
||||||
int sel_options;
|
int sel_options;
|
||||||
int max_lock_age;
|
int max_lock_age;
|
||||||
int stratum;
|
int stratum;
|
||||||
@@ -67,6 +69,7 @@ extern void RCL_Finalise(void);
|
|||||||
extern int RCL_AddRefclock(RefclockParameters *params);
|
extern int RCL_AddRefclock(RefclockParameters *params);
|
||||||
extern void RCL_StartRefclocks(void);
|
extern void RCL_StartRefclocks(void);
|
||||||
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
|
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
|
||||||
|
extern int RCL_ModifyOffset(uint32_t ref_id, double offset);
|
||||||
|
|
||||||
/* functions used by drivers */
|
/* functions used by drivers */
|
||||||
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
|
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
|
||||||
@@ -74,10 +77,13 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
|
|||||||
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
||||||
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
||||||
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
||||||
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
|
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||||
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
|
struct timespec *ref_time, int leap, int quality);
|
||||||
|
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second,
|
||||||
|
int quality);
|
||||||
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||||
double second, double dispersion, double raw_correction);
|
double second, double dispersion, double raw_correction,
|
||||||
|
int quality);
|
||||||
extern double RCL_GetPrecision(RCL_Instance instance);
|
extern double RCL_GetPrecision(RCL_Instance instance);
|
||||||
extern int RCL_GetDriverPoll(RCL_Instance instance);
|
extern int RCL_GetDriverPoll(RCL_Instance instance);
|
||||||
|
|
||||||
|
|||||||
128
refclock_phc.c
128
refclock_phc.c
@@ -2,7 +2,7 @@
|
|||||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Miroslav Lichvar 2013, 2017
|
* Copyright (C) Miroslav Lichvar 2013, 2017, 2023
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -33,6 +33,9 @@
|
|||||||
|
|
||||||
#include "sysincl.h"
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
|
||||||
|
#include "array.h"
|
||||||
#include "refclock.h"
|
#include "refclock.h"
|
||||||
#include "hwclock.h"
|
#include "hwclock.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
@@ -44,36 +47,49 @@
|
|||||||
|
|
||||||
struct phc_instance {
|
struct phc_instance {
|
||||||
int fd;
|
int fd;
|
||||||
|
int dev_index;
|
||||||
int mode;
|
int mode;
|
||||||
int nocrossts;
|
int nocrossts;
|
||||||
int extpps;
|
int extpps;
|
||||||
int pin;
|
int pin;
|
||||||
int channel;
|
int channel;
|
||||||
|
struct timespec last_extts;
|
||||||
HCL_Instance clock;
|
HCL_Instance clock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Array of RCL_Instance with enabled extpps */
|
||||||
|
static ARR_Instance extts_phcs = NULL;
|
||||||
|
|
||||||
static void read_ext_pulse(int sockfd, int event, void *anything);
|
static void read_ext_pulse(int sockfd, int event, void *anything);
|
||||||
|
|
||||||
static int phc_initialise(RCL_Instance instance)
|
static int phc_initialise(RCL_Instance instance)
|
||||||
{
|
{
|
||||||
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc;
|
||||||
int phc_fd, rising_edge;
|
int rising_edge;
|
||||||
|
struct stat st;
|
||||||
char *path, *s;
|
char *path, *s;
|
||||||
|
|
||||||
RCL_CheckDriverOptions(instance, options);
|
RCL_CheckDriverOptions(instance, options);
|
||||||
|
|
||||||
path = RCL_GetDriverParameter(instance);
|
path = RCL_GetDriverParameter(instance);
|
||||||
|
|
||||||
phc_fd = SYS_Linux_OpenPHC(path, 0);
|
|
||||||
if (phc_fd < 0)
|
|
||||||
LOG_FATAL("Could not open PHC");
|
|
||||||
|
|
||||||
phc = MallocNew(struct phc_instance);
|
phc = MallocNew(struct phc_instance);
|
||||||
phc->fd = phc_fd;
|
|
||||||
phc->mode = 0;
|
phc->mode = 0;
|
||||||
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
||||||
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
||||||
|
UTI_ZeroTimespec(&phc->last_extts);
|
||||||
|
|
||||||
|
phc->fd = SYS_Linux_OpenPHC(path, phc->extpps ? O_RDWR : O_RDONLY);
|
||||||
|
if (phc->fd < 0)
|
||||||
|
LOG_FATAL("Could not open PHC");
|
||||||
|
|
||||||
|
if (fstat(phc->fd, &st) < 0 || !S_ISCHR(st.st_mode))
|
||||||
|
LOG_FATAL("Could not get PHC index");
|
||||||
|
phc->dev_index = minor(st.st_rdev);
|
||||||
|
|
||||||
|
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
|
||||||
|
RCL_GetPrecision(instance));
|
||||||
|
|
||||||
if (phc->extpps) {
|
if (phc->extpps) {
|
||||||
s = RCL_GetDriverOption(instance, "pin");
|
s = RCL_GetDriverOption(instance, "pin");
|
||||||
@@ -81,16 +97,18 @@ static int phc_initialise(RCL_Instance instance)
|
|||||||
s = RCL_GetDriverOption(instance, "channel");
|
s = RCL_GetDriverOption(instance, "channel");
|
||||||
phc->channel = s ? atoi(s) : 0;
|
phc->channel = s ? atoi(s) : 0;
|
||||||
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
|
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
|
||||||
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
|
|
||||||
|
|
||||||
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
|
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
|
||||||
rising_edge, !rising_edge, 1))
|
rising_edge, !rising_edge, 1))
|
||||||
LOG_FATAL("Could not enable external PHC timestamping");
|
LOG_FATAL("Could not enable external PHC timestamping");
|
||||||
|
|
||||||
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
|
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
|
||||||
|
|
||||||
|
if (!extts_phcs)
|
||||||
|
extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
|
||||||
|
ARR_AppendElement(extts_phcs, &instance);
|
||||||
} else {
|
} else {
|
||||||
phc->pin = phc->channel = 0;
|
phc->pin = phc->channel = 0;
|
||||||
phc->clock = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCL_SetDriverData(instance, phc);
|
RCL_SetDriverData(instance, phc);
|
||||||
@@ -100,68 +118,108 @@ static int phc_initialise(RCL_Instance instance)
|
|||||||
static void phc_finalise(RCL_Instance instance)
|
static void phc_finalise(RCL_Instance instance)
|
||||||
{
|
{
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||||
|
|
||||||
if (phc->extpps) {
|
if (phc->extpps) {
|
||||||
SCH_RemoveFileHandler(phc->fd);
|
SCH_RemoveFileHandler(phc->fd);
|
||||||
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
|
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
|
||||||
HCL_DestroyInstance(phc->clock);
|
|
||||||
|
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
|
||||||
|
if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
|
||||||
|
ARR_RemoveElement(extts_phcs, i--);
|
||||||
|
}
|
||||||
|
if (ARR_GetSize(extts_phcs) == 0) {
|
||||||
|
ARR_DestroyInstance(extts_phcs);
|
||||||
|
extts_phcs = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HCL_DestroyInstance(phc->clock);
|
||||||
close(phc->fd);
|
close(phc->fd);
|
||||||
Free(phc);
|
Free(phc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
|
||||||
|
{
|
||||||
|
struct phc_instance *phc;
|
||||||
|
struct timespec local_ts;
|
||||||
|
double local_err;
|
||||||
|
|
||||||
|
phc = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
|
if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
|
||||||
|
DEBUG_LOG("Ignoring duplicated PHC timestamp");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
phc->last_extts = *phc_ts;
|
||||||
|
|
||||||
|
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
|
||||||
|
return;
|
||||||
|
|
||||||
|
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
||||||
|
UTI_DiffTimespecsToDouble(phc_ts, &local_ts), 1);
|
||||||
|
}
|
||||||
|
|
||||||
static void read_ext_pulse(int fd, int event, void *anything)
|
static void read_ext_pulse(int fd, int event, void *anything)
|
||||||
{
|
{
|
||||||
RCL_Instance instance;
|
RCL_Instance instance;
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc1, *phc2;
|
||||||
struct timespec phc_ts, local_ts;
|
struct timespec phc_ts;
|
||||||
double local_err;
|
unsigned int i;
|
||||||
int channel;
|
int channel;
|
||||||
|
|
||||||
|
if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
|
||||||
|
return;
|
||||||
|
|
||||||
instance = anything;
|
instance = anything;
|
||||||
phc = RCL_GetDriverData(instance);
|
phc1 = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
|
/* Linux versions before 6.7 had one shared queue of timestamps for all
|
||||||
return;
|
descriptors of the same PHC. Search for all refclocks that expect
|
||||||
|
the timestamp. */
|
||||||
|
|
||||||
if (channel != phc->channel) {
|
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
|
||||||
DEBUG_LOG("Unexpected extts channel %d\n", channel);
|
instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
|
||||||
return;
|
phc2 = RCL_GetDriverData(instance);
|
||||||
|
if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
|
||||||
|
continue;
|
||||||
|
process_ext_pulse(instance, &phc_ts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
|
#define PHC_READINGS 25
|
||||||
return;
|
|
||||||
|
|
||||||
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
|
|
||||||
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int phc_poll(RCL_Instance instance)
|
static int phc_poll(RCL_Instance instance)
|
||||||
{
|
{
|
||||||
|
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
|
||||||
struct phc_instance *phc;
|
struct phc_instance *phc;
|
||||||
struct timespec phc_ts, sys_ts, local_ts;
|
double phc_err, local_err;
|
||||||
double offset, phc_err, local_err;
|
int n_readings, quality;
|
||||||
|
|
||||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||||
|
|
||||||
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
|
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
|
||||||
&phc->mode, &phc_ts, &sys_ts, &phc_err))
|
PHC_READINGS, readings);
|
||||||
|
if (n_readings < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!HCL_ProcessReadings(phc->clock, n_readings, readings,
|
||||||
|
&phc_ts, &sys_ts, &phc_err, &quality))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (phc->extpps) {
|
|
||||||
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
||||||
|
if (quality > 0)
|
||||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||||
|
|
||||||
|
if (phc->extpps)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
|
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
|
||||||
|
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
|
||||||
|
|
||||||
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
|
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal, quality);
|
||||||
|
|
||||||
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefclockDriver RCL_PHC_driver = {
|
RefclockDriver RCL_PHC_driver = {
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ static int pps_poll(RCL_Instance instance)
|
|||||||
|
|
||||||
pps->last_seq = seq;
|
pps->last_seq = seq;
|
||||||
|
|
||||||
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec);
|
return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefclockDriver RCL_PPS_driver = {
|
RefclockDriver RCL_PPS_driver = {
|
||||||
|
|||||||
180
refclock_rtc.c
Normal file
180
refclock_rtc.c
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Uwe Kleine-König, Pengutronix 2021
|
||||||
|
* Copyright (C) Ahmad Fatoum, Pengutronix 2024
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
RTC refclock driver.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "refclock.h"
|
||||||
|
|
||||||
|
#ifdef FEAT_RTC
|
||||||
|
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
#include "local.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "sched.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "rtc_linux.h"
|
||||||
|
|
||||||
|
struct refrtc_instance {
|
||||||
|
int fd;
|
||||||
|
int polling;
|
||||||
|
int utc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
|
||||||
|
time_t rtc_sec, long rtc_nsec)
|
||||||
|
{
|
||||||
|
struct timespec rtc_ts;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
rtc_ts.tv_sec = rtc_sec;
|
||||||
|
rtc_ts.tv_nsec = rtc_nsec;
|
||||||
|
|
||||||
|
status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal, 1);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refrtc_read_after_uie(int rtcfd, int event, void *data)
|
||||||
|
{
|
||||||
|
RCL_Instance instance = (RCL_Instance)data;
|
||||||
|
struct refrtc_instance *rtc = RCL_GetDriverData(instance);
|
||||||
|
struct timespec now;
|
||||||
|
time_t rtc_sec;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
status = RTC_Linux_CheckInterrupt(rtcfd);
|
||||||
|
if (status < 0) {
|
||||||
|
SCH_RemoveFileHandler(rtcfd);
|
||||||
|
RTC_Linux_SwitchInterrupt(rtcfd, 0); /* Likely to raise error too, but just to be sure... */
|
||||||
|
close(rtcfd);
|
||||||
|
rtc->fd = -1;
|
||||||
|
return;
|
||||||
|
} else if (status == 0) {
|
||||||
|
/* Wait for the next interrupt, this one may be bogus */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_sec = RTC_Linux_ReadTimeAfterInterrupt(rtcfd, rtc->utc, NULL, &now);
|
||||||
|
if (rtc_sec == (time_t)-1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
refrtc_add_sample(instance, &now, rtc_sec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int refrtc_initialise(RCL_Instance instance)
|
||||||
|
{
|
||||||
|
const char *options[] = {"utc", NULL};
|
||||||
|
struct refrtc_instance *rtc;
|
||||||
|
int rtcfd, status;
|
||||||
|
const char *path;
|
||||||
|
|
||||||
|
RCL_CheckDriverOptions(instance, options);
|
||||||
|
|
||||||
|
if (CNF_GetRtcSync() || CNF_GetRtcFile())
|
||||||
|
LOG_FATAL("RTC refclock cannot be used together with rtcsync or rtcfile");
|
||||||
|
|
||||||
|
path = RCL_GetDriverParameter(instance);
|
||||||
|
|
||||||
|
rtcfd = open(path, O_RDONLY);
|
||||||
|
if (rtcfd < 0)
|
||||||
|
LOG_FATAL("Could not open RTC device %s : %s", path, strerror(errno));
|
||||||
|
|
||||||
|
/* Close on exec */
|
||||||
|
UTI_FdSetCloexec(rtcfd);
|
||||||
|
|
||||||
|
rtc = MallocNew(struct refrtc_instance);
|
||||||
|
rtc->fd = rtcfd;
|
||||||
|
rtc->utc = RCL_GetDriverOption(instance, "utc") ? 1 : 0;
|
||||||
|
|
||||||
|
RCL_SetDriverData(instance, rtc);
|
||||||
|
|
||||||
|
/* Try to enable update interrupts */
|
||||||
|
status = RTC_Linux_SwitchInterrupt(rtcfd, 1);
|
||||||
|
if (status) {
|
||||||
|
SCH_AddFileHandler(rtcfd, SCH_FILE_INPUT, refrtc_read_after_uie, instance);
|
||||||
|
rtc->polling = 0;
|
||||||
|
} else {
|
||||||
|
LOG(LOGS_INFO, "Falling back to polling for %s", path);
|
||||||
|
rtc->polling = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void refrtc_finalise(RCL_Instance instance)
|
||||||
|
{
|
||||||
|
struct refrtc_instance *rtc;
|
||||||
|
|
||||||
|
rtc = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
|
if (rtc->fd >= 0) {
|
||||||
|
if (!rtc->polling) {
|
||||||
|
SCH_RemoveFileHandler(rtc->fd);
|
||||||
|
RTC_Linux_SwitchInterrupt(rtc->fd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(rtc->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(rtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int refrtc_poll(RCL_Instance instance)
|
||||||
|
{
|
||||||
|
struct refrtc_instance *rtc;
|
||||||
|
struct timespec now;
|
||||||
|
time_t rtc_sec;
|
||||||
|
|
||||||
|
rtc = RCL_GetDriverData(instance);
|
||||||
|
|
||||||
|
if (!rtc->polling)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rtc_sec = RTC_Linux_ReadTimeNow(rtc->fd, rtc->utc, NULL, &now);
|
||||||
|
if (rtc_sec == (time_t)-1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* As the rtc has a resolution of 1s, only add half a second */
|
||||||
|
return refrtc_add_sample(instance, &now, rtc_sec, 500000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
RefclockDriver RCL_RTC_driver = {
|
||||||
|
refrtc_initialise,
|
||||||
|
refrtc_finalise,
|
||||||
|
refrtc_poll
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
RefclockDriver RCL_RTC_driver = { NULL, NULL, NULL };
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -95,7 +95,6 @@ static int shm_poll(RCL_Instance instance)
|
|||||||
{
|
{
|
||||||
struct timespec receive_ts, clock_ts;
|
struct timespec receive_ts, clock_ts;
|
||||||
struct shmTime t, *shm;
|
struct shmTime t, *shm;
|
||||||
double offset;
|
|
||||||
|
|
||||||
shm = (struct shmTime *)RCL_GetDriverData(instance);
|
shm = (struct shmTime *)RCL_GetDriverData(instance);
|
||||||
|
|
||||||
@@ -124,9 +123,8 @@ static int shm_poll(RCL_Instance instance)
|
|||||||
|
|
||||||
UTI_NormaliseTimespec(&clock_ts);
|
UTI_NormaliseTimespec(&clock_ts);
|
||||||
UTI_NormaliseTimespec(&receive_ts);
|
UTI_NormaliseTimespec(&receive_ts);
|
||||||
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
|
|
||||||
|
|
||||||
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
|
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefclockDriver RCL_SHM_driver = {
|
RefclockDriver RCL_SHM_driver = {
|
||||||
|
|||||||
@@ -58,23 +58,63 @@ struct sock_sample {
|
|||||||
int magic;
|
int magic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* On 32-bit glibc-based systems enable conversion between timevals using
|
||||||
|
32-bit and 64-bit time_t to support SOCK clients compiled with different
|
||||||
|
time_t size than chrony */
|
||||||
|
#ifdef __GLIBC_PREREQ
|
||||||
|
#if __GLIBC_PREREQ(2, 34) && __TIMESIZE == 32
|
||||||
|
#define CONVERT_TIMEVAL 1
|
||||||
|
#if defined(_TIME_BITS) && _TIME_BITS == 64
|
||||||
|
typedef int32_t alt_time_t;
|
||||||
|
typedef int32_t alt_suseconds_t;
|
||||||
|
#else
|
||||||
|
typedef int64_t alt_time_t;
|
||||||
|
typedef int64_t alt_suseconds_t;
|
||||||
|
#endif
|
||||||
|
struct alt_timeval {
|
||||||
|
alt_time_t tv_sec;
|
||||||
|
alt_suseconds_t tv_usec;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static void read_sample(int sockfd, int event, void *anything)
|
static void read_sample(int sockfd, int event, void *anything)
|
||||||
{
|
{
|
||||||
|
char buf[sizeof (struct sock_sample) + 16];
|
||||||
|
struct timespec sys_ts, ref_ts;
|
||||||
struct sock_sample sample;
|
struct sock_sample sample;
|
||||||
struct timespec ts;
|
|
||||||
RCL_Instance instance;
|
RCL_Instance instance;
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
instance = (RCL_Instance)anything;
|
instance = (RCL_Instance)anything;
|
||||||
|
|
||||||
s = recv(sockfd, &sample, sizeof (sample), 0);
|
s = recv(sockfd, buf, sizeof (buf), 0);
|
||||||
|
|
||||||
if (s < 0) {
|
if (s < 0) {
|
||||||
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
|
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s != sizeof (sample)) {
|
if (s == sizeof (sample)) {
|
||||||
|
memcpy(&sample, buf, sizeof (sample));
|
||||||
|
#ifdef CONVERT_TIMEVAL
|
||||||
|
} else if (s == sizeof (sample) - sizeof (struct timeval) + sizeof (struct alt_timeval)) {
|
||||||
|
struct alt_timeval atv;
|
||||||
|
memcpy(&atv, buf, sizeof (atv));
|
||||||
|
#ifndef HAVE_LONG_TIME_T
|
||||||
|
if (atv.tv_sec > INT32_MAX || atv.tv_sec < INT32_MIN ||
|
||||||
|
atv.tv_usec > INT32_MAX || atv.tv_usec < INT32_MIN) {
|
||||||
|
DEBUG_LOG("Could not convert 64-bit timeval");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
sample.tv.tv_sec = atv.tv_sec;
|
||||||
|
sample.tv.tv_usec = atv.tv_usec;
|
||||||
|
DEBUG_LOG("Converted %d-bit timeval", 8 * (int)sizeof (alt_time_t));
|
||||||
|
memcpy((char *)&sample + sizeof (struct timeval), buf + sizeof (struct alt_timeval),
|
||||||
|
sizeof (sample) - sizeof (struct timeval));
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
|
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
|
||||||
s, (long)sizeof (sample));
|
s, (long)sizeof (sample));
|
||||||
return;
|
return;
|
||||||
@@ -86,13 +126,18 @@ static void read_sample(int sockfd, int event, void *anything)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UTI_TimevalToTimespec(&sample.tv, &ts);
|
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
|
||||||
UTI_NormaliseTimespec(&ts);
|
UTI_NormaliseTimespec(&sys_ts);
|
||||||
|
|
||||||
|
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
|
||||||
|
return;
|
||||||
|
|
||||||
|
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
|
||||||
|
|
||||||
if (sample.pulse) {
|
if (sample.pulse) {
|
||||||
RCL_AddPulse(instance, &ts, sample.offset);
|
RCL_AddPulse(instance, &sys_ts, sample.offset, 1);
|
||||||
} else {
|
} else {
|
||||||
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
|
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
224
reference.c
224
reference.c
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2009-2018, 2020
|
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
|
||||||
|
* Copyright (C) Andy Fiddaman 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include "reference.h"
|
#include "reference.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
|
#include "leapdb.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
@@ -45,14 +47,15 @@
|
|||||||
/* The update interval of the reference in the local reference mode */
|
/* The update interval of the reference in the local reference mode */
|
||||||
#define LOCAL_REF_UPDATE_INTERVAL 64.0
|
#define LOCAL_REF_UPDATE_INTERVAL 64.0
|
||||||
|
|
||||||
/* Interval between updates of the drift file */
|
|
||||||
#define MAX_DRIFTFILE_AGE 3600.0
|
|
||||||
|
|
||||||
static int are_we_synchronised;
|
static int are_we_synchronised;
|
||||||
static int enable_local_stratum;
|
static int enable_local_stratum;
|
||||||
static int local_stratum;
|
static int local_stratum;
|
||||||
static int local_orphan;
|
static int local_orphan;
|
||||||
static double local_distance;
|
static double local_distance;
|
||||||
|
static int local_activate_ok;
|
||||||
|
static double local_activate;
|
||||||
|
static double local_wait_synced;
|
||||||
|
static double local_wait_unsynced;
|
||||||
static struct timespec local_ref_time;
|
static struct timespec local_ref_time;
|
||||||
static NTP_Leap our_leap_status;
|
static NTP_Leap our_leap_status;
|
||||||
static int our_leap_sec;
|
static int our_leap_sec;
|
||||||
@@ -61,6 +64,7 @@ static int our_stratum;
|
|||||||
static uint32_t our_ref_id;
|
static uint32_t our_ref_id;
|
||||||
static IPAddr our_ref_ip;
|
static IPAddr our_ref_ip;
|
||||||
static struct timespec our_ref_time;
|
static struct timespec our_ref_time;
|
||||||
|
static double unsynchronised_since;
|
||||||
static double our_skew;
|
static double our_skew;
|
||||||
static double our_residual_freq;
|
static double our_residual_freq;
|
||||||
static double our_root_delay;
|
static double our_root_delay;
|
||||||
@@ -106,6 +110,7 @@ static REF_ModeEndHandler mode_end_handler = NULL;
|
|||||||
/* Filename of the drift file. */
|
/* Filename of the drift file. */
|
||||||
static char *drift_file=NULL;
|
static char *drift_file=NULL;
|
||||||
static double drift_file_age;
|
static double drift_file_age;
|
||||||
|
static int drift_file_interval;
|
||||||
|
|
||||||
static void update_drift_file(double, double);
|
static void update_drift_file(double, double);
|
||||||
|
|
||||||
@@ -122,9 +127,6 @@ static int leap_in_progress;
|
|||||||
/* Timer for the leap second handler */
|
/* Timer for the leap second handler */
|
||||||
static SCH_TimeoutID leap_timeout_id;
|
static SCH_TimeoutID leap_timeout_id;
|
||||||
|
|
||||||
/* Name of a system timezone containing leap seconds occuring at midnight */
|
|
||||||
static char *leap_tzname;
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static LOG_FileID logfileid;
|
static LOG_FileID logfileid;
|
||||||
@@ -150,9 +152,11 @@ static SCH_TimeoutID fb_drift_timeout_id;
|
|||||||
static double last_ref_update;
|
static double last_ref_update;
|
||||||
static double last_ref_update_interval;
|
static double last_ref_update_interval;
|
||||||
|
|
||||||
|
static double last_ref_adjustment;
|
||||||
|
static int ref_adjustments;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
|
|
||||||
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
|
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -192,7 +196,6 @@ REF_Initialise(void)
|
|||||||
FILE *in;
|
FILE *in;
|
||||||
double file_freq_ppm, file_skew_ppm;
|
double file_freq_ppm, file_skew_ppm;
|
||||||
double our_frequency_ppm;
|
double our_frequency_ppm;
|
||||||
int tai_offset;
|
|
||||||
|
|
||||||
mode = REF_ModeNormal;
|
mode = REF_ModeNormal;
|
||||||
are_we_synchronised = 0;
|
are_we_synchronised = 0;
|
||||||
@@ -208,9 +211,10 @@ REF_Initialise(void)
|
|||||||
our_frequency_sd = 0.0;
|
our_frequency_sd = 0.0;
|
||||||
our_offset_sd = 0.0;
|
our_offset_sd = 0.0;
|
||||||
drift_file_age = 0.0;
|
drift_file_age = 0.0;
|
||||||
|
local_activate_ok = 0;
|
||||||
|
|
||||||
/* Now see if we can get the drift file opened */
|
/* Now see if we can get the drift file opened */
|
||||||
drift_file = CNF_GetDriftFile();
|
drift_file = CNF_GetDriftFile(&drift_file_interval);
|
||||||
if (drift_file) {
|
if (drift_file) {
|
||||||
in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
|
in = UTI_OpenFile(NULL, drift_file, NULL, 'r', 0);
|
||||||
if (in) {
|
if (in) {
|
||||||
@@ -246,8 +250,12 @@ REF_Initialise(void)
|
|||||||
|
|
||||||
correction_time_ratio = CNF_GetCorrectionTimeRatio();
|
correction_time_ratio = CNF_GetCorrectionTimeRatio();
|
||||||
|
|
||||||
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
|
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
|
||||||
|
&local_distance, &local_activate,
|
||||||
|
&local_wait_synced,
|
||||||
|
&local_wait_unsynced);
|
||||||
UTI_ZeroTimespec(&local_ref_time);
|
UTI_ZeroTimespec(&local_ref_time);
|
||||||
|
unsynchronised_since = SCH_GetLastEventMonoTime();
|
||||||
|
|
||||||
leap_when = 0;
|
leap_when = 0;
|
||||||
leap_timeout_id = 0;
|
leap_timeout_id = 0;
|
||||||
@@ -257,18 +265,6 @@ REF_Initialise(void)
|
|||||||
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
|
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
|
||||||
leap_mode = REF_LeapModeStep;
|
leap_mode = REF_LeapModeStep;
|
||||||
|
|
||||||
leap_tzname = CNF_GetLeapSecTimezone();
|
|
||||||
if (leap_tzname) {
|
|
||||||
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
|
|
||||||
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
|
|
||||||
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
|
|
||||||
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
|
|
||||||
} else {
|
|
||||||
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
|
|
||||||
leap_tzname = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
|
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
|
||||||
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
|
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
|
||||||
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
|
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
|
||||||
@@ -286,6 +282,8 @@ REF_Initialise(void)
|
|||||||
UTI_ZeroTimespec(&our_ref_time);
|
UTI_ZeroTimespec(&our_ref_time);
|
||||||
last_ref_update = 0.0;
|
last_ref_update = 0.0;
|
||||||
last_ref_update_interval = 0.0;
|
last_ref_update_interval = 0.0;
|
||||||
|
last_ref_adjustment = 0.0;
|
||||||
|
ref_adjustments = 0;
|
||||||
|
|
||||||
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
||||||
|
|
||||||
@@ -588,77 +586,6 @@ is_leap_second_day(time_t when)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static NTP_Leap
|
|
||||||
get_tz_leap(time_t when, int *tai_offset)
|
|
||||||
{
|
|
||||||
static time_t last_tz_leap_check;
|
|
||||||
static NTP_Leap tz_leap;
|
|
||||||
static int tz_tai_offset;
|
|
||||||
|
|
||||||
struct tm stm, *tm;
|
|
||||||
time_t t;
|
|
||||||
char *tz_env, tz_orig[128];
|
|
||||||
|
|
||||||
*tai_offset = tz_tai_offset;
|
|
||||||
|
|
||||||
/* Do this check at most twice a day */
|
|
||||||
when = when / (12 * 3600) * (12 * 3600);
|
|
||||||
if (last_tz_leap_check == when)
|
|
||||||
return tz_leap;
|
|
||||||
|
|
||||||
last_tz_leap_check = when;
|
|
||||||
tz_leap = LEAP_Normal;
|
|
||||||
tz_tai_offset = 0;
|
|
||||||
|
|
||||||
tm = gmtime(&when);
|
|
||||||
if (!tm)
|
|
||||||
return tz_leap;
|
|
||||||
|
|
||||||
stm = *tm;
|
|
||||||
|
|
||||||
/* Temporarily switch to the timezone containing leap seconds */
|
|
||||||
tz_env = getenv("TZ");
|
|
||||||
if (tz_env) {
|
|
||||||
if (strlen(tz_env) >= sizeof (tz_orig))
|
|
||||||
return tz_leap;
|
|
||||||
strcpy(tz_orig, tz_env);
|
|
||||||
}
|
|
||||||
setenv("TZ", leap_tzname, 1);
|
|
||||||
tzset();
|
|
||||||
|
|
||||||
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
|
|
||||||
t = mktime(&stm);
|
|
||||||
if (t != -1)
|
|
||||||
tz_tai_offset = t - when + 10;
|
|
||||||
|
|
||||||
/* Set the time to 23:59:60 and see how it overflows in mktime() */
|
|
||||||
stm.tm_sec = 60;
|
|
||||||
stm.tm_min = 59;
|
|
||||||
stm.tm_hour = 23;
|
|
||||||
|
|
||||||
t = mktime(&stm);
|
|
||||||
|
|
||||||
if (tz_env)
|
|
||||||
setenv("TZ", tz_orig, 1);
|
|
||||||
else
|
|
||||||
unsetenv("TZ");
|
|
||||||
tzset();
|
|
||||||
|
|
||||||
if (t == -1)
|
|
||||||
return tz_leap;
|
|
||||||
|
|
||||||
if (stm.tm_sec == 60)
|
|
||||||
tz_leap = LEAP_InsertSecond;
|
|
||||||
else if (stm.tm_sec == 1)
|
|
||||||
tz_leap = LEAP_DeleteSecond;
|
|
||||||
|
|
||||||
*tai_offset = tz_tai_offset;
|
|
||||||
|
|
||||||
return tz_leap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
leap_end_timeout(void *arg)
|
leap_end_timeout(void *arg)
|
||||||
{
|
{
|
||||||
@@ -746,16 +673,16 @@ set_leap_timeout(time_t now)
|
|||||||
static void
|
static void
|
||||||
update_leap_status(NTP_Leap leap, time_t now, int reset)
|
update_leap_status(NTP_Leap leap, time_t now, int reset)
|
||||||
{
|
{
|
||||||
NTP_Leap tz_leap;
|
NTP_Leap ldb_leap;
|
||||||
int leap_sec, tai_offset;
|
int leap_sec, tai_offset;
|
||||||
|
|
||||||
leap_sec = 0;
|
leap_sec = 0;
|
||||||
tai_offset = 0;
|
tai_offset = 0;
|
||||||
|
|
||||||
if (leap_tzname && now) {
|
if (now) {
|
||||||
tz_leap = get_tz_leap(now, &tai_offset);
|
ldb_leap = LDB_GetLeap(now, &tai_offset);
|
||||||
if (leap == LEAP_Normal)
|
if (leap == LEAP_Normal)
|
||||||
leap = tz_leap;
|
leap = ldb_leap;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
|
||||||
@@ -960,6 +887,27 @@ fuzz_ref_time(struct timespec *ts)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
static double
|
||||||
|
get_correction_rate(double offset_sd, double update_interval)
|
||||||
|
{
|
||||||
|
/* We want to correct the offset quickly, but we also want to keep the
|
||||||
|
frequency error caused by the correction itself low.
|
||||||
|
|
||||||
|
Define correction rate as the area of the region bounded by the graph of
|
||||||
|
offset corrected in time. Set the rate so that the time needed to correct
|
||||||
|
an offset equal to the current sourcestats stddev will be equal to the
|
||||||
|
update interval multiplied by the correction time ratio (assuming linear
|
||||||
|
adjustment). The offset and the time needed to make the correction are
|
||||||
|
inversely proportional.
|
||||||
|
|
||||||
|
This is only a suggestion and it's up to the system driver how the
|
||||||
|
adjustment will be executed. */
|
||||||
|
|
||||||
|
return correction_time_ratio * 0.5 * offset_sd * update_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||||
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
|
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
|
||||||
@@ -969,7 +917,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
{
|
{
|
||||||
double uncorrected_offset, accumulate_offset, step_offset;
|
double uncorrected_offset, accumulate_offset, step_offset;
|
||||||
double residual_frequency, local_abs_frequency;
|
double residual_frequency, local_abs_frequency;
|
||||||
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
|
double elapsed, mono_now, update_interval, orig_root_distance;
|
||||||
struct timespec now, raw_now;
|
struct timespec now, raw_now;
|
||||||
int manual;
|
int manual;
|
||||||
|
|
||||||
@@ -1024,21 +972,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
last_ref_update_interval = update_interval;
|
last_ref_update_interval = update_interval;
|
||||||
last_offset = offset;
|
last_offset = offset;
|
||||||
|
|
||||||
/* We want to correct the offset quickly, but we also want to keep the
|
|
||||||
frequency error caused by the correction itself low.
|
|
||||||
|
|
||||||
Define correction rate as the area of the region bounded by the graph of
|
|
||||||
offset corrected in time. Set the rate so that the time needed to correct
|
|
||||||
an offset equal to the current sourcestats stddev will be equal to the
|
|
||||||
update interval multiplied by the correction time ratio (assuming linear
|
|
||||||
adjustment). The offset and the time needed to make the correction are
|
|
||||||
inversely proportional.
|
|
||||||
|
|
||||||
This is only a suggestion and it's up to the system driver how the
|
|
||||||
adjustment will be executed. */
|
|
||||||
|
|
||||||
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
|
|
||||||
|
|
||||||
/* Check if the clock should be stepped */
|
/* Check if the clock should be stepped */
|
||||||
if (is_step_limit_reached(offset, uncorrected_offset)) {
|
if (is_step_limit_reached(offset, uncorrected_offset)) {
|
||||||
/* Cancel the uncorrected offset and correct the total offset by step */
|
/* Cancel the uncorrected offset and correct the total offset by step */
|
||||||
@@ -1050,13 +983,16 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust the clock */
|
/* Adjust the clock */
|
||||||
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
|
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
|
||||||
|
get_correction_rate(offset_sd, update_interval));
|
||||||
|
|
||||||
maybe_log_offset(offset, raw_now.tv_sec);
|
maybe_log_offset(offset, raw_now.tv_sec);
|
||||||
|
|
||||||
if (step_offset != 0.0) {
|
if (step_offset != 0.0) {
|
||||||
if (LCL_ApplyStepOffset(step_offset))
|
if (LCL_ApplyStepOffset(step_offset))
|
||||||
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
|
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
|
||||||
|
else
|
||||||
|
LCL_AccumulateOffset(step_offset, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_leap_status(leap, raw_now.tv_sec, 0);
|
update_leap_status(leap, raw_now.tv_sec, 0);
|
||||||
@@ -1075,7 +1011,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
if (drift_file) {
|
if (drift_file) {
|
||||||
/* Update drift file at most once per hour */
|
/* Update drift file at most once per hour */
|
||||||
drift_file_age += update_interval;
|
drift_file_age += update_interval;
|
||||||
if (drift_file_age >= MAX_DRIFTFILE_AGE) {
|
if (drift_file_age >= drift_file_interval) {
|
||||||
update_drift_file(local_abs_frequency, our_skew);
|
update_drift_file(local_abs_frequency, our_skew);
|
||||||
drift_file_age = 0.0;
|
drift_file_age = 0.0;
|
||||||
}
|
}
|
||||||
@@ -1095,6 +1031,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
|||||||
avg2_moving = 1;
|
avg2_moving = 1;
|
||||||
avg2_offset = SQUARE(offset);
|
avg2_offset = SQUARE(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref_adjustments = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
REF_AdjustReference(double offset, double frequency)
|
||||||
|
{
|
||||||
|
double adj_corr_rate, ref_corr_rate, mono_now;
|
||||||
|
|
||||||
|
mono_now = SCH_GetLastEventMonoTime();
|
||||||
|
ref_adjustments++;
|
||||||
|
|
||||||
|
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
|
||||||
|
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
|
||||||
|
ref_adjustments;
|
||||||
|
last_ref_adjustment = mono_now;
|
||||||
|
|
||||||
|
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
|
||||||
|
MAX(adj_corr_rate, ref_corr_rate));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1144,6 +1101,9 @@ REF_SetUnsynchronised(void)
|
|||||||
our_ref_ip.family = IPADDR_INET4;
|
our_ref_ip.family = IPADDR_INET4;
|
||||||
our_ref_ip.addr.in4 = 0;
|
our_ref_ip.addr.in4 = 0;
|
||||||
our_stratum = 0;
|
our_stratum = 0;
|
||||||
|
|
||||||
|
if (are_we_synchronised)
|
||||||
|
unsynchronised_since = SCH_GetLastEventMonoTime();
|
||||||
are_we_synchronised = 0;
|
are_we_synchronised = 0;
|
||||||
|
|
||||||
LCL_SetSyncStatus(0, 0.0, 0.0);
|
LCL_SetSyncStatus(0, 0.0, 0.0);
|
||||||
@@ -1186,21 +1146,30 @@ REF_GetReferenceParams
|
|||||||
double *root_dispersion
|
double *root_dispersion
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
double dispersion, delta;
|
double dispersion, delta, distance;
|
||||||
|
int wait_local_ok;
|
||||||
|
|
||||||
assert(initialised);
|
assert(initialised);
|
||||||
|
|
||||||
if (are_we_synchronised) {
|
if (are_we_synchronised) {
|
||||||
dispersion = get_root_dispersion(local_time);
|
dispersion = get_root_dispersion(local_time);
|
||||||
|
wait_local_ok = UTI_DiffTimespecsToDouble(local_time, &our_ref_time) >= local_wait_synced;
|
||||||
} else {
|
} else {
|
||||||
dispersion = 0.0;
|
dispersion = 0.0;
|
||||||
|
wait_local_ok = SCH_GetLastEventMonoTime() - unsynchronised_since >= local_wait_unsynced;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
distance = our_root_delay / 2 + dispersion;
|
||||||
|
|
||||||
|
if (local_activate == 0.0 || (are_we_synchronised && distance < local_activate))
|
||||||
|
local_activate_ok = 1;
|
||||||
|
|
||||||
/* Local reference is active when enabled and the clock is not synchronised
|
/* Local reference is active when enabled and the clock is not synchronised
|
||||||
or the root distance exceeds the threshold */
|
or the root distance exceeds the threshold */
|
||||||
|
|
||||||
if (are_we_synchronised &&
|
if (are_we_synchronised &&
|
||||||
!(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
|
!(enable_local_stratum && local_activate_ok && wait_local_ok &&
|
||||||
|
distance > local_distance)) {
|
||||||
|
|
||||||
*is_synchronised = 1;
|
*is_synchronised = 1;
|
||||||
|
|
||||||
@@ -1212,7 +1181,7 @@ REF_GetReferenceParams
|
|||||||
*root_delay = our_root_delay;
|
*root_delay = our_root_delay;
|
||||||
*root_dispersion = dispersion;
|
*root_dispersion = dispersion;
|
||||||
|
|
||||||
} else if (enable_local_stratum) {
|
} else if (enable_local_stratum && local_activate_ok && wait_local_ok) {
|
||||||
|
|
||||||
*is_synchronised = 0;
|
*is_synchronised = 0;
|
||||||
|
|
||||||
@@ -1296,6 +1265,7 @@ void
|
|||||||
REF_ModifyMaxupdateskew(double new_max_update_skew)
|
REF_ModifyMaxupdateskew(double new_max_update_skew)
|
||||||
{
|
{
|
||||||
max_update_skew = new_max_update_skew * 1.0e-6;
|
max_update_skew = new_max_update_skew * 1.0e-6;
|
||||||
|
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1305,17 +1275,23 @@ REF_ModifyMakestep(int limit, double threshold)
|
|||||||
{
|
{
|
||||||
make_step_limit = limit;
|
make_step_limit = limit;
|
||||||
make_step_threshold = threshold;
|
make_step_threshold = threshold;
|
||||||
|
LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
REF_EnableLocal(int stratum, double distance, int orphan)
|
REF_EnableLocal(int stratum, double distance, int orphan, double activate,
|
||||||
|
double wait_synced, double wait_unsynced)
|
||||||
{
|
{
|
||||||
enable_local_stratum = 1;
|
enable_local_stratum = 1;
|
||||||
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
|
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
|
||||||
local_distance = distance;
|
local_distance = distance;
|
||||||
local_orphan = !!orphan;
|
local_orphan = !!orphan;
|
||||||
|
local_activate = activate;
|
||||||
|
local_wait_synced = wait_synced;
|
||||||
|
local_wait_unsynced = wait_unsynced;
|
||||||
|
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1324,6 +1300,7 @@ void
|
|||||||
REF_DisableLocal(void)
|
REF_DisableLocal(void)
|
||||||
{
|
{
|
||||||
enable_local_stratum = 0;
|
enable_local_stratum = 0;
|
||||||
|
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1331,9 +1308,10 @@ REF_DisableLocal(void)
|
|||||||
#define LEAP_SECOND_CLOSE 5
|
#define LEAP_SECOND_CLOSE 5
|
||||||
|
|
||||||
static int
|
static int
|
||||||
is_leap_close(time_t t)
|
is_leap_close(double t)
|
||||||
{
|
{
|
||||||
return t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
|
return leap_when != 0 &&
|
||||||
|
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -1360,7 +1338,7 @@ REF_GetTaiOffset(struct timespec *ts)
|
|||||||
{
|
{
|
||||||
int tai_offset;
|
int tai_offset;
|
||||||
|
|
||||||
get_tz_leap(ts->tv_sec, &tai_offset);
|
LDB_GetLeap(ts->tv_sec, &tai_offset);
|
||||||
|
|
||||||
return tai_offset;
|
return tai_offset;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ extern void REF_SetManualReference
|
|||||||
extern void
|
extern void
|
||||||
REF_SetUnsynchronised(void);
|
REF_SetUnsynchronised(void);
|
||||||
|
|
||||||
|
/* Make a small correction of the clock without updating the reference
|
||||||
|
parameters and calling the clock change handlers */
|
||||||
|
extern int REF_AdjustReference(double offset, double frequency);
|
||||||
|
|
||||||
/* Announce a leap second before the full reference update */
|
/* Announce a leap second before the full reference update */
|
||||||
extern void REF_UpdateLeapStatus(NTP_Leap leap);
|
extern void REF_UpdateLeapStatus(NTP_Leap leap);
|
||||||
|
|
||||||
@@ -181,7 +185,8 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
|
|||||||
/* Modify makestep settings */
|
/* Modify makestep settings */
|
||||||
extern void REF_ModifyMakestep(int limit, double threshold);
|
extern void REF_ModifyMakestep(int limit, double threshold);
|
||||||
|
|
||||||
extern void REF_EnableLocal(int stratum, double distance, int orphan);
|
extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate,
|
||||||
|
double wait_synced, double wait_unsynced);
|
||||||
extern void REF_DisableLocal(void);
|
extern void REF_DisableLocal(void);
|
||||||
|
|
||||||
/* Check if either of the current raw and cooked time, and optionally a
|
/* Check if either of the current raw and cooked time, and optionally a
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
|
|||||||
r = v;
|
r = v;
|
||||||
do {
|
do {
|
||||||
while (l < v && x[l] < piv) l++;
|
while (l < v && x[l] < piv) l++;
|
||||||
while (x[r] > piv) r--;
|
while (r > 0 && x[r] > piv) r--;
|
||||||
if (r <= l) break;
|
if (r <= l) break;
|
||||||
EXCH(x[l], x[r]);
|
EXCH(x[l], x[r]);
|
||||||
l++;
|
l++;
|
||||||
|
|||||||
30
reports.h
30
reports.h
@@ -109,14 +109,23 @@ typedef struct {
|
|||||||
} RPT_ClientAccessByIndex_Report;
|
} RPT_ClientAccessByIndex_Report;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t ntp_hits;
|
uint64_t ntp_hits;
|
||||||
uint32_t nke_hits;
|
uint64_t nke_hits;
|
||||||
uint32_t cmd_hits;
|
uint64_t cmd_hits;
|
||||||
uint32_t ntp_drops;
|
uint64_t ntp_drops;
|
||||||
uint32_t nke_drops;
|
uint64_t nke_drops;
|
||||||
uint32_t cmd_drops;
|
uint64_t cmd_drops;
|
||||||
uint32_t log_drops;
|
uint64_t log_drops;
|
||||||
uint32_t ntp_auth_hits;
|
uint64_t ntp_auth_hits;
|
||||||
|
uint64_t ntp_interleaved_hits;
|
||||||
|
uint64_t ntp_timestamps;
|
||||||
|
uint64_t ntp_span_seconds;
|
||||||
|
uint64_t ntp_daemon_rx_timestamps;
|
||||||
|
uint64_t ntp_daemon_tx_timestamps;
|
||||||
|
uint64_t ntp_kernel_rx_timestamps;
|
||||||
|
uint64_t ntp_kernel_tx_timestamps;
|
||||||
|
uint64_t ntp_hw_rx_timestamps;
|
||||||
|
uint64_t ntp_hw_tx_timestamps;
|
||||||
} RPT_ServerStatsReport;
|
} RPT_ServerStatsReport;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -171,6 +180,11 @@ typedef struct {
|
|||||||
uint32_t total_tx_count;
|
uint32_t total_tx_count;
|
||||||
uint32_t total_rx_count;
|
uint32_t total_rx_count;
|
||||||
uint32_t total_valid_count;
|
uint32_t total_valid_count;
|
||||||
|
uint32_t total_good_count;
|
||||||
|
uint32_t total_kernel_tx_ts;
|
||||||
|
uint32_t total_kernel_rx_ts;
|
||||||
|
uint32_t total_hw_tx_ts;
|
||||||
|
uint32_t total_hw_rx_ts;
|
||||||
} RPT_NTPReport;
|
} RPT_NTPReport;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
3
rtc.c
3
rtc.c
@@ -81,8 +81,9 @@ get_driftfile_time(void)
|
|||||||
{
|
{
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
char *drift_file;
|
char *drift_file;
|
||||||
|
int interval;
|
||||||
|
|
||||||
drift_file = CNF_GetDriftFile();
|
drift_file = CNF_GetDriftFile(&interval);
|
||||||
if (!drift_file)
|
if (!drift_file)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
241
rtc_linux.c
241
rtc_linux.c
@@ -4,6 +4,7 @@
|
|||||||
**********************************************************************
|
**********************************************************************
|
||||||
* Copyright (C) Richard P. Curnow 1997-2003
|
* Copyright (C) Richard P. Curnow 1997-2003
|
||||||
* Copyright (C) Miroslav Lichvar 2012-2014
|
* Copyright (C) Miroslav Lichvar 2012-2014
|
||||||
|
* Copyright (C) Ahmad Fatoum, Pengutronix 2024
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
@@ -64,7 +65,7 @@ static OperatingMode operating_mode = OM_NORMAL;
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int fd = -1;
|
static int fd;
|
||||||
|
|
||||||
#define LOWEST_MEASUREMENT_PERIOD 15
|
#define LOWEST_MEASUREMENT_PERIOD 15
|
||||||
#define HIGHEST_MEASUREMENT_PERIOD 480
|
#define HIGHEST_MEASUREMENT_PERIOD 480
|
||||||
@@ -82,16 +83,12 @@ static int skip_interrupts;
|
|||||||
#define MAX_SAMPLES 64
|
#define MAX_SAMPLES 64
|
||||||
|
|
||||||
/* Real time clock samples. We store the seconds count as originally
|
/* Real time clock samples. We store the seconds count as originally
|
||||||
measured, together with a 'trim' that compensates these values for
|
measured. */
|
||||||
any steps made to the RTC to bring it back into line
|
|
||||||
occasionally. The trim is in seconds. */
|
|
||||||
static time_t *rtc_sec = NULL;
|
static time_t *rtc_sec = NULL;
|
||||||
static double *rtc_trim = NULL;
|
|
||||||
|
|
||||||
/* Reference time, against which delta times on the RTC scale are measured */
|
/* Reference time, against which delta times on the RTC scale are measured */
|
||||||
static time_t rtc_ref;
|
static time_t rtc_ref;
|
||||||
|
|
||||||
|
|
||||||
/* System clock samples associated with the above samples. */
|
/* System clock samples associated with the above samples. */
|
||||||
static struct timespec *system_times = NULL;
|
static struct timespec *system_times = NULL;
|
||||||
|
|
||||||
@@ -145,7 +142,7 @@ static double file_ref_offset, file_rate_ppm;
|
|||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
/* Flag to remember whether to assume the RTC is running on UTC */
|
/* Flag to remember whether to assume the RTC is running on UTC */
|
||||||
static int rtc_on_utc = 1;
|
static int rtc_on_utc;
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
@@ -168,7 +165,6 @@ discard_samples(int new_first)
|
|||||||
n_to_save = n_samples - new_first;
|
n_to_save = n_samples - new_first;
|
||||||
|
|
||||||
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
|
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
|
||||||
memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
|
|
||||||
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
|
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
|
||||||
|
|
||||||
n_samples = n_to_save;
|
n_samples = n_to_save;
|
||||||
@@ -188,21 +184,16 @@ accumulate_sample(time_t rtc, struct timespec *sys)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Discard all samples if the RTC was stepped back (not our trim) */
|
/* Discard all samples if the RTC was stepped back (not our trim) */
|
||||||
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) {
|
if (n_samples > 0 && rtc_sec[n_samples - 1] >= rtc) {
|
||||||
DEBUG_LOG("RTC samples discarded");
|
DEBUG_LOG("RTC samples discarded");
|
||||||
n_samples = 0;
|
n_samples = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Always use most recent sample as reference */
|
/* Always use most recent sample as reference */
|
||||||
/* use sample only if n_sample is not negative*/
|
|
||||||
if(n_samples >=0)
|
|
||||||
{
|
|
||||||
rtc_ref = rtc;
|
rtc_ref = rtc;
|
||||||
rtc_sec[n_samples] = rtc;
|
rtc_sec[n_samples] = rtc;
|
||||||
rtc_trim[n_samples] = 0.0;
|
|
||||||
system_times[n_samples] = *sys;
|
system_times[n_samples] = *sys;
|
||||||
++n_samples_since_regression;
|
++n_samples_since_regression;
|
||||||
}
|
|
||||||
++n_samples;
|
++n_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +218,7 @@ run_regression(int new_sample,
|
|||||||
if (n_samples > 0) {
|
if (n_samples > 0) {
|
||||||
|
|
||||||
for (i=0; i<n_samples; i++) {
|
for (i=0; i<n_samples; i++) {
|
||||||
rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref);
|
rtc_rel[i] = (double)(rtc_sec[i] - rtc_ref);
|
||||||
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
|
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
|
||||||
(1.0e-9 * system_times[i].tv_nsec) +
|
(1.0e-9 * system_times[i].tv_nsec) +
|
||||||
rtc_rel[i]);
|
rtc_rel[i]);
|
||||||
@@ -306,14 +297,25 @@ slew_samples
|
|||||||
corresponding real time clock 'DMY HMS' form, taking account of
|
corresponding real time clock 'DMY HMS' form, taking account of
|
||||||
whether the user runs his RTC on the local time zone or UTC */
|
whether the user runs his RTC on the local time zone or UTC */
|
||||||
|
|
||||||
static struct tm *
|
static void
|
||||||
rtc_from_t(const time_t *t)
|
rtc_from_t(const time_t *t, struct rtc_time *rtc_raw, int utc)
|
||||||
{
|
{
|
||||||
if (rtc_on_utc) {
|
struct tm *rtc_tm;
|
||||||
return gmtime(t);
|
if (utc) {
|
||||||
|
rtc_tm = gmtime(t);
|
||||||
} else {
|
} else {
|
||||||
return localtime(t);
|
rtc_tm = localtime(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtc_raw->tm_sec = rtc_tm->tm_sec;
|
||||||
|
rtc_raw->tm_min = rtc_tm->tm_min;
|
||||||
|
rtc_raw->tm_hour = rtc_tm->tm_hour;
|
||||||
|
rtc_raw->tm_mday = rtc_tm->tm_mday;
|
||||||
|
rtc_raw->tm_mon = rtc_tm->tm_mon;
|
||||||
|
rtc_raw->tm_year = rtc_tm->tm_year;
|
||||||
|
rtc_raw->tm_wday = rtc_tm->tm_wday;
|
||||||
|
rtc_raw->tm_yday = rtc_tm->tm_yday;
|
||||||
|
rtc_raw->tm_isdst = rtc_tm->tm_isdst;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -351,17 +353,27 @@ rtc_from_t(const time_t *t)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static time_t
|
static time_t
|
||||||
t_from_rtc(struct tm *stm) {
|
t_from_rtc(struct rtc_time *rtc_raw, int utc)
|
||||||
struct tm temp1, temp2, *tm;
|
{
|
||||||
|
struct tm rtc_tm, temp1, temp2, *tm;
|
||||||
long diff;
|
long diff;
|
||||||
time_t t1, t2;
|
time_t t1, t2;
|
||||||
|
|
||||||
temp1 = *stm;
|
/* Convert to seconds since 1970 */
|
||||||
|
memset(&rtc_tm, 0, sizeof (rtc_tm));
|
||||||
|
rtc_tm.tm_sec = rtc_raw->tm_sec;
|
||||||
|
rtc_tm.tm_min = rtc_raw->tm_min;
|
||||||
|
rtc_tm.tm_hour = rtc_raw->tm_hour;
|
||||||
|
rtc_tm.tm_mday = rtc_raw->tm_mday;
|
||||||
|
rtc_tm.tm_mon = rtc_raw->tm_mon;
|
||||||
|
rtc_tm.tm_year = rtc_raw->tm_year;
|
||||||
|
|
||||||
|
temp1 = rtc_tm;
|
||||||
temp1.tm_isdst = 0;
|
temp1.tm_isdst = 0;
|
||||||
|
|
||||||
t1 = mktime(&temp1);
|
t1 = mktime(&temp1);
|
||||||
|
|
||||||
tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
|
tm = utc ? gmtime(&t1) : localtime(&t1);
|
||||||
if (!tm) {
|
if (!tm) {
|
||||||
DEBUG_LOG("gmtime()/localtime() failed");
|
DEBUG_LOG("gmtime()/localtime() failed");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -434,6 +446,7 @@ setup_config(void)
|
|||||||
static void
|
static void
|
||||||
read_coefs_from_file(void)
|
read_coefs_from_file(void)
|
||||||
{
|
{
|
||||||
|
double ref_time;
|
||||||
FILE *in;
|
FILE *in;
|
||||||
|
|
||||||
if (!tried_to_load_coefs) {
|
if (!tried_to_load_coefs) {
|
||||||
@@ -444,11 +457,12 @@ read_coefs_from_file(void)
|
|||||||
|
|
||||||
if (coefs_file_name &&
|
if (coefs_file_name &&
|
||||||
(in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
|
(in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
|
||||||
if (fscanf(in, "%d%ld%lf%lf",
|
if (fscanf(in, "%d%lf%lf%lf",
|
||||||
&valid_coefs_from_file,
|
&valid_coefs_from_file,
|
||||||
&file_ref_time,
|
&ref_time,
|
||||||
&file_ref_offset,
|
&file_ref_offset,
|
||||||
&file_rate_ppm) == 4) {
|
&file_rate_ppm) == 4) {
|
||||||
|
file_ref_time = ref_time;
|
||||||
} else {
|
} else {
|
||||||
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
|
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
|
||||||
}
|
}
|
||||||
@@ -472,7 +486,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
|||||||
return RTC_ST_BADFILE;
|
return RTC_ST_BADFILE;
|
||||||
|
|
||||||
/* Gain rate is written out in ppm */
|
/* Gain rate is written out in ppm */
|
||||||
fprintf(out, "%1d %ld %.6f %.3f\n", valid, ref_time, offset, 1.0e6 * rate);
|
fprintf(out, "%1d %.0f %.6f %.3f\n", valid, (double)ref_time, offset, 1.0e6 * rate);
|
||||||
fclose(out);
|
fclose(out);
|
||||||
|
|
||||||
/* Rename the temporary file to the correct location */
|
/* Rename the temporary file to the correct location */
|
||||||
@@ -484,8 +498,8 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static int
|
int
|
||||||
switch_interrupts(int on_off)
|
RTC_Linux_SwitchInterrupt(int fd, int on_off)
|
||||||
{
|
{
|
||||||
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
|
if (ioctl(fd, on_off ? RTC_UIE_ON : RTC_UIE_OFF, 0) < 0) {
|
||||||
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
|
LOG(LOGS_ERR, "Could not %s RTC interrupt : %s",
|
||||||
@@ -516,7 +530,7 @@ RTC_Linux_Initialise(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the RTC supports interrupts */
|
/* Make sure the RTC supports interrupts */
|
||||||
if (!switch_interrupts(1) || !switch_interrupts(0)) {
|
if (!RTC_Linux_SwitchInterrupt(fd, 1) || !RTC_Linux_SwitchInterrupt(fd, 0)) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -525,7 +539,6 @@ RTC_Linux_Initialise(void)
|
|||||||
UTI_FdSetCloexec(fd);
|
UTI_FdSetCloexec(fd);
|
||||||
|
|
||||||
rtc_sec = MallocArray(time_t, MAX_SAMPLES);
|
rtc_sec = MallocArray(time_t, MAX_SAMPLES);
|
||||||
rtc_trim = MallocArray(double, MAX_SAMPLES);
|
|
||||||
system_times = MallocArray(struct timespec, MAX_SAMPLES);
|
system_times = MallocArray(struct timespec, MAX_SAMPLES);
|
||||||
|
|
||||||
/* Setup details depending on configuration options */
|
/* Setup details depending on configuration options */
|
||||||
@@ -566,7 +579,7 @@ RTC_Linux_Finalise(void)
|
|||||||
/* Remove input file handler */
|
/* Remove input file handler */
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
SCH_RemoveFileHandler(fd);
|
SCH_RemoveFileHandler(fd);
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
/* Save the RTC data */
|
/* Save the RTC data */
|
||||||
@@ -578,7 +591,6 @@ RTC_Linux_Finalise(void)
|
|||||||
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
|
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
|
||||||
|
|
||||||
Free(rtc_sec);
|
Free(rtc_sec);
|
||||||
Free(rtc_trim);
|
|
||||||
Free(system_times);
|
Free(system_times);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,7 +600,7 @@ static void
|
|||||||
measurement_timeout(void *any)
|
measurement_timeout(void *any)
|
||||||
{
|
{
|
||||||
timeout_id = 0;
|
timeout_id = 0;
|
||||||
switch_interrupts(1);
|
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -596,21 +608,10 @@ measurement_timeout(void *any)
|
|||||||
static void
|
static void
|
||||||
set_rtc(time_t new_rtc_time)
|
set_rtc(time_t new_rtc_time)
|
||||||
{
|
{
|
||||||
struct tm rtc_tm;
|
|
||||||
struct rtc_time rtc_raw;
|
struct rtc_time rtc_raw;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
rtc_tm = *rtc_from_t(&new_rtc_time);
|
rtc_from_t(&new_rtc_time, &rtc_raw, rtc_on_utc);
|
||||||
|
|
||||||
rtc_raw.tm_sec = rtc_tm.tm_sec;
|
|
||||||
rtc_raw.tm_min = rtc_tm.tm_min;
|
|
||||||
rtc_raw.tm_hour = rtc_tm.tm_hour;
|
|
||||||
rtc_raw.tm_mday = rtc_tm.tm_mday;
|
|
||||||
rtc_raw.tm_mon = rtc_tm.tm_mon;
|
|
||||||
rtc_raw.tm_year = rtc_tm.tm_year;
|
|
||||||
rtc_raw.tm_wday = rtc_tm.tm_wday;
|
|
||||||
rtc_raw.tm_yday = rtc_tm.tm_yday;
|
|
||||||
rtc_raw.tm_isdst = rtc_tm.tm_isdst;
|
|
||||||
|
|
||||||
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
@@ -639,11 +640,7 @@ handle_initial_trim(void)
|
|||||||
run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
|
run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
|
||||||
|
|
||||||
n_samples_since_regression = 0;
|
n_samples_since_regression = 0;
|
||||||
|
n_samples = 0;
|
||||||
/* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/
|
|
||||||
|
|
||||||
n_samples = -1;
|
|
||||||
|
|
||||||
|
|
||||||
read_coefs_from_file();
|
read_coefs_from_file();
|
||||||
|
|
||||||
@@ -764,16 +761,11 @@ process_reading(time_t rtc_time, struct timespec *system_time)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
static void
|
int
|
||||||
read_from_device(int fd_, int event, void *any)
|
RTC_Linux_CheckInterrupt(int fd)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
unsigned long data;
|
unsigned long data;
|
||||||
struct timespec sys_time;
|
|
||||||
struct rtc_time rtc_raw;
|
|
||||||
struct tm rtc_tm;
|
|
||||||
time_t rtc_t;
|
|
||||||
int error = 0;
|
|
||||||
|
|
||||||
status = read(fd, &data, sizeof(data));
|
status = read(fd, &data, sizeof(data));
|
||||||
|
|
||||||
@@ -781,45 +773,63 @@ read_from_device(int fd_, int event, void *any)
|
|||||||
/* This looks like a bad error : the file descriptor was indicating it was
|
/* This looks like a bad error : the file descriptor was indicating it was
|
||||||
* ready to read but we couldn't read anything. Give up. */
|
* ready to read but we couldn't read anything. Give up. */
|
||||||
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
LOG(LOGS_ERR, "Could not read flags %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||||
SCH_RemoveFileHandler(fd);
|
return -1;
|
||||||
switch_interrupts(0); /* Likely to raise error too, but just to be sure... */
|
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skip_interrupts > 0) {
|
if (skip_interrupts > 0) {
|
||||||
/* Wait for the next interrupt, this one may be bogus */
|
/* Wait for the next interrupt, this one may be bogus */
|
||||||
skip_interrupts--;
|
skip_interrupts--;
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((data & RTC_UF) == RTC_UF) {
|
/* Update interrupt detected? */
|
||||||
/* Update interrupt detected */
|
return (data & RTC_UF) == RTC_UF;
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t
|
||||||
|
RTC_Linux_ReadTimeAfterInterrupt(int fd, int utc,
|
||||||
|
struct timespec *sys_time_cooked,
|
||||||
|
struct timespec *sys_time_raw)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct rtc_time rtc_raw;
|
||||||
|
|
||||||
/* Read RTC time, sandwiched between two polls of the system clock
|
/* Read RTC time, sandwiched between two polls of the system clock
|
||||||
so we can bound any error. */
|
so we can bound any error */
|
||||||
|
|
||||||
SCH_GetLastEventTime(&sys_time, NULL, NULL);
|
SCH_GetLastEventTime(sys_time_cooked, NULL, sys_time_raw);
|
||||||
|
|
||||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
LOG(LOGS_ERR, "Could not read time from %s : %s", CNF_GetRtcDevice(), strerror(errno));
|
||||||
error = 1;
|
return -1;
|
||||||
goto turn_off_interrupt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert RTC time into a struct timespec */
|
/* Convert RTC time into a struct timespec */
|
||||||
rtc_tm.tm_sec = rtc_raw.tm_sec;
|
return t_from_rtc(&rtc_raw, utc);
|
||||||
rtc_tm.tm_min = rtc_raw.tm_min;
|
}
|
||||||
rtc_tm.tm_hour = rtc_raw.tm_hour;
|
|
||||||
rtc_tm.tm_mday = rtc_raw.tm_mday;
|
|
||||||
rtc_tm.tm_mon = rtc_raw.tm_mon;
|
|
||||||
rtc_tm.tm_year = rtc_raw.tm_year;
|
|
||||||
|
|
||||||
rtc_t = t_from_rtc(&rtc_tm);
|
static void
|
||||||
|
read_from_device(int fd_, int event, void *any)
|
||||||
|
{
|
||||||
|
struct timespec sys_time;
|
||||||
|
int status, error = 0;
|
||||||
|
time_t rtc_t;
|
||||||
|
|
||||||
if (rtc_t == (time_t)(-1)) {
|
status = RTC_Linux_CheckInterrupt(fd);
|
||||||
|
if (status < 0) {
|
||||||
|
SCH_RemoveFileHandler(fd);
|
||||||
|
RTC_Linux_SwitchInterrupt(fd, 0); /* Likely to raise error too, but just to be sure... */
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
return;
|
||||||
|
} else if (status == 0) {
|
||||||
|
/* Wait for the next interrupt, this one may be bogus */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_t = RTC_Linux_ReadTimeAfterInterrupt(fd, rtc_on_utc, &sys_time, NULL);
|
||||||
|
if (rtc_t == (time_t)-1) {
|
||||||
error = 1;
|
error = 1;
|
||||||
goto turn_off_interrupt;
|
goto turn_off_interrupt;
|
||||||
}
|
}
|
||||||
@@ -838,8 +848,6 @@ read_from_device(int fd_, int event, void *any)
|
|||||||
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
measurement_period = LOWEST_MEASUREMENT_PERIOD << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
turn_off_interrupt:
|
turn_off_interrupt:
|
||||||
|
|
||||||
switch (operating_mode) {
|
switch (operating_mode) {
|
||||||
@@ -849,7 +857,7 @@ turn_off_interrupt:
|
|||||||
operating_mode = OM_NORMAL;
|
operating_mode = OM_NORMAL;
|
||||||
(after_init_hook)(after_init_hook_arg);
|
(after_init_hook)(after_init_hook_arg);
|
||||||
|
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
|
|
||||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||||
}
|
}
|
||||||
@@ -861,7 +869,7 @@ turn_off_interrupt:
|
|||||||
DEBUG_LOG("Could not complete after trim relock due to errors");
|
DEBUG_LOG("Could not complete after trim relock due to errors");
|
||||||
operating_mode = OM_NORMAL;
|
operating_mode = OM_NORMAL;
|
||||||
|
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
|
|
||||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||||
}
|
}
|
||||||
@@ -869,7 +877,7 @@ turn_off_interrupt:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OM_NORMAL:
|
case OM_NORMAL:
|
||||||
switch_interrupts(0);
|
RTC_Linux_SwitchInterrupt(fd, 0);
|
||||||
|
|
||||||
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
timeout_id = SCH_AddTimeoutByDelay((double) measurement_period, measurement_timeout, NULL);
|
||||||
|
|
||||||
@@ -891,7 +899,7 @@ RTC_Linux_TimeInit(void (*after_hook)(void *), void *anything)
|
|||||||
|
|
||||||
operating_mode = OM_INITIAL;
|
operating_mode = OM_INITIAL;
|
||||||
timeout_id = 0;
|
timeout_id = 0;
|
||||||
switch_interrupts(1);
|
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -924,6 +932,33 @@ RTC_Linux_WriteParameters(void)
|
|||||||
return(retval);
|
return(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time_t
|
||||||
|
RTC_Linux_ReadTimeNow(int fd, int utc,
|
||||||
|
struct timespec *old_sys_cooked,
|
||||||
|
struct timespec *old_sys_raw)
|
||||||
|
{
|
||||||
|
struct rtc_time rtc_raw, rtc_raw_retry;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* Retry reading the RTC until both read attempts give the same sec value.
|
||||||
|
This way the race condition is prevented that the RTC has updated itself
|
||||||
|
during the first read operation. */
|
||||||
|
do {
|
||||||
|
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
||||||
|
if (status >= 0) {
|
||||||
|
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
||||||
|
}
|
||||||
|
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
||||||
|
|
||||||
|
/* Read system clock */
|
||||||
|
if (old_sys_raw)
|
||||||
|
LCL_ReadRawTime(old_sys_raw);
|
||||||
|
if (old_sys_cooked)
|
||||||
|
LCL_ReadCookedTime(old_sys_cooked, NULL);
|
||||||
|
|
||||||
|
return status >= 0 ? t_from_rtc(&rtc_raw, utc) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
/* Try to set the system clock from the RTC, in the same manner as
|
/* Try to set the system clock from the RTC, in the same manner as
|
||||||
/sbin/hwclock -s would do. We're not as picky about OS version
|
/sbin/hwclock -s would do. We're not as picky about OS version
|
||||||
@@ -933,12 +968,10 @@ RTC_Linux_WriteParameters(void)
|
|||||||
int
|
int
|
||||||
RTC_Linux_TimePreInit(time_t driftfile_time)
|
RTC_Linux_TimePreInit(time_t driftfile_time)
|
||||||
{
|
{
|
||||||
int fd, status;
|
|
||||||
struct rtc_time rtc_raw, rtc_raw_retry;
|
|
||||||
struct tm rtc_tm;
|
|
||||||
time_t rtc_t;
|
time_t rtc_t;
|
||||||
double accumulated_error, sys_offset;
|
double accumulated_error, sys_offset;
|
||||||
struct timespec new_sys_time, old_sys_time;
|
struct timespec new_sys_time, old_sys_time;
|
||||||
|
int fd;
|
||||||
|
|
||||||
coefs_file_name = CNF_GetRtcFile();
|
coefs_file_name = CNF_GetRtcFile();
|
||||||
|
|
||||||
@@ -951,35 +984,13 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
|||||||
return 0; /* Can't open it, and won't be able to later */
|
return 0; /* Can't open it, and won't be able to later */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retry reading the rtc until both read attempts give the same sec value.
|
rtc_t = RTC_Linux_ReadTimeNow(fd, rtc_on_utc, &old_sys_time, NULL);
|
||||||
This way the race condition is prevented that the RTC has updated itself
|
|
||||||
during the first read operation. */
|
|
||||||
do {
|
|
||||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
|
|
||||||
if (status >= 0) {
|
|
||||||
status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry);
|
|
||||||
}
|
|
||||||
} while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec);
|
|
||||||
|
|
||||||
/* Read system clock */
|
|
||||||
LCL_ReadCookedTime(&old_sys_time, NULL);
|
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
if (status >= 0) {
|
|
||||||
/* Convert to seconds since 1970 */
|
|
||||||
rtc_tm.tm_sec = rtc_raw.tm_sec;
|
|
||||||
rtc_tm.tm_min = rtc_raw.tm_min;
|
|
||||||
rtc_tm.tm_hour = rtc_raw.tm_hour;
|
|
||||||
rtc_tm.tm_mday = rtc_raw.tm_mday;
|
|
||||||
rtc_tm.tm_mon = rtc_raw.tm_mon;
|
|
||||||
rtc_tm.tm_year = rtc_raw.tm_year;
|
|
||||||
|
|
||||||
rtc_t = t_from_rtc(&rtc_tm);
|
|
||||||
|
|
||||||
if (rtc_t != (time_t)(-1)) {
|
if (rtc_t != (time_t)(-1)) {
|
||||||
|
|
||||||
/* Work out approximatation to correct time (to about the
|
/* Work out approximation to correct time (to about the
|
||||||
nearest second) */
|
nearest second) */
|
||||||
if (valid_coefs_from_file) {
|
if (valid_coefs_from_file) {
|
||||||
accumulated_error = file_ref_offset +
|
accumulated_error = file_ref_offset +
|
||||||
@@ -1011,9 +1022,6 @@ RTC_Linux_TimePreInit(time_t driftfile_time)
|
|||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1028,8 +1036,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
|
|||||||
report->n_samples = n_samples;
|
report->n_samples = n_samples;
|
||||||
report->n_runs = n_runs;
|
report->n_runs = n_runs;
|
||||||
if (n_samples > 1) {
|
if (n_samples > 1) {
|
||||||
report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) +
|
report->span_seconds = rtc_sec[n_samples - 1] - rtc_sec[0];
|
||||||
(long)(rtc_trim[n_samples-1] - rtc_trim[0]));
|
|
||||||
} else {
|
} else {
|
||||||
report->span_seconds = 0;
|
report->span_seconds = 0;
|
||||||
}
|
}
|
||||||
@@ -1079,7 +1086,7 @@ RTC_Linux_Trim(void)
|
|||||||
/* And start rapid sampling, interrupts on now */
|
/* And start rapid sampling, interrupts on now */
|
||||||
SCH_RemoveTimeout(timeout_id);
|
SCH_RemoveTimeout(timeout_id);
|
||||||
timeout_id = 0;
|
timeout_id = 0;
|
||||||
switch_interrupts(1);
|
RTC_Linux_SwitchInterrupt(fd, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -42,4 +42,13 @@ extern int RTC_Linux_Trim(void);
|
|||||||
|
|
||||||
extern void RTC_Linux_CycleLogFile(void);
|
extern void RTC_Linux_CycleLogFile(void);
|
||||||
|
|
||||||
|
extern int RTC_Linux_SwitchInterrupt(int fd, int on_off);
|
||||||
|
extern int RTC_Linux_CheckInterrupt(int fd);
|
||||||
|
extern time_t RTC_Linux_ReadTimeAfterInterrupt(int fd, int utc,
|
||||||
|
struct timespec *sys_time_cooked,
|
||||||
|
struct timespec *sys_time_raw);
|
||||||
|
extern time_t RTC_Linux_ReadTimeNow(int fd, int utc,
|
||||||
|
struct timespec *sys_time_cooked,
|
||||||
|
struct timespec *sys_time_raw);
|
||||||
|
|
||||||
#endif /* _GOT_RTC_LINUX_H */
|
#endif /* _GOT_RTC_LINUX_H */
|
||||||
|
|||||||
72
samplefilt.c
72
samplefilt.c
@@ -162,6 +162,14 @@ SPF_GetNumberOfSamples(SPF_Instance filter)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
SPF_GetMaxSamples(SPF_Instance filter)
|
||||||
|
{
|
||||||
|
return filter->max_samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
double
|
double
|
||||||
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
||||||
{
|
{
|
||||||
@@ -170,11 +178,21 @@ SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
static void
|
||||||
SPF_DropSamples(SPF_Instance filter)
|
drop_samples(SPF_Instance filter, int keep_last)
|
||||||
{
|
{
|
||||||
filter->index = -1;
|
filter->index = -1;
|
||||||
filter->used = 0;
|
filter->used = 0;
|
||||||
|
if (!keep_last)
|
||||||
|
filter->last = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
SPF_DropSamples(SPF_Instance filter)
|
||||||
|
{
|
||||||
|
drop_samples(filter, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
@@ -399,17 +417,40 @@ SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
|
|||||||
|
|
||||||
n = select_samples(filter);
|
n = select_samples(filter);
|
||||||
|
|
||||||
|
DEBUG_LOG("selected %d from %d samples", n, filter->used);
|
||||||
|
|
||||||
if (n < 1)
|
if (n < 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!combine_selected_samples(filter, n, sample))
|
if (!combine_selected_samples(filter, n, sample))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
SPF_DropSamples(filter);
|
drop_samples(filter, 1);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_first_last(SPF_Instance filter, int *first, int *last)
|
||||||
|
{
|
||||||
|
if (filter->last < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Always slew the last sample as it may be returned even if no new
|
||||||
|
samples were accumulated */
|
||||||
|
if (filter->used > 0) {
|
||||||
|
*first = 0;
|
||||||
|
*last = filter->used - 1;
|
||||||
|
} else {
|
||||||
|
*first = *last = filter->last;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -418,18 +459,9 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
|
|||||||
int i, first, last;
|
int i, first, last;
|
||||||
double delta_time;
|
double delta_time;
|
||||||
|
|
||||||
if (filter->last < 0)
|
if (!get_first_last(filter, &first, &last))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Always slew the last sample as it may be returned even if no new
|
|
||||||
samples were accumulated */
|
|
||||||
if (filter->used > 0) {
|
|
||||||
first = 0;
|
|
||||||
last = filter->used - 1;
|
|
||||||
} else {
|
|
||||||
first = last = filter->last;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = first; i <= last; i++) {
|
for (i = first; i <= last; i++) {
|
||||||
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
||||||
&delta_time, dfreq, doffset);
|
&delta_time, dfreq, doffset);
|
||||||
@@ -439,6 +471,20 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
|
|||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
SPF_CorrectOffset(SPF_Instance filter, double doffset)
|
||||||
|
{
|
||||||
|
int i, first, last;
|
||||||
|
|
||||||
|
if (!get_first_last(filter, &first, &last))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = first; i <= last; i++)
|
||||||
|
filter->samples[i].offset -= doffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ extern void SPF_DestroyInstance(SPF_Instance filter);
|
|||||||
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
|
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
|
||||||
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
|
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
|
||||||
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
|
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
|
||||||
|
extern int SPF_GetMaxSamples(SPF_Instance filter);
|
||||||
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
|
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
|
||||||
extern void SPF_DropSamples(SPF_Instance filter);
|
extern void SPF_DropSamples(SPF_Instance filter);
|
||||||
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
|
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
|
||||||
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
|
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
|
||||||
double dfreq, double doffset);
|
double dfreq, double doffset);
|
||||||
|
extern void SPF_CorrectOffset(SPF_Instance filter, double doffset);
|
||||||
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
|
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
22
sched.c
22
sched.c
@@ -47,12 +47,6 @@ static int initialised = 0;
|
|||||||
/* One more than the highest file descriptor that is registered */
|
/* One more than the highest file descriptor that is registered */
|
||||||
static unsigned int one_highest_fd;
|
static unsigned int one_highest_fd;
|
||||||
|
|
||||||
#ifndef FD_SETSIZE
|
|
||||||
/* If FD_SETSIZE is not defined, assume that fd_set is implemented
|
|
||||||
as a fixed size array of bits, possibly embedded inside a record */
|
|
||||||
#define FD_SETSIZE (sizeof(fd_set) * 8)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SCH_FileHandler handler;
|
SCH_FileHandler handler;
|
||||||
SCH_ArbitraryArgument arg;
|
SCH_ArbitraryArgument arg;
|
||||||
@@ -104,7 +98,10 @@ static unsigned long n_timer_queue_entries;
|
|||||||
static SCH_TimeoutID next_tqe_id;
|
static SCH_TimeoutID next_tqe_id;
|
||||||
|
|
||||||
/* Pointer to head of free list */
|
/* Pointer to head of free list */
|
||||||
static TimerQueueEntry *tqe_free_list = NULL;
|
static TimerQueueEntry *tqe_free_list;
|
||||||
|
|
||||||
|
/* Array of all allocated tqe blocks to be freed in finalisation */
|
||||||
|
static ARR_Instance tqe_blocks;
|
||||||
|
|
||||||
/* Timestamp when was last timeout dispatched for each class */
|
/* Timestamp when was last timeout dispatched for each class */
|
||||||
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
|
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
|
||||||
@@ -133,6 +130,8 @@ SCH_Initialise(void)
|
|||||||
|
|
||||||
n_timer_queue_entries = 0;
|
n_timer_queue_entries = 0;
|
||||||
next_tqe_id = 0;
|
next_tqe_id = 0;
|
||||||
|
tqe_free_list = NULL;
|
||||||
|
tqe_blocks = ARR_CreateInstance(sizeof (TimerQueueEntry *));
|
||||||
|
|
||||||
timer_queue.next = &timer_queue;
|
timer_queue.next = &timer_queue;
|
||||||
timer_queue.prev = &timer_queue;
|
timer_queue.prev = &timer_queue;
|
||||||
@@ -154,8 +153,16 @@ SCH_Initialise(void)
|
|||||||
|
|
||||||
void
|
void
|
||||||
SCH_Finalise(void) {
|
SCH_Finalise(void) {
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
ARR_DestroyInstance(file_handlers);
|
ARR_DestroyInstance(file_handlers);
|
||||||
|
|
||||||
|
timer_queue.next = &timer_queue;
|
||||||
|
timer_queue.prev = &timer_queue;
|
||||||
|
for (i = 0; i < ARR_GetSize(tqe_blocks); i++)
|
||||||
|
Free(*(TimerQueueEntry **)ARR_GetElement(tqe_blocks, i));
|
||||||
|
ARR_DestroyInstance(tqe_blocks);
|
||||||
|
|
||||||
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
|
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
|
||||||
|
|
||||||
initialised = 0;
|
initialised = 0;
|
||||||
@@ -281,6 +288,7 @@ allocate_tqe(void)
|
|||||||
}
|
}
|
||||||
new_block[0].next = NULL;
|
new_block[0].next = NULL;
|
||||||
tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
|
tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
|
||||||
|
ARR_AppendElement(tqe_blocks, &new_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = tqe_free_list;
|
result = tqe_free_list;
|
||||||
|
|||||||
1
sched.h
1
sched.h
@@ -37,6 +37,7 @@ typedef enum {
|
|||||||
SCH_NtpClientClass,
|
SCH_NtpClientClass,
|
||||||
SCH_NtpPeerClass,
|
SCH_NtpPeerClass,
|
||||||
SCH_NtpBroadcastClass,
|
SCH_NtpBroadcastClass,
|
||||||
|
SCH_PhcPollClass,
|
||||||
SCH_NumberOfClasses /* needs to be last */
|
SCH_NumberOfClasses /* needs to be last */
|
||||||
} SCH_TimeoutClass;
|
} SCH_TimeoutClass;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user