1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-10 05:18:17 +03:00

network: serialize and deserialize current configuration (#34989)

Replaces #34963.

Fixes #26602.
Fixes #32569.
This commit is contained in:
Yu Watanabe 2024-11-14 11:59:44 +09:00 committed by GitHub
commit bdc6edbdab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 693 additions and 146 deletions

View File

@ -1286,21 +1286,21 @@ DuplicateAddressDetection=none</programlisting></para>
<varlistentry>
<term><varname>KeepConfiguration=</varname></term>
<listitem>
<para>Takes a boolean or one of <literal>static</literal>, <literal>dhcp-on-stop</literal>,
<literal>dhcp</literal>. When <literal>static</literal>, <command>systemd-networkd</command>
will not drop static addresses and routes on starting up process. When set to
<literal>dhcp-on-stop</literal>, <command>systemd-networkd</command> will not drop addresses
and routes on stopping the daemon. When <literal>dhcp</literal>,
the addresses and routes provided by a DHCP server will never be dropped even if the DHCP
lease expires. This is contrary to the DHCP specification, but may be the best choice if,
e.g., the root filesystem relies on this connection. The setting <literal>dhcp</literal>
implies <literal>dhcp-on-stop</literal>, and <literal>yes</literal> implies
<literal>dhcp</literal> and <literal>static</literal>. Defaults to
<literal>dhcp-on-stop</literal> when <command>systemd-networkd</command> is running in
initrd, <literal>yes</literal> when the root filesystem is a network filesystem, and
<literal>no</literal> otherwise.</para>
<para>Takes a boolean or one of <literal>static</literal>, <literal>dynamic-on-stop</literal>, and
<literal>dynamic</literal>. When <literal>static</literal>, <command>systemd-networkd</command>
will not drop statically configured addresses and routes on starting up process. When
<literal>dynamic-on-stop</literal>, the dynamically configurad addresses and routes, such as
DHCPv4, DHCPv6, SLAAC, and IPv4 link-local address, will not be dropped when
<command>systemd-networkd</command> is being stopped. When <literal>dynamic</literal>, the
dynamically configured addresses and routes will never be dropped, and the lifetime of DHCPv4
leases will be ignored. This is contrary to the DHCP specification, but may be the best choice if,
e.g., the root filesystem relies on this connection. The setting <literal>dynamic</literal> implies
<literal>dynamic-on-stop</literal>, and <literal>yes</literal> implies <literal>dynamic</literal>
and <literal>static</literal>. Defaults to <literal>dynamic-on-stop</literal> when
<command>systemd-networkd</command> is running in initrd, <literal>yes</literal> when the root
filesystem is a network filesystem, and <literal>no</literal> otherwise.</para>
<xi:include href="version-info.xml" xpointer="v243"/>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
</variablelist>

View File

@ -76,6 +76,7 @@ sources = files(
'networkd-route-nexthop.c',
'networkd-route-util.c',
'networkd-routing-policy-rule.c',
'networkd-serialize.c',
'networkd-setlink.c',
'networkd-speed-meter.c',
'networkd-sriov.c',

View File

@ -1274,8 +1274,8 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
if (address->family != AF_INET)
return false;
/* Even when the address is leased from a DHCP server, networkd assign the address
* without lifetime when KeepConfiguration=dhcp. So, let's check that we have
/* Even if an IPv4 address is leased from a DHCP server with a finite lifetime, networkd assign the
* address without lifetime when KeepConfiguration=dynamic. So, let's check that we have
* corresponding routes with RTPROT_DHCP. */
SET_FOREACH(route, link->manager->routes) {
if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
@ -1410,9 +1410,9 @@ int link_drop_unmanaged_addresses(Link *link) {
continue;
/* link_address_is_dynamic() is slightly heavy. Let's call the function only when
* KeepConfiguration=dhcp or static. */
if (IN_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP, KEEP_CONFIGURATION_STATIC) &&
link_address_is_dynamic(link, address) == (link->network->keep_configuration == KEEP_CONFIGURATION_DHCP))
* KeepConfiguration=dynamic or static. */
if (IN_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC, KEEP_CONFIGURATION_STATIC) &&
link_address_is_dynamic(link, address) == (link->network->keep_configuration == KEEP_CONFIGURATION_DYNAMIC))
continue;
} else if (address->source != NETWORK_CONFIG_SOURCE_STATIC)

View File

@ -915,7 +915,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCP error: failed to get DHCP server IP address: %m");
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
r = sd_dhcp_lease_get_lifetime_timestamp(link->dhcp_lease, CLOCK_BOOTTIME, &lifetime_usec);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: failed to get lifetime: %m");
@ -1168,7 +1168,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
switch (event) {
case SD_DHCP_CLIENT_EVENT_STOP:
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
return 0;
}
@ -1199,7 +1199,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
break;
case SD_DHCP_CLIENT_EVENT_EXPIRED:
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
return 0;
}
@ -1214,7 +1214,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
break;
case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC)) {
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
return 0;
}
@ -1400,8 +1400,8 @@ static int dhcp4_set_request_address(Link *link) {
return sd_dhcp_client_set_request_address(link->dhcp_client, &link->network->dhcp_request_address);
}
/* 3. If KeepConfiguration=dhcp, use a foreign dynamic address. */
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
/* 3. If KeepConfiguration=dynamic, use a foreign dynamic address. */
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC))
return 0;
SET_FOREACH(a, link->addresses) {

View File

@ -175,6 +175,8 @@ static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *u
}
static int ipv4ll_set_address(Link *link) {
int r;
assert(link);
assert(link->network);
assert(link->ipv4ll);
@ -193,6 +195,27 @@ static int ipv4ll_set_address(Link *link) {
if (in4_addr_is_set(&link->network->ipv4ll_start_address))
return sd_ipv4ll_set_address(link->ipv4ll, &link->network->ipv4ll_start_address);
/* 3. If KeepConfiguration=dynamic, use a foreign IPv4LL address. */
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC))
return 0;
SET_FOREACH(a, link->addresses) {
if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
if (a->family != AF_INET)
continue;
if (!in4_addr_is_link_local_dynamic(&a->in_addr.in))
continue;
r = sd_ipv4ll_set_address(link->ipv4ll, &a->in_addr.in);
if (r < 0)
return r;
/* Make sure the address is not removed by link_drop_unmanaged_addresses(). */
a->source = NETWORK_CONFIG_SOURCE_IPV4LL;
return 0;
}
return 0;
}

View File

@ -28,55 +28,78 @@
#include "user-util.h"
#include "wifi-util.h"
static int address_append_json(Address *address, sd_json_variant **array) {
_cleanup_free_ char *scope = NULL, *flags = NULL, *state = NULL;
static int address_append_json(Address *address, bool serializing, sd_json_variant **array) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
int r;
assert(address);
assert(array);
r = route_scope_to_string_alloc(address->scope, &scope);
if (r < 0)
return r;
r = address_flags_to_string_alloc(address->flags, address->family, &flags);
if (r < 0)
return r;
r = network_config_state_to_string_alloc(address->state, &state);
if (r < 0)
return r;
return sd_json_variant_append_arraybo(
array,
r = sd_json_buildo(
&v,
SD_JSON_BUILD_PAIR_INTEGER("Family", address->family),
JSON_BUILD_PAIR_IN_ADDR("Address", &address->in_addr, address->family),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("Peer", &address->in_addr_peer, address->family),
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast),
SD_JSON_BUILD_PAIR_UNSIGNED("PrefixLength", address->prefixlen),
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope),
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags),
SD_JSON_BUILD_PAIR_STRING("FlagsString", flags),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Label", address->label),
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", address->lifetime_preferred_usec),
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUsec", address->lifetime_preferred_usec), /* for backward compat */
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", address->lifetime_valid_usec),
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUsec", address->lifetime_valid_usec), /* for backward compat */
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(address->source)),
SD_JSON_BUILD_PAIR_STRING("ConfigState", state),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &address->provider, address->family));
if (r < 0)
return r;
if (!serializing) {
_cleanup_free_ char *scope = NULL, *flags = NULL, *state = NULL;
r = route_scope_to_string_alloc(address->scope, &scope);
if (r < 0)
return r;
r = address_flags_to_string_alloc(address->flags, address->family, &flags);
if (r < 0)
return r;
r = network_config_state_to_string_alloc(address->state, &state);
if (r < 0)
return r;
r = sd_json_variant_merge_objectbo(
&v,
JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Broadcast", &address->broadcast),
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", address->scope),
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", address->flags),
SD_JSON_BUILD_PAIR_STRING("FlagsString", flags),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Label", address->label),
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUSec", address->lifetime_preferred_usec),
JSON_BUILD_PAIR_FINITE_USEC("PreferredLifetimeUsec", address->lifetime_preferred_usec), /* for backward compat */
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUSec", address->lifetime_valid_usec),
JSON_BUILD_PAIR_FINITE_USEC("ValidLifetimeUsec", address->lifetime_valid_usec), /* for backward compat */
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
if (r < 0)
return r;
}
return sd_json_variant_append_array(array, v);
}
static int addresses_append_json(Set *addresses, sd_json_variant **v) {
int addresses_append_json(Link *link, bool serializing, sd_json_variant **v) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
Address *address;
int r;
assert(link);
assert(v);
SET_FOREACH(address, addresses) {
r = address_append_json(address, &array);
SET_FOREACH(address, link->addresses) {
if (serializing) {
if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
if (!address_is_ready(address))
continue;
log_address_debug(address, "Serializing", link);
}
r = address_append_json(address, serializing, &array);
if (r < 0)
return r;
}
@ -199,35 +222,15 @@ static int nexthops_append_json(Manager *manager, int ifindex, sd_json_variant *
return json_variant_set_field_non_null(v, "NextHops", array);
}
static int route_append_json(Route *route, sd_json_variant **array) {
_cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL;
static int route_append_json(Route *route, bool serializing, sd_json_variant **array) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
int r;
assert(route);
assert(array);
r = route_scope_to_string_alloc(route->scope, &scope);
if (r < 0)
return r;
r = route_protocol_to_string_alloc(route->protocol, &protocol);
if (r < 0)
return r;
r = manager_get_route_table_to_string(route->manager, route->table, /* append_num = */ false, &table);
if (r < 0)
return r;
r = route_flags_to_string_alloc(route->flags, &flags);
if (r < 0)
return r;
r = network_config_state_to_string_alloc(route->state, &state);
if (r < 0)
return r;
return sd_json_variant_append_arraybo(
array,
r = sd_json_buildo(
&v,
SD_JSON_BUILD_PAIR_INTEGER("Family", route->family),
JSON_BUILD_PAIR_IN_ADDR("Destination", &route->dst, route->family),
SD_JSON_BUILD_PAIR_UNSIGNED("DestinationPrefixLength", route->dst_prefixlen),
@ -238,26 +241,67 @@ static int route_append_json(Route *route, sd_json_variant **array) {
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("PreferredSource", &route->prefsrc, route->family),
SD_JSON_BUILD_PAIR_UNSIGNED("TOS", route->tos),
SD_JSON_BUILD_PAIR_UNSIGNED("Scope", route->scope),
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
SD_JSON_BUILD_PAIR_UNSIGNED("Protocol", route->protocol),
SD_JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
SD_JSON_BUILD_PAIR_UNSIGNED("Type", route->type),
SD_JSON_BUILD_PAIR_STRING("TypeString", route_type_to_string(route->type)),
SD_JSON_BUILD_PAIR_UNSIGNED("Priority", route->priority),
SD_JSON_BUILD_PAIR_UNSIGNED("Table", route->table),
SD_JSON_BUILD_PAIR_STRING("TableString", table),
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route_metric_get(&route->metric, RTAX_MTU)),
SD_JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
SD_JSON_BUILD_PAIR_UNSIGNED("Flags", route->flags),
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", route->lifetime_usec),
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("NextHopID", route->nexthop_id),
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(route->source)),
SD_JSON_BUILD_PAIR_STRING("ConfigState", state),
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", &route->provider, route->family));
if (r < 0)
return r;
if (serializing) {
r = sd_json_variant_merge_objectbo(
&v,
SD_JSON_BUILD_PAIR_INTEGER("InterfaceIndex", route->nexthop.ifindex),
JSON_BUILD_PAIR_BYTE_ARRAY_NON_EMPTY("Metrics", route->metric.metrics, route->metric.n_metrics),
JSON_BUILD_PAIR_STRING_NON_EMPTY("TCPCongestionControlAlgorithm", route->metric.tcp_congestion_control_algo));
if (r < 0)
return r;
} else {
_cleanup_free_ char *scope = NULL, *protocol = NULL, *table = NULL, *flags = NULL, *state = NULL;
r = route_scope_to_string_alloc(route->scope, &scope);
if (r < 0)
return r;
r = route_protocol_to_string_alloc(route->protocol, &protocol);
if (r < 0)
return r;
r = manager_get_route_table_to_string(route->manager, route->table, /* append_num = */ false, &table);
if (r < 0)
return r;
r = route_flags_to_string_alloc(route->flags, &flags);
if (r < 0)
return r;
r = network_config_state_to_string_alloc(route->state, &state);
if (r < 0)
return r;
r = sd_json_variant_merge_objectbo(
&v,
SD_JSON_BUILD_PAIR_STRING("ScopeString", scope),
SD_JSON_BUILD_PAIR_STRING("ProtocolString", protocol),
SD_JSON_BUILD_PAIR_STRING("TypeString", route_type_to_string(route->type)),
SD_JSON_BUILD_PAIR_STRING("TableString", table),
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("MTU", route_metric_get(&route->metric, RTAX_MTU)),
SD_JSON_BUILD_PAIR_UNSIGNED("Preference", route->pref),
SD_JSON_BUILD_PAIR_STRING("FlagsString", strempty(flags)),
JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", route->lifetime_usec),
SD_JSON_BUILD_PAIR_STRING("ConfigState", state));
if (r < 0)
return r;
}
return sd_json_variant_append_array(array, v);
}
static int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v) {
int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
Route *route;
int r;
@ -266,10 +310,21 @@ static int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v
assert(v);
SET_FOREACH(route, manager->routes) {
if (route->nexthop.ifindex != ifindex)
continue;
if (ifindex >= 0) {
if (route->nexthop.ifindex != ifindex)
continue;
} else {
/* negative ifindex means we are serializing now. */
r = route_append_json(route, &array);
if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
if (!route_exists(route))
continue;
log_route_debug(route, "Serializing", manager);
}
r = route_append_json(route, /* serializing = */ ifindex < 0, &array);
if (r < 0)
return r;
}
@ -1413,7 +1468,7 @@ int link_build_json(Link *link, sd_json_variant **ret) {
if (r < 0)
return r;
r = addresses_append_json(link->addresses, &v);
r = addresses_append_json(link, /* serializing = */ false, &v);
if (r < 0)
return r;

View File

@ -1,10 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include "sd-json.h"
typedef struct Link Link;
typedef struct Manager Manager;
int addresses_append_json(Link *link, bool serializing, sd_json_variant **v);
int routes_append_json(Manager *manager, int ifindex, sd_json_variant **v);
int link_build_json(Link *link, sd_json_variant **ret);
int manager_build_json(Manager *manager, sd_json_variant **ret);

View File

@ -369,23 +369,41 @@ void link_set_state(Link *link, LinkState state) {
link_dirty(link);
}
int link_stop_engines(Link *link, bool may_keep_dhcp) {
int link_stop_engines(Link *link, bool may_keep_dynamic) {
int r, ret = 0;
assert(link);
assert(link->manager);
assert(link->manager->event);
bool keep_dhcp =
may_keep_dhcp &&
bool keep_dynamic =
may_keep_dynamic &&
link->network &&
(link->manager->state == MANAGER_RESTARTING ||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC_ON_STOP));
if (!keep_dhcp) {
if (!keep_dynamic) {
r = sd_dhcp_client_stop(link->dhcp_client);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m"));
r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"));
r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"));
r = dhcp_pd_remove(link, /* only_marked = */ false);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not remove DHCPv6 PD addresses and routes: %m"));
r = ndisc_stop(link);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"));
ndisc_flush(link);
}
r = sd_dhcp_server_stop(link->dhcp_server);
@ -400,28 +418,10 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop LLDP Tx: %m"));
r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"));
r = ipv4acd_stop(link);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 ACD client: %m"));
r = sd_dhcp6_client_stop(link->dhcp6_client);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"));
r = dhcp_pd_remove(link, /* only_marked = */ false);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not remove DHCPv6 PD addresses and routes: %m"));
r = ndisc_stop(link);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"));
ndisc_flush(link);
r = sd_radv_stop(link->radv);
if (r < 0)
RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Advertisement: %m"));
@ -449,7 +449,7 @@ void link_enter_failed(Link *link) {
return;
stop:
(void) link_stop_engines(link, /* may_keep_dhcp = */ false);
(void) link_stop_engines(link, /* may_keep_dynamic = */ false);
}
void link_check_ready(Link *link) {
@ -1339,7 +1339,7 @@ static void link_enter_unmanaged(Link *link) {
log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
"Unmanaging interface.");
(void) link_stop_engines(link, /* may_keep_dhcp = */ false);
(void) link_stop_engines(link, /* may_keep_dynamic = */ false);
(void) link_drop_requests(link);
(void) link_drop_static_config(link);
@ -1410,10 +1410,10 @@ int link_reconfigure_impl(Link *link, LinkReconfigurationFlag flags) {
/* Then, apply new .network file */
link->network = network_ref(network);
if (FLAGS_SET(network->keep_configuration, KEEP_CONFIGURATION_DHCP) ||
if (FLAGS_SET(network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC) ||
!FLAGS_SET(flags, LINK_RECONFIGURE_CLEANLY)) {
/* To make 'networkctl reconfigure INTERFACE' work safely for an interface whose new .network
* file has KeepConfiguration=dhcp or yes, even if a clean reconfiguration is requested,
* file has KeepConfiguration=dynamic or yes, even if a clean reconfiguration is requested,
* drop only unnecessary or possibly being changed dynamic configurations here. */
r = link_drop_dynamic_config(link, old_network);
if (r < 0)
@ -1799,7 +1799,7 @@ static int link_carrier_lost_impl(Link *link) {
if (!link->network)
return ret;
RET_GATHER(ret, link_stop_engines(link, false));
RET_GATHER(ret, link_stop_engines(link, /* may_keep_dynamic = */ false));
RET_GATHER(ret, link_drop_static_config(link));
return ret;

View File

@ -257,7 +257,7 @@ bool link_ipv6_enabled(Link *link);
int link_ipv6ll_gained(Link *link);
bool link_has_ipv6_connectivity(Link *link);
int link_stop_engines(Link *link, bool may_keep_dhcp);
int link_stop_engines(Link *link, bool may_keep_dynamic);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;

View File

@ -49,6 +49,7 @@
#include "networkd-queue.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-serialize.h"
#include "networkd-speed-meter.h"
#include "networkd-state-file.h"
#include "networkd-wifi.h"
@ -245,6 +246,9 @@ static int manager_listen_fds(Manager *m, int *ret_rtnl_fd) {
continue;
}
if (manager_set_serialization_fd(m, fd, names[i]) >= 0)
continue;
if (manager_add_tuntap_fd(m, fd, names[i]) >= 0)
continue;
@ -442,6 +446,7 @@ static int manager_post_handler(sd_event_source *s, void *userdata) {
fw_ctx_get_reply_callback_count(manager->fw_ctx) > 0)
return 0; /* There are some message calls waiting for their replies. */
(void) manager_serialize(manager);
manager->state = MANAGER_STOPPED;
return sd_event_exit(sd_event_source_get_event(s), 0);
@ -476,7 +481,7 @@ static int manager_stop(Manager *manager, ManagerState state) {
Link *link;
HASHMAP_FOREACH(link, manager->links_by_index)
(void) link_stop_engines(link, /* may_keep_dhcp = */ true);
(void) link_stop_engines(link, /* may_keep_dynamic = */ true);
return 0;
}
@ -503,8 +508,8 @@ static int manager_set_keep_configuration(Manager *m) {
assert(m);
if (in_initrd()) {
log_debug("Running in initrd, keep DHCPv4 addresses on stopping networkd by default.");
m->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
log_debug("Running in initrd, keep dynamically assigned configurations on stopping networkd by default.");
m->keep_configuration = KEEP_CONFIGURATION_DYNAMIC_ON_STOP;
return 0;
}
@ -646,6 +651,7 @@ int manager_new(Manager **ret, bool test_mode) {
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
.dhcp_server_persist_leases = true,
.serialization_fd = -EBADF,
.ip_forwarding = { -1, -1, },
#if HAVE_VMLINUX_H
.cgroup_fd = -EBADF,
@ -727,6 +733,8 @@ Manager* manager_free(Manager *m) {
m->fw_ctx = fw_ctx_free(m->fw_ctx);
m->serialization_fd = safe_close(m->serialization_fd);
return mfree(m);
}

View File

@ -129,6 +129,8 @@ struct Manager {
unsigned reloading;
int serialization_fd;
/* sysctl */
int ip_forwarding[2];
#if HAVE_VMLINUX_H

View File

@ -1093,15 +1093,53 @@ int config_parse_ignore_carrier_loss(
return 0;
}
int config_parse_keep_configuration(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
KeepConfiguration t, *k = ASSERT_PTR(data);
Network *network = ASSERT_PTR(userdata);
if (isempty(rvalue)) {
*k = ASSERT_PTR(network->manager)->keep_configuration;
return 0;
}
/* backward compatibility */
if (streq(rvalue, "dhcp")) {
*k = KEEP_CONFIGURATION_DYNAMIC;
return 0;
}
if (streq(rvalue, "dhcp-on-stop")) {
*k = KEEP_CONFIGURATION_DYNAMIC_ON_STOP;
return 0;
}
t = keep_configuration_from_string(rvalue);
if (t < 0)
return log_syntax_parse_error(unit, filename, line, t, lvalue, rvalue);
*k = t;
return 0;
}
DEFINE_CONFIG_PARSE_ENUM(config_parse_required_family_for_online, link_required_address_family, AddressFamily);
DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration);
static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
[KEEP_CONFIGURATION_NO] = "no",
[KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
[KEEP_CONFIGURATION_DHCP] = "dhcp",
[KEEP_CONFIGURATION_STATIC] = "static",
[KEEP_CONFIGURATION_YES] = "yes",
[KEEP_CONFIGURATION_NO] = "no",
[KEEP_CONFIGURATION_DYNAMIC_ON_STOP] = "dynamic-on-stop",
[KEEP_CONFIGURATION_DYNAMIC] = "dynamic",
[KEEP_CONFIGURATION_STATIC] = "static",
[KEEP_CONFIGURATION_YES] = "yes",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);

View File

@ -32,14 +32,14 @@
#include "socket-netlink.h"
typedef enum KeepConfiguration {
KEEP_CONFIGURATION_NO = 0,
KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
KEEP_CONFIGURATION_DHCP_ON_STOP = 1 << 1,
KEEP_CONFIGURATION_DHCP = KEEP_CONFIGURATION_DHCP_ON_START | KEEP_CONFIGURATION_DHCP_ON_STOP,
KEEP_CONFIGURATION_STATIC = 1 << 2,
KEEP_CONFIGURATION_YES = KEEP_CONFIGURATION_DHCP | KEEP_CONFIGURATION_STATIC,
KEEP_CONFIGURATION_NO = 0,
KEEP_CONFIGURATION_DYNAMIC_ON_START = 1 << 0,
KEEP_CONFIGURATION_DYNAMIC_ON_STOP = 1 << 1,
KEEP_CONFIGURATION_DYNAMIC = KEEP_CONFIGURATION_DYNAMIC_ON_START | KEEP_CONFIGURATION_DYNAMIC_ON_STOP,
KEEP_CONFIGURATION_STATIC = 1 << 2,
KEEP_CONFIGURATION_YES = KEEP_CONFIGURATION_DYNAMIC | KEEP_CONFIGURATION_STATIC,
_KEEP_CONFIGURATION_MAX,
_KEEP_CONFIGURATION_INVALID = -EINVAL,
_KEEP_CONFIGURATION_INVALID = -EINVAL,
} KeepConfiguration;
typedef enum ActivationPolicy {

View File

@ -1486,7 +1486,7 @@ int link_drop_routes(Link *link, bool only_static) {
continue;
if (IN_SET(route->protocol, RTPROT_DHCP, RTPROT_RA, RTPROT_REDIRECT) &&
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DYNAMIC))
continue;
}
}

View File

@ -0,0 +1,403 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "af-list.h"
#include "daemon-util.h"
#include "data-fd-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "iovec-util.h"
#include "json-util.h"
#include "networkd-address.h"
#include "networkd-json.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-route.h"
#include "networkd-serialize.h"
int manager_serialize(Manager *manager) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *array = NULL;
int r;
assert(manager);
log_debug("Serializing...");
Link *link;
HASHMAP_FOREACH(link, manager->links_by_index) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *e = NULL;
/* ignore unmanaged, failed, or removed interfaces. */
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
continue;
r = sd_json_buildo(
&e,
SD_JSON_BUILD_PAIR_INTEGER("Index", link->ifindex));
if (r < 0)
return r;
r = addresses_append_json(link, /* serializing = */ true, &e);
if (r < 0)
return r;
r = sd_json_variant_append_array(&array, e);
if (r < 0)
return r;
}
r = json_variant_set_field_non_null(&v, "Interfaces", array);
if (r < 0)
return r;
r = routes_append_json(manager, /* ifindex = */ -1, &v);
if (r < 0)
return r;
if (!v) {
log_debug("There is nothing to serialize.");
return 0;
}
_cleanup_free_ char *dump = NULL;
r = sd_json_variant_format(v, /* flags = */ 0, &dump);
if (r < 0)
return r;
_cleanup_close_ int fd = -EBADF;
fd = acquire_data_fd(dump);
if (fd < 0)
return fd;
r = notify_push_fd(fd, "manager-serialization");
if (r < 0)
return log_debug_errno(r, "Failed to push serialization file descriptor: %m");
log_debug("Serialization completed.");
return 0;
}
int manager_set_serialization_fd(Manager *manager, int fd, const char *name) {
assert(manager);
assert(fd >= 0);
assert(name);
if (!startswith(name, "manager-serialization"))
return -EINVAL;
if (manager->serialization_fd >= 0)
return -EEXIST;
manager->serialization_fd = fd;
return 0;
}
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_network_config_source, NetworkConfigSource, network_config_source_from_string);
static int json_dispatch_address_family(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
int r, *i = ASSERT_PTR(userdata);
int64_t i64;
assert_return(variant, -EINVAL);
if (FLAGS_SET(flags, SD_JSON_RELAX) && sd_json_variant_is_null(variant)) {
*i = AF_UNSPEC;
return 0;
}
r = sd_json_dispatch_int64(name, variant, flags, &i64);
if (r < 0)
return r;
if (!IN_SET(i64, AF_INET, AF_INET6) && !(FLAGS_SET(flags, SD_JSON_RELAX) && i64 == AF_UNSPEC))
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds for an address family.", strna(name));
*i = (int) i64;
return 0;
}
typedef struct AddressParam {
int family;
struct iovec address;
struct iovec peer;
uint8_t prefixlen;
NetworkConfigSource source;
struct iovec provider;
} AddressParam;
static void address_param_done(AddressParam *p) {
assert(p);
iovec_done(&p->address);
iovec_done(&p->peer);
iovec_done(&p->provider);
}
static int link_deserialize_address(Link *link, sd_json_variant *v) {
static const sd_json_dispatch_field dispatch_table[] = {
{ "Family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(AddressParam, family), SD_JSON_MANDATORY },
{ "Address", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(AddressParam, address), SD_JSON_MANDATORY },
{ "Peer", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(AddressParam, peer), 0 },
{ "PrefixLength", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(AddressParam, prefixlen), SD_JSON_MANDATORY },
{ "ConfigSource", SD_JSON_VARIANT_STRING, json_dispatch_network_config_source, offsetof(AddressParam, source), SD_JSON_MANDATORY },
{ "ConfigProvider", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(AddressParam, provider), 0 },
{},
};
int r;
assert(link);
assert(v);
_cleanup_(address_param_done) AddressParam p = {};
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to dispatch address from json variant: %m");
if (p.address.iov_len != FAMILY_ADDRESS_SIZE(p.family))
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
"Dispatched address size (%zu) is incompatible with the family (%s).",
p.address.iov_len, af_to_ipv4_ipv6(p.family));
if (p.peer.iov_len != 0 && p.peer.iov_len != FAMILY_ADDRESS_SIZE(p.family))
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
"Dispatched peer address size (%zu) is incompatible with the family (%s).",
p.peer.iov_len, af_to_ipv4_ipv6(p.family));
if (p.provider.iov_len != 0 && p.provider.iov_len != FAMILY_ADDRESS_SIZE(p.family))
return log_link_debug_errno(link, SYNTHETIC_ERRNO(EINVAL),
"Dispatched provider address size (%zu) is incompatible with the family (%s).",
p.provider.iov_len, af_to_ipv4_ipv6(p.family));
Address tmp = {
.family = p.family,
.prefixlen = p.prefixlen,
};
memcpy_safe(&tmp.in_addr, p.address.iov_base, p.address.iov_len);
memcpy_safe(&tmp.in_addr_peer, p.peer.iov_base, p.peer.iov_len);
Address *address;
r = address_get(link, &tmp, &address);
if (r < 0) {
log_link_debug_errno(link, r, "Cannot find deserialized address %s: %m",
IN_ADDR_PREFIX_TO_STRING(tmp.family, &tmp.in_addr, tmp.prefixlen));
return 0; /* Already removed? */
}
if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
return 0; /* Huh?? Already deserialized?? */
address->source = p.source;
memcpy_safe(&address->provider, p.provider.iov_base, p.provider.iov_len);
log_address_debug(address, "Deserialized", link);
return 0;
}
static int manager_deserialize_link(Manager *manager, sd_json_variant *v) {
typedef struct LinkParam {
int ifindex;
sd_json_variant *addresses;
} LinkParam;
static const sd_json_dispatch_field dispatch_table[] = {
{ "Index", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LinkParam, ifindex), SD_JSON_MANDATORY | SD_JSON_REFUSE_NULL },
{ "Addresses", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_variant_noref, offsetof(LinkParam, addresses), 0 },
{},
};
int r, ret = 0;
assert(manager);
assert(v);
LinkParam p = {};
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return log_debug_errno(r, "Failed to dispatch interface from json variant: %m");
Link *link;
r = link_get_by_index(manager, p.ifindex, &link);
if (r < 0) {
log_debug_errno(r, "No interface with deserialized ifindex (%i) found: %m", p.ifindex);
return 0; /* Already removed? */
}
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, p.addresses)
RET_GATHER(ret, link_deserialize_address(link, i));
return ret;
}
typedef struct RouteParam {
Route route;
struct iovec dst;
struct iovec src;
struct iovec prefsrc;
struct iovec gw;
struct iovec metrics;
struct iovec provider;
} RouteParam;
static void route_param_done(RouteParam *p) {
assert(p);
free(p->route.metric.metrics);
iovec_done(&p->dst);
iovec_done(&p->src);
iovec_done(&p->prefsrc);
iovec_done(&p->gw);
iovec_done(&p->metrics);
iovec_done(&p->provider);
}
static int manager_deserialize_route(Manager *manager, sd_json_variant *v) {
static const sd_json_dispatch_field dispatch_table[] = {
/* rtmsg header */
{ "Family", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_address_family, offsetof(RouteParam, route.family), SD_JSON_MANDATORY },
{ "DestinationPrefixLength", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.dst_prefixlen), SD_JSON_MANDATORY },
{ "SourcePrefixLength", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.src_prefixlen), 0 },
{ "TOS", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.tos), SD_JSON_MANDATORY },
{ "Protocol", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.protocol), SD_JSON_MANDATORY },
{ "Scope", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.scope), SD_JSON_MANDATORY },
{ "Type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint8, offsetof(RouteParam, route.type), SD_JSON_MANDATORY },
{ "Flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.flags), SD_JSON_MANDATORY },
/* attributes */
{ "Destination", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, dst), SD_JSON_MANDATORY },
{ "Source", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, src), 0 },
{ "Priority", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.priority), SD_JSON_MANDATORY },
{ "PreferredSource", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, prefsrc), 0 },
{ "Table", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.table), SD_JSON_MANDATORY },
/* nexthops */
{ "Gateway", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, gw), 0 },
{ "InterfaceIndex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(RouteParam, route.nexthop.ifindex), SD_JSON_MANDATORY | SD_JSON_RELAX },
{ "NextHopID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32, offsetof(RouteParam, route.nexthop_id), 0 },
/* metrics */
{ "Metrics", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, metrics), 0 },
{ "TCPCongestionControlAlgorithm", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(RouteParam, route.metric.tcp_congestion_control_algo), 0 },
/* config */
{ "ConfigSource", SD_JSON_VARIANT_STRING, json_dispatch_network_config_source, offsetof(RouteParam, route.source), SD_JSON_MANDATORY },
{ "ConfigProvider", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(RouteParam, provider), 0 },
{},
};
int r;
assert(manager);
assert(v);
_cleanup_(route_param_done) RouteParam p = {};
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return log_debug_errno(r, "Failed to dispatch route from json variant: %m");
if (p.dst.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Dispatched destination address size (%zu) is incompatible with the family (%s).",
p.dst.iov_len, af_to_ipv4_ipv6(p.route.family));
if (p.src.iov_len != 0 && p.src.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Dispatched source address size (%zu) is incompatible with the family (%s).",
p.src.iov_len, af_to_ipv4_ipv6(p.route.family));
if (p.prefsrc.iov_len != 0 && p.prefsrc.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Dispatched preferred source address size (%zu) is incompatible with the family (%s).",
p.prefsrc.iov_len, af_to_ipv4_ipv6(p.route.family));
switch (p.gw.iov_len) {
case 0:
p.route.nexthop.family = AF_UNSPEC;
break;
case sizeof(struct in_addr):
p.route.nexthop.family = AF_INET;
break;
case sizeof(struct in6_addr):
p.route.nexthop.family = AF_INET6;
break;
default:
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Dispatched gateway address size (%zu) is invalid.",
p.prefsrc.iov_len);
}
if (p.metrics.iov_len % sizeof(uint32_t) != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Dispatched route metric size (%zu) is invalid.",
p.metrics.iov_len);
if (p.provider.iov_len != 0 && p.provider.iov_len != FAMILY_ADDRESS_SIZE(p.route.family))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Dispatched provider address size (%zu) is incompatible with the family (%s).",
p.provider.iov_len, af_to_ipv4_ipv6(p.route.family));
memcpy_safe(&p.route.dst, p.dst.iov_base, p.dst.iov_len);
memcpy_safe(&p.route.src, p.src.iov_base, p.src.iov_len);
memcpy_safe(&p.route.prefsrc, p.prefsrc.iov_base, p.prefsrc.iov_len);
memcpy_safe(&p.route.nexthop.gw, p.gw.iov_base, p.gw.iov_len);
p.route.metric.n_metrics = p.metrics.iov_len / sizeof(uint32_t);
p.route.metric.metrics = new(uint32_t, p.route.metric.n_metrics);
if (!p.route.metric.metrics)
return log_oom_debug();
memcpy_safe(p.route.metric.metrics, p.metrics.iov_base, p.metrics.iov_len);
Route *route;
r = route_get(manager, &p.route, &route);
if (r < 0) {
log_route_debug(&p.route, "Cannot find deserialized", manager);
return 0; /* Already removed? */
}
if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
return 0; /* Huh?? Already deserialized?? */
route->source = p.route.source;
memcpy_safe(&route->provider, p.provider.iov_base, p.provider.iov_len);
log_route_debug(route, "Deserialized", manager);
return 0;
}
int manager_deserialize(Manager *manager) {
int r, ret = 0;
assert(manager);
_cleanup_close_ int fd = TAKE_FD(manager->serialization_fd);
if (fd < 0)
return 0;
log_debug("Deserializing...");
_cleanup_fclose_ FILE *f = take_fdopen(&fd, "r");
if (!f)
return log_debug_errno(errno, "Failed to fdopen() serialization file descriptor: %m");
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned err_line, err_column;
r = sd_json_parse_file(
f,
/* path = */ NULL,
/* flags = */ 0,
&v,
&err_line,
&err_column);
if (r < 0)
return log_debug_errno(r, "Failed to parse json (line=%u, column=%u): %m", err_line, err_column);
sd_json_variant *i;
JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "Interfaces"))
RET_GATHER(ret, manager_deserialize_link(manager, i));
JSON_VARIANT_ARRAY_FOREACH(i, sd_json_variant_by_key(v, "Routes"))
RET_GATHER(ret, manager_deserialize_route(manager, i));
log_debug("Deserialization completed.");
return ret;
}

View File

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
typedef struct Manager Manager;
int manager_serialize(Manager *manager);
int manager_set_serialization_fd(Manager *manager, int fd, const char *name);
int manager_deserialize(Manager *manager);

View File

@ -16,6 +16,7 @@
#include "networkd-conf.h"
#include "networkd-manager-bus.h"
#include "networkd-manager.h"
#include "networkd-serialize.h"
#include "service-util.h"
#include "signal-util.h"
#include "strv.h"
@ -100,6 +101,10 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
r = manager_deserialize(m);
if (r < 0)
log_warning_errno(r, "Failed to deserialize the previous invocation, ignoring: %m");
r = manager_start(m);
if (r < 0)
return log_error_errno(r, "Could not start manager: %m");

View File

@ -5,4 +5,4 @@ Name=veth99
[Network]
DHCP=ipv4
IPv6AcceptRA=false
KeepConfiguration=dhcp-on-stop
KeepConfiguration=dynamic-on-stop

View File

@ -5,4 +5,4 @@ Name=veth99
[Network]
DHCP=ipv4
IPv6AcceptRA=false
KeepConfiguration=dhcp
KeepConfiguration=dynamic

View File

@ -7277,10 +7277,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertNotIn('test-hostname', output)
self.assertNotIn('26:mtu', output)
def test_dhcp_keep_configuration_dhcp(self):
def test_dhcp_keep_configuration_dynamic(self):
copy_network_unit('25-veth.netdev',
'25-dhcp-server-veth-peer.network',
'25-dhcp-client-keep-configuration-dhcp.network')
'25-dhcp-client-keep-configuration-dynamic.network')
start_networkd()
self.wait_online('veth-peer:carrier')
start_dnsmasq()
@ -7312,7 +7312,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
'valid_lft forever preferred_lft forever')
with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dhcp.network'), mode='a', encoding='utf-8') as f:
with open(os.path.join(network_unit_dir, '25-dhcp-client-keep-configuration-dynamic.network'), mode='a', encoding='utf-8') as f:
f.write('[Network]\nDHCP=no\n')
start_networkd()
@ -7324,10 +7324,10 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global veth99\n *'
'valid_lft forever preferred_lft forever')
def test_dhcp_keep_configuration_dhcp_on_stop(self):
def test_dhcp_keep_configuration_dynamic_on_stop(self):
copy_network_unit('25-veth.netdev',
'25-dhcp-server-veth-peer.network',
'25-dhcp-client-keep-configuration-dhcp-on-stop.network')
'25-dhcp-client-keep-configuration-dynamic-on-stop.network')
start_networkd()
self.wait_online('veth-peer:carrier')
start_dnsmasq()