summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2021-05-14 20:27:33 +0900
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-05-18 20:20:24 +0900
commit0017ba3165f69e8afb7f73127281bb9a7e5b5641 (patch)
tree289177e5396d359a86a84aaca0179fe49bd8e1f0
parent998545a7d9607b49facd0e9868b893ac24942c51 (diff)
downloadsystemd-0017ba3165f69e8afb7f73127281bb9a7e5b5641.tar.gz
network: dhcp-server: introduce ServerAddress= setting
This may be useful when the link which DHCP server running on has multiple static addresses.
-rw-r--r--man/systemd.network.xml8
-rw-r--r--src/network/networkd-address.c26
-rw-r--r--src/network/networkd-dhcp-server.c118
-rw-r--r--src/network/networkd-dhcp-server.h6
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.c2
-rw-r--r--src/network/networkd-network.h3
-rw-r--r--test/fuzz/fuzz-network-parser/directives.network1
8 files changed, 143 insertions, 22 deletions
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index fe330be0be..02e464b193 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -2345,6 +2345,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>
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 462907439a..29ab83a425 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -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);
diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c
index 07d6eb95d1..0a396a957f 100644
--- a/src/network/networkd-dhcp-server.c
+++ b/src/network/networkd-dhcp-server.c
@@ -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;
+}
diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h
index e8c8f192f3..d58978cc05 100644
--- a/src/network/networkd-dhcp-server.h
+++ b/src/network/networkd-dhcp-server.h
@@ -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);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index b3e03bff3f..6616b14774 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -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)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 482c31064f..6f68759a88 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -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;
}
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index e27a27cd02..c1316d2abc 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -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;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 127f26461e..9cbc9e8088 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -364,6 +364,7 @@ BindToInterface=
RelayTarget=
RelayAgentCircuitId=
RelayAgentRemoteId=
+ServerAddress=
[NextHop]
Id=
Gateway=