diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 18a2c1030e..020a5ffb72 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -2405,6 +2405,16 @@ IPv6Token=prefixstable:2002:da8:1::
network traffic.
+
+ UplinkInterface=
+ Specifies name or index of uplink interface, or one of the special values
+ :none and :auto. When emitting DNS, NTP, or SIP servers
+ are enabled but no servers are specified, the servers configured in the uplink interface will
+ be emitted. When :auto, the link which has default gateway with higher
+ priority will be automatically selected. When :none, no uplink interface
+ will be selected. Defaults to :auto.
+
+
EmitDNS=
DNS=
diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c
index 3443e498a1..055aa82ba2 100644
--- a/src/network/networkd-dhcp-server.c
+++ b/src/network/networkd-dhcp-server.c
@@ -97,6 +97,26 @@ static int link_find_dhcp_server_address(Link *link, Address **ret) {
return -ENOENT;
}
+static int dhcp_server_find_uplink(Link *link, Link **ret) {
+ assert(link);
+
+ if (link->network->dhcp_server_uplink_name)
+ return link_get_by_name(link->manager, link->network->dhcp_server_uplink_name, ret);
+
+ if (link->network->dhcp_server_uplink_index > 0)
+ return link_get(link->manager, link->network->dhcp_server_uplink_index, ret);
+
+ if (link->network->dhcp_server_uplink_index == 0) {
+ /* It is not necessary to propagate error in automatic selection. */
+ if (manager_find_uplink(link->manager, AF_INET, link, ret) < 0)
+ *ret = NULL;
+ return 0;
+ }
+
+ *ret = NULL;
+ return 0;
+}
+
static int link_push_uplink_to_dhcp_server(
Link *link,
sd_dhcp_lease_server_type_t what,
@@ -363,7 +383,7 @@ static int dhcp4_server_configure(Link *link) {
else {
/* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */
if (!acquired_uplink) {
- (void) manager_find_uplink(link->manager, AF_INET, link, &uplink);
+ (void) dhcp_server_find_uplink(link, &uplink);
acquired_uplink = true;
}
@@ -468,6 +488,7 @@ int link_request_dhcp_server(Link *link) {
}
static bool dhcp_server_is_ready_to_configure(Link *link) {
+ Link *uplink = NULL;
Address *a;
assert(link);
@@ -493,6 +514,12 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
if (!address_is_ready(a))
return false;
+ if (dhcp_server_find_uplink(link, &uplink) < 0)
+ return false;
+
+ if (uplink && !uplink->network)
+ return false;
+
return true;
}
@@ -633,3 +660,55 @@ int config_parse_dhcp_server_address(
network->dhcp_server_address_prefixlen = prefixlen;
return 0;
}
+
+int config_parse_dhcp_server_uplink(
+ 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;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue) || streq(rvalue, ":auto")) {
+ network->dhcp_server_uplink_index = 0; /* uplink will be selected automatically */
+ network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
+ return 0;
+ }
+
+ if (streq(rvalue, ":none")) {
+ network->dhcp_server_uplink_index = -1; /* uplink will not be selected automatically */
+ network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
+ return 0;
+ }
+
+ r = parse_ifindex(rvalue);
+ if (r > 0) {
+ network->dhcp_server_uplink_index = r;
+ network->dhcp_server_uplink_name = mfree(network->dhcp_server_uplink_name);
+ return 0;
+ }
+
+ if (!ifname_valid_full(rvalue, IFNAME_VALID_ALTERNATIVE)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid interface name in %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ r = free_and_strdup_warn(&network->dhcp_server_uplink_name, rvalue);
+ if (r < 0)
+ return r;
+
+ network->dhcp_server_uplink_index = 0;
+ return 0;
+}
diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h
index a02cd995ec..3f23f97bb8 100644
--- a/src/network/networkd-dhcp-server.h
+++ b/src/network/networkd-dhcp-server.h
@@ -15,3 +15,4 @@ int request_process_dhcp_server(Request *req);
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);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_uplink);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index a0da17ab03..c1e4efd1e4 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -264,6 +264,7 @@ IPv6AcceptRA.PrefixDenyList, config_parse_ndisc_address_filter,
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.UplinkInterface, config_parse_dhcp_server_uplink, 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 3f2dac62c5..143e49b4ac 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -617,6 +617,7 @@ static Network *network_free(Network *network) {
free(network->name);
free(network->dhcp_server_timezone);
+ free(network->dhcp_server_uplink_name);
for (sd_dhcp_lease_server_type_t t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
free(network->dhcp_server_emit[t].addresses);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index ca472a6174..7436b8ea32 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -191,6 +191,8 @@ struct Network {
bool dhcp_server_bind_to_interface;
unsigned char dhcp_server_address_prefixlen;
struct in_addr dhcp_server_address;
+ int dhcp_server_uplink_index;
+ char *dhcp_server_uplink_name;
struct in_addr dhcp_server_relay_target;
char *dhcp_server_relay_agent_circuit_id;
char *dhcp_server_relay_agent_remote_id;
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 05b67512ae..195ed8f0cc 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -366,6 +366,7 @@ RelayTarget=
RelayAgentCircuitId=
RelayAgentRemoteId=
ServerAddress=
+UplinkInterface=
[DHCPServerStaticLease]
MACAddress=
Address=