]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: find DHCP server address only on loading .network file
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 7 Sep 2023 18:27:07 +0000 (03:27 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 8 Sep 2023 11:03:40 +0000 (20:03 +0900)
Previously, we periodically search suitable address for DHCP server,
1. when .network file is loaded,
2. when checking if we can configure sd_dhcp_server object,
3. when configuring sd_dhcp_server.

Especially, the step 2 may be triggered several times.

This makes, when .network file is loaded, find a corresponding Address
object, add a new Address object if not found, then save the found or
added Address object. So, it is not necessary to find address again on
configuring sd_dhcp_server object.

src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/network/networkd-network.h

index 990d24e9b1c751f0327c0c59f4d555982843e3a6..c43655f57aaee564a910b754c4c421992bb5de16 100644 (file)
@@ -110,7 +110,7 @@ int address_new(Address **ret) {
         return 0;
 }
 
-static int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
+int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) {
         _cleanup_(config_section_freep) ConfigSection *n = NULL;
         _cleanup_(address_freep) Address *address = NULL;
         int r;
@@ -401,7 +401,7 @@ static int address_compare_func(const Address *a1, const Address *a2) {
         }
 }
 
-DEFINE_PRIVATE_HASH_OPS(
+DEFINE_HASH_OPS(
         address_hash_ops,
         Address,
         address_hash_func,
@@ -1561,10 +1561,6 @@ int link_request_static_addresses(Link *link) {
         if (r < 0)
                 return r;
 
-        r = link_request_dhcp_server_address(link);
-        if (r < 0)
-                return r;
-
         if (link->static_address_messages == 0) {
                 link->static_addresses_configured = true;
                 link_check_ready(link);
@@ -2334,7 +2330,7 @@ static void address_section_adjust_broadcast(Address *address) {
         address->broadcast.s_addr = 0;
 }
 
-static int address_section_verify(Address *address) {
+int address_section_verify(Address *address) {
         if (section_is_invalid(address->section))
                 return -EINVAL;
 
@@ -2444,7 +2440,9 @@ int network_drop_invalid_addresses(Network *network) {
                 assert(r > 0);
         }
 
-        network_adjust_dhcp_server(network);
+        r = network_adjust_dhcp_server(network, &addresses);
+        if (r < 0)
+                return r;
 
         return 0;
 }
index 87c7d364868b2eb75e7e253ec9bf9a4b2905bcea..35acb81aa35a6ab92acb56a6824a0fb6c07a1b89 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "conf-parser.h"
 #include "firewall-util.h"
+#include "hash-funcs.h"
 #include "in-addr-util.h"
 #include "networkd-link.h"
 #include "networkd-util.h"
@@ -73,7 +74,10 @@ const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) _warn_unu
 
 int address_flags_to_string_alloc(uint32_t flags, int family, char **ret);
 
+extern const struct hash_ops address_hash_ops;
+
 int address_new(Address **ret);
+int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret);
 Address* address_free(Address *address);
 int address_get(Link *link, const Address *in, Address **ret);
 int address_get_harder(Link *link, const Address *in, Address **ret);
@@ -115,6 +119,7 @@ int link_request_static_addresses(Link *link);
 
 int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Manager *m);
 
+int address_section_verify(Address *address);
 int network_drop_invalid_addresses(Network *network);
 
 DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address);
index 3663a506f6a70db2b74d93392421edba4bbc11d5..63c520fa89d1fcccb0998c684a46502a43489f68 100644 (file)
@@ -39,22 +39,28 @@ static bool link_dhcp4_server_enabled(Link *link) {
         return link->network->dhcp_server;
 }
 
-void network_adjust_dhcp_server(Network *network) {
+int network_adjust_dhcp_server(Network *network, Set **addresses) {
+        int r;
+
         assert(network);
+        assert(addresses);
 
         if (!network->dhcp_server)
-                return;
+                return 0;
 
         if (network->bond) {
                 log_warning("%s: DHCPServer= is enabled for bond slave. Disabling DHCP server.",
                             network->filename);
                 network->dhcp_server = false;
-                return;
+                return 0;
         }
 
-        if (!in4_addr_is_set(&network->dhcp_server_address)) {
+        assert(network->dhcp_server_address_prefixlen <= 32);
+
+        if (network->dhcp_server_address_prefixlen == 0) {
                 Address *address;
-                bool have = false;
+
+                /* If the server address is not specified, then find suitable static address. */
 
                 ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) {
                         assert(!section_is_invalid(address->section));
@@ -71,83 +77,69 @@ void network_adjust_dhcp_server(Network *network) {
                         if (in4_addr_is_set(&address->in_addr_peer.in))
                                 continue;
 
-                        have = true;
+                        /* TODO: check if the prefix length is small enough for the pool. */
+
+                        network->dhcp_server_address = address;
                         break;
                 }
-                if (!have) {
-                        log_warning("%s: DHCPServer= is enabled, but no static address configured. "
+                if (!network->dhcp_server_address) {
+                        log_warning("%s: DHCPServer= is enabled, but no suitable static address configured. "
                                     "Disabling DHCP server.",
                                     network->filename);
                         network->dhcp_server = false;
-                        return;
+                        return 0;
                 }
-        }
-}
-
-int link_request_dhcp_server_address(Link *link) {
-        _cleanup_(address_freep) Address *address = NULL;
-        Address *existing;
-        int r;
-
-        assert(link);
-        assert(link->network);
-
-        if (!link_dhcp4_server_enabled(link))
-                return 0;
-
-        if (!in4_addr_is_set(&link->network->dhcp_server_address))
-                return 0;
-
-        r = address_new(&address);
-        if (r < 0)
-                return r;
-
-        address->source = NETWORK_CONFIG_SOURCE_STATIC;
-        address->family = AF_INET;
-        address->in_addr.in = link->network->dhcp_server_address;
-        address->prefixlen = link->network->dhcp_server_address_prefixlen;
 
-        if (address_get_harder(link, address, &existing) >= 0 &&
-            (address_exists(existing) || address_is_requesting(existing)) &&
-            existing->source == NETWORK_CONFIG_SOURCE_STATIC)
-                /* The same address seems explicitly configured in [Address] or [Network] section.
-                 * Configure the DHCP server address only when it is not. */
-                return 0;
+        } else {
+                _cleanup_(address_freep) Address *a = NULL;
+                Address *existing;
+                unsigned line;
+
+                /* TODO: check if the prefix length is small enough for the pool. */
+
+                /* If an address is explicitly specified, then check if the corresponding [Address] section
+                 * is configured, and add one if not. */
+
+                existing = set_get(*addresses,
+                                   &(Address) {
+                                           .family = AF_INET,
+                                           .in_addr.in = network->dhcp_server_address_in_addr,
+                                           .prefixlen = network->dhcp_server_address_prefixlen,
+                                   });
+                if (existing) {
+                        /* Corresponding [Address] section already exists. */
+                        network->dhcp_server_address = existing;
+                        return 0;
+                }
 
-        return link_request_static_address(link, address);
-}
+                r = ordered_hashmap_by_section_find_unused_line(network->addresses_by_section, network->filename, &line);
+                if (r < 0)
+                        return log_warning_errno(r, "%s: Failed to find unused line number for DHCP server address: %m",
+                                                 network->filename);
 
-static int link_find_dhcp_server_address(Link *link, Address **ret) {
-        Address *address;
+                r = address_new_static(network, network->filename, line, &a);
+                if (r < 0)
+                        return log_warning_errno(r, "%s: Failed to add new static address object for DHCP server: %m",
+                                                 network->filename);
 
-        assert(link);
-        assert(link->network);
+                a->family = AF_INET;
+                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);
 
-        /* If ServerAddress= is specified, then use the address. */
-        if (in4_addr_is_set(&link->network->dhcp_server_address))
-                return link_get_ipv4_address(link, &link->network->dhcp_server_address,
-                                             link->network->dhcp_server_address_prefixlen, ret);
+                r = address_section_verify(a);
+                if (r < 0)
+                        return r;
 
-        /* If not, then select one from static addresses. */
-        SET_FOREACH(address, link->addresses) {
-                if (address->source != NETWORK_CONFIG_SOURCE_STATIC)
-                        continue;
-                if (!address_exists(address))
-                        continue;
-                if (address->family != AF_INET)
-                        continue;
-                if (in4_addr_is_localhost(&address->in_addr.in))
-                        continue;
-                if (in4_addr_is_link_local(&address->in_addr.in))
-                        continue;
-                if (in4_addr_is_set(&address->in_addr_peer.in))
-                        continue;
+                r = set_ensure_put(addresses, &address_hash_ops, a);
+                if (r < 0)
+                        return log_oom();
+                assert(r > 0);
 
-                *ret = address;
-                return 0;
+                network->dhcp_server_address = TAKE_PTR(a);
         }
 
-        return -ENOENT;
+        return 0;
 }
 
 static int dhcp_server_find_uplink(Link *link, Link **ret) {
@@ -369,6 +361,8 @@ static int dhcp4_server_configure(Link *link) {
         int r;
 
         assert(link);
+        assert(link->network);
+        assert(link->network->dhcp_server_address);
 
         log_link_debug(link, "Configuring DHCP Server.");
 
@@ -387,7 +381,7 @@ static int dhcp4_server_configure(Link *link) {
         if (r < 0)
                 return log_link_warning_errno(link, r, "Failed to set callback for DHCPv4 server instance: %m");
 
-        r = link_find_dhcp_server_address(link, &address);
+        r = address_get(link, link->network->dhcp_server_address, &address);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to find suitable address for DHCPv4 server instance: %m");
 
@@ -535,6 +529,8 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
         Address *a;
 
         assert(link);
+        assert(link->network);
+        assert(link->network->dhcp_server_address);
 
         if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
                 return false;
@@ -545,7 +541,7 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
         if (!link->static_addresses_configured)
                 return false;
 
-        if (link_find_dhcp_server_address(link, &a) < 0)
+        if (address_get(link, link->network->dhcp_server_address, &a) < 0)
                 return false;
 
         if (!address_is_ready(a))
@@ -711,7 +707,7 @@ int config_parse_dhcp_server_address(
         assert(rvalue);
 
         if (isempty(rvalue)) {
-                network->dhcp_server_address = (struct in_addr) {};
+                network->dhcp_server_address_in_addr = (struct in_addr) {};
                 network->dhcp_server_address_prefixlen = 0;
                 return 0;
         }
@@ -729,7 +725,7 @@ int config_parse_dhcp_server_address(
                 return 0;
         }
 
-        network->dhcp_server_address = a.in;
+        network->dhcp_server_address_in_addr = a.in;
         network->dhcp_server_address_prefixlen = prefixlen;
         return 0;
 }
index cb2a8b6a34b537fc1918fff97036e53ba9e8f866..4fd4429deb3dd9c488424538cedea8b0fe48e0e3 100644 (file)
@@ -2,13 +2,13 @@
 #pragma once
 
 #include "conf-parser.h"
+#include "set.h"
 
 typedef struct Link Link;
 typedef struct Network Network;
 
-void network_adjust_dhcp_server(Network *network);
+int network_adjust_dhcp_server(Network *network, Set **addresses);
 
-int link_request_dhcp_server_address(Link *link);
 int link_request_dhcp_server(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
index 98cc1f5abc97e779895d4ad9fdb0b0959afae446..34bc179d75bbfa50b633b717579c990f963880cd 100644 (file)
@@ -15,6 +15,7 @@
 #include "ipoib.h"
 #include "net-condition.h"
 #include "netdev.h"
+#include "networkd-address.h"
 #include "networkd-bridge-vlan.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp4.h"
@@ -199,7 +200,8 @@ struct Network {
         bool dhcp_server;
         bool dhcp_server_bind_to_interface;
         unsigned char dhcp_server_address_prefixlen;
-        struct in_addr dhcp_server_address;
+        struct in_addr dhcp_server_address_in_addr;
+        const Address *dhcp_server_address;
         int dhcp_server_uplink_index;
         char *dhcp_server_uplink_name;
         struct in_addr dhcp_server_relay_target;