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.
This commit is contained in:
Yu Watanabe
2024-01-03 06:07:17 +09:00
parent aa8200e971
commit 033f119756
5 changed files with 226 additions and 4 deletions

View File

@@ -75,6 +75,8 @@ struct sd_dhcp_server {
char *agent_circuit_id;
char *agent_remote_id;
char *lease_file;
};
typedef struct DHCPRequest {

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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);