network: dhcp-server: introduce ServerAddress= setting

This may be useful when the link which DHCP server running on has
multiple static addresses.
This commit is contained in:
Yu Watanabe 2021-05-14 20:27:33 +09:00
parent 998545a7d9
commit 0017ba3165
8 changed files with 143 additions and 22 deletions

View 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>

View 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);

View 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 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 not, then select one from static addresses. */
SET_FOREACH(address, link->static_addresses)
if (address->family == AF_INET &&
in_addr_is_set(address->family, &address->in_addr))
return address;
!in4_addr_is_localhost(&address->in_addr.in) &&
in4_addr_is_null(&address->in_addr_peer.in)) {
*ret = address;
return 0;
}
/* 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;
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;
}

View 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);

View 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)

View 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;
}

View 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;

View file

@ -364,6 +364,7 @@ BindToInterface=
RelayTarget=
RelayAgentCircuitId=
RelayAgentRemoteId=
ServerAddress=
[NextHop]
Id=
Gateway=