From c321d332e3b2478d4d1d6faf71fd4639016e180d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 2 Nov 2024 06:03:09 +0900 Subject: 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. --- src/network/meson.build | 1 + src/network/networkd-json.c | 191 ++++++++++++------- src/network/networkd-json.h | 4 + src/network/networkd-manager.c | 8 + src/network/networkd-manager.h | 2 + src/network/networkd-serialize.c | 403 +++++++++++++++++++++++++++++++++++++++ src/network/networkd-serialize.h | 8 + src/network/networkd.c | 5 + 8 files changed, 554 insertions(+), 68 deletions(-) create mode 100644 src/network/networkd-serialize.c create mode 100644 src/network/networkd-serialize.h (limited to 'src/network') 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 + #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"); -- cgit v1.2.3 From 4eca221ab8d1ce3b95596aef5b56e784d18d41cc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 7 Nov 2024 03:43:50 +0900 Subject: network: keep all dynamically acquired configurations when KeepConfiguration=dhcp-on-stop By the previous commit, configuration source of addresses and routes are saved on stop and restored on start. Hence, we can keep dynamic configurations on stop. Co-authored-by: Jian Zhang --- src/network/networkd-link.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src/network') diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 082c7d1c38..9d903b7057 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -386,6 +386,24 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { 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")); -- cgit v1.2.3 From d13ce4ea0decbc09525fc578e0a40514e4056d9e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 7 Nov 2024 03:40:04 +0900 Subject: network/ipv4ll: use a foreign IPv4LL address when KeepConfiguration=dhcp This is similar to what we do for DHCPv4 address, but for IPv4LL address. --- src/network/networkd-ipv4ll.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'src/network') diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 7398cefe77..9682d0d874 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=dhcp, use a foreign IPv4LL address. */ + if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) + 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; } -- cgit v1.2.3 From 80a89d1ad59bce6facd98f2d47d0e760f56f9aac Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 5 Nov 2024 12:14:01 +0900 Subject: network: rename KeepConfiguration=dhcp -> dynamic KeepConfiguration=dhcp keeps not only DHCP configurations but also SLAAC or IPV4LL. Let's rename the value to 'dynamic'. --- src/network/networkd-address.c | 10 ++++----- src/network/networkd-dhcp4.c | 12 +++++----- src/network/networkd-ipv4ll.c | 4 ++-- src/network/networkd-link.c | 20 ++++++++--------- src/network/networkd-link.h | 2 +- src/network/networkd-manager.c | 6 ++--- src/network/networkd-network.c | 50 +++++++++++++++++++++++++++++++++++++----- src/network/networkd-network.h | 14 ++++++------ src/network/networkd-route.c | 2 +- 9 files changed, 79 insertions(+), 41 deletions(-) (limited to 'src/network') 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 9682d0d874..046e4ca957 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -195,8 +195,8 @@ 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=dhcp, use a foreign IPv4LL address. */ - if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) + /* 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) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 9d903b7057..4b341a96c7 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -369,20 +369,20 @@ 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")); @@ -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 ca6ab48502..9290255ad0 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -481,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; } @@ -508,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; } 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; } } -- cgit v1.2.3