summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network/sd-dhcp-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network/sd-dhcp-server.c')
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c349
1 files changed, 87 insertions, 262 deletions
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index b87e4d6..c3b0f82 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -14,6 +14,7 @@
#include "dhcp-option.h"
#include "dhcp-packet.h"
#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "in-addr-util.h"
@@ -21,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"
@@ -29,20 +31,17 @@
#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
-DHCPLease *dhcp_lease_free(DHCPLease *lease) {
- if (!lease)
- return NULL;
+static void server_on_lease_change(sd_dhcp_server *server) {
+ int r;
- if (lease->server) {
- hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
- hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
- hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
- hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
- }
+ assert(server);
- free(lease->client_id.data);
- free(lease->hostname);
- return mfree(lease);
+ 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
@@ -102,13 +101,6 @@ int sd_dhcp_server_configure_pool(
server->address = address->s_addr;
server->netmask = netmask;
server->subnet = address->s_addr & netmask;
-
- /* Drop any leases associated with the old address range */
- hashmap_clear(server->bound_leases_by_address);
- hashmap_clear(server->bound_leases_by_client_id);
-
- if (server->callback)
- server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
return 0;
@@ -127,38 +119,6 @@ int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
return in4_addr_is_set(&server->relay_target);
}
-void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
- assert(id);
- assert(id->length > 0);
- assert(id->data);
-
- siphash24_compress(&id->length, sizeof(id->length), state);
- siphash24_compress(id->data, id->length, state);
-}
-
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
- int r;
-
- assert(a->length > 0);
- assert(a->data);
- assert(b->length > 0);
- assert(b->data);
-
- r = CMP(a->length, b->length);
- if (r != 0)
- return r;
-
- return memcmp(a->data, b->data, a->length);
-}
-
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
- dhcp_lease_hash_ops,
- DHCPClientId,
- client_id_hash_func,
- client_id_compare_func,
- DHCPLease,
- dhcp_lease_free);
-
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
assert(server);
@@ -184,6 +144,9 @@ 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);
return mfree(server);
}
@@ -212,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);
@@ -786,16 +750,8 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
break;
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
- if (len >= 2) {
- uint8_t *data;
-
- data = memdup(option, len);
- if (!data)
- return -ENOMEM;
-
- free_and_replace(req->client_id.data, data);
- req->client_id.length = len;
- }
+ if (client_id_size_is_valid(len))
+ (void) sd_dhcp_client_id_set_raw(&req->client_id, option, len);
break;
case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
@@ -835,7 +791,6 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
if (!req)
return NULL;
- free(req->client_id.data);
free(req->hostname);
return mfree(req);
}
@@ -843,6 +798,8 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
+ int r;
+
assert(req);
assert(message);
@@ -852,39 +809,39 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes
return -EBADMSG;
/* set client id based on MAC address if client did not send an explicit one */
- if (!req->client_id.data) {
- uint8_t *data;
-
- if (message->hlen == 0)
+ if (!sd_dhcp_client_id_is_set(&req->client_id)) {
+ if (!client_id_data_size_is_valid(message->hlen))
return -EBADMSG;
- data = new0(uint8_t, message->hlen + 1);
- if (!data)
- return -ENOMEM;
-
- data[0] = 0x01;
- memcpy(data + 1, message->chaddr, message->hlen);
-
- req->client_id.length = message->hlen + 1;
- req->client_id.data = data;
+ r = sd_dhcp_client_id_set(&req->client_id, /* type = */ 1, message->chaddr, message->hlen);
+ if (r < 0)
+ return r;
}
if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) {
+ uint8_t type;
+ const void *data;
+ size_t size;
+
/* See RFC2131 section 4.1.1.
* hlen and chaddr may not be set for non-ethernet interface.
* Let's try to retrieve it from the client ID. */
- if (!req->client_id.data)
+ if (!sd_dhcp_client_id_is_set(&req->client_id))
return -EBADMSG;
- if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1)
+ r = sd_dhcp_client_id_get(&req->client_id, &type, &data, &size);
+ if (r < 0)
+ return r;
+
+ if (type != 1)
return -EBADMSG;
- if (req->client_id.data[0] != 0x01)
+ if (size > sizeof(message->chaddr))
return -EBADMSG;
- message->hlen = req->client_id.length - 1;
- memcpy(message->chaddr, req->client_id.data + 1, message->hlen);
+ memcpy(message->chaddr, data, size);
+ message->hlen = size;
}
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
@@ -1020,44 +977,7 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
return -EBADMSG;
}
-static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest *req, usec_t expiration) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
- assert(ret_lease);
- assert(address != 0);
- assert(req);
- assert(expiration != 0);
-
- lease = new(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- *lease = (DHCPLease) {
- .address = address,
- .client_id.length = req->client_id.length,
- .htype = req->message->htype,
- .hlen = req->message->hlen,
- .gateway = req->message->giaddr,
- .expiration = expiration,
- };
- lease->client_id.data = memdup(req->client_id.data, req->client_id.length);
- if (!lease->client_id.data)
- return -ENOMEM;
-
- memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
-
- if (req->hostname) {
- lease->hostname = strdup(req->hostname);
- if (!lease->hostname)
- return -ENOMEM;
- }
-
- *ret_lease = TAKE_PTR(lease);
-
- return 0;
-}
-
-static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) {
+static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
usec_t expiration;
int r;
@@ -1069,30 +989,9 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
if (r < 0)
return r;
- if (existing_lease) {
- assert(existing_lease->server);
- assert(existing_lease->address == address);
- existing_lease->expiration = expiration;
-
- } else {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
- r = prepare_new_lease(&lease, address, req, expiration);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
-
- lease->server = server; /* This must be set just before hashmap_put(). */
-
- r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
- r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
- TAKE_PTR(lease);
- }
+ r = dhcp_server_set_lease(server, address, req, expiration);
+ if (r < 0)
+ return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
if (r < 0)
@@ -1100,32 +999,11 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
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;
}
-static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
- DHCPLease *lease;
- usec_t time_now;
- int r;
-
- assert(server);
-
- r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
- if (r < 0)
- return r;
-
- HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
- if (lease->expiration < time_now) {
- log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
- dhcp_lease_free(lease);
- }
-
- return 0;
-}
-
static bool address_available(sd_dhcp_server *server, be32_t address) {
assert(server);
@@ -1137,46 +1015,12 @@ static bool address_available(sd_dhcp_server *server, be32_t address) {
return true;
}
-static int server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req, DHCPLease **ret) {
- DHCPLease *static_lease;
- _cleanup_free_ uint8_t *data = NULL;
-
- assert(server);
- assert(req);
- assert(ret);
-
- static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
- if (static_lease) {
- *ret = static_lease;
- return 0;
- }
-
- /* when no lease is found based on the client id fall back to chaddr */
- data = new(uint8_t, req->message->hlen + 1);
- if (!data)
- return -ENOMEM;
-
- /* set client id type to 1: Ethernet Link-Layer (RFC 2132) */
- data[0] = 0x01;
- memcpy(data + 1, req->message->chaddr, req->message->hlen);
-
- static_lease = hashmap_get(server->static_leases_by_client_id,
- &(DHCPClientId) {
- .length = req->message->hlen + 1,
- .data = data,
- });
-
- *ret = static_lease;
-
- return 0;
-}
-
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp) {
_cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
_cleanup_free_ char *error_message = NULL;
- DHCPLease *existing_lease, *static_lease;
+ sd_dhcp_server_lease *existing_lease, *static_lease;
int type, r;
assert(server);
@@ -1204,9 +1048,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return r;
existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
- r = server_get_static_lease(server, req, &static_lease);
- if (r < 0)
- return r;
+ static_lease = dhcp_server_get_static_lease(server, req);
switch (type) {
@@ -1220,10 +1062,19 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 0;
/* for now pick a random free address from the pool */
- if (static_lease)
+ if (static_lease) {
+ if (existing_lease != hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(static_lease->address)))
+ /* The address is already assigned to another host. Refusing. */
+ return 0;
+
+ /* Found a matching static lease. */
address = static_lease->address;
- else if (existing_lease)
+
+ } else if (existing_lease && address_is_in_pool(server, existing_lease->address))
+
+ /* If we previously assigned an address to the host, then reuse it. */
address = existing_lease->address;
+
else {
struct siphash state;
uint64_t hash;
@@ -1252,7 +1103,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 0;
if (server->rapid_commit && req->rapid_commit)
- return server_ack_request(server, req, existing_lease, address);
+ return server_ack_request(server, req, address);
r = server_send_offer_or_ack(server, req, address, DHCP_OFFER);
if (r < 0)
@@ -1321,30 +1172,24 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
/* Silently ignore Rapid Commit option in REQUEST message. */
req->rapid_commit = false;
- /* disallow our own address */
- if (address == server->address)
- return 0;
-
if (static_lease) {
- /* Found a static lease for the client ID. */
-
if (static_lease->address != address)
- /* The client requested an address which is different from the static lease. Refuse. */
+ /* The client requested an address which is different from the static lease. Refusing. */
return server_send_nak_or_ignore(server, init_reboot, req);
- return server_ack_request(server, req, existing_lease, address);
- }
-
- if (address_is_in_pool(server, address)) {
- /* The requested address is in the pool. */
-
- if (existing_lease && existing_lease->address != address)
- /* We previously assigned an address, but the client requested another one. Refuse. */
+ if (existing_lease != hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address)))
+ /* The requested address is already assigned to another host. Refusing. */
return server_send_nak_or_ignore(server, init_reboot, req);
- return server_ack_request(server, req, existing_lease, address);
+ /* Found a static lease for the client ID. */
+ return server_ack_request(server, req, address);
}
+ if (address_is_in_pool(server, address))
+ /* The requested address is in the pool. */
+ return server_ack_request(server, req, address);
+
+ /* Refuse otherwise. */
return server_send_nak_or_ignore(server, init_reboot, req);
}
@@ -1358,10 +1203,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
if (existing_lease->address != req->message->ciaddr)
return 0;
- dhcp_lease_free(existing_lease);
+ 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;
}}
@@ -1515,6 +1359,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;
@@ -1525,7 +1373,7 @@ on_error:
}
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
- DHCPLease *lease;
+ sd_dhcp_server_lease *lease;
int r = 0;
assert_return(server, -EINVAL);
@@ -1740,55 +1588,32 @@ int sd_dhcp_server_set_relay_agent_information(
return 0;
}
-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) {
-
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
+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(client_id, -EINVAL);
- assert_return(client_id_size > 0, -EINVAL);
+ assert_return(!path || (dir_fd >= 0 || dir_fd == AT_FDCWD), -EBADF);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
- /* Static lease with an empty or omitted address is a valid entry,
- * the server removes any static lease with the specified mac address. */
- if (!address || address->s_addr == 0) {
- DHCPClientId c;
-
- c = (DHCPClientId) {
- .length = client_id_size,
- .data = client_id,
- };
-
- dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &c));
+ 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;
}
- lease = new(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- *lease = (DHCPLease) {
- .address = address->s_addr,
- .client_id.length = client_id_size,
- };
- lease->client_id.data = memdup(client_id, client_id_size);
- if (!lease->client_id.data)
- return -ENOMEM;
+ if (!path_is_safe(path))
+ return -EINVAL;
- lease->server = server; /* This must be set just before hashmap_put(). */
+ _cleanup_close_ int fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH);
+ if (fd < 0)
+ return fd;
- r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return r;
- r = hashmap_ensure_put(&server->static_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ r = free_and_strdup(&server->lease_file, path);
if (r < 0)
return r;
- TAKE_PTR(lease);
+ close_and_replace(server->lease_dir_fd, fd);
+
return 0;
}