]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-dhcp-server.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-server.c
index ea4f03df1d5fe881e496ddd3433f4ef5ca34fde0..660358e857e2d4ce8450415402e4658e3512806f 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #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
@@ -47,9 +56,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);
@@ -78,19 +86,28 @@ 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) {
+                DHCPLease *lease;
+
+                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 */
+                while ((lease = hashmap_steal_first(server->leases_by_client_id)))
+                        dhcp_lease_free(lease);
+        }
 
         return 0;
 }
@@ -143,14 +160,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;
 
@@ -178,9 +187,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) {
@@ -199,7 +206,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);
 
@@ -260,7 +271,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,
         };
@@ -859,7 +870,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) {
@@ -981,7 +994,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;
@@ -1024,7 +1037,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;