diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 75a581816d..864dd6500b 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -1491,8 +1491,9 @@
UseGateway=
- When true (the default), the gateway will be requested from the DHCP server and added to the
- routing table with a metric of 1024, and a scope of "link".
+ When true, the gateway will be requested from the DHCP server and added to the routing table with a
+ metric of 1024, and a scope of "link". When unset, the value specified with
+ is used.
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 48e5c15fd0..ada14ecd18 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -325,78 +325,77 @@ static int link_set_dhcp_routes(Link *link) {
}
}
- if (!link->network->dhcp_use_gateway)
- return 0;
+ if (link->network->dhcp_use_gateway) {
+ r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+ if (IN_SET(r, 0, -ENODATA))
+ log_link_info(link, "DHCP: No gateway received from DHCP server.");
+ else if (r < 0)
+ log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
+ else if (in4_addr_is_null(&router[0]))
+ log_link_info(link, "DHCP: Received gateway is null.");
- r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
- if (IN_SET(r, 0, -ENODATA))
- log_link_info(link, "DHCP: No gateway received from DHCP server.");
- else if (r < 0)
- log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
- else if (in4_addr_is_null(&router[0]))
- log_link_info(link, "DHCP: Received gateway is null.");
+ /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
+ a Router option, the DHCP client MUST ignore the Router option. */
+ if (classless_route && static_route)
+ log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");
- /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
- a Router option, the DHCP client MUST ignore the Router option. */
- if (classless_route && static_route)
- log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");
+ if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) {
+ _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;
- if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) {
- _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;
+ r = route_new(&route_gw);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
- r = route_new(&route_gw);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
+ /* The dhcp netmask may mask out the gateway. Add an explicit
+ * route for the gw host so that we can route no matter the
+ * netmask or existing kernel route tables. */
+ route_gw->family = AF_INET;
+ route_gw->dst.in = router[0];
+ route_gw->dst_prefixlen = 32;
+ route_gw->prefsrc.in = address;
+ route_gw->scope = RT_SCOPE_LINK;
+ route_gw->protocol = RTPROT_DHCP;
+ route_gw->priority = link->network->dhcp_route_metric;
+ route_gw->table = table;
+ route_gw->mtu = link->network->dhcp_route_mtu;
- /* The dhcp netmask may mask out the gateway. Add an explicit
- * route for the gw host so that we can route no matter the
- * netmask or existing kernel route tables. */
- route_gw->family = AF_INET;
- route_gw->dst.in = router[0];
- route_gw->dst_prefixlen = 32;
- route_gw->prefsrc.in = address;
- route_gw->scope = RT_SCOPE_LINK;
- route_gw->protocol = RTPROT_DHCP;
- route_gw->priority = link->network->dhcp_route_metric;
- route_gw->table = table;
- route_gw->mtu = link->network->dhcp_route_mtu;
+ r = dhcp_route_configure(&route_gw, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set host route: %m");
- r = dhcp_route_configure(&route_gw, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set host route: %m");
+ r = route_new(&route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
- r = route_new(&route);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not allocate route: %m");
+ route->family = AF_INET;
+ route->gw.in = router[0];
+ route->prefsrc.in = address;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = table;
+ route->mtu = link->network->dhcp_route_mtu;
- route->family = AF_INET;
- route->gw.in = router[0];
- route->prefsrc.in = address;
- route->protocol = RTPROT_DHCP;
- route->priority = link->network->dhcp_route_metric;
- route->table = table;
- route->mtu = link->network->dhcp_route_mtu;
+ r = dhcp_route_configure(&route, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set router: %m");
+ }
- r = dhcp_route_configure(&route, link);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set router: %m");
- }
+ Route *rt;
+ LIST_FOREACH(routes, rt, link->network->static_routes) {
+ if (!rt->gateway_from_dhcp)
+ continue;
- Route *rt;
- LIST_FOREACH(routes, rt, link->network->static_routes) {
- if (!rt->gateway_from_dhcp)
- continue;
+ if (rt->family != AF_INET)
+ continue;
- if (rt->family != AF_INET)
- continue;
+ rt->gw.in = router[0];
- rt->gw.in = router[0];
-
- r = route_configure(rt, link, dhcp4_route_handler);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not set gateway: %m");
- if (r > 0)
- link->dhcp4_messages++;
+ r = route_configure(rt, link, dhcp4_route_handler);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set gateway: %m");
+ if (r > 0)
+ link->dhcp4_messages++;
+ }
}
return link_set_dns_routes(link, &address);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 9597176b1c..1a37c4ac9e 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -164,7 +164,7 @@ DHCPv4.UseMTU, config_parse_bool,
DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
DHCPv4.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
DHCPv4.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
-DHCPv4.UseGateway, config_parse_bool, 0, offsetof(Network, dhcp_use_gateway)
+DHCPv4.UseGateway, config_parse_tristate, 0, offsetof(Network, dhcp_use_gateway)
DHCPv4.RequestOptions, config_parse_dhcp_request_options, 0, 0
DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 9edf9e1561..d9646a21cb 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -267,6 +267,9 @@ int network_verify(Network *network) {
network->dhcp_use_mtu = false;
}
+ if (network->dhcp_use_gateway < 0)
+ network->dhcp_use_gateway = network->dhcp_use_routes;
+
if (network->dhcp_critical >= 0) {
if (network->keep_configuration >= 0)
log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
@@ -385,7 +388,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.dhcp_use_dns = true,
.dhcp_use_hostname = true,
.dhcp_use_routes = true,
- .dhcp_use_gateway = true,
+ .dhcp_use_gateway = -1,
/* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
.dhcp_send_hostname = true,
.dhcp_send_release = true,
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 240f6689dd..2665686143 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -110,7 +110,7 @@ struct Network {
bool dhcp_use_sip;
bool dhcp_use_mtu;
bool dhcp_use_routes;
- bool dhcp_use_gateway;
+ int dhcp_use_gateway;
bool dhcp_use_timezone;
bool rapid_commit;
bool dhcp_use_hostname;
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-gateway-no.network b/test/test-network/conf/dhcp-client-ipv4-use-gateway-no.network
deleted file mode 100644
index fb31772201..0000000000
--- a/test/test-network/conf/dhcp-client-ipv4-use-gateway-no.network
+++ /dev/null
@@ -1,9 +0,0 @@
-[Match]
-Name=veth99
-
-[Network]
-DHCP=ipv4
-IPv6AcceptRA=false
-
-[DHCPv4]
-UseGateway=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-no.network b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network
similarity index 72%
rename from test/test-network/conf/dhcp-client-ipv4-use-routes-no.network
rename to test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network
index 21e21fabb3..c980bf9fca 100644
--- a/test/test-network/conf/dhcp-client-ipv4-use-routes-no.network
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network
@@ -4,6 +4,3 @@ Name=veth99
[Network]
DHCP=ipv4
IPv6AcceptRA=false
-
-[DHCPv4]
-UseRoutes=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf
new file mode 100644
index 0000000000..9c561fba1a
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf
@@ -0,0 +1,2 @@
+[DHCPv4]
+RoutesToDNS=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf
new file mode 100644
index 0000000000..2504c20563
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf
@@ -0,0 +1,2 @@
+[DHCPv4]
+RoutesToDNS=yes
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf
new file mode 100644
index 0000000000..78d0493013
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseGateway=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf
new file mode 100644
index 0000000000..f6f597b0f4
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseGateway=yes
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf
new file mode 100644
index 0000000000..38fa983cd2
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseRoutes=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf
new file mode 100644
index 0000000000..3b2d0abf32
--- /dev/null
+++ b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseRoutes=yes
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 388e615602..3fc93a725f 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -3,6 +3,7 @@
# systemd-networkd tests
import argparse
+import itertools
import os
import re
import shutil
@@ -2921,8 +2922,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'dhcp-client-ipv4-dhcp-settings.network',
'dhcp-client-ipv4-only-ipv6-disabled.network',
'dhcp-client-ipv4-only.network',
- 'dhcp-client-ipv4-use-gateway-no.network',
- 'dhcp-client-ipv4-use-routes-no.network',
+ 'dhcp-client-ipv4-use-routes-use-gateway.network',
'dhcp-client-ipv6-only.network',
'dhcp-client-ipv6-rapid-commit.network',
'dhcp-client-keep-configuration-dhcp-on-stop.network',
@@ -2937,7 +2937,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'dhcp-client-use-dns-no.network',
'dhcp-client-use-dns-yes.network',
'dhcp-client-use-domains.network',
- 'dhcp-client-use-routes-no.network',
'dhcp-client-vrf.network',
'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network',
'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network',
@@ -2946,7 +2945,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'dhcp-server-decline.network',
'dhcp-server-veth-peer.network',
'dhcp-v4-server-veth-peer.network',
- 'dhcp-client-use-domains.network',
'static.network']
def setUp(self):
@@ -3027,8 +3025,23 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
self.assertRegex(output, r'192.168.5.8 proto dhcp scope link src 192.168.5.181 metric 1024')
- def test_dhcp_client_ipv4_use_routes_no(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-use-routes-no.network')
+ def test_dhcp_client_ipv4_use_routes_gateway(self):
+ for (routes, gateway, dnsroutes) in itertools.product([True, False, None], repeat=3):
+ self.setUp()
+ with self.subTest(routes=routes, gateway=gateway, dnsroutes=dnsroutes):
+ self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dnsroutes)
+ self.tearDown()
+
+ def _test_dhcp_client_ipv4_use_routes_gateway(self, routes, gateway, dnsroutes):
+ testunit = 'dhcp-client-ipv4-use-routes-use-gateway.network'
+ testunits = ['25-veth.netdev', 'dhcp-server-veth-peer.network', testunit]
+ if routes != None:
+ testunits.append(f'{testunit}.d/use-routes-{routes}.conf');
+ if gateway != None:
+ testunits.append(f'{testunit}.d/use-gateway-{gateway}.conf');
+ if dnsroutes != None:
+ testunits.append(f'{testunit}.d/use-dns-routes-{dnsroutes}.conf');
+ copy_unit_to_networkd_unit_path(*testunits, dropins=False)
start_networkd()
self.wait_online(['veth-peer:carrier'])
@@ -3037,22 +3050,31 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip route show dev veth99')
print(output)
- self.assertNotRegex(output, r'192.168.5.5')
- self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024')
- self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
- def test_dhcp_client_ipv4_use_gateway_no(self):
- copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-use-gateway-no.network')
+ # UseRoutes= defaults to true
+ useroutes = routes in [True, None]
+ # UseGateway= defaults to useroutes
+ usegateway = useroutes if gateway == None else gateway
- start_networkd()
- self.wait_online(['veth-peer:carrier'])
- start_dnsmasq(additional_options='--dhcp-option=option:dns-server,192.168.5.6,192.168.5.7', lease_time='2m')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ # Check UseRoutes=
+ if useroutes:
+ self.assertRegex(output, r'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.181 metric 1024')
+ else:
+ self.assertNotRegex(output, r'192.168.5.5')
- output = check_output('ip route show dev veth99')
- print(output)
- self.assertRegex(output, r'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.181 metric 1024')
- self.assertNotRegex(output, r'default via 192.168.5.1')
+ # Check UseGateway=
+ if usegateway:
+ self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024')
+ else:
+ self.assertNotRegex(output, r'default via 192.168.5.1')
+
+ # Check RoutesToDNS=, which defaults to false
+ if dnsroutes:
+ self.assertRegex(output, r'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024')
+ self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
+ else:
+ self.assertNotRegex(output, r'192.168.5.6')
+ self.assertNotRegex(output, r'192.168.5.7')
def test_dhcp_client_ipv4_ipv6(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',