diff options
Diffstat (limited to 'src/libsystemd-network/sd-dhcp-server.c')
-rw-r--r-- | src/libsystemd-network/sd-dhcp-server.c | 349 |
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; } |