diff --git a/man/systemd.network.xml b/man/systemd.network.xml index bbfc9a7af0..b9f49a43a8 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -747,6 +747,9 @@ DuplicateAddressDetection=none This is a short-hand for a [Route] section only containing a Gateway= key. This option may be specified more than once. + If an empty string is specified, then the all previous assignments in both [Network] and + [Route] sections are cleared. + diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 4f27ccc81b..94b87d2179 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -2008,6 +2008,12 @@ int config_parse_route_section( if (streq(section, "Network")) { assert(streq_ptr(lvalue, "Gateway")); + /* Clear all previously defined routes when Gateway= (empty) is set in [Network] section */ + if (isempty(rvalue)) { + network->routes_by_section = hashmap_free(network->routes_by_section); + return 0; + } + /* we are not in an Route section, so use line number instead */ r = route_new_static(network, filename, line, &route); } else diff --git a/test/test-network/conf/25-gateway-clear-routes.network b/test/test-network/conf/25-gateway-clear-routes.network new file mode 100644 index 0000000000..0dba1892bd --- /dev/null +++ b/test/test-network/conf/25-gateway-clear-routes.network @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Match] +Name=dummy98 + +[Network] +Address=10.0.0.1/24 +Gateway=10.0.0.2 + +[Route] +Destination=192.168.1.0/24 +Gateway=10.0.0.254 + +[Route] +Destination=192.168.2.0/24 +Gateway=10.0.0.253 + +[Network] +Gateway= diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 7ee6ee1112..beaefbdc89 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -4633,6 +4633,21 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): self.assertNotIn('149.10.124.59', output) self.assertIn('default via 149.10.124.60 proto static', output) + def test_gateway_clear_routes(self): + copy_network_unit('25-gateway-clear-routes.network', '12-dummy.netdev') + start_networkd() + self.wait_online('dummy98:routable') + + print('### ip -4 route show dev dummy98') + output = check_output('ip -4 route show dev dummy98') + print(output) + # All routes should be cleared - no default gateway, no [Route] section routes + self.assertNotIn('default via 10.0.0.2', output) + self.assertNotIn('192.168.1.0/24', output) + self.assertNotIn('192.168.2.0/24', output) + # Only the directly connected network should remain + self.assertIn('10.0.0.0/24 proto kernel scope link src 10.0.0.1', output) + def test_ip_route_ipv6_src_route(self): # a dummy device does not make the addresses go through tentative state, so we # reuse a bond from an earlier test, which does make the addresses go through