Skip to content

Commit 89c4d21

Browse files
authored
Merge pull request #1302 from kernelkit/ntp-server
2 parents fe86744 + a5efb18 commit 89c4d21

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+6176
-89
lines changed

doc/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ All notable changes to the project are documented in this file.
2727
- Upgrade sysrepo to 4.2.10
2828
- Upgrade netopeer2 (NETCONF) to 2.7.0
2929
- Add RIPv2 routing support, issue #582
30+
- Add NTP server support, issue #904
3031
- Add support for configurable OSPF debug logging, issue #1281. Debug options
3132
can now be enabled per category (bfd, packet, ism, nsm, default-information,
3233
nssa). All debug options are disabled by default to prevent log flooding in

doc/ntp.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# NTP Server
2+
3+
The NTP (Network Time Protocol) server provides accurate time synchronization
4+
for network clients. It supports both standalone operation with a local
5+
reference clock and hybrid mode where it synchronizes with upstream servers
6+
while serving time to downstream clients.
7+
8+
> [!NOTE]
9+
> The NTP server is mutually exclusive with the NTP client in system
10+
> configuration context.
11+
12+
## Standalone Mode
13+
14+
Configure a standalone NTP server using only a local reference clock:
15+
16+
```
17+
admin@example:/> configure
18+
admin@example:/config/> edit ntp
19+
admin@example:/config/ntp/> leave
20+
```
21+
22+
When setting up NTP via the CLI the system automatically configures a local
23+
reference clock. The default [stratum](#ntp-stratum-levels) is 16 (unsynchronized),
24+
which is suitable for isolated networks. For production use, configure a specific
25+
stratum level:
26+
27+
```
28+
admin@example:/config/> edit ntp
29+
admin@example:/config/ntp/> set refclock-master master-stratum 10
30+
admin@example:/config/ntp/> leave
31+
```
32+
33+
## Server Mode
34+
35+
Synchronize from upstream NTP servers while serving time to clients:
36+
37+
```
38+
admin@example:/config/> edit ntp
39+
admin@example:/config/ntp/> edit unicast-configuration 0.pool.ntp.org type uc-server
40+
admin@example:/config/ntp/…/0.pool.ntp.org/type/uc-server/> set iburst true
41+
admin@example:/config/ntp/…/0.pool.ntp.org/type/uc-server/> end
42+
admin@example:/config/ntp/> edit unicast-configuration 1.pool.ntp.org type uc-server
43+
admin@example:/config/ntp/…/1.pool.ntp.org/type/uc-server/> set iburst true
44+
admin@example:/config/ntp/…/1.pool.ntp.org/type/uc-server/> end
45+
admin@example:/config/ntp/> leave
46+
```
47+
48+
The `unicast-configuration` uses a composite key with both address and type.
49+
Both hostnames and IP addresses are supported. The `iburst` option enables
50+
fast initial synchronization.
51+
52+
## Peer Mode
53+
54+
In peer mode, two NTP servers synchronize with each other bidirectionally.
55+
Each server acts as both client and server to the other:
56+
57+
**First peer:**
58+
59+
```
60+
admin@peer1:/config/> edit ntp
61+
admin@peer1:/config/ntp/> edit unicast-configuration 192.168.1.2 type uc-peer
62+
admin@peer1:/config/ntp/…/192.168.1.2/type/uc-peer/> end
63+
admin@peer1:/config/ntp/> set refclock-master master-stratum 8
64+
admin@peer1:/config/ntp/> leave
65+
```
66+
67+
**Second peer:**
68+
69+
```
70+
admin@peer2:/config/> edit ntp
71+
admin@peer2:/config/ntp/> edit unicast-configuration 192.168.1.1 type uc-peer
72+
admin@peer2:/config/ntp/…/192.168.1.1/type/uc-peer/> end
73+
admin@peer2:/config/ntp/> set refclock-master master-stratum 8
74+
admin@peer2:/config/ntp/> leave
75+
```
76+
77+
This configuration provides mutual synchronization between peers. If one peer
78+
fails, the other continues to serve time to clients.
79+
80+
> [!NOTE]
81+
> The `iburst` and `burst` options are not supported in peer mode.
82+
83+
### Peer Selection in Symmetric Mode
84+
85+
When both peers have the same stratum (as in the example above where both are
86+
stratum 8), NTP's clock selection algorithm uses the **Reference ID** as the
87+
tie-breaker. The Reference ID is typically derived from the peer's IP address
88+
when using a local reference clock.
89+
90+
This means the peer with the **numerically lower IP address** will be selected
91+
as the sync source by the other peer. In the example above:
92+
93+
- peer1 (192.168.1.1) has a lower Reference ID
94+
- peer2 (192.168.1.2) will select peer1 as sync source
95+
96+
This behavior is deterministic and ensures stable clock selection. If you need
97+
a specific peer to be selected, configure it with a lower stratum level than
98+
the other peer.
99+
100+
## Timing Configuration
101+
102+
### Poll Intervals
103+
104+
Control how often the NTP server polls upstream sources:
105+
106+
```
107+
admin@example:/config/ntp/> edit unicast-configuration 0.pool.ntp.org type uc-server
108+
admin@example:/config/ntp/…/0.pool.ntp.org/type/uc-server/> set minpoll 4
109+
admin@example:/config/ntp/…/0.pool.ntp.org/type/uc-server/> set maxpoll 10
110+
admin@example:/config/ntp/…/0.pool.ntp.org/type/uc-server/> end
111+
```
112+
113+
Poll intervals are specified as powers of 2:
114+
- `minpoll 4` = poll every 2^4 = 16 seconds (minimum polling rate)
115+
- `maxpoll 10` = poll every 2^10 = 1024 seconds (maximum polling rate)
116+
- Defaults: minpoll 6 (64 seconds), maxpoll 10 (1024 seconds)
117+
118+
Use shorter intervals (minpoll 2-4) for faster convergence in test environments
119+
or peer configurations. Use defaults for production servers.
120+
121+
### Fast Initial Synchronization
122+
123+
The `makestep` directive is automatically configured with safe defaults (1.0
124+
seconds threshold, 3 updates limit) when creating an NTP server. This is
125+
critical for embedded systems without RTC that boot with epoch time.
126+
127+
To customize the values:
128+
129+
```
130+
admin@example:/config/ntp/> edit makestep
131+
admin@example:/config/ntp/makestep/> set threshold 2.0
132+
admin@example:/config/ntp/makestep/> set limit 1
133+
admin@example:/config/ntp/makestep/> end
134+
```
135+
136+
- **threshold** - If clock offset exceeds this (in seconds), step immediately
137+
instead of slewing slowly
138+
- **limit** - Number of updates during which stepping is allowed. After this,
139+
only gradual slewing is used for security
140+
141+
With these defaults, a device booting at epoch time (1970-01-01) will sync to
142+
correct time within seconds instead of hours.
143+
144+
## Monitoring
145+
146+
Check NTP server statistics:
147+
148+
```
149+
admin@example:/> show ntp server
150+
NTP SERVER CONFIGURATION
151+
Local Stratum : 10
152+
153+
SERVER STATISTICS
154+
Packets Received : 142
155+
Packets Sent : 142
156+
Packets Dropped : 0
157+
Send Failures : 0
158+
```
159+
160+
## NTP Stratum Levels
161+
162+
NTP uses a hierarchical system called **stratum** to indicate distance from
163+
authoritative time sources:
164+
165+
- **Stratum 0**: Reference clocks (atomic clocks)
166+
- **Stratum 1**: Servers directly connected to stratum 0 (e.g., GPS receivers)
167+
- **Stratum 2-15**: Servers that sync from lower stratum (each hop adds one)
168+
- **Stratum 16**: Unsynchronized (invalid)
169+
170+
The default stratum (16) is not suitable for distributing time in isolated
171+
networks, so when setting up an NTP server remember to adjust this value.
172+
Use, e.g., `10`, this is a safe, low-priority value that ensures clients will
173+
prefer upstream-synchronized servers (stratum 1-9) while still having a
174+
fallback time source in isolated networks.

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ nav:
3737
- Services:
3838
- Device Discovery: discovery.md
3939
- DHCP Server: dhcp.md
40+
- NTP Server: ntp.md
4041
- System:
4142
- Boot Procedure: boot.md
4243
- Configuration: system.md

src/confd/src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ confd_plugin_la_SOURCES = \
4040
if-wifi.c \
4141
keystore.c \
4242
system.c \
43+
ntp.c \
4344
syslog.c \
4445
factory-default.c \
4546
routing.c \

src/confd/src/core.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
328328
if ((rc = keystore_change(session, config, diff, event, confd)))
329329
goto free_diff;
330330

331+
/* ietf-ntp */
332+
if ((rc = ntp_change(session, config, diff, event, confd)))
333+
goto free_diff;
334+
331335
/* infix-services */
332336
if ((rc = services_change(session, config, diff, event, confd)))
333337
goto free_diff;
@@ -366,6 +370,20 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
366370
if ((rc = meta_change_cb(session, config, diff, event, confd)))
367371
goto free_diff;
368372

373+
/*
374+
* Manage chronyd service enable/disable state. Must be done
375+
* after both ietf-system:ntp and ietf-ntp have are done.
376+
*/
377+
if (event == SR_EV_DONE && config) {
378+
bool client = false;
379+
bool server = false;
380+
381+
client = srx_enabled(session, "/ietf-system:system/ntp/enabled");
382+
server = lydx_get_xpathf(config, "/ietf-ntp:ntp") != NULL;
383+
384+
systemf("initctl -nbq %s chronyd", client || server ? "enable" : "disable");
385+
}
386+
369387
if (cfg)
370388
sr_release_data(cfg);
371389

@@ -454,6 +472,11 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
454472
ERROR("Failed to subscribe to ietf-netconf-acm");
455473
goto err;
456474
}
475+
rc = subscribe_model("ietf-ntp", &confd, 0);
476+
if (rc) {
477+
ERROR("Failed to subscribe to ietf-ntp");
478+
goto err;
479+
}
457480
rc = subscribe_model("infix-dhcp-client", &confd, 0);
458481
if (rc) {
459482
ERROR("Failed to subscribe to infix-dhcp-client");
@@ -554,6 +577,10 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
554577
goto err;
555578

556579
rc = dhcp_server_candidate_init(&confd);
580+
if (rc)
581+
goto err;
582+
583+
rc = ntp_candidate_init(&confd);
557584
if (rc)
558585
goto err;
559586
/* YOUR_INIT GOES HERE */

src/confd/src/core.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,10 @@ int firewall_rpc_init(struct confd *confd);
247247
int firewall_candidate_init(struct confd *confd);
248248
int firewall_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
249249

250+
/* ntp.c */
251+
int ntp_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
252+
int ntp_cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
253+
const char *path, sr_event_t event, unsigned request_id, void *priv);
254+
int ntp_candidate_init(struct confd *confd);
255+
250256
#endif /* CONFD_CORE_H_ */

0 commit comments

Comments
 (0)