]> 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 cf7b8e1b629c8424a352e79d1dcd06b6db08bb38..c35102af74a5ff97c4cecd3c872b0183e132cb58 100644 (file)
@@ -1,14 +1,16 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <netinet/in.h>
-#include <net/if.h>
-#include <linux/if_arp.h> /* Must be included after <net/if.h> */
-#include <linux/if.h> /* Must be included after <net/if.h> */
+#include <linux/if_arp.h>
+#include <linux/if.h>
 
 #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-address.h"
 #include "networkd-dhcp-server-bus.h"
 #include "networkd-dhcp-server-static-lease.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"
@@ -80,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) {
@@ -91,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;
 
@@ -126,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)
@@ -142,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);
 
@@ -204,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: {
@@ -227,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;
         }
 
@@ -324,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;
 
@@ -392,19 +529,21 @@ static int dhcp4_server_configure(Link *link) {
                 return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m");
 
         if (link->network->dhcp_server_max_lease_time_usec > 0) {
-                r = sd_dhcp_server_set_max_lease_time(link->dhcp_server,
-                                                      DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
+                r = sd_dhcp_server_set_max_lease_time(link->dhcp_server, link->network->dhcp_server_max_lease_time_usec);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m");
         }
 
         if (link->network->dhcp_server_default_lease_time_usec > 0) {
-                r = sd_dhcp_server_set_default_lease_time(link->dhcp_server,
-                                                          DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
+                r = sd_dhcp_server_set_default_lease_time(link->dhcp_server, link->network->dhcp_server_default_lease_time_usec);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
         }
 
+        r = sd_dhcp_server_set_ipv6_only_preferred_usec(link->dhcp_server, link->network->dhcp_server_ipv6_only_preferred_usec);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to set IPv6 only preferred time for DHCPv4 server instance: %m");
+
         r = sd_dhcp_server_set_boot_server_address(link->dhcp_server, &link->network->dhcp_server_boot_server_address);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to set boot server address for DHCPv4 server instance: %m");
@@ -417,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;
@@ -516,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;
 }
 
@@ -729,3 +872,45 @@ int config_parse_dhcp_server_address(
         network->dhcp_server_address_prefixlen = prefixlen;
         return 0;
 }
+
+int config_parse_dhcp_server_ipv6_only_preferred(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        usec_t t, *usec = ASSERT_PTR(data);
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *usec = 0;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, &t);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
+                return 0;
+        }
+
+        if (t < MIN_V6ONLY_WAIT_USEC && !network_test_mode_enabled()) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid [%s] %s=, ignoring assignment: %s", section, lvalue, rvalue);
+                return 0;
+        }
+
+        *usec = t;
+        return 0;
+}