From 11b88419ae0004547a0724aa459ddcb5d243f25c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 14 Mar 2024 02:05:30 +0900 Subject: [PATCH 1/3] sd-dhcp-server: make sd_dhcp_server_set_lease_file() optionally take directory fd Currently, though, no valid directory fd is passed to the function call. Preparation for later commits. --- src/libsystemd-network/dhcp-server-internal.h | 1 + src/libsystemd-network/fuzz-dhcp-server.c | 8 ++--- src/libsystemd-network/sd-dhcp-server-lease.c | 20 ++++++++----- src/libsystemd-network/sd-dhcp-server.c | 30 +++++++++++++++++-- src/network/networkd-dhcp-server.c | 2 +- src/systemd/sd-dhcp-server.h | 2 +- 6 files changed, 46 insertions(+), 17 deletions(-) diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 8f5707c8e2..0b2fa96524 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -76,6 +76,7 @@ struct sd_dhcp_server { char *agent_circuit_id; char *agent_remote_id; + int lease_dir_fd; char *lease_file; }; diff --git a/src/libsystemd-network/fuzz-dhcp-server.c b/src/libsystemd-network/fuzz-dhcp-server.c index f30ea6353e..c8b03781d0 100644 --- a/src/libsystemd-network/fuzz-dhcp-server.c +++ b/src/libsystemd-network/fuzz-dhcp-server.c @@ -69,7 +69,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; struct in_addr address = { .s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))}; _cleanup_free_ uint8_t *duped = NULL; - _cleanup_free_ char *lease_file = NULL; + _cleanup_close_ int dir_fd = -EBADF; if (size < sizeof(DHCPMessage)) return 0; @@ -78,12 +78,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { assert_se(duped = memdup(data, size)); - assert_se(mkdtemp_malloc(NULL, &tmpdir) >= 0); - assert_se(lease_file = path_join(tmpdir, "leases")); + dir_fd = mkdtemp_open(NULL, 0, &tmpdir); + assert_se(dir_fd >= 0); assert_se(sd_dhcp_server_new(&server, 1) >= 0); assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); - assert_se(sd_dhcp_server_set_lease_file(server, lease_file) >= 0); + assert_se(sd_dhcp_server_set_lease_file(server, dir_fd, "leases") >= 0); server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY); assert_se(server->fd >= 0); assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0); diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c index f4c7e8d0a4..52abb87c1f 100644 --- a/src/libsystemd-network/sd-dhcp-server-lease.c +++ b/src/libsystemd-network/sd-dhcp-server-lease.c @@ -286,7 +286,7 @@ int dhcp_server_static_leases_append_json(sd_dhcp_server *server, JsonVariant ** int dhcp_server_save_leases(sd_dhcp_server *server) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - _cleanup_(unlink_and_freep) char *temp_path = NULL; + _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; sd_id128_t boot_id; int r; @@ -315,11 +315,11 @@ int dhcp_server_save_leases(sd_dhcp_server *server) { if (r < 0) return r; - r = mkdir_parents(server->lease_file, 0755); + r = mkdirat_parents(server->lease_dir_fd, server->lease_file, 0755); if (r < 0) return r; - r = fopen_temporary(server->lease_file, &f, &temp_path); + r = fopen_temporary_at(server->lease_dir_fd, server->lease_file, &f, &temp_path); if (r < 0) return r; @@ -327,14 +327,17 @@ int dhcp_server_save_leases(sd_dhcp_server *server) { r = json_variant_dump(v, JSON_FORMAT_NEWLINE | JSON_FORMAT_FLUSH, f, /* prefix = */ NULL); if (r < 0) - return r; + goto failure; - r = conservative_rename(temp_path, server->lease_file); + r = conservative_renameat(server->lease_dir_fd, temp_path, server->lease_dir_fd, server->lease_file); if (r < 0) - return r; + goto failure; - temp_path = mfree(temp_path); return 0; + +failure: + (void) unlinkat(server->lease_dir_fd, temp_path, /* flags = */ 0); + return r; } static int json_dispatch_dhcp_lease(sd_dhcp_server *server, JsonVariant *v, bool use_boottime) { @@ -445,8 +448,9 @@ int dhcp_server_load_leases(sd_dhcp_server *server) { if (!server->lease_file) return 0; - r = json_parse_file( + r = json_parse_file_at( /* f = */ NULL, + server->lease_dir_fd, server->lease_file, /* flags = */ 0, &v, diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index bbe96be834..6ec84405d9 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -144,6 +144,7 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { free(server->agent_circuit_id); free(server->agent_remote_id); + safe_close(server->lease_dir_fd); free(server->lease_file); free(server->ifname); @@ -174,6 +175,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { .default_lease_time = DHCP_DEFAULT_LEASE_TIME_USEC, .max_lease_time = DHCP_MAX_LEASE_TIME_USEC, .rapid_commit = true, + .lease_dir_fd = -EBADF, }; *ret = TAKE_PTR(server); @@ -1586,12 +1588,34 @@ int sd_dhcp_server_set_relay_agent_information( return 0; } -int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, const char *path) { +int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char *path) { + int r; + assert_return(server, -EINVAL); assert_return(!sd_dhcp_server_is_running(server), -EBUSY); - if (path && !path_is_safe(path)) + if (!path) { + /* When NULL, clear the previous assignment. */ + server->lease_file = mfree(server->lease_file); + server->lease_dir_fd = safe_close(server->lease_dir_fd); + return 0; + } + + if (!path_is_safe(path)) return -EINVAL; - return free_and_strdup(&server->lease_file, path); + if (dir_fd < 0 && dir_fd != AT_FDCWD) + return -EBADF; + + _cleanup_close_ int fd = -EBADF; + fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH); + if (fd < 0) + return fd; + + r = free_and_strdup(&server->lease_file, path); + if (r < 0) + return r; + + server->lease_dir_fd = TAKE_FD(fd); + return 0; } diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index eddbaad917..b7bae37a4d 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -577,7 +577,7 @@ static int dhcp4_server_configure(Link *link) { if (!lease_file) return log_oom(); - r = sd_dhcp_server_set_lease_file(link->dhcp_server, lease_file); + r = sd_dhcp_server_set_lease_file(link->dhcp_server, AT_FDCWD, lease_file); if (r < 0) log_link_warning_errno(link, r, "Failed to load DHCPv4 server leases, ignoring: %m"); } diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 486af48b25..1bba7a6077 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -81,7 +81,7 @@ int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v); int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v); int sd_dhcp_server_set_static_lease(sd_dhcp_server *server, const struct in_addr *address, uint8_t *client_id, size_t client_id_size); -int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, const char *path); +int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char *path); int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint64_t t); int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint64_t t); From d47f83302e25f020e7ec4051c8e0cb49d4c3a14a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 12 Mar 2024 14:07:54 +0900 Subject: [PATCH 2/3] sd-dhcp-server: rearrange the parser of the leases file No functional changes, just refactoring and preparation for later commits. --- src/libsystemd-network/sd-dhcp-server-lease.c | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c index 52abb87c1f..b518b47409 100644 --- a/src/libsystemd-network/sd-dhcp-server-lease.c +++ b/src/libsystemd-network/sd-dhcp-server-lease.c @@ -411,34 +411,44 @@ typedef struct SavedInfo { JsonVariant *leases; } SavedInfo; -static int dhcp_server_dispatch_leases(sd_dhcp_server *server, JsonVariant *v) { - static const JsonDispatch dispatch_table[] = { - { "BootID", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(SavedInfo, boot_id), JSON_MANDATORY }, - { "Leases", JSON_VARIANT_ARRAY, json_dispatch_variant_noref, offsetof(SavedInfo, leases), JSON_MANDATORY }, +static void saved_info_done(SavedInfo *info) { + if (!info) + return; + + json_variant_unref(info->leases); +} + +static int load_leases_file(int dir_fd, const char *path, SavedInfo *ret) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(path); + assert(ret); + + r = json_parse_file_at( + /* f = */ NULL, + dir_fd, + path, + /* flags = */ 0, + &v, + /* ret_line = */ NULL, + /* ret_column = */ NULL); + if (r < 0) + return r; + + static const JsonDispatch dispatch_lease_file_table[] = { + { "BootID", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(SavedInfo, boot_id), JSON_MANDATORY }, + { "Leases", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(SavedInfo, leases), JSON_MANDATORY }, {} }; - SavedInfo info = {}; - sd_id128_t boot_id; - int r; - - r = json_dispatch(v, dispatch_table, JSON_ALLOW_EXTENSIONS, &info); - if (r < 0) - return r; - - r = sd_id128_get_boot(&boot_id); - if (r < 0) - return r; - - JsonVariant *i; - JSON_VARIANT_ARRAY_FOREACH(i, info.leases) - RET_GATHER(r, json_dispatch_dhcp_lease(server, i, /* use_boottime = */ sd_id128_equal(info.boot_id, boot_id))); - - return r; + return json_dispatch(v, dispatch_lease_file_table, JSON_ALLOW_EXTENSIONS, ret); } int dhcp_server_load_leases(sd_dhcp_server *server) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + _cleanup_(saved_info_done) SavedInfo info = {}; + sd_id128_t boot_id; size_t n, m; int r; @@ -448,22 +458,21 @@ int dhcp_server_load_leases(sd_dhcp_server *server) { if (!server->lease_file) return 0; - r = json_parse_file_at( - /* f = */ NULL, - server->lease_dir_fd, - server->lease_file, - /* flags = */ 0, - &v, - /* ret_line = */ NULL, - /* ret_column = */ NULL); + r = load_leases_file(server->lease_dir_fd, server->lease_file, &info); if (r == -ENOENT) return 0; if (r < 0) return r; + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return r; + n = hashmap_size(server->bound_leases_by_client_id); - r = dhcp_server_dispatch_leases(server, v); + JsonVariant *i; + JSON_VARIANT_ARRAY_FOREACH(i, info.leases) + RET_GATHER(r, json_dispatch_dhcp_lease(server, i, /* use_boottime = */ sd_id128_equal(info.boot_id, boot_id))); m = hashmap_size(server->bound_leases_by_client_id); assert(m >= n); From 5ed4c2b944d1188181bfea937ea0a069af26bb53 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 14 Mar 2024 19:35:46 +0900 Subject: [PATCH 3/3] sd-dhcp-server: also save the server address and netmask to the leases file And introduce a tiny helper to retrieve these information. The function is not used at this time, but will be used later. --- .../dhcp-server-lease-internal.h | 5 +++ src/libsystemd-network/sd-dhcp-server-lease.c | 33 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/dhcp-server-lease-internal.h b/src/libsystemd-network/dhcp-server-lease-internal.h index 518c151094..7626552811 100644 --- a/src/libsystemd-network/dhcp-server-lease-internal.h +++ b/src/libsystemd-network/dhcp-server-lease-internal.h @@ -38,3 +38,8 @@ int dhcp_server_static_leases_append_json(sd_dhcp_server *server, JsonVariant ** int dhcp_server_save_leases(sd_dhcp_server *server); int dhcp_server_load_leases(sd_dhcp_server *server); +int dhcp_server_leases_file_get_server_address( + int dir_fd, + const char *path, + struct in_addr *ret_address, + uint8_t *ret_prefixlen); diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c index b518b47409..2f84d51e65 100644 --- a/src/libsystemd-network/sd-dhcp-server-lease.c +++ b/src/libsystemd-network/sd-dhcp-server-lease.c @@ -307,7 +307,11 @@ int dhcp_server_save_leases(sd_dhcp_server *server) { if (r < 0) return r; - r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_ID128("BootID", boot_id))); + r = json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_ID128("BootID", boot_id), + JSON_BUILD_PAIR_IN4_ADDR("Address", &(struct in_addr) { .s_addr = server->address }), + JSON_BUILD_PAIR_UNSIGNED("PrefixLength", + in4_addr_netmask_to_prefixlen(&(struct in_addr) { .s_addr = server->netmask })))); if (r < 0) return r; @@ -408,6 +412,8 @@ static int json_dispatch_dhcp_lease(sd_dhcp_server *server, JsonVariant *v, bool typedef struct SavedInfo { sd_id128_t boot_id; + struct in_addr address; + uint8_t prefixlen; JsonVariant *leases; } SavedInfo; @@ -439,6 +445,8 @@ static int load_leases_file(int dir_fd, const char *path, SavedInfo *ret) { static const JsonDispatch dispatch_lease_file_table[] = { { "BootID", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(SavedInfo, boot_id), JSON_MANDATORY }, + { "Address", JSON_VARIANT_ARRAY, json_dispatch_in_addr, offsetof(SavedInfo, address), JSON_MANDATORY }, + { "PrefixLength", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint8, offsetof(SavedInfo, prefixlen), JSON_MANDATORY }, { "Leases", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(SavedInfo, leases), JSON_MANDATORY }, {} }; @@ -480,3 +488,26 @@ int dhcp_server_load_leases(sd_dhcp_server *server) { return r; } + +int dhcp_server_leases_file_get_server_address( + int dir_fd, + const char *path, + struct in_addr *ret_address, + uint8_t *ret_prefixlen) { + + _cleanup_(saved_info_done) SavedInfo info = {}; + int r; + + if (!ret_address && !ret_prefixlen) + return 0; + + r = load_leases_file(dir_fd, path, &info); + if (r < 0) + return r; + + if (ret_address) + *ret_address = info.address; + if (ret_prefixlen) + *ret_prefixlen = info.prefixlen; + return 0; +}