From: Yu Watanabe Date: Tue, 2 Jan 2024 21:07:17 +0000 (+0900) Subject: sd-dhcp-server: introduce sd_dhcp_server_set_lease_file() and dhcp_server_{save,load... X-Git-Tag: v256-rc1~571^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=033f1197564fdaf7b7bbcbc04f49e3363316b4bb;p=thirdparty%2Fsystemd.git sd-dhcp-server: introduce sd_dhcp_server_set_lease_file() and dhcp_server_{save,load}_leases(). The functionality is not used networkd yet in this commit, but will be used in later commits. --- diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 6e4404fb735..8f5707c8e28 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -75,6 +75,8 @@ struct sd_dhcp_server { char *agent_circuit_id; char *agent_remote_id; + + char *lease_file; }; typedef struct DHCPRequest { diff --git a/src/libsystemd-network/dhcp-server-lease-internal.h b/src/libsystemd-network/dhcp-server-lease-internal.h index 82ad1b0aedc..518c1510941 100644 --- a/src/libsystemd-network/dhcp-server-lease-internal.h +++ b/src/libsystemd-network/dhcp-server-lease-internal.h @@ -35,3 +35,6 @@ sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const int dhcp_server_bound_leases_append_json(sd_dhcp_server *server, JsonVariant **v); int dhcp_server_static_leases_append_json(sd_dhcp_server *server, JsonVariant **v); + +int dhcp_server_save_leases(sd_dhcp_server *server); +int dhcp_server_load_leases(sd_dhcp_server *server); diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c index 17160445632..f4c7e8d0a47 100644 --- a/src/libsystemd-network/sd-dhcp-server-lease.c +++ b/src/libsystemd-network/sd-dhcp-server-lease.c @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "dhcp-server-lease-internal.h" +#include "fd-util.h" +#include "fs-util.h" +#include "mkdir.h" +#include "tmpfile-util.h" static sd_dhcp_server_lease* dhcp_server_lease_free(sd_dhcp_server_lease *lease) { if (!lease) @@ -279,3 +283,187 @@ int dhcp_server_static_leases_append_json(sd_dhcp_server *server, JsonVariant ** return json_variant_set_field_non_null(v, "StaticLeases", array); } + +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_fclose_ FILE *f = NULL; + sd_id128_t boot_id; + int r; + + assert(server); + + if (!server->lease_file) + return 0; + + if (hashmap_isempty(server->bound_leases_by_client_id)) { + if (unlink(server->lease_file) < 0 && errno != ENOENT) + return -errno; + + return 0; + } + + r = sd_id128_get_boot(&boot_id); + if (r < 0) + return r; + + r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_ID128("BootID", boot_id))); + if (r < 0) + return r; + + r = dhcp_server_bound_leases_append_json(server, &v); + if (r < 0) + return r; + + r = mkdir_parents(server->lease_file, 0755); + if (r < 0) + return r; + + r = fopen_temporary(server->lease_file, &f, &temp_path); + if (r < 0) + return r; + + (void) fchmod(fileno(f), 0644); + + r = json_variant_dump(v, JSON_FORMAT_NEWLINE | JSON_FORMAT_FLUSH, f, /* prefix = */ NULL); + if (r < 0) + return r; + + r = conservative_rename(temp_path, server->lease_file); + if (r < 0) + return r; + + temp_path = mfree(temp_path); + return 0; +} + +static int json_dispatch_dhcp_lease(sd_dhcp_server *server, JsonVariant *v, bool use_boottime) { + static const JsonDispatch dispatch_table_boottime[] = { + { "ClientId", JSON_VARIANT_ARRAY, json_dispatch_client_id, offsetof(sd_dhcp_server_lease, client_id), JSON_MANDATORY }, + { "Address", JSON_VARIANT_ARRAY, json_dispatch_in_addr, offsetof(sd_dhcp_server_lease, address), JSON_MANDATORY }, + { "Hostname", JSON_VARIANT_STRING, json_dispatch_string, offsetof(sd_dhcp_server_lease, hostname), 0 }, + { "ExpirationUSec", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(sd_dhcp_server_lease, expiration), JSON_MANDATORY }, + { "ExpirationRealtimeUSec", _JSON_VARIANT_TYPE_INVALID, NULL, 0, JSON_MANDATORY }, + {} + }, dispatch_table_realtime[] = { + { "ClientId", JSON_VARIANT_ARRAY, json_dispatch_client_id, offsetof(sd_dhcp_server_lease, client_id), JSON_MANDATORY }, + { "Address", JSON_VARIANT_ARRAY, json_dispatch_in_addr, offsetof(sd_dhcp_server_lease, address), JSON_MANDATORY }, + { "Hostname", JSON_VARIANT_STRING, json_dispatch_string, offsetof(sd_dhcp_server_lease, hostname), 0 }, + { "ExpirationUSec", _JSON_VARIANT_TYPE_INVALID, NULL, 0, JSON_MANDATORY }, + { "ExpirationRealtimeUSec", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(sd_dhcp_server_lease, expiration), JSON_MANDATORY }, + {} + }; + + _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL; + usec_t now_b; + int r; + + assert(server); + assert(v); + + lease = new(sd_dhcp_server_lease, 1); + if (!lease) + return -ENOMEM; + + *lease = (sd_dhcp_server_lease) { + .n_ref = 1, + }; + + r = json_dispatch(v, use_boottime ? dispatch_table_boottime : dispatch_table_realtime, JSON_ALLOW_EXTENSIONS, lease); + if (r < 0) + return r; + + r = sd_event_now(server->event, CLOCK_BOOTTIME, &now_b); + if (r < 0) + return r; + + if (use_boottime) { + if (lease->expiration < now_b) + return 0; /* already expired */ + } else { + usec_t now_r; + + r = sd_event_now(server->event, CLOCK_REALTIME, &now_r); + if (r < 0) + return r; + + if (lease->expiration < now_r) + return 0; /* already expired */ + + lease->expiration = map_clock_usec_raw(lease->expiration, now_r, now_b); + } + + r = dhcp_server_put_lease(server, lease, /* is_static = */ false); + if (r == -EEXIST) + return 0; + if (r < 0) + return r; + + TAKE_PTR(lease); + return 0; +} + +typedef struct SavedInfo { + sd_id128_t boot_id; + 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 }, + {} + }; + + 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; +} + +int dhcp_server_load_leases(sd_dhcp_server *server) { + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + size_t n, m; + int r; + + assert(server); + assert(server->event); + + if (!server->lease_file) + return 0; + + r = json_parse_file( + /* f = */ NULL, + server->lease_file, + /* flags = */ 0, + &v, + /* ret_line = */ NULL, + /* ret_column = */ NULL); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + n = hashmap_size(server->bound_leases_by_client_id); + + r = dhcp_server_dispatch_leases(server, v); + + m = hashmap_size(server->bound_leases_by_client_id); + assert(m >= n); + log_dhcp_server(server, "Loaded %zu lease(s) from %s.", m - n, server->lease_file); + + return r; +} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index dd4cbd3064b..bbe96be8346 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -22,6 +22,7 @@ #include "memory-util.h" #include "network-common.h" #include "ordered-set.h" +#include "path-util.h" #include "siphash24.h" #include "string-util.h" #include "unaligned.h" @@ -30,6 +31,19 @@ #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) +static void server_on_lease_change(sd_dhcp_server *server) { + int r; + + assert(server); + + r = dhcp_server_save_leases(server); + if (r < 0) + log_dhcp_server_errno(server, r, "Failed to save leases, ignoring: %m"); + + if (server->callback) + server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); +} + /* configures the server's address and subnet, and optionally the pool's size and offset into the subnet * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address * moreover, the server's own address may be in the pool, and is in that case reserved in order not to @@ -130,6 +144,8 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { free(server->agent_circuit_id); free(server->agent_remote_id); + free(server->lease_file); + free(server->ifname); return mfree(server); } @@ -981,8 +997,7 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, be32_t a log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid)); - if (server->callback) - server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); + server_on_lease_change(server); return DHCP_ACK; } @@ -1188,8 +1203,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz sd_dhcp_server_lease_unref(existing_lease); - if (server->callback) - server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); + server_on_lease_change(server); return 0; }} @@ -1343,6 +1357,10 @@ int sd_dhcp_server_start(sd_dhcp_server *server) { goto on_error; } + r = dhcp_server_load_leases(server); + if (r < 0) + log_dhcp_server_errno(server, r, "Failed to load lease file %s, ignoring: %m", strna(server->lease_file)); + log_dhcp_server(server, "STARTED"); return 0; @@ -1567,3 +1585,13 @@ int sd_dhcp_server_set_relay_agent_information( free_and_replace(server->agent_remote_id, remote_id_dup); return 0; } + +int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, const char *path) { + assert_return(server, -EINVAL); + assert_return(!sd_dhcp_server_is_running(server), -EBUSY); + + if (path && !path_is_safe(path)) + return -EINVAL; + + return free_and_strdup(&server->lease_file, path); +} diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index feafa5d1fc6..486af48b251 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -81,6 +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_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);