1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

sd-dhcp-server: support static lease outside of address pool

Closes #20341.
This commit is contained in:
Yu Watanabe 2021-08-11 16:18:45 +09:00
parent 3dc8fb0eb8
commit b713a99b1a
4 changed files with 79 additions and 86 deletions

View File

@ -30,6 +30,8 @@ typedef struct DHCPClientId {
} DHCPClientId; } DHCPClientId;
typedef struct DHCPLease { typedef struct DHCPLease {
sd_dhcp_server *server;
DHCPClientId client_id; DHCPClientId client_id;
be32_t address; be32_t address;
@ -67,10 +69,10 @@ struct sd_dhcp_server {
bool emit_router; bool emit_router;
Hashmap *leases_by_client_id; Hashmap *bound_leases_by_client_id;
Hashmap *bound_leases_by_address;
Hashmap *static_leases_by_client_id; Hashmap *static_leases_by_client_id;
DHCPLease **bound_leases; Hashmap *static_leases_by_address;
DHCPLease invalid_lease;
uint32_t max_lease_time, default_lease_time; uint32_t max_lease_time, default_lease_time;

View File

@ -23,7 +23,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3}; static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
uint8_t *client_id; uint8_t *client_id;
DHCPLease *lease; DHCPLease *lease;
int pool_offset;
if (size < sizeof(DHCPMessage)) if (size < sizeof(DHCPMessage))
return 0; return 0;
@ -46,9 +45,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1)); lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1));
lease->expiration = UINT64_MAX; lease->expiration = UINT64_MAX;
memcpy(lease->chaddr, chaddr, 16); memcpy(lease->chaddr, chaddr, 16);
pool_offset = get_pool_offset(server, lease->address); assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0);
server->bound_leases[pool_offset] = lease; assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0);
assert_se(hashmap_ensure_put(&server->leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0); lease->server = server;
(void) dhcp_server_handle_message(server, (DHCPMessage*)data, size); (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size);

View File

@ -28,6 +28,22 @@ static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
if (!lease) if (!lease)
return NULL; return NULL;
if (lease->server) {
DHCPLease *e;
e = hashmap_get(lease->server->bound_leases_by_client_id, &lease->client_id);
if (e == lease) {
hashmap_remove(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address));
hashmap_remove(lease->server->bound_leases_by_client_id, &lease->client_id);
}
e = hashmap_get(lease->server->static_leases_by_client_id, &lease->client_id);
if (e == lease) {
hashmap_remove(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address));
hashmap_remove(lease->server->static_leases_by_client_id, &lease->client_id);
}
}
free(lease->client_id.data); free(lease->client_id.data);
return mfree(lease); return mfree(lease);
} }
@ -85,11 +101,6 @@ int sd_dhcp_server_configure_pool(
if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) { if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
free(server->bound_leases);
server->bound_leases = new0(DHCPLease*, size);
if (!server->bound_leases)
return -ENOMEM;
server->pool_offset = offset; server->pool_offset = offset;
server->pool_size = size; server->pool_size = size;
@ -97,11 +108,9 @@ int sd_dhcp_server_configure_pool(
server->netmask = netmask; server->netmask = netmask;
server->subnet = address->s_addr & netmask; server->subnet = address->s_addr & netmask;
if (server_off >= offset && server_off - offset < size)
server->bound_leases[server_off - offset] = &server->invalid_lease;
/* Drop any leases associated with the old address range */ /* Drop any leases associated with the old address range */
hashmap_clear(server->leases_by_client_id); hashmap_clear(server->bound_leases_by_address);
hashmap_clear(server->bound_leases_by_client_id);
if (server->callback) if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
@ -166,8 +175,10 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
free(server->servers[i].addr); free(server->servers[i].addr);
hashmap_free(server->leases_by_client_id); server->bound_leases_by_address = hashmap_free(server->bound_leases_by_address);
hashmap_free(server->static_leases_by_client_id); server->bound_leases_by_client_id = hashmap_free(server->bound_leases_by_client_id);
server->static_leases_by_address = hashmap_free(server->static_leases_by_address);
server->static_leases_by_client_id = hashmap_free(server->static_leases_by_client_id);
ordered_set_free(server->extra_options); ordered_set_free(server->extra_options);
ordered_set_free(server->vendor_options); ordered_set_free(server->vendor_options);
@ -175,8 +186,6 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
free(server->agent_circuit_id); free(server->agent_circuit_id);
free(server->agent_remote_id); free(server->agent_remote_id);
free(server->bound_leases);
free(server->ifname); free(server->ifname);
return mfree(server); return mfree(server);
} }
@ -837,18 +846,6 @@ static int prepare_new_lease(
return 0; return 0;
} }
static bool static_leases_have_address(sd_dhcp_server *server, be32_t address) {
DHCPLease *s;
assert(server);
HASHMAP_FOREACH(s, server->static_leases_by_client_id)
if (s->address == address)
return true;
return false;
}
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30) #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) { int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) {
@ -878,14 +875,13 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
/* this only fails on critical errors */ /* this only fails on critical errors */
return r; return r;
existing_lease = hashmap_get(server->leases_by_client_id, &req->client_id); existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id); static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
switch(type) { switch(type) {
case DHCP_DISCOVER: { case DHCP_DISCOVER: {
be32_t address = INADDR_ANY; be32_t address = INADDR_ANY;
unsigned i;
log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid)); log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid));
@ -901,7 +897,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
else { else {
struct siphash state; struct siphash state;
uint64_t hash; uint64_t hash;
uint32_t next_offer;
/* even with no persistence of leases, we try to offer the same client /* even with no persistence of leases, we try to offer the same client
the same IP address. we do this by using the hash of the client id the same IP address. we do this by using the hash of the client id
@ -910,18 +905,16 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
siphash24_init(&state, HASH_KEY.bytes); siphash24_init(&state, HASH_KEY.bytes);
client_id_hash_func(&req->client_id, &state); client_id_hash_func(&req->client_id, &state);
hash = htole64(siphash24_finalize(&state)); hash = htole64(siphash24_finalize(&state));
next_offer = hash % server->pool_size;
for (i = 0; i < server->pool_size; i++) { for (unsigned i = 0; i < server->pool_size; i++) {
if (!server->bound_leases[next_offer]) { be32_t tmp_address;
be32_t tmp = server->subnet | htobe32(server->pool_offset + next_offer);
if (!static_leases_have_address(server, tmp)) { tmp_address = server->subnet | htobe32(server->pool_offset + (hash + i) % server->pool_size);
address = tmp; if (!hashmap_contains(server->bound_leases_by_address, &tmp_address) &&
break; !hashmap_contains(server->static_leases_by_address, &tmp_address)) {
} address = tmp_address;
break;
} }
next_offer = (next_offer + 1) % server->pool_size;
} }
} }
@ -945,6 +938,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 1; return 1;
case DHCP_REQUEST: { case DHCP_REQUEST: {
DHCPLease *existing_lease_by_address;
be32_t address; be32_t address;
bool init_reboot = false; bool init_reboot = false;
int pool_offset; int pool_offset;
@ -995,11 +989,12 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
} }
pool_offset = get_pool_offset(server, address); pool_offset = get_pool_offset(server, address);
existing_lease_by_address = hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address));
/* verify that the requested address is from the pool, and either /* verify that the requested address is from the pool, and either
owned by the current client or free */ owned by the current client or free */
if (pool_offset >= 0 && static_lease) { if (static_lease && static_lease->address == address) {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old_lease = NULL; _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
usec_t time_now, expiration; usec_t time_now, expiration;
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now); r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
@ -1020,12 +1015,16 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid)); log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
server->bound_leases[pool_offset] = lease; dhcp_lease_free(hashmap_remove(server->bound_leases_by_client_id, &lease->client_id));
r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
old_lease = hashmap_remove(server->leases_by_client_id, &lease->client_id);
r = hashmap_ensure_put(&server->leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0) if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m"); 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");
lease->server = server;
TAKE_PTR(lease); TAKE_PTR(lease);
if (server->callback) if (server->callback)
@ -1033,11 +1032,13 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return DHCP_ACK; return DHCP_ACK;
} else if (pool_offset >= 0 && server->bound_leases[pool_offset] == existing_lease) { } else if (pool_offset >= 0 && existing_lease_by_address == existing_lease) {
_cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL; _cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL;
usec_t time_now, expiration; usec_t time_now, expiration;
DHCPLease *lease; DHCPLease *lease;
/* Note that in the above condition we accept the case that both leases are NULL. */
r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now); r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0) if (r < 0)
return r; return r;
@ -1063,10 +1064,14 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid)); log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
server->bound_leases[pool_offset] = lease; r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
r = hashmap_ensure_put(&server->leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
if (r < 0) if (r < 0)
return log_dhcp_server_errno(server, r, "Could not save lease: %m"); 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");
lease->server = server;
TAKE_PTR(new_lease); TAKE_PTR(new_lease);
if (server->callback) if (server->callback)
@ -1088,8 +1093,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
} }
case DHCP_RELEASE: { case DHCP_RELEASE: {
int pool_offset;
log_dhcp_server(server, "RELEASE (0x%x)", log_dhcp_server(server, "RELEASE (0x%x)",
be32toh(req->message->xid)); be32toh(req->message->xid));
@ -1099,18 +1102,10 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
if (existing_lease->address != req->message->ciaddr) if (existing_lease->address != req->message->ciaddr)
return 0; return 0;
pool_offset = get_pool_offset(server, req->message->ciaddr); dhcp_lease_free(existing_lease);
if (pool_offset < 0)
return 0;
if (server->bound_leases[pool_offset] == existing_lease) { if (server->callback)
server->bound_leases[pool_offset] = NULL; server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
hashmap_remove(server->leases_by_client_id, &existing_lease->client_id);
dhcp_lease_free(existing_lease);
if (server->callback)
server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
return 0; return 0;
}} }}
@ -1264,24 +1259,17 @@ on_error:
} }
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
int r = 0; DHCPLease *lease;
int k, r = 0;
assert_return(server, -EINVAL); assert_return(server, -EINVAL);
assert(server->bound_leases);
for (uint32_t i = 0; i < server->pool_size; i++) { log_dhcp_server(server, "FORCERENEW");
DHCPLease *lease = server->bound_leases[i];
if (!lease || lease == &server->invalid_lease) HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
continue; k = server_send_forcerenew(server, lease->address, lease->gateway, lease->chaddr);
if (k < 0)
r = server_send_forcerenew(server, lease->address, r = k;
lease->gateway,
lease->chaddr);
if (r < 0)
return r;
log_dhcp_server(server, "FORCERENEW");
} }
return r; return r;
@ -1477,8 +1465,7 @@ int sd_dhcp_server_set_static_lease(
uint8_t *client_id, uint8_t *client_id,
size_t client_id_size) { size_t client_id_size) {
_cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL, *old = NULL; _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
DHCPClientId c;
int r; int r;
assert_return(server, -EINVAL); assert_return(server, -EINVAL);
@ -1490,6 +1477,7 @@ int sd_dhcp_server_set_static_lease(
* the server removes any static lease with the specified mac address. */ * the server removes any static lease with the specified mac address. */
if (!address || address->s_addr == 0) { if (!address || address->s_addr == 0) {
_cleanup_free_ void *data = NULL; _cleanup_free_ void *data = NULL;
DHCPClientId c;
data = memdup(client_id, client_id_size); data = memdup(client_id, client_id_size);
if (!data) if (!data)
@ -1500,11 +1488,11 @@ int sd_dhcp_server_set_static_lease(
.data = data, .data = data,
}; };
old = hashmap_remove(server->static_leases_by_client_id, &c); dhcp_lease_free(hashmap_remove(server->static_leases_by_client_id, &c));
return 0; return 0;
} }
if (static_leases_have_address(server, address->s_addr)) if (hashmap_contains(server->static_leases_by_address, UINT32_TO_PTR(address->s_addr)))
return -EEXIST; return -EEXIST;
lease = new(DHCPLease, 1); lease = new(DHCPLease, 1);
@ -1522,9 +1510,13 @@ int sd_dhcp_server_set_static_lease(
return -ENOMEM; return -ENOMEM;
r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease); 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);
if (r < 0) if (r < 0)
return r; return r;
lease->server = server;
TAKE_PTR(lease); TAKE_PTR(lease);
return 0; return 0;
} }

View File

@ -38,7 +38,7 @@ static int property_get_leases(
if (r < 0) if (r < 0)
return r; return r;
HASHMAP_FOREACH(lease, s->leases_by_client_id) { HASHMAP_FOREACH(lease, s->bound_leases_by_client_id) {
r = sd_bus_message_open_container(reply, 'r', "uayayayayt"); r = sd_bus_message_open_container(reply, 'r', "uayayayayt");
if (r < 0) if (r < 0)
return r; return r;