1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-24 21:34:08 +03:00

network: move state file related functions to networkd-state-file.[ch]

This commit is contained in:
Yu Watanabe 2021-02-21 14:23:04 +09:00
parent 725ad3b062
commit 3b5a4fc685
10 changed files with 709 additions and 681 deletions

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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",

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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"

View File

@ -0,0 +1,690 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <netinet/in.h>
#include <linux/if.h>
#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;
}

View File

@ -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);