summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorLuca Boccassi <bluca@debian.org>2023-07-29 13:15:38 +0200
committerGitHub <noreply@github.com>2023-07-29 13:15:38 +0200
commitfc3fe92bc8371ba4d21c5538800accf121ca4fe3 (patch)
treede0f29452cf2c934c3f6f24f604c22ad36f1b2af /src/network
parentMerge pull request #28409 from yuwata/network-dhcp4-drop-duid-only (diff)
parentnetwork/dhcp4: drop unused logic of finding default gateway (diff)
downloadsystemd-fc3fe92bc8371ba4d21c5538800accf121ca4fe3.tar.xz
systemd-fc3fe92bc8371ba4d21c5538800accf121ca4fe3.zip
Merge pull request #28508 from yuwata/network-next-dhcp4
network: several cleanups and fixes for DHCPv4 client
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkd-dhcp4.c478
1 files changed, 281 insertions, 197 deletions
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 4f89cdd857..fd8ef402bd 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -52,6 +52,186 @@ void network_adjust_dhcp4(Network *network) {
network->dhcp_client_identifier = network->dhcp_anonymize ? DHCP_CLIENT_ID_MAC : DHCP_CLIENT_ID_DUID;
}
+static int dhcp4_prefix_covers(
+ Link *link,
+ const struct in_addr *in_prefix,
+ uint8_t in_prefixlen) {
+
+ struct in_addr prefix;
+ uint8_t prefixlen;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+ assert(in_prefix);
+
+ /* Return true if the input address or address range is in the assigned network.
+ * E.g. if the DHCP server provides 192.168.0.100/24, then this returns true for the address or
+ * address range in 192.168.0.0/24, and returns false otherwise. */
+
+ r = sd_dhcp_lease_get_prefix(link->dhcp_lease, &prefix, &prefixlen);
+ if (r < 0)
+ return r;
+
+ return in4_addr_prefix_covers_full(&prefix, prefixlen, in_prefix, in_prefixlen);
+}
+
+static int dhcp4_get_router(Link *link, struct in_addr *ret) {
+ const struct in_addr *routers;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+ assert(ret);
+
+ r = sd_dhcp_lease_get_router(link->dhcp_lease, &routers);
+ if (r < 0)
+ return r;
+
+ /* The router option may provide multiple routers, We only use the first non-null address. */
+
+ FOREACH_ARRAY(router, routers, r) {
+ if (in4_addr_is_null(router))
+ continue;
+
+ *ret = *router;
+ return 0;
+ }
+
+ return -ENODATA;
+}
+
+static int dhcp4_get_classless_static_or_static_routes(Link *link, sd_dhcp_route ***ret_routes, size_t *ret_num) {
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+
+ /* If the DHCP server returns both a Classless Static Routes option and a Static Routes option,
+ * the DHCP client MUST ignore the Static Routes option. */
+
+ r = sd_dhcp_lease_get_classless_routes(link->dhcp_lease, &routes);
+ if (r >= 0) {
+ assert(r > 0);
+ if (ret_routes)
+ *ret_routes = TAKE_PTR(routes);
+ if (ret_num)
+ *ret_num = r;
+ return 1; /* classless */
+ } else if (r != -ENODATA)
+ return r;
+
+ r = sd_dhcp_lease_get_static_routes(link->dhcp_lease, &routes);
+ if (r < 0)
+ return r;
+
+ assert(r > 0);
+ if (ret_routes)
+ *ret_routes = TAKE_PTR(routes);
+ if (ret_num)
+ *ret_num = r;
+ return 0; /* static */
+}
+
+static int dhcp4_find_gateway_for_destination(
+ Link *link,
+ const struct in_addr *destination,
+ uint8_t prefixlength,
+ bool allow_null,
+ struct in_addr *ret) {
+
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
+ size_t n_routes = 0;
+ bool is_classless, reachable;
+ uint8_t max_prefixlen = UINT8_MAX;
+ struct in_addr gw;
+ int r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+ assert(destination);
+ assert(ret);
+
+ /* This tries to find the most suitable gateway for an address or address range.
+ * E.g. if the server provides the default gateway 192.168.0.1 and a classless static route for
+ * 8.0.0.0/8 with gateway 192.168.0.2, then this returns 192.168.0.2 for 8.8.8.8/32, and 192.168.0.1
+ * for 9.9.9.9/32. If 'allow_null' flag is set, and the input address or address range is in the
+ * assigned network, then the default gateway will be ignored and the null address will be returned
+ * unless a matching non-default gateway found. */
+
+ r = dhcp4_prefix_covers(link, destination, prefixlength);
+ if (r < 0)
+ return r;
+ reachable = r > 0;
+
+ r = dhcp4_get_classless_static_or_static_routes(link, &routes, &n_routes);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ is_classless = r > 0;
+
+ /* First, find most suitable gateway. */
+ FOREACH_ARRAY(e, routes, n_routes) {
+ struct in_addr dst;
+ uint8_t len;
+
+ r = sd_dhcp_route_get_destination(*e, &dst);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_route_get_destination_prefix_length(*e, &len);
+ if (r < 0)
+ return r;
+
+ r = in4_addr_prefix_covers_full(&dst, len, destination, prefixlength);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (max_prefixlen != UINT8_MAX && max_prefixlen > len)
+ continue;
+
+ r = sd_dhcp_route_get_gateway(*e, &gw);
+ if (r < 0)
+ return r;
+
+ max_prefixlen = len;
+ }
+
+ /* Found a suitable gateway in classless static routes or static routes. */
+ if (max_prefixlen != UINT8_MAX) {
+ if (max_prefixlen == 0 && reachable && allow_null)
+ /* Do not return the default gateway, if the destination is in the assigned network. */
+ *ret = (struct in_addr) {};
+ else
+ *ret = gw;
+ return 0;
+ }
+
+ /* When the destination is in the assigned network, return the null address if allowed. */
+ if (reachable && allow_null) {
+ *ret = (struct in_addr) {};
+ return 0;
+ }
+
+ /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
+ * a Router option, the DHCP client MUST ignore the Router option. */
+ if (!is_classless) {
+ r = dhcp4_get_router(link, ret);
+ if (r >= 0)
+ return 0;
+ if (r != -ENODATA)
+ return r;
+ }
+
+ if (!reachable)
+ return -EHOSTUNREACH; /* Not in the same network, cannot reach the destination. */
+
+ assert(!allow_null);
+ return -ENODATA; /* No matching gateway found. */
+}
+
static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
Address *address;
Route *route;
@@ -224,7 +404,6 @@ static bool link_prefixroute(Link *link) {
static int dhcp4_request_prefix_route(Link *link) {
_cleanup_(route_freep) Route *route = NULL;
- struct in_addr address, netmask;
int r;
assert(link);
@@ -234,23 +413,20 @@ static int dhcp4_request_prefix_route(Link *link) {
/* When true, the route will be created by kernel. See dhcp4_update_address(). */
return 0;
- r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+ r = route_new(&route);
if (r < 0)
return r;
- r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
+ route->scope = RT_SCOPE_LINK;
+
+ r = sd_dhcp_lease_get_prefix(link->dhcp_lease, &route->dst.in, &route->dst_prefixlen);
if (r < 0)
return r;
- r = route_new(&route);
+ r = sd_dhcp_lease_get_address(link->dhcp_lease, &route->prefsrc.in);
if (r < 0)
return r;
- route->dst.in.s_addr = address.s_addr & netmask.s_addr;
- route->dst_prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
- route->prefsrc.in = address;
- route->scope = RT_SCOPE_LINK;
-
return dhcp4_request_route(TAKE_PTR(route), link);
}
@@ -282,12 +458,10 @@ static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw)
static int dhcp4_request_route_auto(
Route *in,
Link *link,
- const struct in_addr *gw,
- bool force_use_gw) {
+ const struct in_addr *gw) {
_cleanup_(route_freep) Route *route = in;
- struct in_addr address, netmask, prefix;
- uint8_t prefixlen;
+ struct in_addr address;
int r;
assert(route);
@@ -299,13 +473,6 @@ static int dhcp4_request_route_auto(
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, "
@@ -328,26 +495,23 @@ static int dhcp4_request_route_auto(
route->gw = IN_ADDR_NULL;
route->prefsrc.in = address;
- } else if (!force_use_gw &&
- 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));
+ } else if (in4_addr_is_null(gw)) {
+ r = dhcp4_prefix_covers(link, &route->dst.in, route->dst_prefixlen);
+ if (r < 0)
+ return r;
+ if (r == 0 && DEBUG_LOGGING) {
+ struct in_addr prefix;
+ uint8_t prefixlen;
- route->scope = RT_SCOPE_LINK;
- route->gw_family = AF_UNSPEC;
- route->gw = IN_ADDR_NULL;
- route->prefsrc.in = address;
+ r = sd_dhcp_lease_get_prefix(link->dhcp_lease, &prefix, &prefixlen);
+ if (r < 0)
+ return r;
- } 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, using 'link' scope.",
- IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen,
- IPV4_ADDRESS_FMT_VAL(prefix), prefixlen);
+ 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, using 'link' scope.",
+ IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen,
+ IPV4_ADDRESS_FMT_VAL(prefix), prefixlen);
+ }
route->scope = RT_SCOPE_LINK;
route->gw_family = AF_UNSPEC;
@@ -368,88 +532,24 @@ static int dhcp4_request_route_auto(
return dhcp4_request_route(TAKE_PTR(route), link);
}
-static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_gw) {
- _cleanup_free_ sd_dhcp_route **static_routes = NULL, **classless_routes = NULL;
- size_t n_static_routes, n_classless_routes, n;
- struct in_addr default_gw = {};
- sd_dhcp_route **routes;
+static int dhcp4_request_classless_static_or_static_routes(Link *link) {
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
+ size_t n_routes;
int r;
assert(link);
assert(link->dhcp_lease);
- assert(ret_default_gw);
-
- r = sd_dhcp_lease_get_static_routes(link->dhcp_lease, &static_routes);
- if (r == -ENODATA)
- n_static_routes = 0;
- else if (r < 0)
- return r;
- else
- n_static_routes = r;
- r = sd_dhcp_lease_get_classless_routes(link->dhcp_lease, &classless_routes);
- if (r == -ENODATA)
- n_classless_routes = 0;
- else if (r < 0)
- return r;
- else
- n_classless_routes = r;
-
- if (n_classless_routes == 0 && n_static_routes == 0) {
- log_link_debug(link, "DHCP: No static routes received from DHCP server.");
+ if (!link->network->dhcp_use_routes)
return 0;
- }
- /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option,
- * the DHCP client MUST ignore the Static Routes option. */
- if (n_classless_routes > 0 && n_static_routes > 0)
- log_link_debug(link, "Classless static routes received from DHCP server: ignoring static-route option");
-
- if (!link->network->dhcp_use_routes) {
-
- /* Even if UseRoutes=no, try to find default gateway to make semi-static routes and
- * routes to DNS or NTP servers can be configured in later steps. */
-
- for (size_t i = 0; i < n_classless_routes; i++) {
- struct in_addr dst;
- uint8_t prefixlen;
-
- r = sd_dhcp_route_get_destination(classless_routes[i], &dst);
- if (r < 0)
- return r;
-
- if (in4_addr_is_set(&dst))
- continue;
-
- r = sd_dhcp_route_get_destination_prefix_length(classless_routes[i], &prefixlen);
- if (r < 0)
- return r;
-
- if (prefixlen != 0)
- continue;
-
- r = sd_dhcp_route_get_gateway(classless_routes[i], ret_default_gw);
- if (r < 0)
- return r;
-
- break;
- }
-
- /* Do not return 1 here, to ensure the router option can override the default gateway
- * that was found. */
+ r = dhcp4_get_classless_static_or_static_routes(link, &routes, &n_routes);
+ if (r == -ENODATA)
return 0;
- }
-
- if (n_classless_routes > 0) {
- n = n_classless_routes;
- routes = classless_routes;
- } else if (n_static_routes > 0){
- n = n_static_routes;
- routes = static_routes;
- } else
- assert_not_reached();
+ if (r < 0)
+ return r;
- for (size_t i = 0; i < n; i++) {
+ FOREACH_ARRAY(e, routes, n_routes) {
_cleanup_(route_freep) Route *route = NULL;
struct in_addr gw;
@@ -457,78 +557,58 @@ static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_g
if (r < 0)
return r;
- route->gw_family = AF_INET;
-
- r = sd_dhcp_route_get_gateway(routes[i], &gw);
+ r = sd_dhcp_route_get_gateway(*e, &gw);
if (r < 0)
return r;
- r = sd_dhcp_route_get_destination(routes[i], &route->dst.in);
+ r = sd_dhcp_route_get_destination(*e, &route->dst.in);
if (r < 0)
return r;
- r = sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen);
+ r = sd_dhcp_route_get_destination_prefix_length(*e, &route->dst_prefixlen);
if (r < 0)
return r;
- /* When classless static routes are provided, then router option will be ignored. To
- * use the default gateway later in other routes, e.g., routes to dns servers, here we
- * need to find the default gateway in the classless static routes. */
- if (n_classless_routes > 0 &&
- in4_addr_is_null(&route->dst.in) && route->dst_prefixlen == 0 &&
- in4_addr_is_null(&default_gw))
- default_gw = gw;
-
- /* Do not ignore the gateway given by the classless route option even if the destination is
- * in the same network. See issue #28280. */
- r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw, /* force_use_gw = */ n_classless_routes > 0);
+ r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
if (r < 0)
return r;
}
- *ret_default_gw = default_gw;
- return n_classless_routes > 0;
+ return 0;
}
-static int dhcp4_request_gateway(Link *link, struct in_addr *gw) {
+static int dhcp4_request_default_gateway(Link *link) {
_cleanup_(route_freep) Route *route = NULL;
- const struct in_addr *router;
- struct in_addr address;
+ struct in_addr address, router;
int r;
assert(link);
assert(link->dhcp_lease);
- assert(gw);
+
+ if (!link->network->dhcp_use_gateway)
+ return 0;
+
+ /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
+ * a Router option, the DHCP client MUST ignore the Router option. */
+ if (link->network->dhcp_use_routes &&
+ dhcp4_get_classless_static_or_static_routes(link, NULL, NULL) > 0)
+ return 0;
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r < 0)
return r;
- r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
- if (IN_SET(r, 0, -ENODATA)) {
- log_link_debug(link, "DHCP: No gateway received from DHCP server.");
+ r = dhcp4_get_router(link, &router);
+ if (r == -ENODATA) {
+ log_link_debug(link, "DHCP: No valid router address received from DHCP server.");
return 0;
}
if (r < 0)
return r;
- if (in4_addr_is_null(&router[0])) {
- log_link_debug(link, "DHCP: Received gateway address is null.");
- return 0;
- }
-
- if (!link->network->dhcp_use_gateway) {
- /* When no classless static route is provided, even if UseGateway=no, use the gateway
- * address to configure semi-static routes or routes to DNS or NTP servers. Note, if
- * neither UseRoutes= nor UseGateway= is disabled, use the default gateway in classless
- * static routes if provided (in that case, in4_addr_is_null(gw) below is true). */
- if (in4_addr_is_null(gw))
- *gw = router[0];
- return 0;
- }
/* The dhcp netmask may mask out the gateway. First, add an explicit route for the gateway host
* so that we can route no matter the netmask or existing kernel route tables. */
- r = dhcp4_request_route_to_gateway(link, &router[0]);
+ r = dhcp4_request_route_to_gateway(link, &router);
if (r < 0)
return r;
@@ -538,33 +618,23 @@ static int dhcp4_request_gateway(Link *link, struct in_addr *gw) {
/* Next, add a default gateway. */
route->gw_family = AF_INET;
- route->gw.in = router[0];
+ route->gw.in = router;
route->prefsrc.in = address;
- r = dhcp4_request_route(TAKE_PTR(route), link);
- if (r < 0)
- return r;
-
- /* When no classless static route is provided, or UseRoutes=no, then use the router address to
- * configure semi-static routes and routes to DNS or NTP servers in later steps. */
- *gw = router[0];
- return 0;
+ return dhcp4_request_route(TAKE_PTR(route), link);
}
-static int dhcp4_request_semi_static_routes(Link *link, const struct in_addr *gw) {
+static int dhcp4_request_semi_static_routes(Link *link) {
Route *rt;
int r;
assert(link);
assert(link->dhcp_lease);
assert(link->network);
- assert(gw);
-
- if (in4_addr_is_null(gw))
- return 0;
HASHMAP_FOREACH(rt, link->network->routes_by_section) {
_cleanup_(route_freep) Route *route = NULL;
+ struct in_addr gw;
if (!rt->gateway_from_dhcp_or_ra)
continue;
@@ -572,7 +642,18 @@ static int dhcp4_request_semi_static_routes(Link *link, const struct in_addr *gw
if (rt->gw_family != AF_INET)
continue;
- r = dhcp4_request_route_to_gateway(link, gw);
+ assert(rt->family == AF_INET);
+
+ r = dhcp4_find_gateway_for_destination(link, &rt->dst.in, rt->dst_prefixlen, /* allow_null = */ false, &gw);
+ if (IN_SET(r, -EHOSTUNREACH, -ENODATA)) {
+ log_link_debug_errno(link, r, "DHCP: Cannot find suitable gateway for destination %s of semi-static route, ignoring: %m",
+ IN4_ADDR_PREFIX_TO_STRING(&rt->dst.in, rt->dst_prefixlen));
+ continue;
+ }
+ if (r < 0)
+ return r;
+
+ r = dhcp4_request_route_to_gateway(link, &gw);
if (r < 0)
return r;
@@ -580,7 +661,7 @@ static int dhcp4_request_semi_static_routes(Link *link, const struct in_addr *gw
if (r < 0)
return r;
- route->gw.in = *gw;
+ route->gw.in = gw;
r = dhcp4_request_route(TAKE_PTR(route), link);
if (r < 0)
@@ -593,8 +674,7 @@ static int dhcp4_request_semi_static_routes(Link *link, const struct in_addr *gw
static int dhcp4_request_routes_to_servers(
Link *link,
const struct in_addr *servers,
- size_t n_servers,
- const struct in_addr *gw) {
+ size_t n_servers) {
int r;
@@ -602,22 +682,31 @@ static int dhcp4_request_routes_to_servers(
assert(link->dhcp_lease);
assert(link->network);
assert(servers || n_servers == 0);
- assert(gw);
- for (size_t i = 0; i < n_servers; i++) {
+ FOREACH_ARRAY(dst, servers, n_servers) {
_cleanup_(route_freep) Route *route = NULL;
+ struct in_addr gw;
- if (in4_addr_is_null(&servers[i]))
+ if (in4_addr_is_null(dst))
continue;
+ r = dhcp4_find_gateway_for_destination(link, dst, 32, /* allow_null = */ true, &gw);
+ if (r == -EHOSTUNREACH) {
+ log_link_debug_errno(link, r, "DHCP: Cannot find suitable gateway for destination %s, ignoring: %m",
+ IN4_ADDR_PREFIX_TO_STRING(dst, 32));
+ continue;
+ }
+ if (r < 0)
+ return r;
+
r = route_new(&route);
if (r < 0)
return r;
- route->dst.in = servers[i];
+ route->dst.in = *dst;
route->dst_prefixlen = 32;
- r = dhcp4_request_route_auto(TAKE_PTR(route), link, gw, /* force_use_gw = */ false);
+ r = dhcp4_request_route_auto(TAKE_PTR(route), link, &gw);
if (r < 0)
return r;
}
@@ -625,14 +714,13 @@ static int dhcp4_request_routes_to_servers(
return 0;
}
-static int dhcp4_request_routes_to_dns(Link *link, const struct in_addr *gw) {
+static int dhcp4_request_routes_to_dns(Link *link) {
const struct in_addr *dns;
int r;
assert(link);
assert(link->dhcp_lease);
assert(link->network);
- assert(gw);
if (!link->network->dhcp_use_dns ||
!link->network->dhcp_routes_to_dns)
@@ -644,17 +732,16 @@ static int dhcp4_request_routes_to_dns(Link *link, const struct in_addr *gw) {
if (r < 0)
return r;
- return dhcp4_request_routes_to_servers(link, dns, r, gw);
+ return dhcp4_request_routes_to_servers(link, dns, r);
}
-static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) {
+static int dhcp4_request_routes_to_ntp(Link *link) {
const struct in_addr *ntp;
int r;
assert(link);
assert(link->dhcp_lease);
assert(link->network);
- assert(gw);
if (!link->network->dhcp_use_ntp ||
!link->network->dhcp_routes_to_ntp)
@@ -666,11 +753,10 @@ static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) {
if (r < 0)
return r;
- return dhcp4_request_routes_to_servers(link, ntp, r, gw);
+ return dhcp4_request_routes_to_servers(link, ntp, r);
}
static int dhcp4_request_routes(Link *link) {
- struct in_addr gw = {};
int r;
assert(link);
@@ -680,26 +766,23 @@ static int dhcp4_request_routes(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: Could not request prefix route: %m");
- r = dhcp4_request_static_routes(link, &gw);
+ r = dhcp4_request_default_gateway(link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "DHCP error: Could not request default gateway: %m");
+
+ r = dhcp4_request_classless_static_or_static_routes(link);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: Could not request static routes: %m");
- if (r == 0) {
- /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and
- * a Router option, the DHCP client MUST ignore the Router option. */
- r = dhcp4_request_gateway(link, &gw);
- if (r < 0)
- return log_link_error_errno(link, r, "DHCP error: Could not request gateway: %m");
- }
- r = dhcp4_request_semi_static_routes(link, &gw);
+ r = dhcp4_request_semi_static_routes(link);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: Could not request routes with Gateway=_dhcp4 setting: %m");
- r = dhcp4_request_routes_to_dns(link, &gw);
+ r = dhcp4_request_routes_to_dns(link);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: Could not request routes to DNS servers: %m");
- r = dhcp4_request_routes_to_ntp(link, &gw);
+ r = dhcp4_request_routes_to_ntp(link);
if (r < 0)
return log_link_error_errno(link, r, "DHCP error: Could not request routes to NTP servers: %m");
@@ -804,8 +887,8 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
static int dhcp4_request_address(Link *link, bool announce) {
_cleanup_(address_freep) Address *addr = NULL;
- struct in_addr address, netmask, server;
- unsigned prefixlen;
+ struct in_addr address, server;
+ uint8_t prefixlen;
Address *existing;
usec_t lifetime_usec;
int r;
@@ -819,7 +902,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no address: %m");
- r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
+ r = sd_dhcp_lease_get_prefix(link->dhcp_lease, NULL, &prefixlen);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
@@ -840,8 +923,6 @@ static int dhcp4_request_address(Link *link, bool announce) {
} else
lifetime_usec = USEC_INFINITY;
- prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
-
if (announce) {
const struct in_addr *router;
@@ -882,6 +963,9 @@ static int dhcp4_request_address(Link *link, bool announce) {
addr->lifetime_preferred_usec = lifetime_usec;
addr->lifetime_valid_usec = lifetime_usec;
addr->prefixlen = prefixlen;
+ r = sd_dhcp_lease_get_broadcast(link->dhcp_lease, &addr->broadcast);
+ if (r < 0 && r != -ENODATA)
+ return log_link_warning_errno(link, r, "DHCP: failed to get broadcast address: %m");
address_set_broadcast(addr, link);
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
addr->route_metric = link->network->dhcp_route_metric;