mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
Merge pull request #29119 from yuwata/network-dhcp-server-allow-null-server-address
network/dhcp-server: allow null server address
This commit is contained in:
@@ -3277,12 +3277,43 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ServerAddress=</varname></term>
|
||||
<listitem><para>Specifies server address for the DHCP server. Takes an IPv4 address with prefix
|
||||
length, for example 192.168.0.1/24. This setting may be useful when the link on
|
||||
which the DHCP server is running has multiple static addresses. When unset, one of static addresses
|
||||
in the link will be automatically selected. Defaults to unset.</para>
|
||||
<listitem>
|
||||
<para>Specifies the server address for the DHCP server. Takes an IPv4 address with prefix length
|
||||
separated with a slash, e.g. <literal>192.168.0.1/24</literal>. Defaults to unset, and one of
|
||||
static IPv4 addresses configured in [Network] or [Address] section will be automatically selected.
|
||||
This setting may be useful when the interface on which the DHCP server is running has multiple
|
||||
static IPv4 addresses.</para>
|
||||
<para>This implies <varname>Address=</varname> in [Network] or [Address] section with the same
|
||||
address and prefix length. That is,
|
||||
<programlisting>[Network]
|
||||
DHCPServer=yes
|
||||
Address=192.168.0.1/24
|
||||
Address=192.168.0.2/24
|
||||
[DHCPServer]
|
||||
ServerAddress=192.168.0.1/24</programlisting>
|
||||
or
|
||||
<programlisting>[Network]
|
||||
DHCPServer=yes
|
||||
[Address]
|
||||
Address=192.168.0.1/24
|
||||
[Address]
|
||||
Address=192.168.0.2/24
|
||||
[DHCPServer]
|
||||
ServerAddress=192.168.0.1/24</programlisting>
|
||||
are equivalent to the following.
|
||||
<programlisting>[Network]
|
||||
DHCPServer=yes
|
||||
Address=192.168.0.2/24
|
||||
[DHCPServer]
|
||||
ServerAddress=192.168.0.1/24</programlisting>
|
||||
</para>
|
||||
<para>Since version 255, like the <varname>Address=</varname> setting in [Network] or [Address]
|
||||
section, this also supports a null address, e.g. <literal>0.0.0.0/24</literal>, and an unused
|
||||
address will be automatically selected. For more details about the automatic address selection,
|
||||
see <varname>Address=</varname> setting in [Network] section in the above.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
|
||||
<xi:include href="version-info.xml" xpointer="v249"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
@@ -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,6 +2440,10 @@ int network_drop_invalid_addresses(Network *network) {
|
||||
assert(r > 0);
|
||||
}
|
||||
|
||||
r = network_adjust_dhcp_server(network, &addresses);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -38,26 +38,31 @@ 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) {
|
||||
if (section_is_invalid(address->section))
|
||||
continue;
|
||||
assert(!section_is_invalid(address->section));
|
||||
|
||||
if (address->family != AF_INET)
|
||||
continue;
|
||||
@@ -71,83 +76,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;
|
||||
} else {
|
||||
_cleanup_(address_freep) Address *a = NULL;
|
||||
Address *existing;
|
||||
unsigned line;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
/* TODO: check if the prefix length is small enough for the pool. */
|
||||
|
||||
if (!link_dhcp4_server_enabled(link))
|
||||
return 0;
|
||||
/* If an address is explicitly specified, then check if the corresponding [Address] section
|
||||
* is configured, and add one if not. */
|
||||
|
||||
if (!in4_addr_is_set(&link->network->dhcp_server_address))
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
r = address_new(&address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
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);
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
return link_request_static_address(link, address);
|
||||
}
|
||||
r = address_section_verify(a);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
static int link_find_dhcp_server_address(Link *link, Address **ret) {
|
||||
Address *address;
|
||||
r = set_ensure_put(addresses, &address_hash_ops, a);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
assert(r > 0);
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
/* 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->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;
|
||||
|
||||
*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 +360,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 +380,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 +528,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 +540,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 +706,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;
|
||||
}
|
||||
@@ -722,14 +717,14 @@ int config_parse_dhcp_server_address(
|
||||
"Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
if (in4_addr_is_null(&a.in) || in4_addr_is_localhost(&a.in)) {
|
||||
if (in4_addr_is_localhost(&a.in) || in4_addr_is_link_local(&a.in)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"DHCP server address cannot be the ANY address or a localhost address, "
|
||||
"DHCP server address cannot be a localhost or link-local address, "
|
||||
"ignoring assignment: %s", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
network->dhcp_server_address = a.in;
|
||||
network->dhcp_server_address_in_addr = a.in;
|
||||
network->dhcp_server_address_prefixlen = prefixlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "networkd-bridge-mdb.h"
|
||||
#include "networkd-dhcp-common.h"
|
||||
#include "networkd-dhcp-server-static-lease.h"
|
||||
#include "networkd-dhcp-server.h"
|
||||
#include "networkd-ipv6-proxy-ndp.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
@@ -326,8 +325,6 @@ int network_verify(Network *network) {
|
||||
return r; /* sr_iov_drop_invalid_sections() logs internally. */
|
||||
network_drop_invalid_static_leases(network);
|
||||
|
||||
network_adjust_dhcp_server(network);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Match]
|
||||
Name=veth-peer
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=false
|
||||
DHCPServer=yes
|
||||
|
||||
[DHCPServer]
|
||||
ServerAddress=0.0.0.0/24
|
||||
PoolOffset=10
|
||||
PoolSize=50
|
||||
DNS=_server_address
|
||||
NTP=_server_address
|
||||
@@ -4935,6 +4935,29 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
|
||||
self.assertRegex(output, "Offered DHCP leases: 192.168.5.[0-9]*")
|
||||
|
||||
def test_dhcp_server_null_server_address(self):
|
||||
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-null-server-address.network')
|
||||
start_networkd()
|
||||
self.wait_online(['veth99:routable', 'veth-peer:routable'])
|
||||
|
||||
output = check_output('ip --json address show dev veth-peer')
|
||||
server_address = json.loads(output)[0]['addr_info'][0]['local']
|
||||
print(server_address)
|
||||
|
||||
output = check_output('ip --json address show dev veth99')
|
||||
client_address = json.loads(output)[0]['addr_info'][0]['local']
|
||||
print(client_address)
|
||||
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth99', env=env)
|
||||
print(output)
|
||||
self.assertRegex(output, rf'Address: {client_address} \(DHCP4 via {server_address}\)')
|
||||
self.assertIn(f'Gateway: {server_address}', output)
|
||||
self.assertIn(f'DNS: {server_address}', output)
|
||||
self.assertIn(f'NTP: {server_address}', output)
|
||||
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'veth-peer', env=env)
|
||||
self.assertIn(f'Offered DHCP leases: {client_address}', output)
|
||||
|
||||
def test_dhcp_server_with_uplink(self):
|
||||
copy_network_unit('25-veth.netdev', '25-dhcp-client.network', '25-dhcp-server-downstream.network',
|
||||
'12-dummy.netdev', '25-dhcp-server-uplink.network')
|
||||
|
||||
Reference in New Issue
Block a user