mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 08:25:20 +09:00
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:
@@ -75,6 +75,8 @@ struct sd_dhcp_server {
|
||||
|
||||
char *agent_circuit_id;
|
||||
char *agent_remote_id;
|
||||
|
||||
char *lease_file;
|
||||
};
|
||||
|
||||
typedef struct DHCPRequest {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user