From 3b5a4fc685d6db71d68db639f274b644d9422bb1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 21 Feb 2021 14:23:04 +0900 Subject: [PATCH 1/9] network: move state file related functions to networkd-state-file.[ch] --- src/network/meson.build | 2 + src/network/networkd-dhcp4.c | 1 + src/network/networkd-link-bus.c | 1 + src/network/networkd-link.c | 385 +---------------- src/network/networkd-link.h | 5 - src/network/networkd-manager.c | 291 +------------ src/network/networkd-manager.h | 2 - src/network/networkd-ndisc.c | 1 + src/network/networkd-state-file.c | 690 ++++++++++++++++++++++++++++++ src/network/networkd-state-file.h | 12 + 10 files changed, 709 insertions(+), 681 deletions(-) create mode 100644 src/network/networkd-state-file.c create mode 100644 src/network/networkd-state-file.h diff --git a/src/network/meson.build b/src/network/meson.build index 3ff04467b0..4fca3106dc 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -113,6 +113,8 @@ sources = files(''' networkd-speed-meter.h networkd-sriov.c networkd-sriov.h + networkd-state-file.c + networkd-state-file.h networkd-sysctl.c networkd-sysctl.h networkd-util.c diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index b0c783c774..242b977f60 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -17,6 +17,7 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" +#include "networkd-state-file.h" #include "string-table.h" #include "strv.h" #include "sysctl-util.h" diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 3ffe587a86..a23c98110e 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -13,6 +13,7 @@ #include "networkd-link-bus.h" #include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-state-file.h" #include "parse-util.h" #include "resolve-util.h" #include "socket-netlink.h" diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 96d4d9235b..a8f021fa6d 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -42,6 +42,7 @@ #include "networkd-sysctl.h" #include "networkd-radv.h" #include "networkd-routing-policy-rule.h" +#include "networkd-state-file.h" #include "networkd-wifi.h" #include "set.h" #include "socket-util.h" @@ -2806,390 +2807,6 @@ int link_update(Link *link, sd_netlink_message *m) { 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) { - 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 ***)) { - int r; - - bool _space = false; - 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); -} - -int link_save(Link *link) { - const char *admin_state, *oper_state, *carrier_state, *address_state; - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - assert(link); - assert(link->state_file); - assert(link->lease_file); - assert(link->manager); - - if (link->state == LINK_STATE_LINGER) { - (void) unlink(link->state_file); - return 0; - } - - link_lldp_save(link); - - admin_state = link_state_to_string(link->state); - assert(admin_state); - - oper_state = link_operstate_to_string(link->operstate); - assert(oper_state); - - carrier_state = link_carrier_state_to_string(link->carrier_state); - assert(carrier_state); - - address_state = link_address_state_to_string(link->address_state); - assert(address_state); - - r = fopen_temporary(link->state_file, &f, &temp_path); - if (r < 0) - goto fail; - - (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", - admin_state, oper_state, carrier_state, address_state); - - if (link->network) { - char **dhcp6_domains = NULL, **dhcp_domains = NULL; - const char *dhcp_domainname = NULL, *p; - bool space; - - fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", - yes_no(link->network->required_for_online)); - - LinkOperationalStateRange st = link->network->required_operstate_for_online; - fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n", - strempty(link_operstate_to_string(st.min)), - st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "", - st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : ""); - - fprintf(f, "ACTIVATION_POLICY=%s\n", - activation_policy_to_string(link->network->activation_policy)); - - fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); - - /************************************************************/ - - fputs("DNS=", f); - space = false; - if (link->n_dns != (unsigned) -1) - link_save_dns(link, f, link->dns, link->n_dns, &space); - else - link_save_dns(link, f, link->network->dns, link->network->n_dns, &space); - - serialize_addresses(f, NULL, &space, - NULL, - link->dhcp_lease, - link->network->dhcp_use_dns, - SD_DHCP_LEASE_DNS, - link->dhcp6_lease, - link->network->dhcp6_use_dns, - sd_dhcp6_lease_get_dns, - NULL); - - /* Make sure to flush out old entries before we use the NDisc data */ - ndisc_vacuum(link); - - if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) { - NDiscRDNSS *dd; - - SET_FOREACH(dd, link->ndisc_rdnss) - serialize_in6_addrs(f, &dd->address, 1, &space); - } - - fputc('\n', f); - - /************************************************************/ - - serialize_addresses(f, "NTP", NULL, - link->ntp ?: link->network->ntp, - link->dhcp_lease, - link->network->dhcp_use_ntp, - SD_DHCP_LEASE_NTP, - link->dhcp6_lease, - link->network->dhcp6_use_ntp, - 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); - - /************************************************************/ - - if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { - if (link->dhcp_lease) { - (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); - (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains); - } - if (link->dhcp6_lease) - (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains); - } - - fputs("DOMAINS=", f); - space = false; - ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains) - fputs_with_space(f, p, NULL, &space); - - if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { - if (dhcp_domainname) - fputs_with_space(f, dhcp_domainname, NULL, &space); - if (dhcp_domains) - fputstrv(f, dhcp_domains, NULL, &space); - if (dhcp6_domains) - fputstrv(f, dhcp6_domains, NULL, &space); - } - - if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_YES) { - NDiscDNSSL *dd; - - SET_FOREACH(dd, link->ndisc_dnssl) - fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); - } - - fputc('\n', f); - - /************************************************************/ - - fputs("ROUTE_DOMAINS=", f); - space = false; - ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains) - fputs_with_space(f, p, NULL, &space); - - if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { - if (dhcp_domainname) - fputs_with_space(f, dhcp_domainname, NULL, &space); - if (dhcp_domains) - fputstrv(f, dhcp_domains, NULL, &space); - if (dhcp6_domains) - fputstrv(f, dhcp6_domains, NULL, &space); - } - - if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_ROUTE) { - NDiscDNSSL *dd; - - SET_FOREACH(dd, link->ndisc_dnssl) - fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); - } - - 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; - - fputs("DNSSEC_NTA=", f); - space = false; - SET_FOREACH(n, nta_anchors) - fputs_with_space(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) - goto fail; - - 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) - goto fail; - - r = fflush_and_check(f); - if (r < 0) - goto fail; - - r = conservative_rename(temp_path, link->state_file); - if (r < 0) - goto fail; - - return 0; - -fail: - (void) unlink(link->state_file); - if (temp_path) - (void) unlink(temp_path); - - return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file); -} - -/* The serialized state in /run is no longer up-to-date. */ -void link_dirty(Link *link) { - int r; - - assert(link); - - /* mark manager dirty as link is dirty */ - manager_dirty(link->manager); - - 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); -} - -/* The serialized state in /run is up-to-date */ -void link_clean(Link *link) { - assert(link); - assert(link->manager); - - link_unref(set_remove(link->manager->dirty_links, link)); -} - -int link_save_and_clean(Link *link) { - int r; - - r = link_save(link); - if (r < 0) - return r; - - link_clean(link); - return 0; -} - static const char* const link_state_table[_LINK_STATE_MAX] = { [LINK_STATE_PENDING] = "pending", [LINK_STATE_INITIALIZED] = "initialized", diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 8129fd2554..347da1c928 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -217,11 +217,6 @@ void link_check_ready(Link *link); void link_update_operstate(Link *link, bool also_update_bond_master); int link_update(Link *link, sd_netlink_message *message); -void link_dirty(Link *link); -void link_clean(Link *link); -int link_save(Link *link); -int link_save_and_clean(Link *link); - int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 08da9120ad..0b1cb1e42a 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -38,6 +38,7 @@ #include "networkd-nexthop.h" #include "networkd-routing-policy-rule.h" #include "networkd-speed-meter.h" +#include "networkd-state-file.h" #include "ordered-set.h" #include "path-lookup.h" #include "path-util.h" @@ -466,289 +467,6 @@ static int manager_connect_rtnl(Manager *m) { return 0; } -static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) { - const char *p; - int r; - - assert(s); - assert(dns); - - if (dns->ifindex != 0 && dns->ifindex != ifindex) - return 0; - - p = in_addr_full_to_string(dns); - if (!p) - return 0; - - r = ordered_set_put_strdup(s, p); - if (r == -EEXIST) - return 0; - - return r; -} - -static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) { - int r, c = 0; - - assert(s); - assert(dns || n == 0); - - for (unsigned i = 0; i < n; i++) { - r = ordered_set_put_dns_server(s, ifindex, dns[i]); - if (r < 0) - return r; - - c += r; - } - - return c; -} - -static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) { - char *p; - int r; - - assert(s); - assert(address); - - r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p); - if (r < 0) - return r; - - r = ordered_set_consume(s, p); - if (r == -EEXIST) - return 0; - - return r; -} - -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, c = 0; - - assert(s); - assert(n == 0 || addresses); - - for (size_t i = 0; i < n; i++) { - if (predicate && !predicate(&addresses[i])) - continue; - r = ordered_set_put_in4_addr(s, addresses+i); - if (r < 0) - return r; - - c += r; - } - - return c; -} - -static int manager_save(Manager *m) { - _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL; - const char *operstate_str, *carrier_state_str, *address_state_str; - LinkOperationalState operstate = LINK_OPERSTATE_OFF; - LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF; - LinkAddressState address_state = LINK_ADDRESS_STATE_OFF; - _cleanup_free_ char *temp_path = NULL; - _cleanup_strv_free_ char **p = NULL; - _cleanup_fclose_ FILE *f = NULL; - Link *link; - int r; - - assert(m); - assert(m->state_file); - - /* We add all NTP and DNS server to a set, to filter out duplicates */ - dns = ordered_set_new(&string_hash_ops); - if (!dns) - return -ENOMEM; - - ntp = ordered_set_new(&string_hash_ops); - if (!ntp) - return -ENOMEM; - - sip = ordered_set_new(&string_hash_ops); - if (!sip) - return -ENOMEM; - - search_domains = ordered_set_new(&dns_name_hash_ops); - if (!search_domains) - return -ENOMEM; - - route_domains = ordered_set_new(&dns_name_hash_ops); - if (!route_domains) - return -ENOMEM; - - HASHMAP_FOREACH(link, m->links) { - const struct in_addr *addresses; - - if (link->flags & IFF_LOOPBACK) - continue; - - if (link->operstate > operstate) - operstate = link->operstate; - - if (link->carrier_state > carrier_state) - carrier_state = link->carrier_state; - - if (link->address_state > address_state) - address_state = link->address_state; - - if (!link->network) - continue; - - /* First add the static configured entries */ - if (link->n_dns != (unsigned) -1) - r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns); - else - r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns); - if (r < 0) - return r; - - r = ordered_set_put_strdupv(ntp, link->ntp ?: link->network->ntp); - if (r < 0) - return r; - - r = ordered_set_put_string_set(search_domains, link->search_domains ?: link->network->search_domains); - if (r < 0) - return r; - - r = ordered_set_put_string_set(route_domains, link->route_domains ?: link->network->route_domains); - if (r < 0) - return r; - - if (!link->dhcp_lease) - continue; - - /* Secondly, add the entries acquired via DHCP */ - if (link->network->dhcp_use_dns) { - r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); - if (r > 0) { - r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local); - if (r < 0) - return r; - } else if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->dhcp_use_ntp) { - r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); - if (r > 0) { - r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local); - if (r < 0) - return r; - } else if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->dhcp_use_sip) { - r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses); - if (r > 0) { - r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local); - if (r < 0) - return r; - } else if (r < 0 && r != -ENODATA) - return r; - } - - if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { - const char *domainname; - char **domains = NULL; - - OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains; - r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); - if (r >= 0) { - r = ordered_set_put_strdup(target_domains, domainname); - if (r < 0) - return r; - } else if (r != -ENODATA) - return r; - - r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains); - if (r >= 0) { - r = ordered_set_put_strdupv(target_domains, domains); - if (r < 0) - return r; - } else if (r != -ENODATA) - return r; - } - } - - if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED) - carrier_state = LINK_CARRIER_STATE_CARRIER; - - operstate_str = link_operstate_to_string(operstate); - assert(operstate_str); - - carrier_state_str = link_carrier_state_to_string(carrier_state); - assert(carrier_state_str); - - address_state_str = link_address_state_to_string(address_state); - assert(address_state_str); - - 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", - operstate_str, carrier_state_str, address_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) - goto fail; - - r = conservative_rename(temp_path, m->state_file); - if (r < 0) - goto fail; - - 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 (p) { - r = manager_send_changed_strv(m, p); - if (r < 0) - log_error_errno(r, "Could not emit changed properties: %m"); - } - - m->dirty = false; - - return 0; - -fail: - (void) unlink(m->state_file); - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file); -} - static int manager_dirty_handler(sd_event_source *s, void *userdata) { Manager *m = userdata; Link *link; @@ -1155,13 +873,6 @@ Link* manager_find_uplink(Manager *m, Link *exclude) { return NULL; } -void manager_dirty(Manager *manager) { - assert(manager); - - /* the serialized state in /run is no longer up-to-date */ - manager->dirty = true; -} - static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { const sd_bus_error *e; int r; diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index c0cd427517..929855daaf 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -100,8 +100,6 @@ bool manager_should_reload(Manager *m); int manager_enumerate(Manager *m); -void manager_dirty(Manager *m); - Link* manager_find_uplink(Manager *m, Link *exclude); int manager_set_hostname(Manager *m, const char *hostname); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index acc1de66d2..bb62ddd93f 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -15,6 +15,7 @@ #include "networkd-dhcp6.h" #include "networkd-manager.h" #include "networkd-ndisc.h" +#include "networkd-state-file.h" #include "string-table.h" #include "string-util.h" #include "strv.h" diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c new file mode 100644 index 0000000000..7cf1948d03 --- /dev/null +++ b/src/network/networkd-state-file.c @@ -0,0 +1,690 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "alloc-util.h" +#include "dns-domain.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "network-internal.h" +#include "networkd-link.h" +#include "networkd-manager-bus.h" +#include "networkd-manager.h" +#include "networkd-network.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_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) { + const char *p; + int r; + + assert(s); + assert(dns); + + if (dns->ifindex != 0 && dns->ifindex != ifindex) + return 0; + + p = in_addr_full_to_string(dns); + if (!p) + return 0; + + r = ordered_set_put_strdup(s, p); + if (r == -EEXIST) + return 0; + + return r; +} + +static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) { + int r, c = 0; + + assert(s); + assert(dns || n == 0); + + for (unsigned i = 0; i < n; i++) { + r = ordered_set_put_dns_server(s, ifindex, dns[i]); + if (r < 0) + return r; + + c += r; + } + + return c; +} + +static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address) { + char *p; + int r; + + assert(s); + assert(address); + + r = in_addr_to_string(AF_INET, (const union in_addr_union*) address, &p); + if (r < 0) + return r; + + r = ordered_set_consume(s, p); + if (r == -EEXIST) + return 0; + + return r; +} + +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, c = 0; + + assert(s); + assert(n == 0 || addresses); + + for (size_t i = 0; i < n; i++) { + if (predicate && !predicate(&addresses[i])) + continue; + r = ordered_set_put_in4_addr(s, addresses+i); + if (r < 0) + return r; + + c += r; + } + + return c; +} + +int manager_save(Manager *m) { + _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL; + const char *operstate_str, *carrier_state_str, *address_state_str; + LinkOperationalState operstate = LINK_OPERSTATE_OFF; + LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF; + LinkAddressState address_state = LINK_ADDRESS_STATE_OFF; + _cleanup_free_ char *temp_path = NULL; + _cleanup_strv_free_ char **p = NULL; + _cleanup_fclose_ FILE *f = NULL; + Link *link; + int r; + + assert(m); + assert(m->state_file); + + /* We add all NTP and DNS server to a set, to filter out duplicates */ + dns = ordered_set_new(&string_hash_ops); + if (!dns) + return -ENOMEM; + + ntp = ordered_set_new(&string_hash_ops); + if (!ntp) + return -ENOMEM; + + sip = ordered_set_new(&string_hash_ops); + if (!sip) + return -ENOMEM; + + search_domains = ordered_set_new(&dns_name_hash_ops); + if (!search_domains) + return -ENOMEM; + + route_domains = ordered_set_new(&dns_name_hash_ops); + if (!route_domains) + return -ENOMEM; + + HASHMAP_FOREACH(link, m->links) { + const struct in_addr *addresses; + + if (link->flags & IFF_LOOPBACK) + continue; + + if (link->operstate > operstate) + operstate = link->operstate; + + if (link->carrier_state > carrier_state) + carrier_state = link->carrier_state; + + if (link->address_state > address_state) + address_state = link->address_state; + + if (!link->network) + continue; + + /* First add the static configured entries */ + if (link->n_dns != (unsigned) -1) + r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns); + else + r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns); + if (r < 0) + return r; + + r = ordered_set_put_strdupv(ntp, link->ntp ?: link->network->ntp); + if (r < 0) + return r; + + r = ordered_set_put_string_set(search_domains, link->search_domains ?: link->network->search_domains); + if (r < 0) + return r; + + r = ordered_set_put_string_set(route_domains, link->route_domains ?: link->network->route_domains); + if (r < 0) + return r; + + if (!link->dhcp_lease) + continue; + + /* Secondly, add the entries acquired via DHCP */ + if (link->network->dhcp_use_dns) { + r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); + if (r > 0) { + r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local); + if (r < 0) + return r; + } else if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->dhcp_use_ntp) { + r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); + if (r > 0) { + r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local); + if (r < 0) + return r; + } else if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->dhcp_use_sip) { + r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses); + if (r > 0) { + r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local); + if (r < 0) + return r; + } else if (r < 0 && r != -ENODATA) + return r; + } + + if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { + const char *domainname; + char **domains = NULL; + + OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains; + r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); + if (r >= 0) { + r = ordered_set_put_strdup(target_domains, domainname); + if (r < 0) + return r; + } else if (r != -ENODATA) + return r; + + r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains); + if (r >= 0) { + r = ordered_set_put_strdupv(target_domains, domains); + if (r < 0) + return r; + } else if (r != -ENODATA) + return r; + } + } + + if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED) + carrier_state = LINK_CARRIER_STATE_CARRIER; + + operstate_str = link_operstate_to_string(operstate); + assert(operstate_str); + + carrier_state_str = link_carrier_state_to_string(carrier_state); + assert(carrier_state_str); + + address_state_str = link_address_state_to_string(address_state); + assert(address_state_str); + + 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", + operstate_str, carrier_state_str, address_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) + goto fail; + + r = conservative_rename(temp_path, m->state_file); + if (r < 0) + goto fail; + + 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 (p) { + r = manager_send_changed_strv(m, p); + if (r < 0) + log_error_errno(r, "Could not emit changed properties: %m"); + } + + m->dirty = false; + + return 0; + +fail: + (void) unlink(m->state_file); + (void) unlink(temp_path); + + return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file); +} + +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) { + 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 ***)) { + int r; + + bool _space = false; + 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); +} + +int link_save(Link *link) { + const char *admin_state, *oper_state, *carrier_state, *address_state; + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(link); + assert(link->state_file); + assert(link->lease_file); + assert(link->manager); + + if (link->state == LINK_STATE_LINGER) { + (void) unlink(link->state_file); + return 0; + } + + link_lldp_save(link); + + admin_state = link_state_to_string(link->state); + assert(admin_state); + + oper_state = link_operstate_to_string(link->operstate); + assert(oper_state); + + carrier_state = link_carrier_state_to_string(link->carrier_state); + assert(carrier_state); + + address_state = link_address_state_to_string(link->address_state); + assert(address_state); + + r = fopen_temporary(link->state_file, &f, &temp_path); + if (r < 0) + goto fail; + + (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", + admin_state, oper_state, carrier_state, address_state); + + if (link->network) { + char **dhcp6_domains = NULL, **dhcp_domains = NULL; + const char *dhcp_domainname = NULL, *p; + bool space; + + fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", + yes_no(link->network->required_for_online)); + + LinkOperationalStateRange st = link->network->required_operstate_for_online; + fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n", + strempty(link_operstate_to_string(st.min)), + st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "", + st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : ""); + + fprintf(f, "ACTIVATION_POLICY=%s\n", + activation_policy_to_string(link->network->activation_policy)); + + fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); + + /************************************************************/ + + fputs("DNS=", f); + space = false; + if (link->n_dns != (unsigned) -1) + link_save_dns(link, f, link->dns, link->n_dns, &space); + else + link_save_dns(link, f, link->network->dns, link->network->n_dns, &space); + + serialize_addresses(f, NULL, &space, + NULL, + link->dhcp_lease, + link->network->dhcp_use_dns, + SD_DHCP_LEASE_DNS, + link->dhcp6_lease, + link->network->dhcp6_use_dns, + sd_dhcp6_lease_get_dns, + NULL); + + /* Make sure to flush out old entries before we use the NDisc data */ + ndisc_vacuum(link); + + if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) { + NDiscRDNSS *dd; + + SET_FOREACH(dd, link->ndisc_rdnss) + serialize_in6_addrs(f, &dd->address, 1, &space); + } + + fputc('\n', f); + + /************************************************************/ + + serialize_addresses(f, "NTP", NULL, + link->ntp ?: link->network->ntp, + link->dhcp_lease, + link->network->dhcp_use_ntp, + SD_DHCP_LEASE_NTP, + link->dhcp6_lease, + link->network->dhcp6_use_ntp, + 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); + + /************************************************************/ + + if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { + if (link->dhcp_lease) { + (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); + (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains); + } + if (link->dhcp6_lease) + (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains); + } + + fputs("DOMAINS=", f); + space = false; + ORDERED_SET_FOREACH(p, link->search_domains ?: link->network->search_domains) + fputs_with_space(f, p, NULL, &space); + + if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { + if (dhcp_domainname) + fputs_with_space(f, dhcp_domainname, NULL, &space); + if (dhcp_domains) + fputstrv(f, dhcp_domains, NULL, &space); + if (dhcp6_domains) + fputstrv(f, dhcp6_domains, NULL, &space); + } + + if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_YES) { + NDiscDNSSL *dd; + + SET_FOREACH(dd, link->ndisc_dnssl) + fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); + } + + fputc('\n', f); + + /************************************************************/ + + fputs("ROUTE_DOMAINS=", f); + space = false; + ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains) + fputs_with_space(f, p, NULL, &space); + + if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { + if (dhcp_domainname) + fputs_with_space(f, dhcp_domainname, NULL, &space); + if (dhcp_domains) + fputstrv(f, dhcp_domains, NULL, &space); + if (dhcp6_domains) + fputstrv(f, dhcp6_domains, NULL, &space); + } + + if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_ROUTE) { + NDiscDNSSL *dd; + + SET_FOREACH(dd, link->ndisc_dnssl) + fputs_with_space(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space); + } + + 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; + + fputs("DNSSEC_NTA=", f); + space = false; + SET_FOREACH(n, nta_anchors) + fputs_with_space(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) + goto fail; + + 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) + goto fail; + + r = fflush_and_check(f); + if (r < 0) + goto fail; + + r = conservative_rename(temp_path, link->state_file); + if (r < 0) + goto fail; + + return 0; + +fail: + (void) unlink(link->state_file); + if (temp_path) + (void) unlink(temp_path); + + return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file); +} + +void link_dirty(Link *link) { + int r; + + assert(link); + assert(link->manager); + + /* 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(Link *link) { + int r; + + r = link_save(link); + if (r < 0) + return r; + + link_clean(link); + return 0; +} diff --git a/src/network/networkd-state-file.h b/src/network/networkd-state-file.h new file mode 100644 index 0000000000..44a109fdfd --- /dev/null +++ b/src/network/networkd-state-file.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +typedef struct Link Link; +typedef struct Manager Manager; + +void link_dirty(Link *link); +void link_clean(Link *link); +int link_save(Link *link); +int link_save_and_clean(Link *link); + +int manager_save(Manager *m); From d23a66f27476a8ae0563614116085108d8bf539a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 21 Feb 2021 15:21:12 +0900 Subject: [PATCH 2/9] network: use unlink_and_freep() cleanup functions This also makes state files not removed on failure. --- src/network/networkd-manager.c | 26 ++++++++++++++------ src/network/networkd-state-file.c | 41 +++++++++++-------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 0b1cb1e42a..e4a933ae5e 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -470,14 +470,21 @@ static int manager_connect_rtnl(Manager *m) { static int manager_dirty_handler(sd_event_source *s, void *userdata) { Manager *m = userdata; Link *link; + int r; assert(m); - if (m->dirty) - manager_save(m); + if (m->dirty) { + r = manager_save(m); + if (r < 0) + log_warning_errno(r, "Failed to update state file %s, ignoring: %m", m->state_file); + } - SET_FOREACH(link, m->dirty_links) - (void) link_save_and_clean(link); + SET_FOREACH(link, m->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); + } return 1; } @@ -647,10 +654,15 @@ int manager_start(Manager *m) { /* The dirty handler will deal with future serialization, but the first one must be done explicitly. */ - manager_save(m); + r = manager_save(m); + if (r < 0) + log_warning_errno(r, "Failed to update state file %s, ignoring: %m", m->state_file); - HASHMAP_FOREACH(link, m->links) - (void) link_save(link); + HASHMAP_FOREACH(link, m->links) { + r = link_save(link); + if (r < 0) + log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file); + } return 0; } diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c index 7cf1948d03..c09880f14d 100644 --- a/src/network/networkd-state-file.c +++ b/src/network/networkd-state-file.c @@ -103,7 +103,7 @@ int manager_save(Manager *m) { LinkOperationalState operstate = LINK_OPERSTATE_OFF; LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF; LinkAddressState address_state = LINK_ADDRESS_STATE_OFF; - _cleanup_free_ char *temp_path = NULL; + _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_strv_free_ char **p = NULL; _cleanup_fclose_ FILE *f = NULL; Link *link; @@ -261,11 +261,13 @@ int manager_save(Manager *m) { r = fflush_and_check(f); if (r < 0) - goto fail; + return r; r = conservative_rename(temp_path, m->state_file); if (r < 0) - goto fail; + return r; + + temp_path = mfree(temp_path); if (m->operational_state != operstate) { m->operational_state = operstate; @@ -288,18 +290,12 @@ int manager_save(Manager *m) { if (p) { r = manager_send_changed_strv(m, p); if (r < 0) - log_error_errno(r, "Could not emit changed properties: %m"); + log_warning_errno(r, "Could not emit changed properties, ignoring: %m"); } m->dirty = false; return 0; - -fail: - (void) unlink(m->state_file); - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save network state to %s: %m", m->state_file); } static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { @@ -394,7 +390,7 @@ static void serialize_addresses( int link_save(Link *link) { const char *admin_state, *oper_state, *carrier_state, *address_state; - _cleanup_free_ char *temp_path = NULL; + _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -403,10 +399,8 @@ int link_save(Link *link) { assert(link->lease_file); assert(link->manager); - if (link->state == LINK_STATE_LINGER) { - (void) unlink(link->state_file); + if (link->state == LINK_STATE_LINGER) return 0; - } link_lldp_save(link); @@ -424,7 +418,7 @@ int link_save(Link *link) { r = fopen_temporary(link->state_file, &f, &temp_path); if (r < 0) - goto fail; + return r; (void) fchmod(fileno(f), 0644); @@ -621,7 +615,7 @@ int link_save(Link *link) { if (link->dhcp_lease) { r = dhcp_lease_save(link->dhcp_lease, link->lease_file); if (r < 0) - goto fail; + return r; fprintf(f, "DHCP_LEASE=%s\n", @@ -631,24 +625,19 @@ int link_save(Link *link) { r = link_serialize_dhcp6_client(link, f); if (r < 0) - goto fail; + return r; r = fflush_and_check(f); if (r < 0) - goto fail; + return r; r = conservative_rename(temp_path, link->state_file); if (r < 0) - goto fail; + return r; + + temp_path = mfree(temp_path); return 0; - -fail: - (void) unlink(link->state_file); - if (temp_path) - (void) unlink(temp_path); - - return log_link_error_errno(link, r, "Failed to save link data to %s: %m", link->state_file); } void link_dirty(Link *link) { From 44e1f7e3dc29b6d5cc790a66da04d4ff806eab35 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 21 Feb 2021 15:26:15 +0900 Subject: [PATCH 3/9] network: minor style fixes --- src/network/networkd-state-file.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/network/networkd-state-file.c b/src/network/networkd-state-file.c index c09880f14d..06670a7b8b 100644 --- a/src/network/networkd-state-file.c +++ b/src/network/networkd-state-file.c @@ -75,10 +75,12 @@ static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address return r; } -static int ordered_set_put_in4_addrv(OrderedSet *s, - const struct in_addr *addresses, - size_t n, - bool (*predicate)(const struct in_addr *addr)) { +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, c = 0; assert(s); @@ -350,9 +352,10 @@ static void serialize_addresses( bool conditional6, int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**), int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) { - int r; bool _space = false; + int r; + if (!space) space = &_space; From 24e3ed843fa3f5041d743739893d8d97f967fd42 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 21 Feb 2021 15:45:00 +0900 Subject: [PATCH 4/9] network: drop unnecessary {} --- src/network/networkd-manager.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index e4a933ae5e..86432a6dc3 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -358,14 +358,13 @@ static int systemd_netlink_fd(void) { if (n <= 0) return -EINVAL; - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) { + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { if (rtnl_fd >= 0) return -EINVAL; rtnl_fd = fd; } - } return rtnl_fd; } From 3be9d62ad1e3178a7defc0f48b64bd27eeb99d77 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 21 Feb 2021 15:51:51 +0900 Subject: [PATCH 5/9] network: move manager_{rtnl,udev}_process_link() to networkd-link.[ch] --- src/network/networkd-link.c | 154 +++++++++++++++++++++++++++++++-- src/network/networkd-link.h | 7 +- src/network/networkd-manager.c | 141 ------------------------------ 3 files changed, 150 insertions(+), 152 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index a8f021fa6d..e935c897df 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -11,6 +11,8 @@ #include "bond.h" #include "bridge.h" #include "bus-util.h" +#include "device-private.h" +#include "device-util.h" #include "dhcp-identifier.h" #include "dhcp-lease-internal.h" #include "env-file.h" @@ -1734,7 +1736,7 @@ static void link_detach_from_manager(Link *link) { link_unref(link); } -void link_drop(Link *link) { +static void link_drop(Link *link) { if (!link || link->state == LINK_STATE_LINGER) return; @@ -2381,7 +2383,7 @@ static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Lin return 1; } -int link_initialized(Link *link, sd_device *device) { +static int link_initialized(Link *link, sd_device *device) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -2421,7 +2423,7 @@ int link_initialized(Link *link, sd_device *device) { return 0; } -int link_add(Manager *m, sd_netlink_message *message, Link **ret) { +static int link_add(Manager *m, sd_netlink_message *message, Link **ret) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; char ifindex_str[2 + DECIMAL_STR_MAX(int)]; Link *link; @@ -2506,6 +2508,57 @@ int link_ipv6ll_gained(Link *link, const struct in6_addr *address) { return 0; } +int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) { + sd_device_action_t action; + Manager *m = userdata; + Link *link = NULL; + int r, ifindex; + + assert(m); + assert(device); + + r = sd_device_get_action(device, &action); + if (r < 0) { + log_device_debug_errno(device, r, "Failed to get udev action, ignoring device: %m"); + return 0; + } + + /* Ignore the "remove" uevent — let's remove a device only if rtnetlink says so. All other uevents + * are "positive" events in some form, i.e. inform us about a changed or new network interface, that + * still exists — and we are interested in that. */ + if (action == SD_DEVICE_REMOVE) + return 0; + + r = sd_device_get_ifindex(device, &ifindex); + if (r < 0) { + log_device_debug_errno(device, r, "Ignoring udev %s event for device without ifindex or with invalid ifindex: %m", + device_action_to_string(action)); + return 0; + } + + r = device_is_renaming(device); + if (r < 0) { + log_device_error_errno(device, r, "Failed to determine the device is renamed or not, ignoring '%s' uevent: %m", + device_action_to_string(action)); + return 0; + } + if (r > 0) { + log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed."); + return 0; + } + + r = link_get(m, ifindex, &link); + if (r < 0) { + if (r != -ENODEV) + log_debug_errno(r, "Failed to get link from ifindex %i, ignoring: %m", ifindex); + return 0; + } + + (void) link_initialized(link, device); + + return 0; +} + static int link_carrier_gained(Link *link) { int r; @@ -2619,13 +2672,14 @@ int link_carrier_reset(Link *link) { return 0; } -/* This is called every time an interface admin state changes to up; - * specifically, when IFF_UP flag changes from unset to set */ static int link_admin_state_up(Link *link) { int r; assert(link); + /* This is called every time an interface admin state changes to up; + * specifically, when IFF_UP flag changes from unset to set. */ + if (!link->network) return 0; @@ -2649,7 +2703,6 @@ static int link_admin_state_up(Link *link) { } static int link_admin_state_down(Link *link) { - assert(link); if (!link->network) @@ -2663,7 +2716,7 @@ static int link_admin_state_down(Link *link) { return 0; } -int link_update(Link *link, sd_netlink_message *m) { +static int link_update(Link *link, sd_netlink_message *m) { _cleanup_strv_free_ char **s = NULL; hw_addr_data hw_addr; const char *ifname; @@ -2807,6 +2860,93 @@ int link_update(Link *link, sd_netlink_message *m) { return 0; } +int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + Link *link = NULL; + NetDev *netdev = NULL; + uint16_t type; + const char *name; + int r, ifindex; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: Could not receive link message, ignoring"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) { + log_warning("rtnl: Received unexpected message type %u when processing link, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_link_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: Could not get ifindex from link message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received link message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } + + r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name); + if (r < 0) { + log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m"); + return 0; + } + + (void) link_get(m, ifindex, &link); + (void) netdev_get(m, name, &netdev); + + switch (type) { + case RTM_NEWLINK: + if (!link) { + /* link is new, so add it */ + r = link_add(m, message, &link); + if (r < 0) { + log_warning_errno(r, "Could not process new link message, ignoring: %m"); + return 0; + } + } + + if (netdev) { + /* netdev exists, so make sure the ifindex matches */ + r = netdev_set_ifindex(netdev, message); + if (r < 0) { + log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m"); + return 0; + } + } + + r = link_update(link, message); + if (r < 0) { + log_warning_errno(r, "Could not process link message, ignoring: %m"); + return 0; + } + + break; + + case RTM_DELLINK: + link_drop(link); + netdev_drop(netdev); + + break; + + default: + assert_not_reached("Received link message with invalid RTNL message type."); + } + + return 1; +} + static const char* const link_state_table[_LINK_STATE_MAX] = { [LINK_STATE_PENDING] = "pending", [LINK_STATE_INITIALIZED] = "initialized", diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 347da1c928..d3353a1c4f 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -203,19 +203,15 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); DEFINE_TRIVIAL_DESTRUCTOR(link_netlink_destroy_callback, Link, link_unref); int link_get(Manager *m, int ifindex, Link **ret); -int link_add(Manager *manager, sd_netlink_message *message, Link **ret); -void link_drop(Link *link); int link_down(Link *link, link_netlink_message_handler_t callback); void link_enter_failed(Link *link); -int link_initialized(Link *link, sd_device *device); void link_set_state(Link *link, LinkState state); void link_check_ready(Link *link); void link_update_operstate(Link *link, bool also_update_bond_master); -int link_update(Link *link, sd_netlink_message *message); int link_carrier_reset(Link *link); bool link_has_carrier(Link *link); @@ -236,6 +232,9 @@ LinkState link_state_from_string(const char *s) _pure_; int link_configure(Link *link); int link_reconfigure(Link *link, bool force); +int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata); +int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); + int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg); #define log_link_message_error_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_ERR, err, msg) #define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg) diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 86432a6dc3..bfdb1f8c96 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -17,8 +17,6 @@ #include "bus-util.h" #include "conf-parser.h" #include "def.h" -#include "device-private.h" -#include "device-util.h" #include "dns-domain.h" #include "fd-util.h" #include "fileio.h" @@ -49,7 +47,6 @@ #include "strv.h" #include "sysctl-util.h" #include "tmpfile-util.h" -#include "udev-util.h" /* use 128 MB for receive socket kernel queue. */ #define RCVBUF_SIZE (128*1024*1024) @@ -182,57 +179,6 @@ int manager_connect_bus(Manager *m) { return 0; } -static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) { - sd_device_action_t action; - Manager *m = userdata; - Link *link = NULL; - int r, ifindex; - - assert(m); - assert(device); - - r = sd_device_get_action(device, &action); - if (r < 0) { - log_device_debug_errno(device, r, "Failed to get udev action, ignoring device: %m"); - return 0; - } - - /* Ignore the "remove" uevent — let's remove a device only if rtnetlink says so. All other uevents - * are "positive" events in some form, i.e. inform us about a changed or new network interface, that - * still exists — and we are interested in that. */ - if (action == SD_DEVICE_REMOVE) - return 0; - - r = sd_device_get_ifindex(device, &ifindex); - if (r < 0) { - log_device_debug_errno(device, r, "Ignoring udev %s event for device without ifindex or with invalid ifindex: %m", - device_action_to_string(action)); - return 0; - } - - r = device_is_renaming(device); - if (r < 0) { - log_device_error_errno(device, r, "Failed to determine the device is renamed or not, ignoring '%s' uevent: %m", - device_action_to_string(action)); - return 0; - } - if (r > 0) { - log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed."); - return 0; - } - - r = link_get(m, ifindex, &link); - if (r < 0) { - if (r != -ENODEV) - log_debug_errno(r, "Failed to get link from ifindex %i, ignoring: %m", ifindex); - return 0; - } - - (void) link_initialized(link, device); - - return 0; -} - static int manager_connect_udev(Manager *m) { int r; @@ -264,93 +210,6 @@ static int manager_connect_udev(Manager *m) { return 0; } -static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { - Link *link = NULL; - NetDev *netdev = NULL; - uint16_t type; - const char *name; - int r, ifindex; - - assert(rtnl); - assert(message); - assert(m); - - if (sd_netlink_message_is_error(message)) { - r = sd_netlink_message_get_errno(message); - if (r < 0) - log_message_warning_errno(message, r, "rtnl: Could not receive link message, ignoring"); - - return 0; - } - - r = sd_netlink_message_get_type(message, &type); - if (r < 0) { - log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m"); - return 0; - } else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) { - log_warning("rtnl: Received unexpected message type %u when processing link, ignoring.", type); - return 0; - } - - r = sd_rtnl_message_link_get_ifindex(message, &ifindex); - if (r < 0) { - log_warning_errno(r, "rtnl: Could not get ifindex from link message, ignoring: %m"); - return 0; - } else if (ifindex <= 0) { - log_warning("rtnl: received link message with invalid ifindex %d, ignoring.", ifindex); - return 0; - } - - r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name); - if (r < 0) { - log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m"); - return 0; - } - - (void) link_get(m, ifindex, &link); - (void) netdev_get(m, name, &netdev); - - switch (type) { - case RTM_NEWLINK: - if (!link) { - /* link is new, so add it */ - r = link_add(m, message, &link); - if (r < 0) { - log_warning_errno(r, "Could not process new link message, ignoring: %m"); - return 0; - } - } - - if (netdev) { - /* netdev exists, so make sure the ifindex matches */ - r = netdev_set_ifindex(netdev, message); - if (r < 0) { - log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m"); - return 0; - } - } - - r = link_update(link, message); - if (r < 0) { - log_warning_errno(r, "Could not process link message, ignoring: %m"); - return 0; - } - - break; - - case RTM_DELLINK: - link_drop(link); - netdev_drop(netdev); - - break; - - default: - assert_not_reached("Received link message with invalid RTNL message type."); - } - - return 1; -} - static int systemd_netlink_fd(void) { int n, fd, rtnl_fd = -EINVAL; From ab7153b3f4f47d1c9763ba406c64abbf61a50cac Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Mar 2021 16:25:54 +0900 Subject: [PATCH 6/9] dhcp: use unlink_and_freep() in dhcp_lease_save() --- src/libsystemd-network/sd-dhcp-lease.c | 31 +++++++++----------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 1c0b431165..9fcbe17006 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -869,7 +869,7 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { } int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { - _cleanup_free_ char *temp_path = NULL; + _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; struct sd_dhcp_raw_option *option; struct in_addr address; @@ -889,7 +889,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = fopen_temporary(lease_file, &f, &temp_path); if (r < 0) - goto fail; + return r; (void) fchmod(fileno(f), 0644); @@ -992,10 +992,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_free_ char *client_id_hex = NULL; client_id_hex = hexmem(client_id, client_id_len); - if (!client_id_hex) { - r = -ENOMEM; - goto fail; - } + if (!client_id_hex) + return -ENOMEM; fprintf(f, "CLIENTID=%s\n", client_id_hex); } @@ -1004,10 +1002,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_free_ char *option_hex = NULL; option_hex = hexmem(data, data_len); - if (!option_hex) { - r = -ENOMEM; - goto fail; - } + if (!option_hex) + return -ENOMEM; fprintf(f, "VENDOR_SPECIFIC=%s\n", option_hex); } @@ -1017,28 +1013,23 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { xsprintf(key, "OPTION_%" PRIu8, option->tag); r = serialize_dhcp_option(f, key, option->data, option->length); if (r < 0) - goto fail; + return r; } r = fflush_and_check(f); if (r < 0) - goto fail; + return r; r = conservative_rename(temp_path, lease_file); if (r < 0) - goto fail; + return r; + + temp_path = mfree(temp_path); return 0; - -fail: - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save lease data %s: %m", lease_file); } int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { - _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_free_ char *address = NULL, From a34e58d445b9dfa553bbc363edcda290a17e0634 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Mar 2021 16:31:28 +0900 Subject: [PATCH 7/9] network: remove DHCP lease and LLDP state file on link_free() --- src/network/networkd-link.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index e935c897df..9693e73351 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -546,8 +546,6 @@ static Link *link_free(Link *link) { link->ndisc_addresses = set_free(link->ndisc_addresses); link_free_engines(link); - free(link->lease_file); - free(link->lldp_file); free(link->ifname); strv_free(link->alternative_names); @@ -555,8 +553,9 @@ static Link *link_free(Link *link) { free(link->ssid); free(link->driver); - (void) unlink(link->state_file); - free(link->state_file); + unlink_and_free(link->lease_file); + unlink_and_free(link->lldp_file); + unlink_and_free(link->state_file); sd_device_unref(link->sd_device); From 5288861bf6c167e12a08ecd1ec56be51788cfc84 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Mar 2021 16:34:51 +0900 Subject: [PATCH 8/9] network: use conservative_rename() at one more place --- src/network/networkd-lldp-rx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c index 76ff3689e1..068adca571 100644 --- a/src/network/networkd-lldp-rx.c +++ b/src/network/networkd-lldp-rx.c @@ -6,6 +6,7 @@ #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "networkd-link.h" #include "networkd-lldp-rx.h" #include "networkd-lldp-tx.h" @@ -181,10 +182,9 @@ int link_lldp_save(Link *link) { if (r < 0) goto finish; - if (rename(temp_path, link->lldp_file) < 0) { - r = -errno; + r = conservative_rename(temp_path, link->lldp_file); + if (r < 0) goto finish; - } finish: if (r < 0) { From 66d233026526215f27372aaca4ccd56186762d86 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Mar 2021 16:42:18 +0900 Subject: [PATCH 9/9] network: do not remove LLDP state file on failure --- src/network/networkd-lldp-rx.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/network/networkd-lldp-rx.c b/src/network/networkd-lldp-rx.c index 068adca571..bf1dd045b8 100644 --- a/src/network/networkd-lldp-rx.c +++ b/src/network/networkd-lldp-rx.c @@ -135,7 +135,7 @@ int link_update_lldp(Link *link) { } int link_lldp_save(Link *link) { - _cleanup_free_ char *temp_path = NULL; + _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; sd_lldp_neighbor **l = NULL; int n = 0, r, i; @@ -150,10 +150,10 @@ int link_lldp_save(Link *link) { r = sd_lldp_get_neighbors(link->lldp, &l); if (r < 0) - goto finish; + return r; if (r == 0) { (void) unlink(link->lldp_file); - goto finish; + return 0; } n = r; @@ -187,13 +187,8 @@ int link_lldp_save(Link *link) { goto finish; finish: - if (r < 0) { - (void) unlink(link->lldp_file); - if (temp_path) - (void) unlink(temp_path); - + if (r < 0) log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); - } if (l) { for (i = 0; i < n; i++)