From 95c48613f4820bde38594272265a94fba74abe77 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Jan 2024 06:06:42 +0900 Subject: [PATCH 1/2] sd-dhcp-server: use sd_dhcp_client_id --- .../dhcp-client-id-internal.h | 4 + src/libsystemd-network/dhcp-server-internal.h | 13 +- src/libsystemd-network/fuzz-dhcp-server.c | 10 +- src/libsystemd-network/sd-dhcp-client-id.c | 15 ++ src/libsystemd-network/sd-dhcp-server.c | 143 ++++++------------ src/libsystemd-network/test-dhcp-server.c | 26 ++-- src/network/networkd-dhcp-server-bus.c | 2 +- src/network/networkd-json.c | 8 +- 8 files changed, 85 insertions(+), 136 deletions(-) diff --git a/src/libsystemd-network/dhcp-client-id-internal.h b/src/libsystemd-network/dhcp-client-id-internal.h index eb7b1847e6..72f13de24e 100644 --- a/src/libsystemd-network/dhcp-client-id-internal.h +++ b/src/libsystemd-network/dhcp-client-id-internal.h @@ -5,6 +5,7 @@ #include "dhcp-duid-internal.h" #include "macro.h" +#include "siphash24.h" #include "sparse-endian.h" /* RFC 2132 section 9.14: its minimum length is 2. @@ -51,3 +52,6 @@ static inline bool client_id_size_is_valid(size_t size) { static inline bool client_id_data_size_is_valid(size_t size) { return size >= MIN_CLIENT_ID_DATA_LEN && size <= MAX_CLIENT_ID_DATA_LEN; } + +void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state); +int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b); diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index da9e56b943..5991ae6ada 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -8,6 +8,7 @@ #include "sd-dhcp-server.h" #include "sd-event.h" +#include "dhcp-client-id-internal.h" #include "dhcp-option.h" #include "network-common.h" #include "ordered-set.h" @@ -24,15 +25,10 @@ typedef enum DHCPRawOption { _DHCP_RAW_OPTION_DATA_INVALID, } DHCPRawOption; -typedef struct DHCPClientId { - size_t length; - uint8_t *data; -} DHCPClientId; - typedef struct DHCPLease { sd_dhcp_server *server; - DHCPClientId client_id; + sd_dhcp_client_id client_id; uint8_t htype; /* e.g. ARPHRD_ETHER */ uint8_t hlen; /* e.g. ETH_ALEN */ @@ -100,7 +96,7 @@ typedef struct DHCPRequest { DHCPMessage *message; /* options */ - DHCPClientId client_id; + sd_dhcp_client_id client_id; size_t max_optlen; be32_t server_id; be32_t requested_ip; @@ -121,9 +117,6 @@ int dhcp_server_send_packet(sd_dhcp_server *server, DHCPRequest *req, DHCPPacket *packet, int type, size_t optoffset); -void client_id_hash_func(const DHCPClientId *p, struct siphash *state); -int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b); - DHCPLease *dhcp_lease_free(DHCPLease *lease); DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free); diff --git a/src/libsystemd-network/fuzz-dhcp-server.c b/src/libsystemd-network/fuzz-dhcp-server.c index fddb3a59eb..3929a2a4d7 100644 --- a/src/libsystemd-network/fuzz-dhcp-server.c +++ b/src/libsystemd-network/fuzz-dhcp-server.c @@ -35,15 +35,11 @@ static int add_lease(sd_dhcp_server *server, const struct in_addr *server_addres .hlen = ETH_ALEN, .htype = ARPHRD_ETHER, - .client_id.length = 2, + .client_id.size = 2, }; - lease->client_id.data = new(uint8_t, lease->client_id.length); - if (!lease->client_id.data) - return -ENOMEM; - - lease->client_id.data[0] = 2; - lease->client_id.data[1] = i; + lease->client_id.raw[0] = 2; + lease->client_id.raw[1] = i; lease->server = server; /* This must be set just before hashmap_put(). */ diff --git a/src/libsystemd-network/sd-dhcp-client-id.c b/src/libsystemd-network/sd-dhcp-client-id.c index 03dca43822..6feec25adf 100644 --- a/src/libsystemd-network/sd-dhcp-client-id.c +++ b/src/libsystemd-network/sd-dhcp-client-id.c @@ -157,3 +157,18 @@ int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, cha return sd_dhcp_client_id_to_string(&client_id, ret); } + +void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state) { + assert(sd_dhcp_client_id_is_set(client_id)); + assert(state); + + siphash24_compress_typesafe(client_id->size, state); + siphash24_compress(client_id->raw, client_id->size, state); +} + +int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b) { + assert(sd_dhcp_client_id_is_set(a)); + assert(sd_dhcp_client_id_is_set(b)); + + return memcmp_nn(a->raw, a->size, b->raw, b->size); +} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 7eaca0bb71..7ece30cf7b 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -40,7 +40,6 @@ DHCPLease *dhcp_lease_free(DHCPLease *lease) { hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease); } - free(lease->client_id.data); free(lease->hostname); return mfree(lease); } @@ -127,33 +126,9 @@ 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_typesafe(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, + sd_dhcp_client_id, client_id_hash_func, client_id_compare_func, DHCPLease, @@ -786,16 +761,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: @@ -833,7 +800,6 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) { if (!req) return NULL; - free(req->client_id.data); free(req->hostname); return mfree(req); } @@ -841,6 +807,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); @@ -850,39 +818,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) @@ -1032,15 +1000,12 @@ static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest *lease = (DHCPLease) { .address = address, - .client_id.length = req->client_id.length, + .client_id = req->client_id, .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); @@ -1135,38 +1100,25 @@ 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) { +static DHCPLease* server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req) { DHCPLease *static_lease; - _cleanup_free_ uint8_t *data = NULL; + sd_dhcp_client_id client_id; 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; - } + if (static_lease) + return static_lease; /* 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; + if (!client_id_data_size_is_valid(req->message->hlen)) + return NULL; - /* set client id type to 1: Ethernet Link-Layer (RFC 2132) */ - data[0] = 0x01; - memcpy(data + 1, req->message->chaddr, req->message->hlen); + if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0) + return NULL; - static_lease = hashmap_get(server->static_leases_by_client_id, - &(DHCPClientId) { - .length = req->message->hlen + 1, - .data = data, - }); - - *ret = static_lease; - - return 0; + return hashmap_get(server->static_leases_by_client_id, &client_id); } #define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30) @@ -1202,9 +1154,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 = server_get_static_lease(server, req); switch (type) { @@ -1741,28 +1691,26 @@ int sd_dhcp_server_set_relay_agent_information( int sd_dhcp_server_set_static_lease( sd_dhcp_server *server, const struct in_addr *address, - uint8_t *client_id, + uint8_t *client_id_raw, size_t client_id_size) { _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL; + sd_dhcp_client_id client_id; int r; assert_return(server, -EINVAL); - assert_return(client_id, -EINVAL); - assert_return(client_id_size > 0, -EINVAL); + assert_return(client_id_raw, -EINVAL); + assert_return(client_id_size_is_valid(client_id_size), -EINVAL); assert_return(!sd_dhcp_server_is_running(server), -EBUSY); + r = sd_dhcp_client_id_set_raw(&client_id, client_id_raw, client_id_size); + if (r < 0) + return r; + /* 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)); + dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &client_id)); return 0; } @@ -1772,11 +1720,8 @@ int sd_dhcp_server_set_static_lease( *lease = (DHCPLease) { .address = address->s_addr, - .client_id.length = client_id_size, + .client_id = client_id, }; - lease->client_id.data = memdup(client_id, client_id_size); - if (!lease->client_id.data) - return -ENOMEM; lease->server = server; /* This must be set just before hashmap_put(). */ diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index e04a819eea..9c343f8368 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -223,7 +223,7 @@ static void test_message_handler(void) { assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK); } -static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) { +static uint64_t client_id_hash_helper(sd_dhcp_client_id *id, uint8_t key[HASH_KEY_SIZE]) { struct siphash state; siphash24_init(&state, key); @@ -233,10 +233,10 @@ static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZ } static void test_client_id_hash(void) { - DHCPClientId a = { - .length = 4, + sd_dhcp_client_id a = { + .size = 4, }, b = { - .length = 4, + .size = 4, }; uint8_t hash_key[HASH_KEY_SIZE] = { '0', '1', '2', '3', '4', '5', '6', '7', @@ -245,29 +245,25 @@ static void test_client_id_hash(void) { log_debug("/* %s */", __func__); - a.data = (uint8_t*)strdup("abcd"); - b.data = (uint8_t*)strdup("abcd"); + memcpy(a.raw, "abcd", 4); + memcpy(b.raw, "abcd", 4); assert_se(client_id_compare_func(&a, &b) == 0); assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); - a.length = 3; + a.size = 3; assert_se(client_id_compare_func(&a, &b) != 0); - a.length = 4; + a.size = 4; assert_se(client_id_compare_func(&a, &b) == 0); assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); - b.length = 3; + b.size = 3; assert_se(client_id_compare_func(&a, &b) != 0); - b.length = 4; + b.size = 4; assert_se(client_id_compare_func(&a, &b) == 0); assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key)); - free(b.data); - b.data = (uint8_t*)strdup("abce"); + memcpy(b.raw, "abce", 4); assert_se(client_id_compare_func(&a, &b) != 0); - - free(a.data); - free(b.data); } static void test_static_lease(void) { diff --git a/src/network/networkd-dhcp-server-bus.c b/src/network/networkd-dhcp-server-bus.c index e3397c3208..0197f8ad7b 100644 --- a/src/network/networkd-dhcp-server-bus.c +++ b/src/network/networkd-dhcp-server-bus.c @@ -44,7 +44,7 @@ static int property_get_leases( if (r < 0) return r; - r = sd_bus_message_append_array(reply, 'y', lease->client_id.data, lease->client_id.length); + r = sd_bus_message_append_array(reply, 'y', lease->client_id.raw, lease->client_id.size); if (r < 0) return r; diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index bc7e51ebe3..f7a4ed0141 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -909,8 +909,8 @@ static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) { JSON_BUILD_OBJECT( JSON_BUILD_PAIR_BYTE_ARRAY( "ClientId", - lease->client_id.data, - lease->client_id.length), + lease->client_id.raw, + lease->client_id.size), JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address), JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname), JSON_BUILD_PAIR_FINITE_USEC( @@ -941,8 +941,8 @@ static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) { JSON_BUILD_OBJECT( JSON_BUILD_PAIR_BYTE_ARRAY( "ClientId", - lease->client_id.data, - lease->client_id.length), + lease->client_id.raw, + lease->client_id.size), JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &address))); if (r < 0) return r; From dd8ab4a2060ed1dd0ccf1044f221750bd193cd85 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 3 Jan 2024 06:06:45 +0900 Subject: [PATCH 2/2] sd-dhcp-server: rename DHCPLease -> sd_dhcp_server_lease Then, move basic functions for the object to sd-dhcp-server-lease.[ch]. No effective funcional changes. --- src/libsystemd-network/dhcp-server-internal.h | 19 -- .../dhcp-server-lease-internal.h | 33 +++ src/libsystemd-network/fuzz-dhcp-server.c | 15 +- src/libsystemd-network/meson.build | 1 + src/libsystemd-network/sd-dhcp-server-lease.c | 193 ++++++++++++++++++ src/libsystemd-network/sd-dhcp-server.c | 190 ++--------------- src/network/networkd-dhcp-server-bus.c | 4 +- src/network/networkd-json.c | 6 +- src/systemd/meson.build | 1 + src/systemd/sd-dhcp-server-lease.h | 34 +++ 10 files changed, 284 insertions(+), 212 deletions(-) create mode 100644 src/libsystemd-network/dhcp-server-lease-internal.h create mode 100644 src/libsystemd-network/sd-dhcp-server-lease.c create mode 100644 src/systemd/sd-dhcp-server-lease.h diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 5991ae6ada..6e4404fb73 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -25,20 +25,6 @@ typedef enum DHCPRawOption { _DHCP_RAW_OPTION_DATA_INVALID, } DHCPRawOption; -typedef struct DHCPLease { - sd_dhcp_server *server; - - sd_dhcp_client_id client_id; - - uint8_t htype; /* e.g. ARPHRD_ETHER */ - uint8_t hlen; /* e.g. ETH_ALEN */ - be32_t address; - be32_t gateway; - uint8_t chaddr[16]; - usec_t expiration; - char *hostname; -} DHCPLease; - struct sd_dhcp_server { unsigned n_ref; @@ -109,17 +95,12 @@ typedef struct DHCPRequest { triple_timestamp timestamp; } DHCPRequest; -extern const struct hash_ops dhcp_lease_hash_ops; - int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp); int dhcp_server_send_packet(sd_dhcp_server *server, DHCPRequest *req, DHCPPacket *packet, int type, size_t optoffset); -DHCPLease *dhcp_lease_free(DHCPLease *lease); -DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free); - #define log_dhcp_server_errno(server, error, fmt, ...) \ log_interface_prefix_full_errno( \ "DHCPv4 server: ", \ diff --git a/src/libsystemd-network/dhcp-server-lease-internal.h b/src/libsystemd-network/dhcp-server-lease-internal.h new file mode 100644 index 0000000000..49bc173307 --- /dev/null +++ b/src/libsystemd-network/dhcp-server-lease-internal.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-dhcp-server-lease.h" + +#include "dhcp-client-id-internal.h" +#include "dhcp-server-internal.h" +#include "time-util.h" + +typedef struct sd_dhcp_server_lease { + unsigned n_ref; + + sd_dhcp_server *server; + + sd_dhcp_client_id client_id; + + uint8_t htype; /* e.g. ARPHRD_ETHER */ + uint8_t hlen; /* e.g. ETH_ALEN */ + be32_t address; + be32_t gateway; + uint8_t chaddr[16]; + usec_t expiration; + char *hostname; +} sd_dhcp_server_lease; + +extern const struct hash_ops dhcp_server_lease_hash_ops; + +int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static); + +int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration); +int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server); + +sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req); diff --git a/src/libsystemd-network/fuzz-dhcp-server.c b/src/libsystemd-network/fuzz-dhcp-server.c index 3929a2a4d7..3e1efcf7fe 100644 --- a/src/libsystemd-network/fuzz-dhcp-server.c +++ b/src/libsystemd-network/fuzz-dhcp-server.c @@ -18,16 +18,17 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { } static int add_lease(sd_dhcp_server *server, const struct in_addr *server_address, uint8_t i) { - _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL; + _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL; int r; assert(server); - lease = new(DHCPLease, 1); + lease = new(sd_dhcp_server_lease, 1); if (!lease) return -ENOMEM; - *lease = (DHCPLease) { + *lease = (sd_dhcp_server_lease) { + .n_ref = 1, .address = htobe32(UINT32_C(10) << 24 | i), .chaddr = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, .expiration = UINT64_MAX, @@ -41,13 +42,7 @@ static int add_lease(sd_dhcp_server *server, const struct in_addr *server_addres lease->client_id.raw[0] = 2; lease->client_id.raw[1] = i; - 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 r; - - r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease); + r = dhcp_server_put_lease(server, lease, /* is_static = */ false); if (r < 0) return r; diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index 33ab5f7f8c..277935296f 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -19,6 +19,7 @@ sources = files( 'sd-dhcp-client.c', 'sd-dhcp-duid.c', 'sd-dhcp-lease.c', + 'sd-dhcp-server-lease.c', 'sd-dhcp-server.c', 'sd-dhcp6-client.c', 'sd-dhcp6-lease.c', diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c new file mode 100644 index 0000000000..16880a80b6 --- /dev/null +++ b/src/libsystemd-network/sd-dhcp-server-lease.c @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dhcp-server-lease-internal.h" + +static sd_dhcp_server_lease* dhcp_server_lease_free(sd_dhcp_server_lease *lease) { + if (!lease) + return NULL; + + 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); + } + + free(lease->hostname); + return mfree(lease); +} + +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease, dhcp_server_lease_free); + +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + dhcp_server_lease_hash_ops, + sd_dhcp_client_id, + client_id_hash_func, + client_id_compare_func, + sd_dhcp_server_lease, + sd_dhcp_server_lease_unref); + +int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static) { + int r; + + assert(server); + assert(lease); + + lease->server = server; /* This must be set before hashmap_put(). */ + + r = hashmap_ensure_put(is_static ? &server->static_leases_by_client_id : &server->bound_leases_by_client_id, + &dhcp_server_lease_hash_ops, &lease->client_id, lease); + if (r < 0) + return r; + + r = hashmap_ensure_put(is_static ? &server->static_leases_by_address : &server->bound_leases_by_address, + NULL, UINT32_TO_PTR(lease->address), lease); + if (r < 0) + return r; + + return 0; +} + +int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration) { + _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL; + int r; + + assert(server); + assert(address != 0); + assert(req); + assert(expiration != 0); + + /* If a lease for the host already exists, update it. */ + lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id); + if (lease) { + if (lease->address != address) { + hashmap_remove_value(server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease); + lease->address = address; + + r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease); + if (r < 0) + return r; + } + + lease->expiration = expiration; + + TAKE_PTR(lease); + return 0; + } + + /* Otherwise, add a new lease. */ + + lease = new(sd_dhcp_server_lease, 1); + if (!lease) + return -ENOMEM; + + *lease = (sd_dhcp_server_lease) { + .n_ref = 1, + .address = address, + .client_id = req->client_id, + .htype = req->message->htype, + .hlen = req->message->hlen, + .gateway = req->message->giaddr, + .expiration = expiration, + }; + + memcpy(lease->chaddr, req->message->chaddr, req->message->hlen); + + if (req->hostname) { + lease->hostname = strdup(req->hostname); + if (!lease->hostname) + return -ENOMEM; + } + + r = dhcp_server_put_lease(server, lease, /* is_static = */ false); + if (r < 0) + return r; + + TAKE_PTR(lease); + return 0; +} + +int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) { + sd_dhcp_server_lease *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)); + sd_dhcp_server_lease_unref(lease); + } + + return 0; +} + +sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req) { + sd_dhcp_server_lease *static_lease; + sd_dhcp_client_id client_id; + + assert(server); + assert(req); + + static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id); + if (static_lease) + return static_lease; + + /* when no lease is found based on the client id fall back to chaddr */ + if (!client_id_data_size_is_valid(req->message->hlen)) + return NULL; + + if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0) + return NULL; + + return hashmap_get(server->static_leases_by_client_id, &client_id); +} + +int sd_dhcp_server_set_static_lease( + sd_dhcp_server *server, + const struct in_addr *address, + uint8_t *client_id_raw, + size_t client_id_size) { + + _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL; + sd_dhcp_client_id client_id; + int r; + + assert_return(server, -EINVAL); + assert_return(client_id_raw, -EINVAL); + assert_return(client_id_size_is_valid(client_id_size), -EINVAL); + assert_return(!sd_dhcp_server_is_running(server), -EBUSY); + + r = sd_dhcp_client_id_set_raw(&client_id, client_id_raw, client_id_size); + if (r < 0) + return r; + + /* 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) { + sd_dhcp_server_lease_unref(hashmap_get(server->static_leases_by_client_id, &client_id)); + return 0; + } + + lease = new(sd_dhcp_server_lease, 1); + if (!lease) + return -ENOMEM; + + *lease = (sd_dhcp_server_lease) { + .n_ref = 1, + .address = address->s_addr, + .client_id = client_id, + }; + + r = dhcp_server_put_lease(server, lease, /* is_static = */ true); + if (r < 0) + return r; + + TAKE_PTR(lease); + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 7ece30cf7b..b1c35064ce 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" @@ -29,21 +30,6 @@ #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; - - 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); - } - - free(lease->hostname); - return mfree(lease); -} - /* 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 @@ -126,14 +112,6 @@ int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) { return in4_addr_is_set(&server->relay_target); } -DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( - dhcp_lease_hash_ops, - sd_dhcp_client_id, - 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); @@ -986,41 +964,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 = req->client_id, - .htype = req->message->htype, - .hlen = req->message->hlen, - .gateway = req->message->giaddr, - .expiration = expiration, - }; - - 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; @@ -1032,30 +976,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) @@ -1069,26 +992,6 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas 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); @@ -1100,33 +1003,12 @@ static bool address_available(sd_dhcp_server *server, be32_t address) { return true; } -static DHCPLease* server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req) { - DHCPLease *static_lease; - sd_dhcp_client_id client_id; - - assert(server); - assert(req); - - static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id); - if (static_lease) - return static_lease; - - /* when no lease is found based on the client id fall back to chaddr */ - if (!client_id_data_size_is_valid(req->message->hlen)) - return NULL; - - if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0) - return NULL; - - return hashmap_get(server->static_leases_by_client_id, &client_id); -} - #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); @@ -1154,7 +1036,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); - static_lease = server_get_static_lease(server, req); + static_lease = dhcp_server_get_static_lease(server, req); switch (type) { @@ -1200,7 +1082,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) @@ -1280,7 +1162,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz /* The client requested an address which is different from the static lease. Refuse. */ return server_send_nak_or_ignore(server, init_reboot, req); - return server_ack_request(server, req, existing_lease, address); + return server_ack_request(server, req, address); } if (address_is_in_pool(server, address)) { @@ -1290,7 +1172,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz /* We previously assigned an address, but the client requested another one. Refuse. */ return server_send_nak_or_ignore(server, init_reboot, req); - return server_ack_request(server, req, existing_lease, address); + return server_ack_request(server, req, address); } return server_send_nak_or_ignore(server, init_reboot, req); @@ -1306,7 +1188,7 @@ 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); @@ -1473,7 +1355,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); @@ -1687,51 +1569,3 @@ 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_static_lease( - sd_dhcp_server *server, - const struct in_addr *address, - uint8_t *client_id_raw, - size_t client_id_size) { - - _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL; - sd_dhcp_client_id client_id; - int r; - - assert_return(server, -EINVAL); - assert_return(client_id_raw, -EINVAL); - assert_return(client_id_size_is_valid(client_id_size), -EINVAL); - assert_return(!sd_dhcp_server_is_running(server), -EBUSY); - - r = sd_dhcp_client_id_set_raw(&client_id, client_id_raw, client_id_size); - if (r < 0) - return r; - - /* 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) { - dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &client_id)); - return 0; - } - - lease = new(DHCPLease, 1); - if (!lease) - return -ENOMEM; - - *lease = (DHCPLease) { - .address = address->s_addr, - .client_id = client_id, - }; - - lease->server = server; /* This must be set just before hashmap_put(). */ - - 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) - return r; - - TAKE_PTR(lease); - return 0; -} diff --git a/src/network/networkd-dhcp-server-bus.c b/src/network/networkd-dhcp-server-bus.c index 0197f8ad7b..470e5599e2 100644 --- a/src/network/networkd-dhcp-server-bus.c +++ b/src/network/networkd-dhcp-server-bus.c @@ -3,7 +3,7 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-util.h" -#include "dhcp-server-internal.h" +#include "dhcp-server-lease-internal.h" #include "networkd-dhcp-server-bus.h" #include "networkd-link-bus.h" #include "networkd-manager.h" @@ -19,7 +19,7 @@ static int property_get_leases( sd_bus_error *error) { Link *l = ASSERT_PTR(userdata); sd_dhcp_server *s; - DHCPLease *lease; + sd_dhcp_server_lease *lease; int r; assert(reply); diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index f7a4ed0141..d972588b3e 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -2,7 +2,7 @@ #include -#include "dhcp-server-internal.h" +#include "dhcp-server-lease-internal.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "dns-domain.h" @@ -892,7 +892,7 @@ static int pref64_append_json(Link *link, JsonVariant **v) { static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) { _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; - DHCPLease *lease; + sd_dhcp_server_lease *lease; int r; assert(link); @@ -924,7 +924,7 @@ static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) { static int dhcp_server_static_leases_append_json(Link *link, JsonVariant **v) { _cleanup_(json_variant_unrefp) JsonVariant *array = NULL; - DHCPLease *lease; + sd_dhcp_server_lease *lease; int r; assert(link); diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 7cf30b652b..76d87cc9d4 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -26,6 +26,7 @@ _not_installed_headers = [ 'sd-dhcp-lease.h', 'sd-dhcp-option.h', 'sd-dhcp-protocol.h', + 'sd-dhcp-server-lease.h', 'sd-dhcp-server.h', 'sd-dhcp6-client.h', 'sd-dhcp6-lease.h', diff --git a/src/systemd/sd-dhcp-server-lease.h b/src/systemd/sd-dhcp-server-lease.h new file mode 100644 index 0000000000..754f71953b --- /dev/null +++ b/src/systemd/sd-dhcp-server-lease.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddhcpserverleasehfoo +#define foosddhcpserverleasehfoo + +/*** + Copyright © 2013 Intel Corporation. All rights reserved. + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dhcp_server_lease sd_dhcp_server_lease; + +sd_dhcp_server_lease *sd_dhcp_server_lease_ref(sd_dhcp_server_lease *lease); +sd_dhcp_server_lease *sd_dhcp_server_lease_unref(sd_dhcp_server_lease *lease); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease_unref); + +_SD_END_DECLARATIONS; + +#endif