]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: dhcp-server: introduce ServerAddress= setting
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 14 May 2021 11:27:33 +0000 (20:27 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 18 May 2021 11:20:24 +0000 (20:20 +0900)
This may be useful when the link which DHCP server running on has
multiple static addresses.

man/systemd.network.xml
src/network/networkd-address.c
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
test/fuzz/fuzz-network-parser/directives.network

index fe330be0be70db4c4a4fdf5c8cf5c8b0b187b29a..02e464b193db39b92ce874f04b20ade02a663e0b 100644 (file)
@@ -2344,6 +2344,14 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
 
     <variablelist class='network-directives'>
 
+      <varlistentry>
+        <term><varname>ServerAddress=</varname></term>
+        <listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
+        length, e.g., <literal>192.168.0.1/24</literal>. This setting may be useful when the link which
+        DHCP server running on has multiple static addresses. When unset, one of static addresses in
+        the link will be automatically selected. Defaults to unset.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>PoolOffset=</varname></term>
         <term><varname>PoolSize=</varname></term>
index 462907439a9455ed4c73d17a835ef5e09d1cb1c1..29ab83a425fd5d3d8fbbb39b21a8ea53ea83b31d 100644 (file)
@@ -1157,6 +1157,32 @@ int link_request_static_addresses(Link *link) {
                 req->after_configure = static_address_after_configure;
         }
 
+        if (in4_addr_is_set(&link->network->dhcp_server_address)) {
+                _cleanup_(address_freep) Address *address = NULL;
+
+                r = address_new(&address);
+                if (r < 0)
+                        return log_oom();
+
+                address->family = AF_INET;
+                address->in_addr.in = link->network->dhcp_server_address;
+                address->prefixlen = link->network->dhcp_server_address_prefixlen;
+                address_set_broadcast(address);
+
+                /* The same address may be explicitly configured in [Address] or [Network] section.
+                 * Configure the DHCP server address only when it is not. */
+                if (!link_is_static_address_configured(link, address)) {
+                        Request *req;
+
+                        r = link_request_address(link, TAKE_PTR(address), true, &link->static_address_messages,
+                                                 static_address_handler, &req);
+                        if (r < 0)
+                                return r;
+
+                        req->after_configure = static_address_after_configure;
+                }
+        }
+
         if (link->static_address_messages == 0) {
                 link->static_addresses_configured = true;
                 link_check_ready(link);
index 07d6eb95d1b05d24f2dc5c0e4a2ac042bb64d997..0a396a957f70483245cf875df20caf86381b7ffd 100644 (file)
@@ -29,33 +29,70 @@ static bool link_dhcp4_server_enabled(Link *link) {
         if (!link->network)
                 return false;
 
-        if (link->network->bond)
-                return false;
-
         if (link->iftype == ARPHRD_CAN)
                 return false;
 
         return link->network->dhcp_server;
 }
 
-static Address* link_find_dhcp_server_address(Link *link) {
+void network_adjust_dhcp_server(Network *network) {
+        assert(network);
+
+        if (!network->dhcp_server)
+                return;
+
+        if (network->bond) {
+                log_warning("%s: DHCPServer= is enabled for bond slave. Disabling DHCP server.",
+                            network->filename);
+                network->dhcp_server = false;
+                return;
+        }
+
+        if (!in4_addr_is_set(&network->dhcp_server_address)) {
+                Address *address;
+                bool have = false;
+
+                ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) {
+                        if (section_is_invalid(address->section))
+                                continue;
+                        if (address->family == AF_INET &&
+                            !in4_addr_is_localhost(&address->in_addr.in) &&
+                            in4_addr_is_null(&address->in_addr_peer.in)) {
+                                have = true;
+                                break;
+                        }
+                }
+                if (!have) {
+                        log_warning("%s: DHCPServer= is enabled, but no static address configured. "
+                                    "Disabling DHCP server.",
+                                    network->filename);
+                        network->dhcp_server = false;
+                        return;
+                }
+        }
+}
+
+static int link_find_dhcp_server_address(Link *link, Address **ret) {
         Address *address;
 
         assert(link);
         assert(link->network);
 
-        /* The first statically configured address if there is any */
-        ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section)
-                if (address->family == AF_INET &&
-                    in_addr_is_set(address->family, &address->in_addr))
-                        return address;
+        /* 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);
 
-        /* If that didn't work, find a suitable address we got from the pool */
-        SET_FOREACH(address, link->pool_addresses)
-                if (address->family == AF_INET)
-                        return address;
+        /* If not, then select one from static addresses. */
+        SET_FOREACH(address, link->static_addresses)
+                if (address->family == AF_INET &&
+                    !in4_addr_is_localhost(&address->in_addr.in) &&
+                    in4_addr_is_null(&address->in_addr_peer.in)) {
+                        *ret = address;
+                        return 0;
+                }
 
-        return NULL;
+        return -ENOENT;
 }
 
 static int link_push_uplink_to_dhcp_server(
@@ -277,10 +314,9 @@ 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");
 
-        address = link_find_dhcp_server_address(link);
-        if (!address)
-                return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY),
-                                            "Failed to find suitable address for DHCPv4 server instance.");
+        r = link_find_dhcp_server_address(link, &address);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to find suitable address for DHCPv4 server instance: %m");
 
         /* use the server address' subnet as the pool */
         r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
@@ -429,7 +465,6 @@ int config_parse_dhcp_server_relay_agent_suboption(
         assert(lvalue);
         assert(rvalue);
 
-
         if (isempty(rvalue)) {
                 *suboption_value = mfree(*suboption_value);
                 return 0;
@@ -492,3 +527,48 @@ int config_parse_dhcp_server_emit(
                 emit->addresses[emit->n_addresses++] = a.in;
         }
 }
+
+int config_parse_dhcp_server_address(
+                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) {
+
+        Network *network = userdata;
+        union in_addr_union a;
+        unsigned char prefixlen;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                network->dhcp_server_address = (struct in_addr) {};
+                network->dhcp_server_address_prefixlen = 0;
+                return 0;
+        }
+
+        r = in_addr_prefix_from_string(rvalue, AF_INET, &a, &prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+        if (in4_addr_is_null(&a.in) || in4_addr_is_localhost(&a.in)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "DHCP server address cannot be the ANY address or a localhost address, "
+                           "ignoring assignment: %s", rvalue);
+                return 0;
+        }
+
+        network->dhcp_server_address = a.in;
+        network->dhcp_server_address_prefixlen = prefixlen;
+        return 0;
+}
index e8c8f192f331454a730a6cbe16ffcdc4f2aa984d..d58978cc051f6629ea2432ca75f264c5b51fca80 100644 (file)
@@ -2,12 +2,14 @@
 #pragma once
 
 #include "conf-parser.h"
-#include "networkd-link.h"
-#include "networkd-util.h"
 
 typedef struct Link Link;
+typedef struct Network Network;
+
+void network_adjust_dhcp_server(Network *network);
 
 int dhcp4_server_configure(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_address);
index b3e03bff3f4bf7b0ec3677d208f572a7490531af..6616b1477448a1727b8161c9dc17bffd30712ebc 100644 (file)
@@ -261,6 +261,7 @@ IPv6AcceptRA.PrefixAllowList,                config_parse_ndisc_address_filter,
 IPv6AcceptRA.PrefixDenyList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_prefix)
 IPv6AcceptRA.RouteAllowList,                 config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_allow_listed_route_prefix)
 IPv6AcceptRA.RouteDenyList,                  config_parse_ndisc_address_filter,                        0,                             offsetof(Network, ndisc_deny_listed_route_prefix)
+DHCPServer.ServerAddress,                    config_parse_dhcp_server_address,                         0,                             0
 DHCPServer.RelayTarget,                      config_parse_in_addr_non_null,                            AF_INET,                       offsetof(Network, dhcp_server_relay_target)
 DHCPServer.RelayAgentCircuitId,              config_parse_dhcp_server_relay_agent_suboption,           0,                             offsetof(Network, dhcp_server_relay_agent_circuit_id)
 DHCPServer.RelayAgentRemoteId,               config_parse_dhcp_server_relay_agent_suboption,           0,                             offsetof(Network, dhcp_server_relay_agent_remote_id)
index 482c31064fc2d7ea7bd346f946bfeabeebc7c13a..6f68759a88ce4e0fec2c9a48f7b1dc9b827e7002 100644 (file)
@@ -240,6 +240,8 @@ int network_verify(Network *network) {
         network_drop_invalid_traffic_control(network);
         network_drop_invalid_sr_iov(network);
 
+        network_adjust_dhcp_server(network);
+
         return 0;
 }
 
index e27a27cd02dcaea38ffa165457722618c075cf16..c1316d2abc9544d901cc1ab13f2555ed5a54e0b5 100644 (file)
@@ -189,10 +189,11 @@ struct Network {
         /* DHCP Server Support */
         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_relay_target;
         char *dhcp_server_relay_agent_circuit_id;
         char *dhcp_server_relay_agent_remote_id;
-
         NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
         bool dhcp_server_emit_router;
         bool dhcp_server_emit_timezone;
index 127f26461e03687723545223fcfff67261ea383e..9cbc9e80888002f588b53a19e671844e8c44739c 100644 (file)
@@ -364,6 +364,7 @@ BindToInterface=
 RelayTarget=
 RelayAgentCircuitId=
 RelayAgentRemoteId=
+ServerAddress=
 [NextHop]
 Id=
 Gateway=