]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-dhcp-server.c
network: introduce link_get_use_ntp()
[thirdparty/systemd.git] / src / network / networkd-dhcp-server.c
index 569398571c02796a36e90705be0dcddcaf0e774c..c35102af74a5ff97c4cecd3c872b0183e132cb58 100644 (file)
@@ -7,6 +7,7 @@
 #include "sd-dhcp-server.h"
 
 #include "dhcp-protocol.h"
+#include "dhcp-server-lease-internal.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "network-common.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-ntp.h"
 #include "networkd-queue.h"
 #include "networkd-route-util.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "socket-netlink.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -81,6 +84,7 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
                         /* TODO: check if the prefix length is small enough for the pool. */
 
                         network->dhcp_server_address = address;
+                        address->used_by_dhcp_server = true;
                         break;
                 }
                 if (!network->dhcp_server_address) {
@@ -92,7 +96,7 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
                 }
 
         } else {
-                _cleanup_(address_freep) Address *a = NULL;
+                _cleanup_(address_unrefp) Address *a = NULL;
                 Address *existing;
                 unsigned line;
 
@@ -127,6 +131,7 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
                 a->prefixlen = network->dhcp_server_address_prefixlen;
                 a->in_addr.in = network->dhcp_server_address_in_addr;
                 a->requested_as_null = !in4_addr_is_set(&network->dhcp_server_address_in_addr);
+                a->used_by_dhcp_server = true;
 
                 r = address_section_verify(a);
                 if (r < 0)
@@ -143,6 +148,139 @@ int network_adjust_dhcp_server(Network *network, Set **addresses) {
         return 0;
 }
 
+static bool dhcp_server_persist_leases(Link *link) {
+        assert(link);
+        assert(link->manager);
+        assert(link->network);
+
+        if (in4_addr_is_set(&link->network->dhcp_server_relay_target))
+                return false; /* On relay mode. Nothing saved in the persistent storage. */
+
+        if (link->network->dhcp_server_persist_leases >= 0)
+                return link->network->dhcp_server_persist_leases;
+
+        return link->manager->dhcp_server_persist_leases;
+}
+
+int address_acquire_from_dhcp_server_leases_file(Link *link, const Address *address, union in_addr_union *ret) {
+        struct in_addr a;
+        uint8_t prefixlen;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(address);
+        assert(ret);
+
+        /* If the DHCP server address is configured as a null address, reuse the server address of the
+         * previous instance. */
+        if (address->family != AF_INET)
+                return -ENOENT;
+
+        if (!address->used_by_dhcp_server)
+                return -ENOENT;
+
+        if (!link_dhcp4_server_enabled(link))
+                return -ENOENT;
+
+        if (!dhcp_server_persist_leases(link))
+                return -ENOENT;
+
+        if (link->manager->persistent_storage_fd < 0)
+                return -EBUSY; /* The persistent storage is not ready, try later again. */
+
+        _cleanup_free_ char *lease_file = path_join("dhcp-server-lease", link->ifname);
+        if (!lease_file)
+                return -ENOMEM;
+
+        r = dhcp_server_leases_file_get_server_address(
+                        link->manager->persistent_storage_fd,
+                        lease_file,
+                        &a,
+                        &prefixlen);
+        if (r == -ENOENT)
+                return r;
+        if (r < 0)
+                return log_warning_errno(r, "Failed to load lease file %s: %s",
+                                         lease_file,
+                                         r == -ENXIO ? "expected JSON content not found" :
+                                         r == -EINVAL ? "invalid JSON" :
+                                         STRERROR(r));
+
+        if (prefixlen != address->prefixlen)
+                return -ENOENT;
+
+        ret->in = a;
+        return 0;
+}
+
+int link_start_dhcp4_server(Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->manager);
+
+        if (!link->dhcp_server)
+                return 0; /* Not configured yet. */
+
+        if (!link_has_carrier(link))
+                return 0;
+
+        if (sd_dhcp_server_is_running(link->dhcp_server))
+                return 0; /* already started. */
+
+        /* TODO: Maybe, also check the system time is synced. If the system does not have RTC battery, then
+         * the realtime clock in not usable in the early boot stage, and all saved leases may be wrongly
+         * handled as expired and dropped. */
+        if (dhcp_server_persist_leases(link)) {
+
+                if (link->manager->persistent_storage_fd < 0)
+                        return 0; /* persistent storage is not ready. */
+
+                _cleanup_free_ char *lease_file = path_join("dhcp-server-lease", link->ifname);
+                if (!lease_file)
+                        return -ENOMEM;
+
+                r = sd_dhcp_server_set_lease_file(link->dhcp_server, link->manager->persistent_storage_fd, lease_file);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_dhcp_server_start(link->dhcp_server);
+        if (r < 0)
+                return r;
+
+        log_link_debug(link, "Offering DHCPv4 leases");
+        return 0;
+}
+
+void manager_toggle_dhcp4_server_state(Manager *manager, bool start) {
+        Link *link;
+        int r;
+
+        assert(manager);
+
+        HASHMAP_FOREACH(link, manager->links_by_index) {
+                if (!link->dhcp_server)
+                        continue;
+                if (!dhcp_server_persist_leases(link))
+                        continue;
+
+                /* Even if 'start' is true, first we need to stop the server. Otherwise, we cannot (re)set
+                 * the lease file in link_start_dhcp4_server(). */
+                r = sd_dhcp_server_stop(link->dhcp_server);
+                if (r < 0)
+                        log_link_debug_errno(link, r, "Failed to stop DHCP server, ignoring: %m");
+
+                if (!start)
+                        continue;
+
+                r = link_start_dhcp4_server(link);
+                if (r < 0)
+                        log_link_debug_errno(link, r, "Failed to start DHCP server, ignoring: %m");
+        }
+}
+
 static int dhcp_server_find_uplink(Link *link, Link **ret) {
         assert(link);
 
@@ -205,7 +343,7 @@ static int link_push_uplink_to_dhcp_server(
                         addresses[n_addresses++] = ia;
                 }
 
-                use_dhcp_lease_data = link->network->dhcp_use_dns;
+                use_dhcp_lease_data = link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP4);
                 break;
 
         case SD_DHCP_LEASE_NTP: {
@@ -228,7 +366,7 @@ static int link_push_uplink_to_dhcp_server(
                         addresses[n_addresses++] = ia.in;
                 }
 
-                use_dhcp_lease_data = link->network->dhcp_use_ntp;
+                use_dhcp_lease_data = link_get_use_ntp(link, NETWORK_CONFIG_SOURCE_DHCP4);
                 break;
         }
 
@@ -325,19 +463,17 @@ static int dhcp4_server_set_dns_from_resolve_conf(Link *link) {
         for (;;) {
                 _cleanup_free_ char *line = NULL;
                 const char *a;
-                char *l;
 
-                r = read_line(f, LONG_LINE_MAX, &line);
+                r = read_stripped_line(f, LONG_LINE_MAX, &line);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m");
                 if (r == 0)
                         break;
 
-                l = strstrip(line);
-                if (IN_SET(*l, '#', ';', 0))
+                if (IN_SET(*line, '#', ';', 0))
                         continue;
 
-                a = first_word(l, "nameserver");
+                a = first_word(line, "nameserver");
                 if (!a)
                         continue;
 
@@ -420,7 +556,12 @@ static int dhcp4_server_configure(Link *link) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to set boot filename for DHCPv4 server instance: %m");
 
-        for (sd_dhcp_lease_server_type_t type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type ++) {
+        r = sd_dhcp_server_set_rapid_commit(link->dhcp_server, link->network->dhcp_server_rapid_commit);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to %s Rapid Commit support for DHCPv4 server instance: %m",
+                                              enable_disable(link->network->dhcp_server_rapid_commit));
+
+        for (sd_dhcp_lease_server_type_t type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type++) {
 
                 if (!link->network->dhcp_server_emit[type].emit)
                         continue;
@@ -519,11 +660,10 @@ static int dhcp4_server_configure(Link *link) {
                         return log_link_error_errno(link, r, "Failed to set DHCPv4 static lease for DHCP server: %m");
         }
 
-        r = sd_dhcp_server_start(link->dhcp_server);
+        r = link_start_dhcp4_server(link);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m");
 
-        log_link_debug(link, "Offering DHCPv4 leases");
         return 0;
 }