networkd: Allow DHCP servers to be re-configured on carrier gain
authorRichard Maw <richard.maw@codethink.co.uk>
Fri, 9 Jun 2017 15:18:25 +0000 (16:18 +0100)
committerRichard Maw <richard.maw@codethink.co.uk>
Mon, 12 Jun 2017 15:54:42 +0000 (16:54 +0100)
In normal operation this would trigger an assertion
when a DHCP server is configured every time the link goes up.

This change makes sd_dhcp_server_configure_pool idempotent
and stops the DHCP server when the link loses carrier.

In addition to this stopping the assertion being triggered,
this has the useful side-effect of allowing the link to be taken down
and then brought back up as a way to have it use DNS from an "upstream"
interface that got its DNS configuration via DHCP
after the downstream link was configured.

src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-server.c
src/network/networkd-link.c

index 315cbf1..5a59c37 100644 (file)
 #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,7 +55,6 @@ 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));
         netmask = netmask_addr.s_addr;
@@ -78,19 +85,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 +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;
 
index e81c508..26f2178 100644 (file)
@@ -63,7 +63,7 @@ static int test_basic(sd_event *event) {
         assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL);
         assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE);
         assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
-        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY);
+        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
 
         test_pool(&address_any, 1, -EINVAL);
         test_pool(&address_lo, 1, 0);
index 4af61df..4c57fa1 100644 (file)
@@ -3060,6 +3060,8 @@ static int link_carrier_lost(Link *link) {
                 return r;
         }
 
+        (void) sd_dhcp_server_stop(link->dhcp_server);
+
         r = link_drop_config(link);
         if (r < 0)
                 return r;