diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 0c856720e9..42aa429c0e 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -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; }