X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Flibsystemd-network%2Fsd-dhcp-server.c;h=907b72391b60d7d78b75b14f1f961be073d81ce2;hb=dccca82b1a28c861a466a0905faee19ee009429a;hp=fb335337c4515dc7478bf9f59b850f76584606c0;hpb=51e0b250779d28eaffff886b5a3cb208c6813a40;p=thirdparty%2Fsystemd.git diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index fb335337c45..907b72391b6 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -27,12 +28,22 @@ #include "dhcp-server-internal.h" #include "fd-util.h" #include "in-addr-util.h" +#include "sd-id128.h" #include "siphash24.h" #include "string-util.h" +#include "unaligned.h" #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) +static void dhcp_lease_free(DHCPLease *lease) { + if (!lease) + return; + + free(lease->client_id.data); + free(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 @@ -46,9 +57,8 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres assert_return(address, -EINVAL); assert_return(address->s_addr != INADDR_ANY, -EINVAL); assert_return(prefixlen <= 32, -ERANGE); - assert_return(server->address == INADDR_ANY, -EBUSY); - assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); + assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); netmask = netmask_addr.s_addr; server_off = be32toh(address->s_addr & ~netmask); @@ -77,19 +87,26 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres else size = size_max; - server->bound_leases = new0(DHCPLease*, size); - if (!server->bound_leases) - return -ENOMEM; + 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_size = size; + server->pool_offset = offset; + server->pool_size = size; - server->address = address->s_addr; - server->netmask = netmask; - server->subnet = address->s_addr & netmask; + server->address = address->s_addr; + server->netmask = netmask; + server->subnet = address->s_addr & netmask; - if (server_off >= offset && server_off - offset < size) - server->bound_leases[server_off - offset] = &server->invalid_lease; + 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 */ + hashmap_clear_with_destructor(server->leases_by_client_id, dhcp_lease_free); + } return 0; } @@ -142,14 +159,6 @@ static const struct hash_ops client_id_hash_ops = { .compare = client_id_compare_func }; -static void dhcp_lease_free(DHCPLease *lease) { - if (!lease) - return; - - free(lease->client_id.data); - free(lease); -} - sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { DHCPLease *lease; @@ -177,9 +186,7 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { hashmap_free(server->leases_by_client_id); free(server->bound_leases); - free(server); - - return NULL; + return mfree(server); } int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { @@ -198,7 +205,11 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { server->address = htobe32(INADDR_ANY); server->netmask = htobe32(INADDR_ANY); server->ifindex = ifindex; + server->leases_by_client_id = hashmap_new(&client_id_hash_ops); + if (!server->leases_by_client_id) + return -ENOMEM; + server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC); server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC); @@ -259,7 +270,7 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, DHCPPacket *packet, size_t len) { union sockaddr_union link = { .ll.sll_family = AF_PACKET, - .ll.sll_protocol = htons(ETH_P_IP), + .ll.sll_protocol = htobe16(ETH_P_IP), .ll.sll_ifindex = server->ifindex, .ll.sll_halen = ETH_ALEN, }; @@ -604,17 +615,17 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us switch(code) { case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: if (len == 4) - req->lifetime = be32toh(*(be32_t*)option); + req->lifetime = unaligned_read_be32(option); break; case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS: if (len == 4) - req->requested_ip = *(be32_t*)option; + memcpy(&req->requested_ip, option, sizeof(be32_t)); break; case SD_DHCP_OPTION_SERVER_IDENTIFIER: if (len == 4) - req->server_id = *(be32_t*)option; + memcpy(&req->server_id, option, sizeof(be32_t)); break; case SD_DHCP_OPTION_CLIENT_IDENTIFIER: @@ -632,9 +643,9 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us break; case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE: - if (len == 2) - req->max_optlen = be16toh(*(be16_t*)option) - - - sizeof(DHCPPacket); + + if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket)) + req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket); break; } @@ -858,7 +869,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (!existing_lease) { lease = new0(DHCPLease, 1); - lease->address = req->requested_ip; + if (!lease) + return -ENOMEM; + lease->address = address; lease->client_id.data = memdup(req->client_id.data, req->client_id.length); if (!lease->client_id.data) { @@ -980,7 +993,7 @@ static int server_receive_message(sd_event_source *s, int fd, len = recvmsg(fd, &msg, 0); if (len < 0) { - if (errno == EAGAIN || errno == EINTR) + if (IN_SET(errno, EAGAIN, EINTR)) return 0; return -errno; @@ -1023,7 +1036,7 @@ int sd_dhcp_server_start(sd_dhcp_server *server) { } server->fd_raw = r; - r = dhcp_network_bind_udp_socket(INADDR_ANY, DHCP_PORT_SERVER); + r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY, DHCP_PORT_SERVER); if (r < 0) { sd_dhcp_server_stop(server); return r;