]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: dhcp4: ignore gateway in static routes if destination is link-local or in...
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 12 Apr 2021 14:52:49 +0000 (23:52 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 22 Apr 2021 23:49:29 +0000 (08:49 +0900)
This also configures routes to gateways in static routes if the
destination is not in the same network.

src/network/networkd-dhcp4.c

index 0c856720e9a0688a5b06196c010e36b2e00cd33b..42aa429c0eda41ffb66d572ee977959a2eac17ad 100644 (file)
@@ -143,19 +143,6 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
         return 1;
 }
 
-static int route_scope_from_address(const Route *route, const struct in_addr *self_addr) {
-        assert(route);
-        assert(self_addr);
-
-        if (in4_addr_is_localhost(&route->dst.in) ||
-            (in4_addr_is_set(self_addr) && in4_addr_equal(&route->dst.in, self_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 dhcp_route_configure(Route *route, Link *link) {
         Route *ret;
         int r;
@@ -250,11 +237,95 @@ static int link_set_dhcp_route_to_gateway(Link *link, const struct in_addr *gw)
         return dhcp_route_configure(route, link);
 }
 
+static int dhcp_route_configure_auto(
+                Route *route,
+                Link *link,
+                const struct in_addr *gw) {
+
+        struct in_addr address, netmask, prefix;
+        uint8_t prefixlen;
+        int r;
+
+        assert(route);
+        assert(link);
+        assert(link->dhcp_lease);
+        assert(gw);
+
+        /* The route object may be reused in an iteration. All elements must be set or cleared. */
+
+        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+        if (r < 0)
+                return r;
+
+        r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
+        if (r < 0)
+                return r;
+
+        prefix.s_addr = address.s_addr & netmask.s_addr;
+        prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
+
+        if (in4_addr_is_localhost(&route->dst.in)) {
+                if (in4_addr_is_set(gw))
+                        log_link_debug(link, "DHCP: requested route destination "IPV4_ADDRESS_FMT_STR"/%u is localhost, "
+                                       "ignoring gateway address "IPV4_ADDRESS_FMT_STR,
+                                       IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen, IPV4_ADDRESS_FMT_VAL(*gw));
+
+                route->scope = RT_SCOPE_HOST;
+                route->gw_family = AF_UNSPEC;
+                route->gw = IN_ADDR_NULL;
+                route->prefsrc = IN_ADDR_NULL;
+
+        } else if (in4_addr_equal(&route->dst.in, &address)) {
+                if (in4_addr_is_set(gw))
+                        log_link_debug(link, "DHCP: requested route destination "IPV4_ADDRESS_FMT_STR"/%u is equivalent to the acquired address, "
+                                       "ignoring gateway address "IPV4_ADDRESS_FMT_STR,
+                                       IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen, IPV4_ADDRESS_FMT_VAL(*gw));
+
+                route->scope = RT_SCOPE_HOST;
+                route->gw_family = AF_UNSPEC;
+                route->gw = IN_ADDR_NULL;
+                route->prefsrc.in = address;
+
+        } else if (route->dst_prefixlen >= prefixlen &&
+                   (route->dst.in.s_addr & netmask.s_addr) == prefix.s_addr) {
+                if (in4_addr_is_set(gw))
+                        log_link_debug(link, "DHCP: requested route destination "IPV4_ADDRESS_FMT_STR"/%u is in the assigned network "
+                                       IPV4_ADDRESS_FMT_STR"/%u, ignoring gateway address "IPV4_ADDRESS_FMT_STR,
+                                       IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen,
+                                       IPV4_ADDRESS_FMT_VAL(prefix), prefixlen,
+                                       IPV4_ADDRESS_FMT_VAL(*gw));
+
+                route->scope = RT_SCOPE_LINK;
+                route->gw_family = AF_UNSPEC;
+                route->gw = IN_ADDR_NULL;
+                route->prefsrc.in = address;
+
+        } else {
+                if (in4_addr_is_null(gw)) {
+                        log_link_debug(link, "DHCP: requested route destination "IPV4_ADDRESS_FMT_STR"/%u is not in the assigned network "
+                                       IPV4_ADDRESS_FMT_STR"/%u, but no gateway is specified, ignoring.",
+                                       IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen,
+                                       IPV4_ADDRESS_FMT_VAL(prefix), prefixlen);
+                        return 0;
+                }
+
+                r = link_set_dhcp_route_to_gateway(link, gw);
+                if (r < 0)
+                        return r;
+
+                route->scope = RT_SCOPE_UNIVERSE;
+                route->gw_family = AF_INET;
+                route->gw.in = *gw;
+                route->prefsrc.in = address;
+        }
+
+        return dhcp_route_configure(route, link);
+}
+
 static int link_set_dhcp_static_routes(Link *link) {
         _cleanup_free_ sd_dhcp_route **static_routes = NULL;
         bool classless_route = false, static_route = false;
         _cleanup_(route_freep) Route *route = NULL;
-        struct in_addr address;
         int n, r;
 
         assert(link);
@@ -263,10 +334,6 @@ static int link_set_dhcp_static_routes(Link *link) {
         if (!link->network->dhcp_use_routes)
                 return 0;
 
-        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
-        if (r < 0)
-                return r;
-
         n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes);
         if (IN_SET(n, 0, -ENODATA)) {
                 log_link_debug(link, "DHCP: No static routes received from DHCP server.");
@@ -303,11 +370,13 @@ static int link_set_dhcp_static_routes(Link *link) {
         route->mtu = link->network->dhcp_route_mtu;
 
         for (int i = 0; i < n; i++) {
+                struct in_addr gw;
+
                 if (sd_dhcp_route_get_option(static_routes[i]) !=
                     (classless_route ? SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE : SD_DHCP_OPTION_STATIC_ROUTE))
                         continue;
 
-                r = sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in);
+                r = sd_dhcp_route_get_gateway(static_routes[i], &gw);
                 if (r < 0)
                         return r;
 
@@ -319,13 +388,7 @@ static int link_set_dhcp_static_routes(Link *link) {
                 if (r < 0)
                         return r;
 
-                route->scope = route_scope_from_address(route, &address);
-                if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
-                        route->prefsrc.in = address;
-                else
-                        route->prefsrc = IN_ADDR_NULL;
-
-                r = dhcp_route_configure(route, link);
+                r = dhcp_route_configure_auto(route, link, &gw);
                 if (r < 0)
                         return r;
         }