]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: clear existing routes if Gateway= is empty in [Network]
authorQuentin Deslandes <qde@naccy.de>
Tue, 18 Nov 2025 20:36:49 +0000 (21:36 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 19 Nov 2025 22:16:26 +0000 (07:16 +0900)
Add support for an empty Gateway= in [Network] to clear the existing
routes. This change will allow users to remove the default route from a
drop-in file.

man/systemd.network.xml
src/network/networkd-route.c
test/test-network/conf/25-gateway-clear-routes.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

index bbfc9a7af09067a3080b56c5253528c4e1442cdd..b9f49a43a804ad300e95919be3e7173416b37beb 100644 (file)
@@ -747,6 +747,9 @@ DuplicateAddressDetection=none</programlisting></para>
           This is a short-hand for a [Route] section only containing a <varname>Gateway=</varname> key.
           This option may be specified more than once.</para>
 
+          <para>If an empty string is specified, then the all previous assignments in both [Network] and
+          [Route] sections are cleared.</para>
+
           <xi:include href="version-info.xml" xpointer="v211"/>
         </listitem>
       </varlistentry>
index 4f27ccc81be085c1f5ed9b8175e8bcdfc68734f9..94b87d21799a9d7523a0741c7970386089493b26 100644 (file)
@@ -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 (file)
index 0000000..0dba189
--- /dev/null
@@ -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=
index 7ee6ee111299cf92a6834f7bfb252a4cf7396836..beaefbdc8957259f30169ed9875017a2e29f22d5 100755 (executable)
@@ -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