mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
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:
@@ -4201,6 +4201,23 @@ ServerAddress=192.168.0.1/24</programlisting>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ systemd_networkd_extract_sources = files(
|
||||
'networkd-ntp.c',
|
||||
'networkd-queue.c',
|
||||
'networkd-radv.c',
|
||||
'networkd-resolve-hook.c',
|
||||
'networkd-route.c',
|
||||
'networkd-route-metric.c',
|
||||
'networkd-route-nexthop.c',
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#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"
|
||||
@@ -747,6 +748,8 @@ static int dhcp4_server_configure(Link *link) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#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"
|
||||
@@ -53,6 +54,7 @@
|
||||
#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"
|
||||
@@ -1070,6 +1072,8 @@ static Link *link_drop(Link *link) {
|
||||
|
||||
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
|
||||
@@ -1098,6 +1102,10 @@ static Link *link_drop(Link *link) {
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@@ -1351,6 +1359,8 @@ static void link_enter_unmanaged(Link *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.");
|
||||
|
||||
@@ -1367,6 +1377,9 @@ static void link_enter_unmanaged(Link *link) {
|
||||
|
||||
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) {
|
||||
@@ -3061,3 +3074,12 @@ static const char * const kernel_operstate_table[] = {
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -257,3 +257,5 @@ const char* kernel_operstate_to_string(int t) _const_;
|
||||
|
||||
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);
|
||||
|
||||
@@ -335,9 +335,3 @@ int manager_connect_varlink(Manager *m, int fd) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
#include "networkd-forward.h"
|
||||
|
||||
int manager_connect_varlink(Manager *m, int fd);
|
||||
void manager_varlink_done(Manager *m);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "sd-event.h"
|
||||
#include "sd-netlink.h"
|
||||
#include "sd-resolve.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-error.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
#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"
|
||||
@@ -205,13 +207,14 @@ static int manager_connect_udev(Manager *m) {
|
||||
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)
|
||||
@@ -235,6 +238,11 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd, int *ret_varlink_fd)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (streq(names[i], "resolve-hook")) {
|
||||
resolve_hook_fd = fd;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (manager_set_serialization_fd(m, fd, names[i]) >= 0)
|
||||
continue;
|
||||
|
||||
@@ -250,6 +258,7 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd, int *ret_varlink_fd)
|
||||
|
||||
*ret_rtnl_fd = rtnl_fd;
|
||||
*ret_varlink_fd = varlink_fd;
|
||||
*ret_resolve_hook_fd = resolve_hook_fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -543,7 +552,7 @@ static int manager_set_keep_configuration(Manager *m) {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -567,7 +576,7 @@ int manager_setup(Manager *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;
|
||||
|
||||
@@ -590,6 +599,10 @@ int manager_setup(Manager *m) {
|
||||
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;
|
||||
@@ -737,7 +750,9 @@ Manager* manager_free(Manager *m) {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ typedef struct Manager {
|
||||
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;
|
||||
|
||||
@@ -393,6 +393,7 @@ DHCPServer.BootServerName, config_parse_dns_name,
|
||||
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
|
||||
|
||||
@@ -761,6 +761,7 @@ static Network *network_free(Network *network) {
|
||||
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);
|
||||
|
||||
@@ -233,6 +233,7 @@ typedef struct Network {
|
||||
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;
|
||||
|
||||
261
src/network/networkd-resolve-hook.c
Normal file
261
src/network/networkd-resolve-hook.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/* 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;
|
||||
}
|
||||
7
src/network/networkd-resolve-hook.h
Normal file
7
src/network/networkd-resolve-hook.h
Normal file
@@ -0,0 +1,7 @@
|
||||
/* 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);
|
||||
@@ -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])
|
||||
|
||||
|
||||
10
test/test-network/conf/25-dhcp-client-resolve-hook.network
Normal file
10
test/test-network/conf/25-dhcp-client-resolve-hook.network
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=veth99
|
||||
|
||||
[Network]
|
||||
DHCP=yes
|
||||
|
||||
[DHCPv4]
|
||||
SendHostname=yes
|
||||
Hostname=flummy
|
||||
13
test/test-network/conf/25-dhcp-server-resolve-hook.network
Normal file
13
test/test-network/conf/25-dhcp-server-resolve-hook.network
Normal file
@@ -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):
|
||||
|
||||
@@ -497,6 +497,10 @@ units = [
|
||||
'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'],
|
||||
|
||||
26
units/systemd-networkd-resolve-hook.socket
Normal file
26
units/systemd-networkd-resolve-hook.socket
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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
|
||||
@@ -46,7 +46,7 @@ RestrictRealtime=yes
|
||||
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
|
||||
@@ -56,7 +56,7 @@ User=systemd-network
|
||||
|
||||
[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
|
||||
|
||||
Reference in New Issue
Block a user