diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2024-11-14 03:59:44 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-14 03:59:44 +0100 |
commit | bdc6edbdabfa0c8844d00f9863ec1d81d7804cc5 (patch) | |
tree | c62ad9e23c9391ff4a40bbc42da23156e960c4aa /src/network | |
parent | namespace-util: pin pid via pidfd during namespace_open() (diff) | |
parent | test-network: update KeepConfiguration=dhcp -> dynamic (diff) | |
download | systemd-bdc6edbdabfa0c8844d00f9863ec1d81d7804cc5.tar.xz systemd-bdc6edbdabfa0c8844d00f9863ec1d81d7804cc5.zip |
network: serialize and deserialize current configuration (#34989)
Replaces #34963.
Fixes #26602.
Fixes #32569.
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/meson.build | 1 | ||||
-rw-r--r-- | src/network/networkd-address.c | 10 | ||||
-rw-r--r-- | src/network/networkd-dhcp4.c | 12 | ||||
-rw-r--r-- | src/network/networkd-ipv4ll.c | 23 | ||||
-rw-r--r-- | src/network/networkd-json.c | 191 | ||||
-rw-r--r-- | src/network/networkd-json.h | 4 | ||||
-rw-r--r-- | src/network/networkd-link.c | 56 | ||||
-rw-r--r-- | src/network/networkd-link.h | 2 | ||||
-rw-r--r-- | src/network/networkd-manager.c | 14 | ||||
-rw-r--r-- | src/network/networkd-manager.h | 2 | ||||
-rw-r--r-- | src/network/networkd-network.c | 50 | ||||
-rw-r--r-- | src/network/networkd-network.h | 14 | ||||
-rw-r--r-- | src/network/networkd-route.c | 2 | ||||
-rw-r--r-- | src/network/networkd-serialize.c | 403 | ||||
-rw-r--r-- | src/network/networkd-serialize.h | 8 | ||||
-rw-r--r-- | src/network/networkd.c | 5 |
16 files changed, 672 insertions, 125 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-address.c b/src/network/networkd-address.c index a689fa68bd..0b7fbec28b 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -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) diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 684580f5b6..d94ac1a213 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -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) { diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 7398cefe77..046e4ca957 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -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; } 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-link.c b/src/network/networkd-link.c index 082c7d1c38..4b341a96c7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -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; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 848e67c843..113217cdb6 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -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_; diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 47299e3b27..9290255ad0 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); @@ -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); } 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-network.c b/src/network/networkd-network.c index 2488530829..89dcf4f6c4 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -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); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 3ea94e0e49..03e3108623 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -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 { diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index b85d2a037a..0f3f79ec4f 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -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; } } 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"); |