summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2024-11-01 22:03:09 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2024-11-14 02:21:55 +0100
commitc321d332e3b2478d4d1d6faf71fd4639016e180d (patch)
treeac4380a8b3371dcc7c90fed80d683e72fffbaaf2 /src/network
parentnamespace-util: pin pid via pidfd during namespace_open() (diff)
downloadsystemd-c321d332e3b2478d4d1d6faf71fd4639016e180d.tar.xz
systemd-c321d332e3b2478d4d1d6faf71fd4639016e180d.zip
network: introduce manager_serialize()/deserialize()
Currently, only configuration sources and providers of addresses and routes are serialized/deserialized. This should mostly not change behavior, as dynamic (except for DHCPv4) configurations will be dropped before stopping networkd, and for DHCPv4 protocol, we have already had another logic to handle DHCPv4 configurations. Preparation for later commits.
Diffstat (limited to 'src/network')
-rw-r--r--src/network/meson.build1
-rw-r--r--src/network/networkd-json.c191
-rw-r--r--src/network/networkd-json.h4
-rw-r--r--src/network/networkd-manager.c8
-rw-r--r--src/network/networkd-manager.h2
-rw-r--r--src/network/networkd-serialize.c403
-rw-r--r--src/network/networkd-serialize.h8
-rw-r--r--src/network/networkd.c5
8 files changed, 554 insertions, 68 deletions
diff --git a/src/network/meson.build b/src/network/meson.build
index 73c48e06af..295d015e7c 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -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',
diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c
index fd2b709d9d..460fbe5968 100644
--- a/src/network/networkd-json.c
+++ b/src/network/networkd-json.c
@@ -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. */
+
+ if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)
+ continue;
+ if (!route_exists(route))
+ continue;
+
+ log_route_debug(route, "Serializing", manager);
+ }
- r = route_append_json(route, &array);
+ 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;
diff --git a/src/network/networkd-json.h b/src/network/networkd-json.h
index b3cdb5cae8..e8be60458f 100644
--- a/src/network/networkd-json.h
+++ b/src/network/networkd-json.h
@@ -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);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 47299e3b27..ca6ab48502 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -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);
@@ -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);
}
diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h
index 5f4508ea6f..faa3357151 100644
--- a/src/network/networkd-manager.h
+++ b/src/network/networkd-manager.h
@@ -129,6 +129,8 @@ struct Manager {
unsigned reloading;
+ int serialization_fd;
+
/* sysctl */
int ip_forwarding[2];
#if HAVE_VMLINUX_H
diff --git a/src/network/networkd-serialize.c b/src/network/networkd-serialize.c
new file mode 100644
index 0000000000..8cc74baae9
--- /dev/null
+++ b/src/network/networkd-serialize.c
@@ -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;
+}
diff --git a/src/network/networkd-serialize.h b/src/network/networkd-serialize.h
new file mode 100644
index 0000000000..f04378fa5d
--- /dev/null
+++ b/src/network/networkd-serialize.h
@@ -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);
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 2d7a6421f9..883f16d81b 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -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");