networkd: add support for resolved hook for DHCP server

Let's synthesize DNS RRs for leases handed out by our DHCP server. This
way local VMs can have resolvable hostnames locally.

This does not implement reverse look ups for now. We can add this
later in similar fashion.
This commit is contained in:
Lennart Poettering
2025-11-15 07:52:42 +01:00
parent f041d40dee
commit a7fa29b1b5
21 changed files with 434 additions and 15 deletions
+15 -1
View File
@@ -65,6 +65,7 @@ def setUpModule():
for u in [
'systemd-networkd.socket',
'systemd-networkd-varlink.socket',
'systemd-networkd-resolve-hook.socket',
'systemd-networkd.service',
'systemd-resolved-varlink.socket',
'systemd-resolved-monitor.socket',
@@ -90,12 +91,15 @@ def setUpModule():
subprocess.call(['useradd', '--system', '--no-create-home', 'systemd-network'])
for d in ['/etc/systemd/network', '/run/systemd/network',
'/run/systemd/netif', '/run/systemd/resolve']:
'/run/systemd/netif', '/run/systemd/resolve', '/run/systemd/resolve.hook']:
subprocess.check_call(["mount", "-m", "-t", "tmpfs", "none", d])
tmpmounts.append(d)
if os.path.isdir('/run/systemd/resolve'):
os.chmod('/run/systemd/resolve', 0o755)
shutil.chown('/run/systemd/resolve', 'systemd-resolve', 'systemd-resolve')
if os.path.isdir('/run/systemd/resolve.hook'):
os.chmod('/run/systemd/resolve.hook', 0o755)
shutil.chown('/run/systemd/resolve.hook', 'systemd-network', 'systemd-network')
if os.path.isdir('/run/systemd/netif'):
os.chmod('/run/systemd/netif', 0o755)
shutil.chown('/run/systemd/netif', 'systemd-network', 'systemd-network')
@@ -278,6 +282,8 @@ Gateway=192.168.250.1
def tearDown(self):
subprocess.check_call(['systemctl', 'stop', 'systemd-networkd.socket'])
subprocess.check_call(['systemctl', 'stop', 'systemd-networkd-varlink.socket'])
subprocess.check_call(['systemctl', 'stop', 'systemd-networkd-resolve-hook.socket'])
subprocess.check_call(['systemctl', 'stop', 'systemd-networkd.service'])
subprocess.check_call(['ip', 'link', 'del', 'mybridge'])
subprocess.check_call(['ip', 'link', 'del', 'port1'])
@@ -373,6 +379,8 @@ class ClientTestBase(NetworkdTestingUtilities):
def tearDown(self):
self.shutdown_iface()
subprocess.call(['systemctl', 'stop', 'systemd-networkd.socket'])
subprocess.call(['systemctl', 'stop', 'systemd-networkd-varlink.socket'])
subprocess.call(['systemctl', 'stop', 'systemd-networkd-resolve-hook.socket'])
subprocess.call(['systemctl', 'stop', 'systemd-networkd.service'])
subprocess.call(['ip', 'link', 'del', 'dummy0'],
stderr=subprocess.DEVNULL)
@@ -930,9 +938,11 @@ class NetworkdClientTest(ClientTestBase, unittest.TestCase):
set -eu
mkdir -p /run/systemd/network
mkdir -p /run/systemd/netif
mkdir -p /run/systemd/resolve.hook
mkdir -p /var/lib/systemd/network
mount -t tmpfs none /run/systemd/network
mount -t tmpfs none /run/systemd/netif
mount -t tmpfs none /run/systemd/resolve.hook
mount -t tmpfs none /var/lib/systemd/network
[ ! -e /run/dbus ] || mount -t tmpfs none /run/dbus
# create router/client veth pair
@@ -966,6 +976,9 @@ EOF
# Hence, 'networkctl persistent-storage yes' cannot be used.
export SYSTEMD_NETWORK_PERSISTENT_STORAGE_READY=1
# Don't try to register resolved hook for our testcase
export SYSTEMD_NETWORK_RESOLVE_HOOK=0
# Generate debugging logs.
export SYSTEMD_LOG_LEVEL=debug
@@ -982,6 +995,7 @@ exec $(systemctl cat systemd-networkd.service | sed -n '/^ExecStart=/ {{ s/^.*=/
'-p', 'InaccessibleDirectories=-/etc/systemd/network',
'-p', 'InaccessibleDirectories=-/run/systemd/network',
'-p', 'InaccessibleDirectories=-/run/systemd/netif',
'-p', 'InaccessibleDirectories=-/run/systemd/resolve.hook',
'-p', 'InaccessibleDirectories=-/var/lib/systemd/network',
'--service-type=notify', script])
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
[Network]
DHCP=yes
[DHCPv4]
SendHostname=yes
Hostname=flummy
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth-peer
[Network]
IPv6AcceptRA=false
DHCPServer=yes
[DHCPServer]
ServerAddress=192.168.5.1/24
PoolOffset=20
PoolSize=10
LocalLeaseDomain=_networkdtest
@@ -426,6 +426,7 @@ def save_active_units():
for u in [
'systemd-networkd.socket',
'systemd-networkd-varlink.socket',
'systemd-networkd-resolve-hook.socket',
'systemd-networkd.service',
'systemd-resolved-monitor.socket',
'systemd-resolved-varlink.socket',
@@ -449,6 +450,10 @@ def restore_active_units():
call('systemctl stop systemd-networkd-varlink.socket')
has_network_socket = True
if 'systemd-networkd-resolve-hook.socket' in active_units:
call('systemctl stop systemd-networkd-resolve-hook.socket')
has_network_socket = True
if 'systemd-resolved-monitor.socket' in active_units:
call('systemctl stop systemd-resolved-monitor.socket')
has_resolve_socket = True
@@ -525,6 +530,7 @@ def setup_system_units():
'systemd-networkd.service',
'systemd-networkd.socket',
'systemd-networkd-varlink.socket',
'systemd-networkd-resolve-hook.socket',
'systemd-networkd-persistent-storage.service',
'systemd-resolved.service',
'systemd-timesyncd.service',
@@ -572,6 +578,13 @@ def setup_system_units():
'StartLimitIntervalSec=0',
]
)
create_unit_dropin(
'systemd-networkd-resolve-hook.socket',
[
'[Unit]',
'StartLimitIntervalSec=0',
]
)
create_unit_dropin(
'systemd-networkd-persistent-storage.service',
[
@@ -604,6 +617,7 @@ def clear_system_units():
rm_unit('systemd-networkd.service')
rm_unit('systemd-networkd.socket')
rm_unit('systemd-networkd-varlink.socket')
rm_unit('systemd-networkd-resolve-hook.socket')
rm_unit('systemd-networkd-persistent-storage.service')
rm_unit('systemd-resolved.service')
rm_unit('systemd-timesyncd.service')
@@ -990,10 +1004,12 @@ def stop_networkd(show_logs=True, check_failed=True):
if check_failed:
check_output('systemctl stop systemd-networkd.socket')
check_output('systemctl stop systemd-networkd-varlink.socket')
check_output('systemctl stop systemd-networkd-resolve-hook.socket')
check_output('systemctl stop systemd-networkd.service')
else:
call('systemctl stop systemd-networkd.socket')
call('systemctl stop systemd-networkd-varlink.socket')
call('systemctl stop systemd-networkd-resolve-hook.socket')
call('systemctl stop systemd-networkd.service')
if show_logs:
@@ -7366,6 +7382,16 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
data = json.loads(output)
self.assertEqual(data['DHCPv4Client']['Lease']['Hostname'], 'fqdn.example.com')
def test_dhcp_server_resolve_hook(self):
copy_network_unit('25-veth.netdev', '25-dhcp-client-resolve-hook.network', '25-dhcp-server-resolve-hook.network')
start_networkd()
self.wait_online('veth99:routable', 'veth-peer:routable')
output = check_output('resolvectl query flummy._networkdtest')
print(output)
self.assertIn('192.168.5.2', output)
class NetworkdDHCPServerRelayAgentTests(unittest.TestCase, Utilities):
def setUp(self):