1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-23 21:35:11 +03:00

Merge pull request #28508 from yuwata/network-next-dhcp4

network: several cleanups and fixes for DHCPv4 client
This commit is contained in:
Luca Boccassi 2023-07-29 12:15:38 +01:00 committed by GitHub
commit fc3fe92bc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 355 additions and 218 deletions

View File

@ -727,10 +727,11 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen)
}
}
int in4_addr_prefix_covers(
int in4_addr_prefix_covers_full(
const struct in_addr *prefix,
unsigned char prefixlen,
const struct in_addr *address) {
const struct in_addr *address,
unsigned char address_prefixlen) {
struct in_addr masked_prefix, masked_address;
int r;
@ -738,6 +739,9 @@ int in4_addr_prefix_covers(
assert(prefix);
assert(address);
if (prefixlen > address_prefixlen)
return false;
masked_prefix = *prefix;
r = in4_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@ -751,10 +755,11 @@ int in4_addr_prefix_covers(
return in4_addr_equal(&masked_prefix, &masked_address);
}
int in6_addr_prefix_covers(
int in6_addr_prefix_covers_full(
const struct in6_addr *prefix,
unsigned char prefixlen,
const struct in6_addr *address) {
const struct in6_addr *address,
unsigned char address_prefixlen) {
struct in6_addr masked_prefix, masked_address;
int r;
@ -762,6 +767,9 @@ int in6_addr_prefix_covers(
assert(prefix);
assert(address);
if (prefixlen > address_prefixlen)
return false;
masked_prefix = *prefix;
r = in6_addr_mask(&masked_prefix, prefixlen);
if (r < 0)
@ -775,20 +783,21 @@ int in6_addr_prefix_covers(
return in6_addr_equal(&masked_prefix, &masked_address);
}
int in_addr_prefix_covers(
int in_addr_prefix_covers_full(
int family,
const union in_addr_union *prefix,
unsigned char prefixlen,
const union in_addr_union *address) {
const union in_addr_union *address,
unsigned char address_prefixlen) {
assert(prefix);
assert(address);
switch (family) {
case AF_INET:
return in4_addr_prefix_covers(&prefix->in, prefixlen, &address->in);
return in4_addr_prefix_covers_full(&prefix->in, prefixlen, &address->in, address_prefixlen);
case AF_INET6:
return in6_addr_prefix_covers(&prefix->in6, prefixlen, &address->in6);
return in6_addr_prefix_covers_full(&prefix->in6, prefixlen, &address->in6, address_prefixlen);
default:
return -EAFNOSUPPORT;
}

View File

@ -144,9 +144,18 @@ int in4_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mas
int in4_addr_mask(struct in_addr *addr, unsigned char prefixlen);
int in6_addr_mask(struct in6_addr *addr, unsigned char prefixlen);
int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen);
int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address);
int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address);
int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address);
int in4_addr_prefix_covers_full(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address, unsigned char address_prefixlen);
int in6_addr_prefix_covers_full(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address, unsigned char address_prefixlen);
int in_addr_prefix_covers_full(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address, unsigned char address_prefixlen);
static inline int in4_addr_prefix_covers(const struct in_addr *prefix, unsigned char prefixlen, const struct in_addr *address) {
return in4_addr_prefix_covers_full(prefix, prefixlen, address, 32);
}
static inline int in6_addr_prefix_covers(const struct in6_addr *prefix, unsigned char prefixlen, const struct in6_addr *address) {
return in6_addr_prefix_covers_full(prefix, prefixlen, address, 128);
}
static inline int in_addr_prefix_covers(int family, const union in_addr_union *prefix, unsigned char prefixlen, const union in_addr_union *address) {
return in_addr_prefix_covers_full(family, prefix, prefixlen, address, family == AF_INET ? 32 : family == AF_INET6 ? 128 : 0);
}
int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret);
int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, unsigned char *ret_prefixlen);

View File

@ -201,6 +201,34 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) {
return 0;
}
int sd_dhcp_lease_get_prefix(sd_dhcp_lease *lease, struct in_addr *ret_prefix, uint8_t *ret_prefixlen) {
struct in_addr address, netmask;
uint8_t prefixlen;
int r;
assert_return(lease, -EINVAL);
r = sd_dhcp_lease_get_address(lease, &address);
if (r < 0)
return r;
r = sd_dhcp_lease_get_netmask(lease, &netmask);
if (r < 0)
return r;
prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
r = in4_addr_mask(&address, prefixlen);
if (r < 0)
return r;
if (ret_prefix)
*ret_prefix = address;
if (ret_prefixlen)
*ret_prefixlen = prefixlen;
return 0;
}
int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr) {
assert_return(lease, -EINVAL);
assert_return(addr, -EINVAL);

View File

@ -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);
if (r < 0)
return r;
r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
if (r < 0)
return r;
r = route_new(&route);
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;
r = sd_dhcp_lease_get_prefix(link->dhcp_lease, &route->dst.in, &route->dst_prefixlen);
if (r < 0)
return r;
r = sd_dhcp_lease_get_address(link->dhcp_lease, &route->prefsrc.in);
if (r < 0)
return r;
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));
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, using 'link' scope.",
IPV4_ADDRESS_FMT_VAL(route->dst.in), route->dst_prefixlen,
IPV4_ADDRESS_FMT_VAL(prefix), prefixlen);
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;
r = sd_dhcp_lease_get_prefix(link->dhcp_lease, &prefix, &prefixlen);
if (r < 0)
return r;
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 (r < 0)
return r;
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();
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;

View File

@ -52,6 +52,7 @@ int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2);
int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_prefix(sd_dhcp_lease *lease, struct in_addr *ret_prefix, uint8_t *ret_prefixlen);
int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);

View File

@ -4994,7 +4994,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
additional_options = [
'--dhcp-option=option:dns-server,192.168.5.10,8.8.8.8',
'--dhcp-option=option:ntp-server,192.168.5.11,9.9.9.9',
'--dhcp-option=option:static-route,192.168.5.100,192.168.5.2,8.8.8.8,192.168.5.3'
'--dhcp-option=option:static-route,192.168.6.100,192.168.5.2,8.8.8.8,192.168.5.3'
]
if classless:
additional_options += [
@ -5015,16 +5015,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
self.assertRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
else:
self.assertRegex(output, r'192.168.5.0/24 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
else:
self.assertNotRegex(output, r'default via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'192.168.5.4 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'192.168.5.5 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'192.168.5.0/24 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'192.168.6.0/24 via 192.168.5.2 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'8.0.0.0/8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'192.168.5.2 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertNotRegex(output, r'192.168.5.3 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
# Check UseGateway=
@ -5043,9 +5045,13 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
if dns_and_ntp_routes:
self.assertRegex(output, r'192.168.5.10 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'192.168.5.11 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
if classless and use_routes:
self.assertRegex(output, r'8.8.8.8 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
if use_routes:
if classless:
self.assertRegex(output, r'8.8.8.8 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'9.9.9.9 via 192.168.5.4 proto dhcp src 192.168.5.[0-9]* metric 1024')
else:
self.assertRegex(output, r'8.8.8.8 via 192.168.5.3 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
else:
self.assertRegex(output, r'8.8.8.8 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
self.assertRegex(output, r'9.9.9.9 via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')