resolved: make sure DNS configuration pushed in by the user stays around on restarts

Let's make sure that all settings pushed in stay around when systemd-resolved
is restarted.
This commit is contained in:
Lennart Poettering 2016-06-15 22:38:23 +02:00
parent d97c5aeab8
commit 943ef07ce0
5 changed files with 353 additions and 7 deletions

View File

@ -265,6 +265,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
dns_server_unlink_marked(l->dns_servers);
link_allocate_scopes(l);
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
return sd_bus_reply_method_return(message, NULL);
@ -346,6 +347,7 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
dns_search_domain_unlink_marked(l->search_domains);
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
return sd_bus_reply_method_return(message, NULL);
@ -384,6 +386,8 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
link_allocate_scopes(l);
link_add_rrs(l, false);
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -416,6 +420,8 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
link_allocate_scopes(l);
link_add_rrs(l, false);
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -446,6 +452,8 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
link_set_dnssec_mode(l, mode);
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -489,6 +497,8 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
l->dnssec_negative_trust_anchors = ns;
ns = NULL;
(void) link_save_user(l);
return sd_bus_reply_method_return(message, NULL);
}
@ -507,6 +517,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
link_allocate_scopes(l);
link_add_rrs(l, false);
(void) link_save_user(l);
(void) manager_write_resolv_conf(l->manager);
return sd_bus_reply_method_return(message, NULL);

View File

@ -22,7 +22,10 @@
#include "sd-network.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "resolved-link.h"
#include "string-util.h"
@ -49,6 +52,9 @@ int link_new(Manager *m, Link **ret, int ifindex) {
l->dnssec_mode = _DNSSEC_MODE_INVALID;
l->operstate = IF_OPER_UNKNOWN;
if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0)
return -ENOMEM;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
return r;
@ -93,6 +99,8 @@ Link *link_free(Link *l) {
dns_scope_free(l->mdns_ipv4_scope);
dns_scope_free(l->mdns_ipv6_scope);
free(l->state_file);
free(l);
return NULL;
}
@ -165,7 +173,7 @@ void link_add_rrs(Link *l, bool force_remove) {
link_address_add_rrs(a, force_remove);
}
int link_update_rtnl(Link *l, sd_netlink_message *m) {
int link_process_rtnl(Link *l, sd_netlink_message *m) {
const char *n = NULL;
int r;
@ -511,10 +519,11 @@ static void link_read_settings(Link *l) {
log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
}
int link_update_monitor(Link *l) {
int link_update(Link *l) {
assert(l);
link_read_settings(l);
link_load_user(l);
link_allocate_scopes(l);
link_add_rrs(l, false);
@ -846,3 +855,261 @@ bool link_address_relevant(LinkAddress *a, bool local_multicast) {
return true;
}
static bool link_needs_save(Link *l) {
assert(l);
/* Returns true if any of the settings where set different from the default */
if (l->is_managed)
return false;
if (l->llmnr_support != RESOLVE_SUPPORT_YES ||
l->mdns_support != RESOLVE_SUPPORT_NO ||
l->dnssec_mode != _DNSSEC_MODE_INVALID)
return true;
if (l->dns_servers ||
l->search_domains)
return true;
if (!set_isempty(l->dnssec_negative_trust_anchors))
return true;
return false;
}
int link_save_user(Link *l) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *v;
int r;
assert(l);
assert(l->state_file);
if (!link_needs_save(l)) {
(void) unlink(l->state_file);
return 0;
}
r = mkdir_parents(l->state_file, 0700);
if (r < 0)
goto fail;
r = fopen_temporary(l->state_file, &f, &temp_path);
if (r < 0)
goto fail;
fputs("# This is private data. Do not parse.\n", f);
v = resolve_support_to_string(l->llmnr_support);
if (v)
fprintf(f, "LLMNR=%s\n", v);
v = resolve_support_to_string(l->mdns_support);
if (v)
fprintf(f, "MDNS=%s\n", v);
v = dnssec_mode_to_string(l->dnssec_mode);
if (v)
fprintf(f, "DNSSEC=%s\n", v);
if (l->dns_servers) {
DnsServer *server;
fputs("SERVERS=", f);
LIST_FOREACH(servers, server, l->dns_servers) {
if (server != l->dns_servers)
fputc(' ', f);
v = dns_server_string(server);
if (!v) {
r = -ENOMEM;
goto fail;
}
fputs(v, f);
}
fputc('\n', f);
}
if (l->search_domains) {
DnsSearchDomain *domain;
fputs("DOMAINS=", f);
LIST_FOREACH(domains, domain, l->search_domains) {
if (domain != l->search_domains)
fputc(' ', f);
if (domain->route_only)
fputc('~', f);
fputs(DNS_SEARCH_DOMAIN_NAME(domain), f);
}
fputc('\n', f);
}
if (!set_isempty(l->dnssec_negative_trust_anchors)) {
bool space = false;
Iterator i;
char *nta;
fputs("NTAS=", f);
SET_FOREACH(nta, l->dnssec_negative_trust_anchors, i) {
if (space)
fputc(' ', f);
fputs(nta, f);
space = true;
}
fputc('\n', f);
}
r = fflush_and_check(f);
if (r < 0)
goto fail;
if (rename(temp_path, l->state_file) < 0) {
r = -errno;
goto fail;
}
return 0;
fail:
(void) unlink(l->state_file);
if (temp_path)
(void) unlink(temp_path);
return log_error_errno(r, "Failed to save link data %s: %m", l->state_file);
}
int link_load_user(Link *l) {
_cleanup_free_ char
*llmnr = NULL,
*mdns = NULL,
*dnssec = NULL,
*servers = NULL,
*domains = NULL,
*ntas = NULL;
ResolveSupport s;
int r;
assert(l);
assert(l->state_file);
/* Try to load only a single time */
if (l->loaded)
return 0;
l->loaded = true;
if (l->is_managed)
return 0; /* if the device is managed, then networkd is our configuration source, not the bus API */
r = parse_env_file(l->state_file, NEWLINE,
"LLMNR", &llmnr,
"MDNS", &mdns,
"DNSSEC", &dnssec,
"SERVERS", &servers,
"DOMAINS", &domains,
"NTAS", &ntas,
NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
goto fail;
link_flush_settings(l);
/* If we can't recognize the LLMNR or MDNS setting we don't override the default */
s = resolve_support_from_string(llmnr);
if (s >= 0)
l->llmnr_support = s;
s = resolve_support_from_string(mdns);
if (s >= 0)
l->mdns_support = s;
/* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
l->dnssec_mode = dnssec_mode_from_string(dnssec);
if (servers) {
const char *p = servers;
for (;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
goto fail;
if (r == 0)
break;
r = link_update_dns_server_one(l, word);
if (r < 0) {
log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word);
continue;
}
}
}
if (domains) {
const char *p = domains;
for (;;) {
_cleanup_free_ char *word = NULL;
const char *n;
bool is_route;
r = extract_first_word(&p, &word, NULL, 0);
if (r < 0)
goto fail;
if (r == 0)
break;
is_route = word[0] == '~';
n = is_route ? word + 1 : word;
r = link_update_search_domain_one(l, n, is_route);
if (r < 0) {
log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word);
continue;
}
}
}
if (ntas) {
_cleanup_set_free_free_ Set *ns = NULL;
ns = set_new(&dns_name_hash_ops);
if (!ns) {
r = -ENOMEM;
goto fail;
}
r = set_put_strsplit(ns, ntas, NULL, 0);
if (r < 0)
goto fail;
l->dnssec_negative_trust_anchors = ns;
ns = NULL;
}
return 0;
fail:
return log_error_errno(r, "Failed to load link data %s: %m", l->state_file);
}
void link_remove_user(Link *l) {
assert(l);
assert(l->state_file);
(void) unlink(l->state_file);
}

View File

@ -81,12 +81,15 @@ struct Link {
char name[IF_NAMESIZE];
uint32_t mtu;
uint8_t operstate;
bool loaded;
char *state_file;
};
int link_new(Manager *m, Link **ret, int ifindex);
Link *link_free(Link *l);
int link_update_rtnl(Link *l, sd_netlink_message *m);
int link_update_monitor(Link *l);
int link_process_rtnl(Link *l, sd_netlink_message *m);
int link_update(Link *l);
bool link_relevant(Link *l, int family, bool local_multicast);
LinkAddress* link_find_address(Link *l, int family, const union in_addr_union *in_addr);
void link_add_rrs(Link *l, bool force_remove);
@ -102,6 +105,10 @@ void link_next_dns_server(Link *l);
DnssecMode link_get_dnssec_mode(Link *l);
bool link_dnssec_supported(Link *l);
int link_save_user(Link *l);
int link_load_user(Link *l);
void link_remove_user(Link *l);
int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
LinkAddress *link_address_free(LinkAddress *a);
int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);

View File

@ -23,6 +23,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "dirent-util.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "fileio-label.h"
@ -78,11 +79,11 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
goto fail;
}
r = link_update_rtnl(l, mm);
r = link_process_rtnl(l, mm);
if (r < 0)
goto fail;
r = link_update_monitor(l);
r = link_update(l);
if (r < 0)
goto fail;
@ -95,6 +96,7 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void *
case RTM_DELLINK:
if (l) {
log_debug("Removing link %i/%s", l->ifindex, l->name);
link_remove_user(l);
link_free(l);
}
@ -279,7 +281,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *
sd_network_monitor_flush(m->network_monitor);
HASHMAP_FOREACH(l, m->links, i) {
r = link_update_monitor(l);
r = link_update(l);
if (r < 0)
log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex);
}
@ -540,6 +542,8 @@ int manager_new(Manager **ret) {
(void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m);
(void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m);
manager_cleanup_saved_user(m);
*ret = m;
m = NULL;
@ -1269,3 +1273,58 @@ void manager_flush_caches(Manager *m) {
log_info("Flushed all caches.");
}
void manager_cleanup_saved_user(Manager *m) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r;
assert(m);
/* Clean up all saved per-link files in /run/systemd/resolve/netif/ that don't have a matching interface
* anymore. These files are created to persist settings pushed in by the user via the bus, so that resolved can
* be restarted without losing this data. */
d = opendir("/run/systemd/resolve/netif/");
if (!d) {
if (errno == ENOENT)
return;
log_warning_errno(errno, "Failed to open interface directory: %m");
return;
}
FOREACH_DIRENT_ALL(de, d, log_error_errno(errno, "Failed to read interface directory: %m")) {
_cleanup_free_ char *p = NULL;
int ifindex;
Link *l;
if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG))
continue;
if (STR_IN_SET(de->d_name, ".", ".."))
continue;
r = parse_ifindex(de->d_name, &ifindex);
if (r < 0) /* Probably some temporary file from a previous run. Delete it */
goto rm;
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l) /* link vanished */
goto rm;
if (l->is_managed) /* now managed by networkd, hence the bus settings are useless */
goto rm;
continue;
rm:
p = strappend("/run/systemd/resolve/netif/", de->d_name);
if (!p) {
log_oom();
return;
}
(void) unlink(p);
}
}

View File

@ -172,3 +172,5 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource
bool manager_routable(Manager *m, int family);
void manager_flush_caches(Manager *m);
void manager_cleanup_saved_user(Manager *m);