The following explains how to integrate a local time server such GNATS into a local network. Some of this material was included in the first version of the post describing the construction of GNATS, but it should be separated for two reasons. First, the subject discussed here has nothing to do with GNATS in particular, it applies to any local NTP server. Second, adding a local NTP server to a LAN is a rather more complex task than described initially.
As far as I can ascertain, there are three ways of using the local NTP server.
- Modify the configuration of each device that needs to access an NTP server, specifying the IP address of the local NTP server. That was exactly what was done with the Raspberry Pi using GNATS as a time source. Let's call this a static NTP client configuration.
- Modify the configuration of each device that needs to access an NTP server so that it will query the DHCP server for the addresses of available NTP servers. Of course this is where the local NTP server's IP address would be provided to the client. In this scenario, the configuration of the DHCP server must be changed so that it knows the IP address of NTP servers and that it responds to request for that information from clients. The whole process, which could be labelled automatic NTP configuration, is thus a two-step procedure which is slightly more complex than the static configuration.
- Use a hybrid solution combining the above two methods. Spoiler alert: that will probably be the only practical solution.
Table of Contents
- Static NTP Client Configuration
systemd-timesyncd
chrony
ntp
tasmota
- Automatic NTP Configuration
- DHCP Server Configuration
- NTP Servers Option in OpenWRT
- NTP Servers Option in pfSense and OPNsense
- Client NTP Discovery Configuration
systemd-timesyncd
chrony
ntp
tasmota
- Practical Considerations
The Raspberry Pi uses the systemd-timesyncd
service to set the time from NTP servers. However that is far from a universal choice as the following table about some of the devices in the house makes abundantly clear.
Device | OS | NTP service |
Raspberry Pi 3B | Raspberry Pi OS | systemd-timesyncd |
Rock Pi S | Ubuntu 20.04 LTS | systemd-timesyncd |
Orange Pi PC 2 | Armbian 22.11.4 Jammy | chrony |
HP i5 NAS | OMV (Debian 11 bullseye) | chrony |
HP i7 Desktop | Linux Mint 20.1 | ntp |
Add to that list a phone and a number of tablets that I use that are running different versions of Android, my spouse's iMac and iPad, the 20 or more Tasmota based IoT devices and probably other things that I can't remember right now. It's all a bit of a mess.
This has already been covered at length in the previous post about GNATS.
Edit /etc/systemd/timesyncd.conf
configuration file. It belongs to root
, so upgrading privileges with sudo
will be necessary unless one has opened a session as root
. Modify the first or first and second entries in the [Time<]
section.
[Time]
#NTP=
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org >
As an example, here is the section on the Raspberrry Pi used for testing GNATS.
[Time]
NTP=192.168.1.23
FallbackNTP=ca.pool.ntp.org debian.pool.ntp.org pool.ntp.org
To check on the configuration file
pi@tarte:~ $ date
Wed 14 Jun 21:13:35 ADT 2023
pi@tarte:~ $ timedatectl status
Local time: Wed 2023-06-14 21:13:55 ADT
Universal time: Thu 2023-06-15 00:13:55 UTC
RTC time: Thu 2023-06-15 00:13:55 UTC
Time zone: America/Halifax (ADT, -0300)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
pi@tarte:~ $ timedatectl show-timesync
SystemNTPServers=192.168.1.23
FallbackNTPServers=192.168.1.88
ServerName=192.168.1.23
ServerAddress=192.168.1.23
RootDistanceMaxUSec=5s
PollIntervalMinUSec=32s
PollIntervalMaxUSec=34min 8s
PollIntervalUSec=2min 8s
NTPMessage={ Leap=0, Version=4, Mode=4, Stratum=1, Precision=-15, RootDelay=15us, RootDispersion=15us, Reference=, OriginateTimestamp=Wed 2023-06-14 21:13:53 ADT, ReceiveTimestamp=Wed 2023-06-14 21:13:53 ADT, TransmitTimestamp=Wed 2023-06-14 21:13:53 ADT, DestinationTimestamp=Wed 2023-06-14 21:13:53 ADT, Ignored=no PacketCount=3, Jitter=31.612ms }
Frequency=-8163025
pi@tarte:~ $ timedatectl timesync-status
Server: 192.168.1.23 (192.168.1.23)
Poll interval: 2min 8s (min: 32s; max 34min 8s)
Leap: normal
Version: 4
Stratum: 1
Reference:
Precision: 31us (-15)
Root distance: 22us (max: 5s)
Offset: -31.887ms
Delay: 29.701ms
Jitter: 31.612ms
Packet count: 3
Frequency: -124.558ppm
I know very little about chrony, consequently what follows should be seen as an indication of a possible way to go about this. Let's start with a look at list of time sources defined by default.
citrus@fruity:~$ chronyc sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^- prod-ntp-3.ntp4.ps5.cano> 2 10 377 1035 +1156us[+1179us] +/- 50ms
^- prod-ntp-5.ntp1.ps5.cano> 2 10 377 667 +1459us[+1459us] +/- 48ms
^+ prod-ntp-4.ntp1.ps5.cano> 2 10 377 1013 +6843us[+6866us] +/- 50ms
^- pugot.canonical.com 2 10 377 259 -1183us[-1183us] +/- 88ms
^+ ntp2.wiktel.com 1 10 377 586 +568us[ +568us] +/- 28ms
^+ four10.gac.edu 2 10 377 117 -1183us[-1183us] +/- 39ms
^* edge-iad.txryan.com 2 10 377 952 -582us[ -559us] +/- 16ms
^- 104.167.241.253 2 9 377 15 +983us[ +983us] +/- 55ms
Looking at the content of a few files gives a good indication of how to configure the time sources for this service.
citrus@fruity:~$ cat /etc/chrony/chrony.conf
# Welcome to the chrony configuration file. See chrony.conf(5) for more
# information about usable directives.
# Include configuration files found in /etc/chrony/conf.d.
confdir /etc/chrony/conf.d
# This will use (up to):
# - 4 sources from ntp.ubuntu.com which some are ipv6 enabled
# - 2 sources from 2.ubuntu.pool.ntp.org which is ipv6 enabled as well
# - 1 source from [01].ubuntu.pool.ntp.org each (ipv4 only atm)
# This means by default, up to 6 dual-stack and up to 2 additional IPv4-only
# sources will be used.
# At the same time it retains some protection against one of the entries being
# down (compare to just using one of the lines). See (LP: #1754358) for the
# discussion.
#
# About using servers from the NTP Pool Project in general see (LP: #104525).
# Approved by Ubuntu Technical Board on 2011-02-08.
# See http://www.pool.ntp.org/join.html for more information.
pool ntp.ubuntu.com iburst maxsources 4
pool 0.ubuntu.pool.ntp.org iburst maxsources 1
pool 1.ubuntu.pool.ntp.org iburst maxsources 1
pool 2.ubuntu.pool.ntp.org iburst maxsources 2
# Use time sources from DHCP.
sourcedir /run/chrony-dhcp
# Use NTP sources found in /etc/chrony/sources.d.
sourcedir /etc/chrony/sources.d
...
citrus@fruity:~$ cat /etc/chrony/sources.d/README
Only NTP sources can be specified in the /etc/chrony/sources.d directory.
Files in this directory must end with the ".sources" suffix. They can only
contain the "peer", "pool" and "server" directives and must have all
lines terminated by a trailing newline.
There is no need to restart chronyd for these time sources to be usable,
running 'chronyc reload sources' is sufficient.
Example:
# echo 'server 192.0.2.1 iburst' > /etc/chrony/sources.d/local-ntp-server.sources
# chronyc reload sources
So let's execute this as instructed.
citrus@fruity:~$ sudo su
root@fruity:/home/citrus# echo 'server 192.168.1.23 iburst' > /etc/chrony/sources.d/local-ntp-server.sources
root@fruity:/home/citrus# chronyc reload sources
200 OK
root@fruity:/home/citrus# exit
exit
citrus@fruity:~$ chronyc sources -v
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
| / 'x' = may be in error, '~' = too variable, '?' = unusable.
|| .- xxxx [ yyyy ] +/- zzzz
|| Reachability register (octal) -. | xxxx = adjusted offset,
|| Log2(Polling interval) --. | | yyyy = measured offset,
|| \ | | zzzz = estimated error.
|| | | MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^- prod-ntp-3.ntp4.ps5.cano> 2 10 377 25m +1210us[+1278us] +/- 49ms
^- prod-ntp-5.ntp1.ps5.cano> 2 10 377 1158 +985us[ +970us] +/- 48ms
^+ prod-ntp-4.ntp1.ps5.cano> 2 10 377 471 +1234us[+1220us] +/- 45ms
^- pugot.canonical.com 2 10 377 751 -2765us[-2779us] +/- 89ms
^+ ntp2.wiktel.com 1 10 377 51 -886us[ -886us] +/- 27ms
^+ four10.gac.edu 2 10 377 609 +1021us[+1007us] +/- 36ms
^* edge-iad.txryan.com 2 10 377 422 -356us[ -370us] +/- 17ms
^- 104.167.241.253 2 10 377 507 +912us[ +898us] +/- 55ms
^x 192.168.1.23 1 6 377 37 +222ms[ +222ms] +/- 47ms
The *
beside edge-iad.txryan.com
indicates [that it is] the source to which chronyd is currently synchronised while the x
beside the IP address of the local NTP server indicates a clock which chronyd thinks is a falseticker (i.e. its time is inconsistent with a majority of other sources) (source: chronyc(1) Manual Page). Adding the prefer
option (server 192.168.1.23 iburst prefer
) which would make GNATS the primary time source does nothing. Probably because the service will not use a "falseticker" in preference to consistent time source. So with this configuration, the local time server is the backup, and remote servers will be used preferentially. This is not unreasonable, but it is the opposite of what I wanted to achieve when creating GNATS.
One could remove all the pooled servers in the main configuration file /etc/chrony/chrony.conf
, and after a while, the chrony
will use the local NTP server at the time source.
citrus@fruity:~$ chronyc sources -v
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined,
| / 'x' = may be in error, '~' = too variable, '?' = unusable.
|| .- xxxx [ yyyy ] +/- zzzz
|| Reachability register (octal) -. | xxxx = adjusted offset,
|| Log2(Polling interval) --. | | yyyy = measured offset,
|| \ | | zzzz = estimated error.
|| | | MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* 192.168.1.23 1 6 177 24 -2719us[ -76us] +/- 13ms
Of course there is no backup in this situation.
There seems to be a sentiment that chrony
is a newer and better replacement of the venerable ntp
from the Network Time Foundation. I am in no position to judge, but I did notice a close similarity between the programs. As before, will start the list of time sources defined by default.
michel@hp:~$ ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000
+dev.smatwebdesi 204.9.54.119 2 u 924 1024 377 58.303 1.589 0.614
-B1-66ER.matrix. 18.26.4.105 2 u 1025 1024 377 44.235 2.387 0.879
-sensei.ruselabs 164.67.62.194 2 u 279 1024 377 87.562 -0.092 0.835
+alphyn.canonica 194.58.200.20 2 u 806 1024 375 27.171 0.846 0.695
*shaka.ruselabs. 59.61.64.227 2 u 974 1024 377 35.937 0.944 0.603
It doesn't look as if ntp
will load additional configuration files, so I added the local NTP server directly in the main configuration file
michel@hp:~$ sudo nano /etc/ntp.conf
...
#Specify one or more NTP servers.
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
pool 0.ubuntu.pool.ntp.org iburst
pool 1.ubuntu.pool.ntp.org iburst
pool 2.ubuntu.pool.ntp.org iburst
pool 3.ubuntu.pool.ntp.org iburst
# Use Ubuntu's ntp server as a fallback.
pool ntp.ubuntu.com
# Adding local server
server 192.168.1.23 iburst
...
I don't know if there's a command to reload sources, so I juste restarted the ntp.service
and checked that it was working properly
michel@hp:~$ sudo systemctl restart ntp.service
michel@hp:~$ sudo systemctl status ntp.service
● ntp.service - Network Time Service
Loaded: loaded (/lib/systemd/system/ntp.service; enabled; vendor preset: enabled)
Active: active (running) since Sat 2023-06-17 14:27:55 ADT; 4s ago
Docs: man:ntpd(8)
Process: 116020 ExecStart=/usr/lib/ntp/ntp-systemd-wrapper (code=exited, status=0/SUCCESS)
Main PID: 116028 (ntpd)
Tasks: 2 (limit: 14133)
Memory: 940.0K
CGroup: /system.slice/ntp.service
└─116028 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 106:111
jun 17 14:27:56 hp ntpd[116028]: Soliciting pool server 73.239.145.47
jun 17 14:27:57 hp ntpd[116028]: Soliciting pool server 204.2.134.162
jun 17 14:27:57 hp ntpd[116028]: Soliciting pool server 194.116.227.9
jun 17 14:27:58 hp ntpd[116028]: Soliciting pool server 45.79.111.167
jun 17 14:27:58 hp ntpd[116028]: Soliciting pool server 15.204.21.148
jun 17 14:27:58 hp ntpd[116028]: Soliciting pool server 171.66.97.126
jun 17 14:27:59 hp ntpd[116028]: Soliciting pool server 174.136.99.6
jun 17 14:27:59 hp ntpd[116028]: Soliciting pool server 206.82.16.3
jun 17 14:27:59 hp ntpd[116028]: Soliciting pool server 69.164.202.202
jun 17 14:27:59 hp ntpd[116028]: Soliciting pool server 168.235.89.132
One has to wait a while before the service will look at the local time server.
michel@hp:~$ ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000
ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000
192.168.1.23 1 u 38 64 1 11.052 -253.55 20.547
-ny-time.gofile. 192.5.41.40 2 u 31 64 17 40.347 0.078 2.250
#t1.time.gq1.yah 98.137.249.214 2 u 40 64 7 86.825 5.759 2.105
-ntp2.wiktel.com .GPS. 1 u 35 64 7 50.983 -1.315 3.188
#216.31.17.12 172.18.56.13 2 u 37 64 7 148.374 4.156 3.174
-dutch.arpnet.ne 66.220.9.122 2 u 37 64 7 21.145 -0.953 3.305
-li1210-167.memb 132.163.96.2 2 u 29 64 17 89.959 -6.631 3.236
#12.167.151.1 158.51.134.123 3 u 40 64 7 30.650 -69.055 3.198
-50.205.57.38 .GPS. 1 u 37 64 7 46.245 0.033 3.200
-edge-pdx.txryan 129.134.25.123 2 u 38 64 7 93.111 5.291 2.150
*clock.nyc.he.ne 66.220.9.122 2 u 49 64 37 21.228 -0.707 1.284
+50.46.167.147 ( 23.252.63.82 2 u 47 64 37 96.338 -7.885 1.410
+65-100-46-166.d 10.30.90.252 2 u 43 64 37 83.383 -1.502 1.204
-static.36.62.78 225.254.30.190 4 u 41 64 37 95.317 6.795 1.230
As can be seen, the situation appears to be very similar to what was obtained in chrony
. It would be necessary to eliminate the server pool to ensure that the local NTP server is used, but then there would be no back up.
Accoring to the online documentation about the set up of NTP servers in Tasmota,
up to three servers can be specified. For example, NtpServer1 0.can.pool.ntp.org
would set 0.can.pool.ntp.org
as the first NTP server, while NtpServer1 0
would clear the value. So the following command ensures that only the local NTP server be used.
Backlog0 NtpServer1 192.168.1.23; NtpServer2 0; NtpServer3 0
However the documentation is clear. NTP servers are consulted in order, from server 1 to 3, stopping as soon as a time request is obtained. So it would probably be best to change only NtpServer1 and to let the other two unchanged or to specify explicitely which server to place in all three slots
Accordingly, the NTP servers of the Tasmota devices in the home ise the following.
Backlog0 NtpServer1 192.168.1.23 ; NtpServer2 ca.pool.ntp.org ; NtpServer3 pool.ntp.org
Since the NTP server will provide a UTC time stamp and local time is more meaningful when using triggers such as the time, sunset, or sunrise, the time zone must be defined. As one would expect Tasmota does not store a time zone database, differences between UTC and local time have to be specified numerically. Luckily, there's no need to into the arcane details about specifying the correct time zone. Just go to the Tasmota Timezone Table~ and search for the best city for your local time.
In my case there are a few entries that could be used such as the one for Halifax. It's lucky that the only Halifax in the database is in nearby Nova Scotia, because there's at least another ten municipalities named Halifax elsewhere.
America/Halifax Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
Combining these commands, the pertinent time values for a new Tasmota device could be fixed in a single backlog
command.
Backlog0 NtpServer1 192.168.1.23; NtpServer2 0; NtpServer3 0; Backlog0 Timezone 99; TimeStd 0,1,11,1,2,-240; TimeDst 0,2,3,1,2,-180
However, it would be very tedious to find the IP address of the 20 plus Tasmota devices, open the Web interface of each of them and then manually enter the command in the Web console, if I wanted to change the address of an NTP server or the time zone of all the installed devices. Instead the commands can be sent to the devices by publishing MQTT messages. As far as I know, backlog
is not supported in MQTT commands, so each command must be published independantly.
mqtt subject | mqtt publish message |
cmnd/tasmota/NtpServer1 | 192.168.1.23 |
cmnd/tasmota/NtpServer2 | ca.pool.ntp.org |
cmnd/tasmota/NtpServer3 | pool.ntp.org |
cmnd/tasmota/Timezone | 99 |
cmnd/tasmota/TimeStd | 0,1,11,1,2,-240 |
cmnd/tasmota/TimeDst | 0,2,3,1,2,-180 |
Suscribe to "stat/+/RESULT" to see the reply of all the Tasmota devices connected to the MQTT broker. I use my own MQTT client, lazmqttc, written in Lazarus / Free pascal to do this. Here is my tasmota.json
file that I systematically use to send and receive MQTT messages from all the Tasmota devices on our home system. Of course the address of the MQTT broker would need to be adjusted.
{ "Host" : "192.168.1.22",
"Port" : 1883,
"User" : "",
"Password" : "",
"SSL" : false,
"SSLCert" : "",
"KeepAlives" : 60,
"ReconnectDelay" : 10,
"ReconnectBackoff" : true,
"AutoReconnect" : true,
"PubTopic" : "cmnd/tasmotas/status",
"PubPayload" : "5",
"PubQoS" : 0,
"PubRetain" : false,
"SubTopics" : [
{ "Topic" : "#", "QoS" : 0, "Use" : false },
{ "Topic" : "fruityticz/in", "QoS" : 0, "Use" : false },
{ "Topic" : "fruityticz/out", "QoS" : 0, "Use" : false },
{ "Topic" : "stat/+/STATUS5", "QoS" : 0, "Use" : true },
{ "Topic" : "stat/+/RESULT", "QoS" : 0, "Use" : false },
{ "Topic" : "stat/#", "QoS" : 0, "Use" : false },
{ "Topic" : "tele/#", "QoS" : 0, "Use" : false }
]
}
Sorry for the self-promotion, mosquitto_pub, mosquitt_rr or any of the innumerable MQTT clients available for desktops or tablets and phones could just as easily be used.
Would it not be better if the local network provided the address of our tiny NTP server instead of having to manually configure the address of the NTP server of each device connected to the network? Indeed DHCP servers can provide NTP server addresses along with dynamically assigned IP addresses to clients. It took a little while to figure it out, but here is what needs to be done in broad strokes.
- Ensure that the DHCP server (usually in the router) forwards the address of the local time server using option 42.
- Ensure that all DHCP clients that need to access NTP servers request their addresses from the DHCP server. In other words, DHCP clients must be told to avail themselves of option 42.
As already said, my ISP provided "all-in-one" ONT/Ethernet switch/Wifi router/DHCP server etc. is very much a "consumer grade" device with very few user settings. In particular it does not have any NTP options. Consequently this approach may not be possible for many.
Networked devices with self-assigned IP addresses never communicate with the DHCP server. So the proposed scheme only works if network peripherals that need access to the NTP server (and DNS servers for that matter) use dynamic IP addressing. For servers that need a fixed IP address, have the DHCP server assign them their fixed address. So far all "consumer grade" routers that I have seen have that capability.
Among the optional configuration parameters that can be returned by a DHCP server to its clients there is the
8.3. Network Time Protocol Servers Option
This option specifies a list of IP addresses indicating NTP [18]
servers available to the client. Servers SHOULD be listed in order
of preference.
The code for this option is 42. Its minimum length is 4, and the
length MUST be a multiple of 4.
Code Len Address 1 Address 2
+-----+-----+-----+-----+-----+-----+-----+-----+--
| 42 | n | a1 | a2 | a3 | a4 | a1 | a2 | ...
+-----+-----+-----+-----+-----+-----+-----+-----+--
Source:
RFC 2132, p.18
The NTP servers option (or option 42) is not usually included by DHCP servers by default. It needs to be enabled in the DHCP server and DHCP clients must request this information. Explaining in detail how to enable NTP Servers option (option 42) in all possible routers / DHCP servers is just not possible. Unfortunately, I have a single complete and tested example of how to perform the first step.
By the way, there are time zone options for DHCP as per RFC4833... we are not going down that rabbit hole.
The firmware of a Rosewill 300M Wireless N which is a rebranded TP-Link TL-WR841ND v7 router has been LEDE 17.01.4. In other words, it is running a 2017 version of OpenWRT (formerly known as LEDE, itself formerly known as OpenWRT... it's complicated). In principle, it could be upgraded but that will not be done. The router, which is not in constant use, has been a very useful tool for testing network configurations without interfering with the working LAN. Hopefully, changes to OpenWrt will not have been so so major as to make the information in this section useless.
Enabling option 42 in the DHCP server can be done from the command line or with the LuCi graphical interface. The command-line method is the easiest to present so let's start with that.
michel@hp:~$ ssh root@192.168.1.1
root@192.168.1.1's password: **********
BusyBox v1.25.1 () built-in shell (ash)
_________
/ /\ _ ___ ___ ___
/ LE / \ | | | __| \| __|
/ DE / \ | |__| _|| |) | _|
/________/ LE \ |____|___|___/|___| lede-project.org
\ \ DE /
\ LE \ / -----------------------------------------------------------
\ DE \ / Reboot (17.01.4, r3560-79f57e422d)
\________\/ -----------------------------------------------------------
root@LEDE:~# uci show dhcp.lan.dhcp_option=42
uci: Entry not found the option is not set
root@LEDE:~# uci set dhcp.lan.dhcp_option=42,192.168.1.23 add the local NTP server \
root@LEDE:~# uci show dhcp.lan.dhcp_option=42 as the only one available to clients
dhcp.lan.dhcp_option='42,192.168.1.23' just checking
root@LEDE:~# uci commit dhcp write the change to the configuration file
root@LEDE:~# /etc/init.d/dnsmasq restart restart the DHCP/DNS server
udhcpc: started, v1.25.1
udhcpc: sending discover
udhcpc: no lease, failing
Warning: the 'option dhcp_option' syntax is deprecated, use 'list dhcp_option'
I could not figure out the proper syntax, but that last line is just a warning so let's not worry about it now.
It is just as easy to enable DHCP option 42 using the graphical interface, but it is a bit more awkward to present here. Start by clicking on the Network
menu and select Interfaces
. Then click on the Edit
button of the desired interface which in my case was the LAN interface.

In the bottom of the
Intefaces - LAN page, in the
DHCP Server
section, click on the
Advances Settings
tab.

Add same setting as before,
42,192.168.1.23
, in the
DHCP-Options
box as shown above.
There is not much documentation on this, basically just an example entitled DHC options. Maybe I just didn't know where to look.
Presumably the same can be done with other routers, but as already said, not with the one rented from my Internet service provider.
The Netgate documentation for pfSense has the following description on how to add the option to its DHCP server.
NTP Servers
To specify NTP Servers (Network Time Protocol Servers), click the Display Advanced button to the right of that field, and enter IP addresses for up to two NTP servers.
I could not find specific documentation for the equivalent in OPNsense. But in an issue in the forum,
Udo says I've configured "Services -> DHCPv4 -> [Interface] -> NTP servers" with one IP which indicates that this is done in the Web interface much like in pfSense and OpenWRT.
In another topic in the forum, NTP on DHCP there is an explanation by guest19228 on how to add the option ntp-server 192.168.1.23
line in the DHCP service configuration file dhcpd.conf
. Since both pfSense and OPNsense are based on FreeBDS, that explanation may be valid for both.
Enabling NTP discovery in DHCP clients is very similar to what was required to enable NTP servers option in DHCP servers. But of course, the details are different depending on the NTP service running on the client.
Remember that systemd-timesyncd
is the NTP client in Raspbery Pi OS. Accordingly it is the configuration of the Raspberry Pi that will be examined in this section.
- Make sure that the Raspberry Pi is a DHCP client, so remove any static IP address.
- Remove the addition of a specific NTP server in
etc/systemd/timesyncd.conf
if that was done in the past
- Ensure that the
request in
/etc/dhcp/dhclient.conf
has ntp-servers
. This is true by default. It means that the content of option 42 will be requested from the DHCP server.
- Enable the
ntp_servers
option in the /etc/dhcpcd.conf
file. This means that the list of NTP servers returned by the DHCP server will be used.
Here are commands that verify that all that was done correctly.
pi@tarte:~ $ cat /etc/dhcpcd.conf | grep static
option classless_static_routes
# Example static IP configuration:
#static ip_address=192.168.1.10/24
#static ip6_address=fd51:42f8:caae:d92e::ff/64
#static routers=192.168.1.1
#static domain_name_servers=192.168.1.1 8.8.8.8 fd51:42f8:caae:d92e::1
# It is possible to fall back to a static IP if DHCP fails:
# define static profile
#profile static_eth0
#static ip_address=192.168.1.23/24
#static routers=192.168.1.1
#static domain_name_servers=192.168.1.1
# fallback to static profile on eth0
#fallback static_eth0
pi@tarte:~ $ cat /etc/systemd/timesyncd.conf | grep -A 1 \#NTP=
#NTP=
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
pi@tarte:~ $ cat /etc/dhcp/dhclient.conf | grep -B 2 ntp-servers
request subnet-mask, broadcast-address, time-offset, routers,
domain-name, domain-name-servers, domain-search, host-name,
dhcp6.name-servers, dhcp6.domain-search, dhcp6.fqdn, dhcp6.sntp-servers,
netbios-name-servers, netbios-scope, interface-mtu,
rfc3442-classless-static-routes, ntp-servers;
pi@tarte:~ $ cat /etc/dhcpcd.conf | grep -B 1 ntp_servers
# Most distributions have NTP support.
option ntp_servers
Yes it's ntp_servers
with an underscore in dhcpcd.conf
and ntp-servers
with a dash in dhclient.conf
.
Then clear the journal files as much as possible. This is to make it easier to check what happened after the Pi is rebooted, just in case something goes wrong.
pi@tarte:~ $ sudo journalctl --rotate; sudo journalctl -m --vacuum-time=1s
Vacuuming done, freed 0B of archived journals from /run/log/journal.
Deleted archived journal /run/log/journal/f0fc159a3f5844b5a29aaa488532c69b/system@21b31e5e6ea940999f0233af43801239-0000000000000001-0005f06c06d67277.journal (2.2M).
Deleted archived journal /run/log/journal/f0fc159a3f5844b5a29aaa488532c69b/system@21b31e5e6ea940999f0233af43801239-00000000000002d7-0005f06c2650e119.journal (2.2M).
Vacuuming done, freed 4.5M of archived journals from /run/log/journal/f0fc159a3f5844b5a29aaa488532c69b.
Before rebooting, I try to wipe out as many timestamps saved in the file system as possible. It's important get rid of /var/lib/systemd/timesync/clock
because timesync
would set the system time to its timestamp in the absence of an NTP time. I am not too sure why fake-hwclock
is installed. It does pretty much what systemd-timesync
does with clock
only it actually writes the time in its own timestamp fake-hwclock.data
.
pi@tarte:~ $ sudo rm -r /var/log/lastlog; sudo systemctl stop fake-hwclock; sudo rm /var/lib/systemd/timesync/clock; echo "2022-12-08 00:01:00" | sudo tee /etc/fake-hwclock.data; sudo touch -t 202212080001 /etc/fake-hwclock.data; sudo reboot
2022-12-08 00:01:00
Connection to 192.168.1.22 closed by remote host.
Connection to 192.168.1.22 closed.
As soon as I could log back in, I checked the date and time and it was correct.
michel@hp:~$ ssh pi@tarte.local
pi@tarte.local's password: *********
Linux tarte 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr 3 17:24:16 BST 2023 aarch64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Wi-Fi is currently blocked by rfkill.
Use raspi-config to set the country before use.
pi@tarte:~ $ date
Mon 12 Jun 20:37:04 ADT 2023
pi@tarte:~ $ timedatectl timesync-status
Server: 192.168.1.23 (192.168.1.23)
Poll interval: 32s (min: 32s; max 34min 8s)
Leap: normal
Version: 4
Stratum: 2
Reference: 535047
Precision: 1.954ms (-9)
Root distance: 703.246ms (max: 5s)
Offset: +17.820232s
Delay: 1.312ms
Jitter: 0
Packet count: 1
Frequency: +0.000ppm
pi@tarte:~ $ timedatectl show-timesync
SystemNTPServers=192.168.1.23
FallbackNTPServers=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
ServerName=192.168.1.23
ServerAddress=192.168.1.23
RootDistanceMaxUSec=5s
PollIntervalMinUSec=32s
PollIntervalMaxUSec=34min 8s
PollIntervalUSec=1min 4s
NTPMessage={ Leap=0, Version=4, Mode=4, Stratum=2, Precision=-9, RootDelay=40.161ms, RootDispersion=683.166ms, Reference=535047, OriginateTimestamp=Mon 2023-06-12 20:37:04 ADT, ReceiveTimestamp=Mon 2023-06-12 20:37:04 ADT, TransmitTimestamp=Mon 2023-06-12 20:37:04 ADT, DestinationTimestamp=Mon 2023-06-12 20:37:04 ADT, Ignored=no PacketCount=2, Jitter=2.613ms }
Frequency=0
All that looks very good. The time is correct, the router NTP server is being used by the Pi and the only way it could know to do that is because the DHCP server on the router provided the address.
Finally, here is an edited portion of the system journal showing how the system time was set.
pi@tarte:~ $ journalctl | grep -E 'ntp|dhcp|time'
...
Dec 22 07:55:44 tarte systemd[1]: System time before build time, advancing clock.
...
Dec 22 07:55:44 tarte fake-hwclock[130]: Current system time: 2022-12-22 11:55:43
Dec 22 07:55:44 tarte fake-hwclock[130]: To set system time to this saved clock anyway, use "force"
Dec 22 07:55:48 tarte systemd[1]: apt-daily.timer: Not using persistent file timestamp Mon 2023-06-12 19:09:44 ADT as it is in the future.
Dec 22 07:55:48 tarte systemd[1]: apt-daily-upgrade.timer: Not using persistent file timestamp Mon 2023-06-12 15:42:32 ADT as it is in the future.
Dec 22 07:55:48 tarte systemd[1]: e2scrub_all.timer: Not using persistent file timestamp Mon 2023-06-12 15:42:32 ADT as it is in the future.
Dec 22 07:55:48 tarte systemd[1]: fstrim.timer: Not using persistent file timestamp Mon 2023-06-12 16:21:29 ADT as it is in the future.
Dec 22 07:55:48 tarte systemd[1]: logrotate.timer: Not using persistent file timestamp Mon 2023-06-12 15:42:32 ADT as it is in the future.
Dec 22 07:55:48 tarte systemd[1]: man-db.timer: Not using persistent file timestamp Mon 2023-06-12 15:42:32 ADT as it is in the future.
...
Dec 22 07:55:50 tarte dhcpcd[438]: eth0: waiting for carrier
...
Dec 22 07:55:50 tarte dhcpcd[438]: eth0: carrier acquired
...
Dec 22 07:55:50 tarte dhcpcd[438]: eth0: rebinding lease of 192.168.1.22
Dec 22 07:55:50 tarte dhcpcd[438]: eth0: probing address 192.168.1.22/24
...
Dec 22 07:55:55 tarte dhcpcd[438]: eth0: leased 192.168.1.22 for 43200 seconds
Dec 22 07:55:55 tarte dhcpcd[438]: eth0: adding route to 192.168.1.0/24
Dec 22 07:55:55 tarte dhcpcd[438]: eth0: adding default route via 192.168.1.1
...
Dec 22 07:57:51 tarte systemd[1]: apt-daily-upgrade.timer: Succeeded.
Dec 22 07:57:51 tarte systemd[1]: apt-daily.timer: Succeeded.
Dec 22 07:57:51 tarte systemd[1]: e2scrub_all.timer: Succeeded.
Dec 22 07:57:51 tarte systemd[1]: fstrim.timer: Succeeded.
Dec 22 07:57:51 tarte systemd[1]: logrotate.timer: Succeeded.
Dec 22 07:57:51 tarte systemd[1]: man-db.timer: Succeeded.
Dec 22 07:57:51 tarte systemd[1]: systemd-tmpfiles-clean.timer: Succeeded.
...
Dec 22 07:57:51 tarte dhcpcd[611]: dhcpcd exited
...
Dec 22 07:57:53 tarte systemd[1]: dhcpcd.service: Succeeded.
Dec 22 07:57:54 tarte systemd[1]: user.slice: Consumed 1.589s CPU time.
Dec 22 07:57:54 tarte systemd[1]: systemd-timesyncd.service: Succeeded.
...
Jun 12 20:21:41 tarte systemd-timesyncd[619]: Initial synchronization to time server 192.168.1.23:123 (192.168.1.23).
Clearly there are many more timestamps that I thought but none of those shown in the journal are used to jump the system time forward except for the build time of systemd
, the modified timestamp (mtime) of clock
and the modified timestamp of fake-hwclock.data
or its content I am not sure which. Having deleted the first two, the initial time, Dec 22 07:55:44
is derived from the compilation date of systemd.
pi@tarte:~ $ ls -l /bin/systemd
lrwxrwxrwx 1 root root 20 Dec 22 07:55 /bin/systemd -> /lib/systemd/systemd
As shown in the table above, Chrony is the NTP client installed in Armbian which is running on the Orange Pi PC 2 while systemd-timesyncd
is not installed at all. There are other significant differences between Raspberry Pi OS and Armbian. NetworkManager is the network management service. In addition networking configuration is done with netplan. I'll paraphrase the start of the description in the Netplan man page. It reads abstract network definitions written in YAML and converts them to actual configuration files for a specific networking service. Once these configuration files are written into the /run
directory, the networking service starts using the newly written configuration. It sounds rather complicated, but as will be demonstrated it works. Just as in the previous section, we test that chrony
will use a time server proposed by the DHCP server by removing all other time sources.
- Since the Orange Pi PC 2 is a server with a fixed address, it was necessary to make it a DHCP client
- All NTP time sources had to be removed from the chrony configuration files.
- Ensure that chrony picked up the NTP servers proposed by the DHCP server.
To remove the fixed IP address assigned to the Ethernet interface, eth0
, nmtui
, the NetworkManager configuration tool with a text user interface, could be used. Edit the appropriate connection, which here is called Wired&nbps;connection 1
.
citrus@fruity:~$ sudo nmtui
┌────────────────────────────┤ Edit Connection ├──────────────────────────┐
│ │
│ Profile name Wired connection 1______________________ │
│ Device eth0 (02:01:42:00:F8:1B)________________ │
│ │
│ ═ ETHERNET <Show> │
│ │
│ ╤ IPv4 CONFIGURATION <Manual> <Hide> │
│ │ Addresses 192.168.1.48/24__________ <Remove> │
│ │ <Add...> │
│ │ Gateway 192.168.1.1______________ │
Then change the IPv4 configuration from <Manual>
to <Automatic>
. I prefered adding a couple of lines in the interfaces
configuration file to achieve the same end.
citrus@fruity:~$ cat /etc/network/interfaces
source /etc/network/interfaces.d/*
# Network is managed by Network manager
auto lo
iface lo inet loopback
# override the Network manager settings
auto eth0
iface eth0 inet dhcp
Explicit NTP server addresses can be found in the configuration file chrony.conf
. They will be lines that begin with either poll
or server
. Other sources can be listed in files in the /etc/chrony/sources.d
directory and ending with the .sources
extension. The following verifies that no such explicit time sources remain.
citrus@fruity:~$ grep -c -i -E '^pool|^server' /etc/chrony/chrony.conf
0
citrus@fruity:~$ ls /etc/chrony/sources.d/*.sources
ls: cannot access '/etc/chrony/sources.d/*.sources': No such file or directory
Finally let's make sure that chrony will use the NTP servers obtained from the DHCP server.
citrus@fruity:~$ grep -i -1 'sourcedir.*dhcp' /etc/chrony/chrony.conf
# Use time sources from DHCP.
sourcedir /run/chrony-dhcp
It was not necessary to add that line, it was already present.
After rebooting, it was obvious that the local time server was being used by chrony.
citrus@fruity:~$ timedatectl status
Local time: Mon 2023-06-19 08:33:40 ADT
Universal time: Mon 2023-06-19 11:33:40 UTC
RTC time: Mon 2023-06-19 11:33:28
Time zone: America/Halifax (ADT, -0300)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
citrus@fruity:~$ chronyc tracking
Reference ID : C0A80117 (192.168.1.23)
Stratum : 2
Ref time (UTC) : Mon Jun 19 11:33:50 2023
System time : 0.007926481 seconds slow of NTP time
Last offset : -0.000734664 seconds
RMS offset : 0.008753908 seconds
Frequency : 31.217 ppm fast
Residual freq : -184.958 ppm
Skew : 0.644 ppm
Root delay : 0.085670374 seconds
Root dispersion : 0.021219740 seconds
Update interval : 64.9 seconds
Leap status : Normal
citrus@fruity:~$ chronyc sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* 192.168.1.23 1 6 377 31 +16ms[ +15ms] +/- 43ms
Finally, I found it interesting to look at which DHCP client is used and at the file written to the /run
directory.
>
citrus@fruity:~$ journalctl | grep dhcp
Jun 19 07:57:20 fruity dhclient[517]: For info, please visit https://www.isc.org/software/dhcp/
Jun 19 07:57:27 fruity sh[517]: For info, please visit https://www.isc.org/software/dhcp/
Jun 19 07:57:32 fruity NetworkManager[723]: <info> [1687172252.3390] dhcp-init: Using DHCP client 'internal'
citrus@fruity:~$ cat /run/chrony-dhcp/eth0.sources
server 192.168.1.23 iburst
The ntp client that is installed on my desktop Linux Mint computer is similar to chrony although one could argue that it is slightly less elegant. The complete configuration file, /etc/ntp.conf
is copied to /run/ntp.conf.dhcp
if the DHCP server returned a list of NTP time sources (option 42). That list of discovered sources is injected into the copied configuration file as the first command as shown below.
michel@hp:~$ cat /run/ntp.conf.dhcp
# This file was copied from /etc/ntp.conf with the server options changed
# to reflect the information sent by the DHCP server. Any changes made
# here will be lost at the next DHCP event. Edit /etc/ntp.conf instead.
# NTP server entries received from DHCP server
server 192.168.1.23 iburst
# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
driftfile /var/lib/ntp/ntp.drift
# Leap seconds definition provided by tzdata
leapfile /usr/share/zoneinfo/leap-seconds.list
# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable
# Specify one or more NTP servers.
# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
#pool 0.ubuntu.pool.ntp.org iburst
#pool 1.ubuntu.pool.ntp.org iburst
#pool 2.ubuntu.pool.ntp.org iburst
#pool 3.ubuntu.pool.ntp.org iburst
# Use Ubuntu's ntp server as a fallback.
#pool ntp.ubuntu.com
# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
# details. The web page
# might also be helpful.
#
# Note that "restrict" applies to both servers and clients, so a configuration
# that might be intended to block requests from certain clients could also end
# up blocking replies from your own upstream servers.
# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited
# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1
# Needed for adding pool entries
restrict source notrap nomodify noquery
# Clients from this (example!) subnet have unlimited access, but only if
# cryptographically authenticated.
#restrict 192.168.123.0 mask 255.255.255.0 notrust
# If you want to provide time to your local subnet, change the next line.
# (Again, the address is an example only.)
#broadcast 192.168.123.255
# If you want to listen to time broadcasts on your local subnet, de-comment the
# next lines. Please do this only if you trust everybody on the network!
#disable auth
#broadcastclient
As can be seen I had removed all other time sources, so that only the local time server is used.
michel@hp:~$ timedatectl
Local time: lun 2023-06-19 19:39:09 ADT
Universal time: lun 2023-06-19 22:39:09 UTC
RTC time: lun 2023-06-19 22:39:09
Time zone: America/Halifax (ADT, -0300)
System clock synchronized: no
NTP service: n/a
RTC in local TZ: no
michel@hp:~$ ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
*192.168.1.23 1 u 22 64 1 155.611 -11.311 33.474
michel@hp:~$ timedatectl
Local time: lun 2023-06-19 22:14:50 ADT
Universal time: mar 2023-06-20 01:14:50 UTC
RTC time: mar 2023-06-20 01:14:50
Time zone: America/Halifax (ADT, -0300)
System clock synchronized: yes
NTP service: n/a
RTC in local TZ: no
michel@hp:~$ ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
192.168.1.23 1 u 227 256 37 43.924 -19.674 70.465
Just when the system clock was deemed synchronized can't be said with certainty, but according to the two checks done above, it took less than three hours.
This section will be short. Tasmota does not support this feature. According to Jason2866 in an answer to the Timezone not set form (sic) NTP server #14804 discussion, Tasmota does not support getting NTP servers via DHCP. This feature was removed since many routers does not support (correctly). This generated issues. You have to set via command see documentation. Of we have already seen how to set the NTP servers explicitely so there's no need to repeat that here.
The above has all been about testing the addition of a local time server. But what would be a good configuration? Recall that this adventure into the world of clocks began in the previous post where I professed wanting to be a good Internet citizen by setting up a local NTP server to fulfill all NTP requests from the many devices on our home network. Here is how I would like to do it.
- One NTP server MUST be setup on the local network. Call this server LNTP.
- The machine hosting LNTP MUST to be on at all times.
- The LNTP SHOULD have access to the Internet to "discipline" its own clock using external time sources.
- The LNTP MUST have access to the GPS based local NTP server (GNATS or better) as a back up.
- The LNTP MUST be accessible from all devices connected to the LAN.
While it may be prefereable that LNTP be on a router, but it could be on a NAS or the host of a home automation system. Access to LNTP from within the LAN will not be a problem with the typical flat configuration in most homes, but if subnets or VLANs are in place then appropriate firewall rules and routing will be needed.
- The LAN's DHCP server SHOULD forward the LNTP IP address to DHCP clients.
- All devices permanently connected to the LAN MUST use the LNTP as a time source.
- All devices permanently connected to the LAN MUST NOT access external time sources. They COULD use the GPS based NTP server as a time source in addition to LNTP.
- Portable devices SHOULD accept DHCP forwarded time sources.
- Where it is not possible to enable use of DHCP supplied time sources, LNTP SHOULD be added to the list of time sources of portable devices.
I am planning to create a home lab (and this) in the near future that will be based on pfSense or OPNsense. Consequently, that list of MUSTs and SHOULDs may be modified as well as some of the instructions above. In the meantime, here is how I tested some of these ideas with the old OpenWRT router.

The image on the left (or top) shows how to enable the NTP client and server in the router which takes care of point 1. Do not pay attention to the URLs to external time sources. What is shown is not what is typically done and I have yet to investigate the best way to select public NTP servers. The image on the right shows that the DHCP server on the router will provide its own IP address as a time source to its client, which takes care of point 2. The image below is a simplified representation of how it would work.
