/* 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; }