mirror of
https://github.com/morgan9e/systemd
synced 2026-04-15 08:56:15 +09:00
E.g. a bonding port does not support addressing, hence the default
should be 'enslaved'.
Follow-up for 3255bda698.
Closes #27724.
887 lines
30 KiB
C
887 lines
30 KiB
C
/* 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 "escape.h"
|
|
#include "fd-util.h"
|
|
#include "fileio.h"
|
|
#include "fs-util.h"
|
|
#include "network-internal.h"
|
|
#include "networkd-dhcp-common.h"
|
|
#include "networkd-link.h"
|
|
#include "networkd-manager-bus.h"
|
|
#include "networkd-manager.h"
|
|
#include "networkd-network.h"
|
|
#include "networkd-state-file.h"
|
|
#include "ordered-set.h"
|
|
#include "set.h"
|
|
#include "strv.h"
|
|
#include "tmpfile-util.h"
|
|
|
|
static int ordered_set_put_dns_servers(OrderedSet **s, int ifindex, struct in_addr_full **dns, unsigned n) {
|
|
int r;
|
|
|
|
assert(s);
|
|
assert(dns || n == 0);
|
|
|
|
FOREACH_ARRAY(a, dns, n) {
|
|
const char *p;
|
|
|
|
if ((*a)->ifindex != 0 && (*a)->ifindex != ifindex)
|
|
return 0;
|
|
|
|
p = in_addr_full_to_string(*a);
|
|
if (!p)
|
|
return 0;
|
|
|
|
r = ordered_set_put_strdup(s, p);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ordered_set_put_in4_addrv(
|
|
OrderedSet **s,
|
|
const struct in_addr *addresses,
|
|
size_t n,
|
|
bool (*predicate)(const struct in_addr *addr)) {
|
|
|
|
int r;
|
|
|
|
assert(s);
|
|
assert(n == 0 || addresses);
|
|
|
|
FOREACH_ARRAY(a, addresses, n) {
|
|
if (predicate && !predicate(a))
|
|
continue;
|
|
|
|
r = ordered_set_put_strdup(s, IN4_ADDR_TO_STRING(a));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ordered_set_put_in6_addrv(
|
|
OrderedSet **s,
|
|
const struct in6_addr *addresses,
|
|
size_t n) {
|
|
|
|
int r;
|
|
|
|
assert(s);
|
|
assert(n == 0 || addresses);
|
|
|
|
FOREACH_ARRAY(a, addresses, n) {
|
|
r = ordered_set_put_strdup(s, IN6_ADDR_TO_STRING(a));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int link_put_dns(Link *link, OrderedSet **s) {
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(link->network);
|
|
assert(s);
|
|
|
|
if (link->n_dns != UINT_MAX)
|
|
return ordered_set_put_dns_servers(s, link->ifindex, link->dns, link->n_dns);
|
|
|
|
r = ordered_set_put_dns_servers(s, link->ifindex, link->network->dns, link->network->n_dns);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (link->dhcp_lease && link->network->dhcp_use_dns) {
|
|
const struct in_addr *addresses;
|
|
|
|
r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (link->dhcp6_lease && link->network->dhcp6_use_dns) {
|
|
const struct in6_addr *addresses;
|
|
|
|
r = sd_dhcp6_lease_get_dns(link->dhcp6_lease, &addresses);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_in6_addrv(s, addresses, r);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (link->network->ipv6_accept_ra_use_dns) {
|
|
NDiscRDNSS *a;
|
|
|
|
SET_FOREACH(a, link->ndisc_rdnss) {
|
|
r = ordered_set_put_in6_addrv(s, &a->router, 1);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int link_put_ntp(Link *link, OrderedSet **s) {
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(link->network);
|
|
assert(s);
|
|
|
|
if (link->ntp)
|
|
return ordered_set_put_strdupv(s, link->ntp);
|
|
|
|
r = ordered_set_put_strdupv(s, link->network->ntp);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (link->dhcp_lease && link->network->dhcp_use_ntp) {
|
|
const struct in_addr *addresses;
|
|
|
|
r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (link->dhcp6_lease && link->network->dhcp6_use_ntp) {
|
|
const struct in6_addr *addresses;
|
|
char **fqdn;
|
|
|
|
r = sd_dhcp6_lease_get_ntp_addrs(link->dhcp6_lease, &addresses);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_in6_addrv(s, addresses, r);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = sd_dhcp6_lease_get_ntp_fqdn(link->dhcp6_lease, &fqdn);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_strdupv(s, fqdn);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int link_put_sip(Link *link, OrderedSet **s) {
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(link->network);
|
|
assert(s);
|
|
|
|
if (link->dhcp_lease && link->network->dhcp_use_ntp) {
|
|
const struct in_addr *addresses;
|
|
|
|
r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_in4_addrv(s, addresses, r, in4_addr_is_non_local);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int link_put_domains(Link *link, bool is_route, OrderedSet **s) {
|
|
OrderedSet *link_domains, *network_domains;
|
|
DHCPUseDomains use_domains;
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(link->network);
|
|
assert(s);
|
|
|
|
link_domains = is_route ? link->route_domains : link->search_domains;
|
|
network_domains = is_route ? link->network->route_domains : link->network->search_domains;
|
|
use_domains = is_route ? DHCP_USE_DOMAINS_ROUTE : DHCP_USE_DOMAINS_YES;
|
|
|
|
if (link_domains)
|
|
return ordered_set_put_string_set(s, link_domains);
|
|
|
|
r = ordered_set_put_string_set(s, network_domains);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (link->dhcp_lease && link->network->dhcp_use_domains == use_domains) {
|
|
const char *domainname;
|
|
char **domains;
|
|
|
|
r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_strdup(s, domainname);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_strdupv(s, domains);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (link->dhcp6_lease && link->network->dhcp6_use_domains == use_domains) {
|
|
char **domains;
|
|
|
|
r = sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains);
|
|
if (r >= 0) {
|
|
r = ordered_set_put_strdupv(s, domains);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
if (link->network->ipv6_accept_ra_use_domains == use_domains) {
|
|
NDiscDNSSL *a;
|
|
|
|
SET_FOREACH(a, link->ndisc_dnssl) {
|
|
r = ordered_set_put_strdup(s, NDISC_DNSSL_DOMAIN(a));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int manager_save(Manager *m) {
|
|
_cleanup_ordered_set_free_ OrderedSet *dns = NULL, *ntp = NULL, *sip = NULL, *search_domains = NULL, *route_domains = NULL;
|
|
const char *operstate_str, *carrier_state_str, *address_state_str, *ipv4_address_state_str, *ipv6_address_state_str, *online_state_str;
|
|
LinkOperationalState operstate = LINK_OPERSTATE_OFF;
|
|
LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
|
|
LinkAddressState ipv4_address_state = LINK_ADDRESS_STATE_OFF, ipv6_address_state = LINK_ADDRESS_STATE_OFF,
|
|
address_state = LINK_ADDRESS_STATE_OFF;
|
|
LinkOnlineState online_state;
|
|
size_t links_offline = 0, links_online = 0;
|
|
_cleanup_(unlink_and_freep) char *temp_path = NULL;
|
|
_cleanup_strv_free_ char **p = NULL;
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
Link *link;
|
|
int r;
|
|
|
|
assert(m);
|
|
|
|
if (isempty(m->state_file))
|
|
return 0; /* Do not update state file when running in test mode. */
|
|
|
|
HASHMAP_FOREACH(link, m->links_by_index) {
|
|
if (link->flags & IFF_LOOPBACK)
|
|
continue;
|
|
|
|
operstate = MAX(operstate, link->operstate);
|
|
carrier_state = MAX(carrier_state, link->carrier_state);
|
|
address_state = MAX(address_state, link->address_state);
|
|
ipv4_address_state = MAX(ipv4_address_state, link->ipv4_address_state);
|
|
ipv6_address_state = MAX(ipv6_address_state, link->ipv6_address_state);
|
|
|
|
if (!link->network)
|
|
continue;
|
|
|
|
if (link->network->required_for_online) {
|
|
if (link->online_state == LINK_ONLINE_STATE_OFFLINE)
|
|
links_offline++;
|
|
else if (link->online_state == LINK_ONLINE_STATE_ONLINE)
|
|
links_online++;
|
|
}
|
|
|
|
r = link_put_dns(link, &dns);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = link_put_ntp(link, &ntp);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = link_put_sip(link, &sip);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = link_put_domains(link, /* is_route = */ false, &search_domains);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = link_put_domains(link, /* is_route = */ true, &route_domains);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED)
|
|
carrier_state = LINK_CARRIER_STATE_CARRIER;
|
|
|
|
online_state = links_online > 0 ?
|
|
(links_offline > 0 ? LINK_ONLINE_STATE_PARTIAL : LINK_ONLINE_STATE_ONLINE) :
|
|
(links_offline > 0 ? LINK_ONLINE_STATE_OFFLINE : _LINK_ONLINE_STATE_INVALID);
|
|
|
|
operstate_str = 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);
|
|
|
|
ipv4_address_state_str = link_address_state_to_string(ipv4_address_state);
|
|
assert(ipv4_address_state_str);
|
|
|
|
ipv6_address_state_str = link_address_state_to_string(ipv6_address_state);
|
|
assert(ipv6_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"
|
|
"IPV4_ADDRESS_STATE=%s\n"
|
|
"IPV6_ADDRESS_STATE=%s\n",
|
|
operstate_str, carrier_state_str, address_state_str, ipv4_address_state_str, ipv6_address_state_str);
|
|
|
|
online_state_str = link_online_state_to_string(online_state);
|
|
if (online_state_str)
|
|
fprintf(f, "ONLINE_STATE=%s\n", online_state_str);
|
|
|
|
ordered_set_print(f, "DNS=", dns);
|
|
ordered_set_print(f, "NTP=", ntp);
|
|
ordered_set_print(f, "SIP=", sip);
|
|
ordered_set_print(f, "DOMAINS=", search_domains);
|
|
ordered_set_print(f, "ROUTE_DOMAINS=", route_domains);
|
|
|
|
r = fflush_and_check(f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = conservative_rename(temp_path, m->state_file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
temp_path = mfree(temp_path);
|
|
|
|
if (m->operational_state != operstate) {
|
|
m->operational_state = operstate;
|
|
if (strv_extend(&p, "OperationalState") < 0)
|
|
log_oom();
|
|
}
|
|
|
|
if (m->carrier_state != carrier_state) {
|
|
m->carrier_state = carrier_state;
|
|
if (strv_extend(&p, "CarrierState") < 0)
|
|
log_oom();
|
|
}
|
|
|
|
if (m->address_state != address_state) {
|
|
m->address_state = address_state;
|
|
if (strv_extend(&p, "AddressState") < 0)
|
|
log_oom();
|
|
}
|
|
|
|
if (m->ipv4_address_state != ipv4_address_state) {
|
|
m->ipv4_address_state = ipv4_address_state;
|
|
if (strv_extend(&p, "IPv4AddressState") < 0)
|
|
log_oom();
|
|
}
|
|
|
|
if (m->ipv6_address_state != ipv6_address_state) {
|
|
m->ipv6_address_state = ipv6_address_state;
|
|
if (strv_extend(&p, "IPv6AddressState") < 0)
|
|
log_oom();
|
|
}
|
|
|
|
if (m->online_state != online_state) {
|
|
m->online_state = online_state;
|
|
if (strv_extend(&p, "OnlineState") < 0)
|
|
log_oom();
|
|
}
|
|
|
|
if (p) {
|
|
r = manager_send_changed_strv(m, p);
|
|
if (r < 0)
|
|
log_warning_errno(r, "Could not emit changed properties, ignoring: %m");
|
|
}
|
|
|
|
m->dirty = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
|
|
bool space = false;
|
|
Link *link;
|
|
|
|
assert(f);
|
|
assert(prefix);
|
|
|
|
if (hashmap_isempty(h))
|
|
return;
|
|
|
|
fputs(prefix, f);
|
|
HASHMAP_FOREACH(link, h) {
|
|
if (space)
|
|
fputc(' ', f);
|
|
|
|
fprintf(f, "%i", link->ifindex);
|
|
space = true;
|
|
}
|
|
|
|
fputc('\n', f);
|
|
}
|
|
|
|
static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
|
|
bool _space = false;
|
|
|
|
if (!space)
|
|
space = &_space;
|
|
|
|
for (unsigned j = 0; j < n_dns; j++) {
|
|
const char *str;
|
|
|
|
if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
|
|
continue;
|
|
|
|
str = in_addr_full_to_string(dns[j]);
|
|
if (!str)
|
|
continue;
|
|
|
|
if (*space)
|
|
fputc(' ', f);
|
|
fputs(str, f);
|
|
*space = true;
|
|
}
|
|
}
|
|
|
|
static void serialize_addresses(
|
|
FILE *f,
|
|
const char *lvalue,
|
|
bool *space,
|
|
char **addresses,
|
|
sd_dhcp_lease *lease,
|
|
bool conditional,
|
|
sd_dhcp_lease_server_type_t what,
|
|
sd_dhcp6_lease *lease6,
|
|
bool conditional6,
|
|
int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**),
|
|
int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) {
|
|
|
|
bool _space = false;
|
|
int r;
|
|
|
|
if (!space)
|
|
space = &_space;
|
|
|
|
if (lvalue)
|
|
fprintf(f, "%s=", lvalue);
|
|
fputstrv(f, addresses, NULL, space);
|
|
|
|
if (lease && conditional) {
|
|
const struct in_addr *lease_addresses;
|
|
|
|
r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses);
|
|
if (r > 0)
|
|
serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local);
|
|
}
|
|
|
|
if (lease6 && conditional6 && lease6_get_addr) {
|
|
const struct in6_addr *in6_addrs;
|
|
|
|
r = lease6_get_addr(lease6, &in6_addrs);
|
|
if (r > 0)
|
|
serialize_in6_addrs(f, in6_addrs, r, space);
|
|
}
|
|
|
|
if (lease6 && conditional6 && lease6_get_fqdn) {
|
|
char **in6_hosts;
|
|
|
|
r = lease6_get_fqdn(lease6, &in6_hosts);
|
|
if (r > 0)
|
|
fputstrv(f, in6_hosts, NULL, space);
|
|
}
|
|
|
|
if (lvalue)
|
|
fputc('\n', f);
|
|
}
|
|
|
|
static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, DHCPUseDomains use_domains) {
|
|
bool space = false;
|
|
const char *p;
|
|
|
|
assert(link);
|
|
assert(link->network);
|
|
assert(f);
|
|
|
|
ORDERED_SET_FOREACH(p, static_domains)
|
|
fputs_with_separator(f, p, NULL, &space);
|
|
|
|
if (use_domains == DHCP_USE_DOMAINS_NO)
|
|
return;
|
|
|
|
if (link->dhcp_lease && link->network->dhcp_use_domains == use_domains) {
|
|
const char *domainname;
|
|
char **domains;
|
|
|
|
if (sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname) >= 0)
|
|
fputs_with_separator(f, domainname, NULL, &space);
|
|
if (sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains) >= 0)
|
|
fputstrv(f, domains, NULL, &space);
|
|
}
|
|
|
|
if (link->dhcp6_lease && link->network->dhcp6_use_domains == use_domains) {
|
|
char **domains;
|
|
|
|
if (sd_dhcp6_lease_get_domains(link->dhcp6_lease, &domains) >= 0)
|
|
fputstrv(f, domains, NULL, &space);
|
|
}
|
|
|
|
if (link->network->ipv6_accept_ra_use_domains == use_domains) {
|
|
NDiscDNSSL *dd;
|
|
|
|
SET_FOREACH(dd, link->ndisc_dnssl)
|
|
fputs_with_separator(f, NDISC_DNSSL_DOMAIN(dd), NULL, &space);
|
|
}
|
|
}
|
|
|
|
static int link_save(Link *link) {
|
|
const char *admin_state, *oper_state, *carrier_state, *address_state, *ipv4_address_state, *ipv6_address_state,
|
|
*captive_portal;
|
|
_cleanup_(unlink_and_freep) char *temp_path = NULL;
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(link->manager);
|
|
|
|
if (isempty(link->state_file))
|
|
return 0; /* Do not update state files when running in test mode. */
|
|
|
|
if (link->state == LINK_STATE_LINGER)
|
|
return 0;
|
|
|
|
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);
|
|
|
|
ipv4_address_state = link_address_state_to_string(link->ipv4_address_state);
|
|
assert(ipv4_address_state);
|
|
|
|
ipv6_address_state = link_address_state_to_string(link->ipv6_address_state);
|
|
assert(ipv6_address_state);
|
|
|
|
r = fopen_temporary(link->state_file, &f, &temp_path);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
(void) fchmod(fileno(f), 0644);
|
|
|
|
fprintf(f,
|
|
"# This is private data. Do not parse.\n"
|
|
"ADMIN_STATE=%s\n"
|
|
"OPER_STATE=%s\n"
|
|
"CARRIER_STATE=%s\n"
|
|
"ADDRESS_STATE=%s\n"
|
|
"IPV4_ADDRESS_STATE=%s\n"
|
|
"IPV6_ADDRESS_STATE=%s\n",
|
|
admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
|
|
|
|
if (link->network) {
|
|
const char *online_state;
|
|
bool space = false;
|
|
|
|
online_state = link_online_state_to_string(link->online_state);
|
|
if (online_state)
|
|
fprintf(f, "ONLINE_STATE=%s\n", online_state);
|
|
|
|
fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
|
|
yes_no(link->network->required_for_online));
|
|
|
|
LinkOperationalStateRange st;
|
|
link_required_operstate_for_online(link, &st);
|
|
|
|
fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s:%s\n",
|
|
link_operstate_to_string(st.min), link_operstate_to_string(st.max));
|
|
|
|
fprintf(f, "REQUIRED_FAMILY_FOR_ONLINE=%s\n",
|
|
link_required_address_family_to_string(link_required_family_for_online(link)));
|
|
|
|
fprintf(f, "ACTIVATION_POLICY=%s\n",
|
|
activation_policy_to_string(link->network->activation_policy));
|
|
|
|
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
|
|
|
|
fputs("NETWORK_FILE_DROPINS=\"", f);
|
|
STRV_FOREACH(d, link->network->dropins) {
|
|
_cleanup_free_ char *escaped = NULL;
|
|
|
|
escaped = xescape(*d, ":");
|
|
if (!escaped)
|
|
return -ENOMEM;
|
|
|
|
fputs_with_separator(f, escaped, ":", &space);
|
|
}
|
|
fputs("\"\n", f);
|
|
|
|
/************************************************************/
|
|
|
|
fputs("DNS=", f);
|
|
if (link->n_dns != UINT_MAX)
|
|
link_save_dns(link, f, link->dns, link->n_dns, NULL);
|
|
else {
|
|
space = false;
|
|
link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
|
|
|
|
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);
|
|
|
|
if (link->network->ipv6_accept_ra_use_dns) {
|
|
NDiscRDNSS *dd;
|
|
|
|
SET_FOREACH(dd, link->ndisc_rdnss)
|
|
serialize_in6_addrs(f, &dd->address, 1, &space);
|
|
}
|
|
}
|
|
|
|
fputc('\n', f);
|
|
|
|
/************************************************************/
|
|
|
|
if (link->ntp) {
|
|
fputs("NTP=", f);
|
|
fputstrv(f, link->ntp, NULL, NULL);
|
|
fputc('\n', f);
|
|
} else
|
|
serialize_addresses(f, "NTP", NULL,
|
|
link->network->ntp,
|
|
link->dhcp_lease,
|
|
link->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);
|
|
|
|
/************************************************************/
|
|
|
|
r = link_get_captive_portal(link, &captive_portal);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (captive_portal)
|
|
fprintf(f, "CAPTIVE_PORTAL=%s\n", captive_portal);
|
|
|
|
/************************************************************/
|
|
|
|
fputs("DOMAINS=", f);
|
|
if (link->search_domains)
|
|
link_save_domains(link, f, link->search_domains, DHCP_USE_DOMAINS_NO);
|
|
else
|
|
link_save_domains(link, f, link->network->search_domains, DHCP_USE_DOMAINS_YES);
|
|
fputc('\n', f);
|
|
|
|
/************************************************************/
|
|
|
|
fputs("ROUTE_DOMAINS=", f);
|
|
if (link->route_domains)
|
|
link_save_domains(link, f, link->route_domains, DHCP_USE_DOMAINS_NO);
|
|
else
|
|
link_save_domains(link, f, link->network->route_domains, DHCP_USE_DOMAINS_ROUTE);
|
|
fputc('\n', f);
|
|
|
|
/************************************************************/
|
|
|
|
fprintf(f, "LLMNR=%s\n",
|
|
resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
|
|
|
|
/************************************************************/
|
|
|
|
fprintf(f, "MDNS=%s\n",
|
|
resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
|
|
|
|
/************************************************************/
|
|
|
|
int dns_default_route =
|
|
link->dns_default_route >= 0 ? link->dns_default_route :
|
|
link->network->dns_default_route;
|
|
if (dns_default_route >= 0)
|
|
fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route));
|
|
|
|
/************************************************************/
|
|
|
|
DnsOverTlsMode dns_over_tls_mode =
|
|
link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode :
|
|
link->network->dns_over_tls_mode;
|
|
if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
|
|
fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode));
|
|
|
|
/************************************************************/
|
|
|
|
DnssecMode dnssec_mode =
|
|
link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode :
|
|
link->network->dnssec_mode;
|
|
if (dnssec_mode != _DNSSEC_MODE_INVALID)
|
|
fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode));
|
|
|
|
/************************************************************/
|
|
|
|
Set *nta_anchors = link->dnssec_negative_trust_anchors;
|
|
if (set_isempty(nta_anchors))
|
|
nta_anchors = link->network->dnssec_negative_trust_anchors;
|
|
|
|
if (!set_isempty(nta_anchors)) {
|
|
const char *n;
|
|
|
|
fputs("DNSSEC_NTA=", f);
|
|
space = false;
|
|
SET_FOREACH(n, nta_anchors)
|
|
fputs_with_separator(f, n, NULL, &space);
|
|
fputc('\n', f);
|
|
}
|
|
}
|
|
|
|
print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links);
|
|
print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links);
|
|
|
|
if (link->dhcp_lease) {
|
|
r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
fprintf(f,
|
|
"DHCP_LEASE=%s\n",
|
|
link->lease_file);
|
|
} else
|
|
(void) unlink(link->lease_file);
|
|
|
|
r = link_serialize_dhcp6_client(link, f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = fflush_and_check(f);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = conservative_rename(temp_path, link->state_file);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
temp_path = mfree(temp_path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void link_dirty(Link *link) {
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(link->manager);
|
|
|
|
/* The serialized state in /run is no longer up-to-date. */
|
|
|
|
/* Also mark manager dirty as link is dirty */
|
|
link->manager->dirty = true;
|
|
|
|
r = set_ensure_put(&link->manager->dirty_links, NULL, link);
|
|
if (r <= 0)
|
|
/* Ignore allocation errors and don't take another ref if the link was already dirty */
|
|
return;
|
|
link_ref(link);
|
|
}
|
|
|
|
void link_clean(Link *link) {
|
|
assert(link);
|
|
assert(link->manager);
|
|
|
|
/* The serialized state in /run is up-to-date */
|
|
|
|
link_unref(set_remove(link->manager->dirty_links, link));
|
|
}
|
|
|
|
int link_save_and_clean_full(Link *link, bool also_save_manager) {
|
|
int r, k = 0;
|
|
|
|
assert(link);
|
|
assert(link->manager);
|
|
|
|
if (also_save_manager)
|
|
k = manager_save(link->manager);
|
|
|
|
r = link_save(link);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
link_clean(link);
|
|
return k;
|
|
}
|
|
|
|
int manager_clean_all(Manager *manager) {
|
|
int r, ret = 0;
|
|
|
|
assert(manager);
|
|
|
|
if (manager->dirty) {
|
|
r = manager_save(manager);
|
|
if (r < 0)
|
|
log_warning_errno(r, "Failed to update state file %s, ignoring: %m", manager->state_file);
|
|
RET_GATHER(ret, r);
|
|
}
|
|
|
|
Link *link;
|
|
SET_FOREACH(link, manager->dirty_links) {
|
|
r = link_save_and_clean(link);
|
|
if (r < 0)
|
|
log_link_warning_errno(link, r, "Failed to update link state file %s, ignoring: %m", link->state_file);
|
|
RET_GATHER(ret, r);
|
|
}
|
|
|
|
return ret;
|
|
}
|