</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>LocalLeaseDomain=</varname></term>
+ <listitem>
+ <para>Takes a DNS domain name as argument. If specified,
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ will integrate with
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ and ensure that the hostnames associated with each handed out DHCP lease may be resolved to the IP
+ addresses of the lease. The hostnames are suffixed with the specified domain name.</para>
+
+ <para>Note that this purely about hostname resolution on the local system, i.e. from programs with
+ access to the same <filename>systemd-resolved</filename> instances via D-Bus IPC, Varlink IPC, or
+ the local DNS stub.</para>
+ <xi:include href="version-info.xml" xpointer="v259"/>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
'networkd-ntp.c',
'networkd-queue.c',
'networkd-radv.c',
+ 'networkd-resolve-hook.c',
'networkd-route.c',
'networkd-route-metric.c',
'networkd-route-nexthop.c',
#include "networkd-network.h"
#include "networkd-ntp.h"
#include "networkd-queue.h"
+#include "networkd-resolve-hook.h"
#include "networkd-route-util.h"
#include "path-util.h"
#include "set.h"
if (r < 0)
return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
+ manager_notify_hook_filters(link->manager);
+
return 0;
}
#include "arphrd-util.h"
#include "bitfield.h"
#include "device-util.h"
+#include "dns-domain.h"
#include "errno-util.h"
#include "ethtool-util.h"
#include "event-util.h"
#include "networkd-nexthop.h"
#include "networkd-queue.h"
#include "networkd-radv.h"
+#include "networkd-resolve-hook.h"
#include "networkd-route.h"
#include "networkd-route-util.h"
#include "networkd-routing-policy-rule.h"
assert(link->manager);
+ bool notify = link_has_local_lease_domain(link);
+
link_set_state(link, LINK_STATE_LINGER);
/* Drop all references from other links and manager. Note that async netlink calls may have
/* The following must be called at last. */
assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link);
+
+ if (notify)
+ manager_notify_hook_filters(link->manager);
+
return link_unref(link);
}
if (link->state == LINK_STATE_UNMANAGED)
return;
+ bool notify = link_has_local_lease_domain(link);
+
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
"Unmanaging interface.");
link->network = network_unref(link->network);
link_set_state(link, LINK_STATE_UNMANAGED);
+
+ if (notify)
+ manager_notify_hook_filters(link->manager);
}
static int link_managed_by_us(Link *link) {
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(kernel_operstate, int);
+
+bool link_has_local_lease_domain(Link *link) {
+ assert(link);
+
+ return link->dhcp_server &&
+ link->network &&
+ link->network->dhcp_server_local_lease_domain &&
+ !dns_name_is_root(link->network->dhcp_server_local_lease_domain);
+}
void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret);
AddressFamily link_required_family_for_online(Link *link);
+
+bool link_has_local_lease_domain(Link *link);
m->varlink_server = TAKE_PTR(s);
return 0;
}
-
-void manager_varlink_done(Manager *m) {
- assert(m);
-
- m->varlink_server = sd_varlink_server_unref(m->varlink_server);
-}
#include "networkd-forward.h"
int manager_connect_varlink(Manager *m, int fd);
-void manager_varlink_done(Manager *m);
#include "sd-event.h"
#include "sd-netlink.h"
#include "sd-resolve.h"
+#include "sd-varlink.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
#include "networkd-queue.h"
+#include "networkd-resolve-hook.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-serialize.h"
return 0;
}
-static int manager_listen_fds(Manager *m, int *ret_rtnl_fd, int *ret_varlink_fd) {
+static int manager_listen_fds(Manager *m, int *ret_rtnl_fd, int *ret_varlink_fd, int *ret_resolve_hook_fd) {
_cleanup_strv_free_ char **names = NULL;
- int n, rtnl_fd = -EBADF, varlink_fd = -EBADF;
+ int n, rtnl_fd = -EBADF, varlink_fd = -EBADF, resolve_hook_fd = -EBADF;
assert(m);
assert(ret_rtnl_fd);
assert(ret_varlink_fd);
+ assert(ret_resolve_hook_fd);
n = sd_listen_fds_with_names(/* unset_environment = */ true, &names);
if (n < 0)
continue;
}
+ if (streq(names[i], "resolve-hook")) {
+ resolve_hook_fd = fd;
+ continue;
+ }
+
if (manager_set_serialization_fd(m, fd, names[i]) >= 0)
continue;
*ret_rtnl_fd = rtnl_fd;
*ret_varlink_fd = varlink_fd;
+ *ret_resolve_hook_fd = resolve_hook_fd;
return 0;
}
}
int manager_setup(Manager *m) {
- _cleanup_close_ int rtnl_fd = -EBADF, varlink_fd = -EBADF;
+ _cleanup_close_ int rtnl_fd = -EBADF, varlink_fd = -EBADF, resolve_hook_fd = -EBADF;
int r;
assert(m);
if (r < 0)
return r;
- r = manager_listen_fds(m, &rtnl_fd, &varlink_fd);
+ r = manager_listen_fds(m, &rtnl_fd, &varlink_fd, &resolve_hook_fd);
if (r < 0)
return r;
if (r < 0)
return r;
+ r = manager_varlink_init_resolve_hook(m, TAKE_FD(resolve_hook_fd));
+ if (r < 0)
+ return r;
+
r = manager_connect_bus(m);
if (r < 0)
return r;
sd_device_monitor_unref(m->device_monitor);
- manager_varlink_done(m);
+ m->varlink_server = sd_varlink_server_unref(m->varlink_server);
+ m->varlink_resolve_hook_server = sd_varlink_server_unref(m->varlink_resolve_hook_server);
+ m->query_filter_subscriptions = set_free(m->query_filter_subscriptions);
hashmap_free(m->polkit_registry);
sd_bus_flush_close_unref(m->bus);
sd_resolve *resolve;
sd_bus *bus;
sd_varlink_server *varlink_server;
+ sd_varlink_server *varlink_resolve_hook_server;
+ Set *query_filter_subscriptions;
sd_device_monitor *device_monitor;
Hashmap *polkit_registry;
int ethtool_fd;
DHCPServer.BootFilename, config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, offsetof(Network, dhcp_server_boot_filename)
DHCPServer.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp_server_rapid_commit)
DHCPServer.PersistLeases, config_parse_dhcp_server_persist_leases, 0, offsetof(Network, dhcp_server_persist_leases)
+DHCPServer.LocalLeaseDomain, config_parse_dns_name, 0, offsetof(Network, dhcp_server_local_lease_domain)
DHCPServerStaticLease.Address, config_parse_dhcp_static_lease_address, 0, 0
DHCPServerStaticLease.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0
DHCPServerStaticLease.Hostname, config_parse_dhcp_static_lease_hostname, 0, 0
free(network->dhcp_server_emit[t].addresses);
ordered_hashmap_free(network->dhcp_server_send_options);
ordered_hashmap_free(network->dhcp_server_send_vendor_options);
+ free(network->dhcp_server_local_lease_domain);
/* DHCP client */
free(network->dhcp_vendor_class_identifier);
usec_t dhcp_server_ipv6_only_preferred_usec;
bool dhcp_server_rapid_commit;
DHCPServerPersistLeases dhcp_server_persist_leases;
+ char *dhcp_server_local_lease_domain;
/* link-local addressing support */
AddressFamily link_local;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-dhcp-server.h"
+#include "sd-json.h"
+#include "sd-varlink.h"
+
+#include "alloc-util.h"
+#include "dns-answer.h"
+#include "dns-domain.h"
+#include "dns-packet.h"
+#include "dns-question.h"
+#include "dns-rr.h"
+#include "env-util.h"
+#include "fd-util.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-resolve-hook.h"
+#include "resolve-hook-util.h"
+#include "set.h"
+#include "varlink-io.systemd.Resolve.Hook.h"
+#include "varlink-util.h"
+
+static int manager_make_domain_array(Manager *m, sd_json_variant **ret) {
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ _cleanup_(set_freep) Set *domains = NULL;
+ Link *link;
+ HASHMAP_FOREACH(link, m->links_by_index) {
+ if (!link_has_local_lease_domain(link))
+ continue;
+
+ r = set_put_strdup_full(&domains, &dns_name_hash_ops_free, link->network->dhcp_server_local_lease_domain);
+ if (r < 0 && r != -EEXIST)
+ return r;
+ }
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
+ char *s;
+ SET_FOREACH(s, domains) {
+ r = sd_json_variant_append_arrayb(&array, SD_JSON_BUILD_STRING(s));
+ if (r < 0)
+ return r;
+ }
+
+ if (!array)
+ return sd_json_variant_new_array(ret, /* array= */ NULL, /* n= */ 0);
+
+ *ret = TAKE_PTR(array);
+ return 0;
+}
+
+int manager_notify_hook_filters(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Called whenever a machine is added or dropped from the list */
+
+ if (set_isempty(m->query_filter_subscriptions))
+ return 0;
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
+ r = manager_make_domain_array(m, &array);
+ if (r < 0)
+ return log_error_errno(r, "Failed to generate JSON array with machine names: %m");
+
+ r = varlink_many_notifybo(m->query_filter_subscriptions, SD_JSON_BUILD_PAIR_VARIANT("filterDomains", array));
+ if (r < 0)
+ return log_error_errno(r, "Failed to notify filter subscribers: %m");
+
+ return 0;
+}
+
+static int vl_method_query_filter(
+ sd_varlink *link,
+ sd_json_variant *parameters,
+ sd_varlink_method_flags_t flags,
+ void *userdata) {
+
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ assert(link);
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
+ r = manager_make_domain_array(m, &array);
+ if (r < 0)
+ return r;
+
+ if (flags & SD_VARLINK_METHOD_MORE) {
+ /* If 'more' is set, this is a subscription request, keep track of the link */
+
+ r = sd_varlink_notifybo(link, SD_JSON_BUILD_PAIR_VARIANT("filterDomains", array));
+ if (r < 0)
+ return log_error_errno(r, "Failed to notify filter subscribers: %m");
+
+ r = set_ensure_put(&m->query_filter_subscriptions, &varlink_hash_ops, link);
+ if (r < 0)
+ return r;
+
+ sd_varlink_ref(link);
+ } else {
+ r = sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_VARIANT("filterDomains", array));
+ if (r < 0)
+ return log_error_errno(r, "Failed to notify filter subscribers: %m");
+ }
+
+ return 0;
+}
+
+static int vl_method_resolve_record(
+ sd_varlink *link,
+ sd_json_variant *parameters,
+ sd_varlink_method_flags_t flags,
+ void *userdata) {
+
+ Manager *m = ASSERT_PTR(userdata);
+ int r;
+
+ assert(link);
+
+ _cleanup_(resolve_record_parameters_done) ResolveRecordParameters p = {};
+ r = sd_varlink_dispatch(link, parameters, resolve_record_parameters_dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (dns_question_isempty(p.question))
+ return sd_varlink_error_invalid_parameter_name(link, "question");
+
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ bool found_address = false, found_domain = false;
+ DnsResourceKey *key;
+ DNS_QUESTION_FOREACH(key, p.question) {
+ const char *name = dns_resource_key_name(key);
+
+ Link *l;
+ HASHMAP_FOREACH(l, m->links_by_index) {
+
+ if (!link_has_local_lease_domain(l))
+ continue;
+
+ /* Try to strip the local lease domain suffix from name, so that we have the short hostname left. */
+ _cleanup_free_ char *prefix = NULL;
+ r = dns_name_change_suffix(name, l->network->dhcp_server_local_lease_domain, /* new_suffix= */ NULL, &prefix);
+ if (r <= 0) /* no match? */
+ continue;
+
+ found_domain = true;
+
+ struct in_addr address;
+ r = sd_dhcp_server_get_lease_address_by_name(l->dhcp_server, prefix, &address);
+ if (r <= 0)
+ continue;
+
+ /* The domain exists, so we can give a positive reply. But only for A lookups we have addresses to return. */
+ if (key->type != DNS_TYPE_A)
+ continue;
+
+ found_address = true;
+
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ r = dns_resource_record_new_address(&rr, AF_INET, (union in_addr_union*) &address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add_extend(
+ &answer,
+ rr,
+ l->ifindex,
+ DNS_ANSWER_AUTHENTICATED,
+ /* rrsig= */ NULL);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (!found_address) {
+ /* If this was a lookup in one of our domains, return NXDOMAIN, we are authoritative on that */
+ if (found_domain)
+ return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_INTEGER("rcode", DNS_RCODE_NXDOMAIN));
+
+ /* Otherwise we return an empty response, which means: continue with the usual lookup */
+ return sd_varlink_reply(link, /* parameters= */ NULL);
+ }
+
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *ja = NULL;
+ r = dns_answer_to_json(answer, &ja);
+ if (r < 0)
+ return r;
+
+ return sd_varlink_replybo(
+ link,
+ SD_JSON_BUILD_PAIR_INTEGER("rcode", DNS_RCODE_SUCCESS),
+ SD_JSON_BUILD_PAIR_VARIANT("answer", ja));
+}
+
+static void on_resolve_hook_disconnect(sd_varlink_server *server, sd_varlink *link, void *userdata) {
+ Manager *m = ASSERT_PTR(userdata);
+
+ if (set_remove(m->query_filter_subscriptions, link))
+ sd_varlink_unref(link);
+}
+
+int manager_varlink_init_resolve_hook(Manager *m, int fd) {
+ _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
+ _unused_ _cleanup_close_ int fd_close = fd; /* take possession */
+ int r;
+
+ assert(m);
+
+ if (m->varlink_resolve_hook_server)
+ return 0;
+
+ r = getenv_bool("SYSTEMD_NETWORK_RESOLVE_HOOK");
+ if (r < 0 && r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_NETWORK_RESOLVE_HOOK, ignoring: %m");
+ if (r == 0) {
+ log_notice("Resolve hook disabled via $SYSTEMD_NETWORK_RESOLVE_HOOK.");
+ return 0;
+ }
+
+ r = varlink_server_new(&s, SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA, m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+ (void) sd_varlink_server_set_description(s, "varlink-resolve-hook");
+
+ r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Resolve_Hook);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Resolve.Hook interface to varlink server: %m");
+
+ r = sd_varlink_server_bind_method_many(
+ s,
+ "io.systemd.Resolve.Hook.QueryFilter", vl_method_query_filter,
+ "io.systemd.Resolve.Hook.ResolveRecord", vl_method_resolve_record);
+ if (r < 0)
+ return log_error_errno(r, "Failed to register varlink methods: %m");
+
+ r = sd_varlink_server_bind_disconnect(s, on_resolve_hook_disconnect);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind on resolve hook disconnection events: %m");
+
+ if (fd < 0)
+ r = sd_varlink_server_listen_address(s, "/run/systemd/resolve.hook/io.systemd.Network", 0666 | SD_VARLINK_SERVER_MODE_MKDIR_0755);
+ else
+ r = sd_varlink_server_listen_fd(s, fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind to systemd-resolved hook Varlink socket: %m");
+
+ TAKE_FD(fd_close);
+
+ r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+ m->varlink_resolve_hook_server = TAKE_PTR(s);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "networkd-forward.h"
+
+int manager_notify_hook_filters(Manager *m);
+int manager_varlink_init_resolve_hook(Manager *m, int fd);
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',
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')
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'])
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)
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
# 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
'-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])
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCPv4]
+SendHostname=yes
+Hostname=flummy
--- /dev/null
+# 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
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',
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
'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',
'StartLimitIntervalSec=0',
]
)
+ create_unit_dropin(
+ 'systemd-networkd-resolve-hook.socket',
+ [
+ '[Unit]',
+ 'StartLimitIntervalSec=0',
+ ]
+ )
create_unit_dropin(
'systemd-networkd-persistent-storage.service',
[
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')
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:
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):
'file' : 'systemd-networkd-varlink.socket',
'conditions' : ['ENABLE_NETWORKD'],
},
+ {
+ 'file' : 'systemd-networkd-resolve-hook.socket',
+ 'conditions' : ['ENABLE_NETWORKD'],
+ },
{
'file' : 'systemd-networkd-wait-online.service.in',
'conditions' : ['ENABLE_NETWORKD'],
--- /dev/null
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Network Management Resolve Hook Socket
+Documentation=man:systemd-networkd.service(8)
+ConditionCapability=CAP_NET_ADMIN
+DefaultDependencies=no
+Before=sockets.target shutdown.target
+Conflicts=shutdown.target
+
+[Socket]
+ListenStream=/run/systemd/resolve.hook/io.systemd.Network
+FileDescriptorName=resolve-hook
+SocketMode=0666
+Service=systemd-networkd.service
+RemoveOnStop=yes
+
+[Install]
+WantedBy=sockets.target
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/netif
RuntimeDirectoryPreserve=yes
-Sockets=systemd-networkd.socket systemd-networkd-varlink.socket
+Sockets=systemd-networkd.socket systemd-networkd-varlink.socket systemd-networkd-resolve-hook.socket
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service bpf
[Install]
WantedBy=multi-user.target
-Also=systemd-networkd.socket systemd-networkd-varlink.socket
+Also=systemd-networkd.socket systemd-networkd-varlink.socket systemd-networkd-resolve-hook.socket
Alias=dbus-org.freedesktop.network1.service
# The output from this generator is used by udevd and networkd. Enable it by