/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include "alloc-util.h" #include "dns-domain.h" #include "dns-resolver-internal.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "network-internal.h" #include "networkd-dhcp-common.h" #include "networkd-link.h" #include "networkd-manager-bus.h" #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-ntp.h" #include "networkd-state-file.h" #include "ordered-set.h" #include "set.h" #include "strv.h" #include "tmpfile-util.h" static int ordered_set_put_dns_servers(OrderedSet **s, int ifindex, struct in_addr_full **dns, unsigned n) { int r; assert(s); assert(dns || n == 0); FOREACH_ARRAY(a, dns, n) { const char *p; if ((*a)->ifindex != 0 && (*a)->ifindex != ifindex) return 0; p = in_addr_full_to_string(*a); if (!p) return 0; r = ordered_set_put_strdup(s, p); if (r < 0) return r; } return 0; } static int ordered_set_put_in4_addrv( OrderedSet **s, const struct in_addr *addresses, size_t n, bool (*predicate)(const struct in_addr *addr)) { int r; assert(s); assert(n == 0 || addresses); FOREACH_ARRAY(a, addresses, n) { if (predicate && !predicate(a)) continue; r = ordered_set_put_strdup(s, IN4_ADDR_TO_STRING(a)); if (r < 0) return r; } return 0; } static int ordered_set_put_in6_addrv( OrderedSet **s, const struct in6_addr *addresses, size_t n) { int r; assert(s); assert(n == 0 || addresses); FOREACH_ARRAY(a, addresses, n) { r = ordered_set_put_strdup(s, IN6_ADDR_TO_STRING(a)); if (r < 0) return r; } return 0; } static int link_put_dns(Link *link, OrderedSet **s) { int r; assert(link); assert(link->network); assert(s); if (link->n_dns != UINT_MAX) return ordered_set_put_dns_servers(s, link->ifindex, link->dns, link->n_dns); r = ordered_set_put_dns_servers(s, link->ifindex, link->network->dns, link->network->n_dns); if (r < 0) return r; if (link->dhcp_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4)) { const struct in_addr *addresses; r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); if (r >= 0) { r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local); if (r < 0) return r; } } if (link->dhcp_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4)) { sd_dns_resolver *resolvers; r = sd_dhcp_lease_get_dnr(link->dhcp_lease, &resolvers); if (r >= 0) { struct in_addr_full **dot_servers; size_t n = 0; CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free); r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n); if (r < 0) return r; r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n); if (r < 0) return r; } } if (link->dhcp6_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) { const struct in6_addr *addresses; r = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &addresses); if (r >= 0) { r = ordered_set_put_in6_addrv(s, addresses, r); if (r < 0) return r; } } if (link->dhcp6_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6)) { sd_dns_resolver *resolvers; r = sd_dhcp6_lease_get_dnr(link->dhcp6_lease, &resolvers); if (r >= 0 ) { struct in_addr_full **dot_servers; size_t n = 0; CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free); r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n); if (r < 0) return r; r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n); if (r < 0) return r; } } if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) { NDiscRDNSS *a; SET_FOREACH(a, link->ndisc_rdnss) { r = ordered_set_put_in6_addrv(s, &a->address, 1); if (r < 0) return r; } } if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) { NDiscDNR *a; SET_FOREACH(a, link->ndisc_dnr) { struct in_addr_full **dot_servers = NULL; size_t n = 0; CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free); r = dns_resolvers_to_dot_addrs(&a->resolver, 1, &dot_servers, &n); if (r < 0) return r; r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n); if (r < 0) return r; } } return 0; } static int link_put_ntp(Link *link, OrderedSet **s) { int r; assert(link); assert(link->network); assert(s); if (link->ntp) return ordered_set_put_strdupv(s, link->ntp); r = ordered_set_put_strdupv(s, link->network->ntp); if (r < 0) return r; if (link->dhcp_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4)) { const struct in_addr *addresses; r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); if (r >= 0) { r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local); if (r < 0) return r; } } if (link->dhcp6_lease && link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6)) { const struct in6_addr *addresses; char **fqdn; r = sd_dhcp6_lease_get_ntp_addrs(link->dhcp6_lease, &addresses); if (r >= 0) { r = ordered_set_put_in6_addrv(s, addresses, r); if (r < 0) return r; } r = sd_dhcp6_lease_get_ntp_fqdn(link->dhcp6_lease, &fqdn); if (r >= 0) { r = ordered_set_put_strdupv(s, fqdn); if (r < 0) return r; } } return 0; } static int link_put_sip(Link *link, OrderedSet **s) { int r; assert(link); assert(link->network); assert(s); if (link->dhcp_lease && link->network->dhcp_use_sip) { const struct in_addr *addresses; r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses); if (r >= 0) { r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local); if (r < 0) return r; } } return 0; } static int link_put_domains(Link *link, bool is_route, OrderedSet **s) { OrderedSet *link_domains, *network_domains; UseDomains use_domains; int r; assert(link); assert(link->network); assert(s); link_domains = is_route ? link->route_domains : link->search_domains; network_domains = is_route ? link->network->route_domains : link->network->search_domains; use_domains = is_route ? USE_DOMAINS_ROUTE : USE_DOMAINS_YES; if (link_domains) return ordered_set_put_string_set(s, link_domains); r = ordered_set_put_string_set(s, network_domains); if (r < 0) return r; if (link->dhcp_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) { const char *domainname; char **domains; r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); if (r >= 0) { r = ordered_set_put_strdup(s, domainname); if (r < 0) return r; } r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains); if (r >= 0) { r = ordered_set_put_strdupv(s, domains); if (r < 0) return r; } } if (link->dhcp6_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) { char **domains; r = sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains); if (r >= 0) { r = ordered_set_put_strdupv(s, domains); if (r < 0) return r; } } if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) { NDiscDNSSL *a; SET_FOREACH(a, link->ndisc_dnssl) { r = ordered_set_put_strdup(s, NDISC_DNSSL_DOMAIN(a)); if (r < 0) return r; } } return 0; } int manager_save(Manager *m) { _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL; const char *operstate_str, *carrier_state_str, *address_state_str, *ipv4_address_state_str, *ipv6_address_state_str, *online_state_str; LinkOperationalState operstate = LINK_OPERSTATE_OFF; LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF; LinkAddressState ipv4_address_state = LINK_ADDRESS_STATE_OFF, ipv6_address_state = LINK_ADDRESS_STATE_OFF, address_state = LINK_ADDRESS_STATE_OFF; LinkOnlineState online_state; size_t links_offline = 0, links_online = 0; int r; assert(m); if (isempty(m->state_file)) return 0; /* Do not update state file when running in test mode. */ Link *link; HASHMAP_FOREACH(link, m->links_by_index) { if (link->flags & IFF_LOOPBACK) continue; operstate = MAX(operstate, link->operstate); carrier_state = MAX(carrier_state, link->carrier_state); address_state = MAX(address_state, link->address_state); ipv4_address_state = MAX(ipv4_address_state, link->ipv4_address_state); ipv6_address_state = MAX(ipv6_address_state, link->ipv6_address_state); if (!link->network) continue; if (link->network->required_for_online) { if (link->online_state == LINK_ONLINE_STATE_OFFLINE) links_offline++; else if (link->online_state == LINK_ONLINE_STATE_ONLINE) links_online++; } r = link_put_dns(link, &dns); if (r < 0) return r; r = link_put_ntp(link, &ntp); if (r < 0) return r; r = link_put_sip(link, &sip); if (r < 0) return r; r = link_put_domains(link, /* is_route = */ false, &search_domains); if (r < 0) return r; r = link_put_domains(link, /* is_route = */ true, &route_domains); if (r < 0) return r; } if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED) carrier_state = LINK_CARRIER_STATE_CARRIER; online_state = links_online > 0 ? (links_offline > 0 ? LINK_ONLINE_STATE_PARTIAL : LINK_ONLINE_STATE_ONLINE) : (links_offline > 0 ? LINK_ONLINE_STATE_OFFLINE : _LINK_ONLINE_STATE_INVALID); operstate_str = ASSERT_PTR(link_operstate_to_string(operstate)); carrier_state_str = ASSERT_PTR(link_carrier_state_to_string(carrier_state)); address_state_str = ASSERT_PTR(link_address_state_to_string(address_state)); ipv4_address_state_str = ASSERT_PTR(link_address_state_to_string(ipv4_address_state)); ipv6_address_state_str = ASSERT_PTR(link_address_state_to_string(ipv6_address_state)); _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; r = fopen_temporary(m->state_file, &f, &temp_path); if (r < 0) return r; (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n" "OPER_STATE=%s\n" "CARRIER_STATE=%s\n" "ADDRESS_STATE=%s\n" "IPV4_ADDRESS_STATE=%s\n" "IPV6_ADDRESS_STATE=%s\n", operstate_str, carrier_state_str, address_state_str, ipv4_address_state_str, ipv6_address_state_str); online_state_str = link_online_state_to_string(online_state); if (online_state_str) fprintf(f, "ONLINE_STATE=%s\n", online_state_str); ordered_set_print(f, "DNS=", dns); ordered_set_print(f, "NTP=", ntp); ordered_set_print(f, "SIP=", sip); ordered_set_print(f, "DOMAINS=", search_domains); ordered_set_print(f, "ROUTE_DOMAINS=", route_domains); r = fflush_and_check(f); if (r < 0) return r; r = conservative_rename(temp_path, m->state_file); if (r < 0) return r; temp_path = mfree(temp_path); _cleanup_strv_free_ char **p = NULL; if (m->operational_state != operstate) { m->operational_state = operstate; if (strv_extend(&p, "OperationalState") < 0) log_oom(); } if (m->carrier_state != carrier_state) { m->carrier_state = carrier_state; if (strv_extend(&p, "CarrierState") < 0) log_oom(); } if (m->address_state != address_state) { m->address_state = address_state; if (strv_extend(&p, "AddressState") < 0) log_oom(); } if (m->ipv4_address_state != ipv4_address_state) { m->ipv4_address_state = ipv4_address_state; if (strv_extend(&p, "IPv4AddressState") < 0) log_oom(); } if (m->ipv6_address_state != ipv6_address_state) { m->ipv6_address_state = ipv6_address_state; if (strv_extend(&p, "IPv6AddressState") < 0) log_oom(); } if (m->online_state != online_state) { m->online_state = online_state; if (strv_extend(&p, "OnlineState") < 0) log_oom(); } if (p) { r = manager_send_changed_strv(m, p); if (r < 0) log_warning_errno(r, "Could not emit changed properties, ignoring: %m"); } m->dirty = false; return 0; } static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { bool space = false; Link *link; assert(f); assert(prefix); if (hashmap_isempty(h)) return; fputs(prefix, f); HASHMAP_FOREACH(link, h) { if (space) fputc(' ', f); fprintf(f, "%i", link->ifindex); space = true; } fputc('\n', f); } static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) { bool _space = false; if (!space) space = &_space; for (unsigned j = 0; j < n_dns; j++) { const char *str; if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex) continue; str = in_addr_full_to_string(dns[j]); if (!str) continue; if (*space) fputc(' ', f); fputs(str, f); *space = true; } } static void serialize_addresses( FILE *f, const char *lvalue, bool *space, char **addresses, sd_dhcp_lease *lease, bool conditional, sd_dhcp_lease_server_type_t what, sd_dhcp6_lease *lease6, bool conditional6, int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**), int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) { bool _space = false; int r; if (!space) space = &_space; if (lvalue) fprintf(f, "%s=", lvalue); fputstrv(f, addresses, NULL, space); if (lease && conditional) { const struct in_addr *lease_addresses; r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses); if (r > 0) serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local); } if (lease6 && conditional6 && lease6_get_addr) { const struct in6_addr *in6_addrs; r = lease6_get_addr(lease6, &in6_addrs); if (r > 0) serialize_in6_addrs(f, in6_addrs, r, space); } if (lease6 && conditional6 && lease6_get_fqdn) { char **in6_hosts; r = lease6_get_fqdn(lease6, &in6_hosts); if (r > 0) fputstrv(f, in6_hosts, NULL, space); } if (lvalue) fputc('\n', f); } static void serialize_resolvers( FILE *f, const char *lvalue, bool *space, sd_dhcp_lease *lease, bool conditional, sd_dhcp6_lease *lease6, bool conditional6) { bool _space = false; if (!space) space = &_space; if (lvalue) fprintf(f, "%s=", lvalue); if (lease && conditional) { sd_dns_resolver *resolvers; _cleanup_strv_free_ char **names = NULL; int r; r = sd_dhcp_lease_get_dnr(lease, &resolvers); if (r < 0) return (void) log_debug_errno(r, "Failed to get DNR from DHCP lease, ignoring: %m"); r = dns_resolvers_to_dot_strv(resolvers, r, &names); if (r < 0) return (void) log_warning_errno(r, "Failed to get DoT servers from DHCP DNR, ignoring: %m"); if (r > 0) fputstrv(f, names, NULL, space); } if (lease6 && conditional6) { sd_dns_resolver *resolvers; _cleanup_strv_free_ char **names = NULL; int r; r = sd_dhcp6_lease_get_dnr(lease6, &resolvers); if (r < 0) return (void) log_debug_errno(r, "Failed to get DNR from DHCPv6 lease, ignoring: %m"); r = dns_resolvers_to_dot_strv(resolvers, r, &names); if (r < 0) return (void) log_warning_errno(r, "Failed to get DoT servers from DHCPv6 DNR, ignoring: %m"); if (r > 0) fputstrv(f, names, NULL, space); } if (lvalue) fputc('\n', f); return; } static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, UseDomains use_domains) { bool space = false; const char *p; assert(link); assert(link->network); assert(f); ORDERED_SET_FOREACH(p, static_domains) fputs_with_separator(f, p, NULL, &space); if (use_domains == USE_DOMAINS_NO) return; if (link->dhcp_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP4) == use_domains) { const char *domainname; char **domains; if (sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname) >= 0) fputs_with_separator(f, domainname, NULL, &space); if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0) fputstrv(f, domains, NULL, &space); } if (link->dhcp6_lease && link_get_use_domains(link, NETWORK_CONFIG_SOURCE_DHCP6) == use_domains) { char **domains; if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0) fputstrv(f, domains, NULL, &space); } if (link_get_use_domains(link, NETWORK_CONFIG_SOURCE_NDISC) == use_domains) { NDiscDNSSL *dd; SET_FOREACH(dd, link->ndisc_dnssl) fputs_with_separator(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); } } static int serialize_config_files(FILE *f, const char *prefix, const char *main_config, char * const *dropins) { assert(f); assert(prefix); assert(main_config); fprintf(f, "%s_FILE=%s\n", prefix, main_config); bool space = false; fprintf(f, "%s_FILE_DROPINS=\"", prefix); STRV_FOREACH(d, dropins) { _cleanup_free_ char *escaped = NULL; escaped = xescape(*d, ":"); if (!escaped) return -ENOMEM; fputs_with_separator(f, escaped, ":", &space); } fputs("\"\n", f); return 0; } static int link_save(Link *link) { const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state; _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(link); assert(link->manager); if (isempty(link->state_file)) return 0; /* Do not update state files when running in test mode. */ if (link->state == LINK_STATE_LINGER) return 0; admin_state = ASSERT_PTR(link_state_to_string(link->state)); oper_state = ASSERT_PTR(link_operstate_to_string(link->operstate)); carrier_state = ASSERT_PTR(link_carrier_state_to_string(link->carrier_state)); address_state = ASSERT_PTR(link_address_state_to_string(link->address_state)); ipv4_address_state = ASSERT_PTR(link_address_state_to_string(link->ipv4_address_state)); ipv6_address_state = ASSERT_PTR(link_address_state_to_string(link->ipv6_address_state)); r = fopen_temporary(link->state_file, &f, &temp_path); if (r < 0) return r; (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n" "ADMIN_STATE=%s\n" "OPER_STATE=%s\n" "CARRIER_STATE=%s\n" "ADDRESS_STATE=%s\n" "IPV4_ADDRESS_STATE=%s\n" "IPV6_ADDRESS_STATE=%s\n", admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state); if (link->netdev) { r = serialize_config_files(f, "NETDEV", link->netdev->filename, link->netdev->dropins); if (r < 0) return r; } if (link->network) { const char *online_state, *captive_portal; bool space = false; online_state = link_online_state_to_string(link->online_state); if (online_state) fprintf(f, "ONLINE_STATE=%s\n", online_state); fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", yes_no(link->network->required_for_online)); LinkOperationalStateRange st; link_required_operstate_for_online(link, &st); fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s:%s\n", link_operstate_to_string(st.min), link_operstate_to_string(st.max)); fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n", link_required_address_family_to_string(link_required_family_for_online(link))); fprintf(f, "ACTIVATION_POLICY=%s\n", activation_policy_to_string(link->network->activation_policy)); r = serialize_config_files(f, "NETWORK", link->network->filename, link->network->dropins); if (r < 0) return r; /************************************************************/ fputs("DNS=", f); if (link->n_dns != UINT_MAX) link_save_dns(link, f, link->dns, link->n_dns, NULL); else { space = false; link_save_dns(link, f, link->network->dns, link->network->n_dns, &space); /* DNR resolvers are not required to provide Do53 service, however resolved doesn't * know how to handle such a server so for now Do53 service is required, and * assumed. */ serialize_resolvers(f, NULL, &space, link->dhcp_lease, link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4), link->dhcp6_lease, link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6)); if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) { NDiscDNR *dnr; SET_FOREACH(dnr, link->ndisc_dnr) serialize_dnr(f, &dnr->resolver, 1, &space); } serialize_addresses(f, NULL, &space, NULL, link->dhcp_lease, link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4), SD_DHCP_LEASE_DNS, link->dhcp6_lease, link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6), sd_dhcp6_lease_get_dns, NULL); if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) { NDiscRDNSS *dd; SET_FOREACH(dd, link->ndisc_rdnss) serialize_in6_addrs(f, &dd->address, 1, &space); } } fputc('\n', f); /************************************************************/ if (link->ntp) { fputs("NTP=", f); fputstrv(f, link->ntp, NULL, NULL); fputc('\n', f); } else serialize_addresses(f, "NTP", NULL, link->network->ntp, link->dhcp_lease, link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4), SD_DHCP_LEASE_NTP, link->dhcp6_lease, link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP6), sd_dhcp6_lease_get_ntp_addrs, sd_dhcp6_lease_get_ntp_fqdn); serialize_addresses(f, "SIP", NULL, NULL, link->dhcp_lease, link->network->dhcp_use_sip, SD_DHCP_LEASE_SIP, NULL, false, NULL, NULL); /************************************************************/ r = link_get_captive_portal(link, &captive_portal); if (r < 0) return r; if (captive_portal) fprintf(f, "CAPTIVE_PORTAL=%s\n", captive_portal); /************************************************************/ fputs("DOMAINS=", f); if (link->search_domains) link_save_domains(link, f, link->search_domains, USE_DOMAINS_NO); else link_save_domains(link, f, link->network->search_domains, USE_DOMAINS_YES); fputc('\n', f); /************************************************************/ fputs("ROUTE_DOMAINS=", f); if (link->route_domains) link_save_domains(link, f, link->route_domains, USE_DOMAINS_NO); else link_save_domains(link, f, link->network->route_domains, USE_DOMAINS_ROUTE); fputc('\n', f); /************************************************************/ fprintf(f, "LLMNR=%s\n", resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr)); /************************************************************/ fprintf(f, "MDNS=%s\n", resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns)); /************************************************************/ int dns_default_route = link->dns_default_route >= 0 ? link->dns_default_route : link->network->dns_default_route; if (dns_default_route >= 0) fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route)); /************************************************************/ DnsOverTlsMode dns_over_tls_mode = link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode : link->network->dns_over_tls_mode; if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID) fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode)); /************************************************************/ DnssecMode dnssec_mode = link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode : link->network->dnssec_mode; if (dnssec_mode != _DNSSEC_MODE_INVALID) fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode)); /************************************************************/ Set *nta_anchors = link->dnssec_negative_trust_anchors; if (set_isempty(nta_anchors)) nta_anchors = link->network->dnssec_negative_trust_anchors; if (!set_isempty(nta_anchors)) { const char *n; space = false; fputs("DNSSEC_NTA=", f); SET_FOREACH(n, nta_anchors) fputs_with_separator(f, n, NULL, &space); fputc('\n', f); } } print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links); print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links); if (link->dhcp_lease) { r = dhcp_lease_save(link->dhcp_lease, link->lease_file); if (r < 0) return r; fprintf(f, "DHCP_LEASE=%s\n", link->lease_file); } else (void) unlink(link->lease_file); r = link_serialize_dhcp6_client(link, f); if (r < 0) return r; r = fflush_and_check(f); if (r < 0) return r; r = conservative_rename(temp_path, link->state_file); if (r < 0) return r; temp_path = mfree(temp_path); return 0; } void link_dirty(Link *link) { int r; assert(link); assert(link->manager); /* When the manager is in MANAGER_STOPPED, it is not necessary to update state files anymore, as they * will be removed soon anyway. Moreover, we cannot call link_ref() in that case. */ if (link->manager->state == MANAGER_STOPPED) return; /* The serialized state in /run is no longer up-to-date. */ /* Also mark manager dirty as link is dirty */ link->manager->dirty = true; r = set_ensure_put(&link->manager->dirty_links, NULL, link); if (r <= 0) /* Ignore allocation errors and don't take another ref if the link was already dirty */ return; link_ref(link); } void link_clean(Link *link) { assert(link); assert(link->manager); /* The serialized state in /run is up-to-date */ link_unref(set_remove(link->manager->dirty_links, link)); } int link_save_and_clean_full(Link *link, bool also_save_manager) { int r, k = 0; assert(link); assert(link->manager); if (also_save_manager) k = manager_save(link->manager); r = link_save(link); if (r < 0) return r; link_clean(link); return k; } int manager_clean_all(Manager *manager) { int r, ret = 0; assert(manager); if (manager->dirty) { r = manager_save(manager); if (r < 0) log_warning_errno(r, "Failed to update state file %s, ignoring: %m", manager->state_file); RET_GATHER(ret, r); } Link *link; SET_FOREACH(link, manager->dirty_links) { r = link_save_and_clean(link); if (r < 0) log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file); RET_GATHER(ret, r); } return ret; }