DHCP and NTP for the LAN with systemd-networkd
Handing out addresses on the LAN from the router itself, using the DHCP server built into systemd-networkd, and pushing the router as the NTP source.
Continues from Linux router with WAN, LAN and DMZ: eth1 is the LAN at
10.0.1.0/24 and the router holds 10.0.1.1.
The DHCP server[edit]
No extra package — systemd-networkd already has one. Extend the LAN
file, /etc/systemd/network/20-lan.network:
[Match]
Name=eth1
[Network]
Address=10.0.1.1/24
DHCPServer=yes
ConfigureWithoutCarrier=yes
[DHCPServer]
# Pool starts at the subnet base + offset, so 10.0.1.100 - 10.0.1.199
PoolOffset=100
PoolSize=100
DefaultLeaseTimeSec=1h
MaxLeaseTimeSec=12h
# Gateway. Defaults to this interface's address, so it is only worth
# stating explicitly if the LAN's router is some other box.
EmitRouter=yes
# DNS
EmitDNS=yes
DNS=10.0.1.1
# NTP - the point of the exercise
EmitNTP=yes
NTP=10.0.1.1
Apply:
networkctl reload
networkctl status eth1
PoolOffset= counts from the base of the subnet, not from the router's address. With 10.0.1.0/24 an offset of 100 starts the pool at 10.0.1.100. Keep the pool clear of anything statically addressed.Fixed addresses[edit]
One section per host, in the same .network file (systemd 249 and
later):
[DHCPServerStaticLease]
MACAddress=aa:bb:cc:dd:ee:01
Address=10.0.1.10
[DHCPServerStaticLease]
MACAddress=aa:bb:cc:dd:ee:02
Address=10.0.1.11
Static leases should sit outside the dynamic pool — below 10.0.1.100
with the pool above, so the two can never collide.
Serve NTP as well as advertise it[edit]
NTP=10.0.1.1 only tells clients where to look. The router has to
actually answer, which it does not do by default.
With chrony, in /etc/chrony/chrony.conf (Debian) or
/etc/chrony.conf (RHEL):
# Upstream sources
pool 2.pool.ntp.org iburst
# Answer the LAN
allow 10.0.1.0/24
# Serve local time even while upstream is unreachable, so the LAN does not
# lose its time source when the WAN goes down
local stratum 10
systemctl enable --now chronyd # chrony on Debian/Ubuntu
chronyc sources -v
chronyc clients
The router's own firewall has to let NTP in. The rule is already in the baseline policy:
iptables -A INPUT -i eth1 -p udp --dport 123 -j ACCEPT
allow a network reachable from the WAN. An NTP server open to the internet is a reflection-amplification tool, and it will be found and used.Verify[edit]
On the router:
# Leases the server has handed out
networkctl status eth1
resolvectl status eth1
# Clients currently talking NTP to us
chronyc clients
On a LAN client:
# Did the NTP option arrive?
resolvectl status # systemd-resolved: DNS servers
timedatectl show-timesync # ServerAddress should be 10.0.1.1
timedatectl timesync-status
# Raw lease, if the client runs networkd
networkctl status <iface>
A client that ignores the pushed NTP server almost always has one configured
statically — check NTP= in /etc/systemd/timesyncd.conf,
which takes precedence over anything DHCP supplies.
If you outgrow it[edit]
The networkd server is deliberately small: one pool per interface, no relay, no
option-82, no failover, no dynamic DNS updates. Reach for Kea (the ISC
successor to dhcpd) or dnsmasq when you need any of those.
dnsmasq is the shorter jump — it does DHCP, DNS forwarding and TFTP in one
process, which suits a small router.