Blob Blame History Raw
From c19df00f6def1ee74ae0812b529f2a1b589c256f Mon Sep 17 00:00:00 2001
From: Daniel Wang <wonderfly@users.noreply.github.com>
Date: Sat, 20 May 2017 04:05:18 -0700
Subject: [PATCH] DHCP: when adding static routes set scopes properly (#5982)

DHCP responses could include static routes, but unfortunately not an
option to tell what scope to use. So it's important that the client sets
it properly.

This mimics what the `ip route add` command does when adding a static
route without an explicit scope:

* If the destination IP is on the local host, use scope `host`
* Otherwise if the gateway IP is null (direct route), use scope `link`
* If anything else, use the current default `global`.

Fixes #5979.
(cherry picked from commit d6eac9bd06066c8d041449538a9cdee0fd928835)
---
 man/systemd.network.xml      |  8 +++++---
 src/network/networkd-dhcp4.c | 17 +++++++++++++++--
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index b807ebf29b..aaa7b0968d 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -937,9 +937,11 @@
         <varlistentry>
           <term><varname>UseRoutes=</varname></term>
           <listitem>
-            <para>When true (the default), the static routes will be
-            requested from the DHCP server and added to the routing
-            table with a metric of 1024.</para>
+            <para>When true (the default), the static routes will be requested from the DHCP server and added to the
+              routing table with a metric of 1024, and a scope of "global", "link" or "host", depending on the route's
+              destination and gateway. If the destination is on the local host, e.g., 127.x.x.x, or the same as the
+              link's own address, the scope will be set to "host". Otherwise if the gateway is null (a direct route), a
+              "link" scope will be used. For anything else, scope defaults to "global".</para>
           </listitem>
         </varlistentry>
 
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index c5c5b95c8f..ae0f78daab 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -52,8 +52,21 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m,
         return 1;
 }
 
+static int route_scope_from_address(const Route *route, const struct in_addr *self_addr) {
+        assert(route);
+        assert(self_addr);
+
+        if (in_addr_is_localhost(AF_INET, &route->dst) ||
+            (self_addr->s_addr && route->dst.in.s_addr == self_addr->s_addr))
+                return RT_SCOPE_HOST;
+        else if (in4_addr_is_null(&route->gw.in))
+                return RT_SCOPE_LINK;
+        else
+                return RT_SCOPE_UNIVERSE;
+}
+
 static int link_set_dhcp_routes(Link *link) {
-        struct in_addr gateway;
+        struct in_addr gateway, address;
         _cleanup_free_ sd_dhcp_route **static_routes = NULL;
         int r, n, i;
 
@@ -69,7 +82,6 @@ static int link_set_dhcp_routes(Link *link) {
                 return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
 
         if (r >= 0) {
-                struct in_addr address;
                 _cleanup_route_free_ Route *route = NULL;
                 _cleanup_route_free_ Route *route_gw = NULL;
 
@@ -141,6 +153,7 @@ static int link_set_dhcp_routes(Link *link) {
                 assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
                 route->priority = link->network->dhcp_route_metric;
                 route->table = link->network->dhcp_route_table;
+                route->scope = route_scope_from_address(route, &address);
 
                 r = route_configure(route, link, dhcp4_route_handler);
                 if (r < 0)