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:
commit
fc3fe92bc8
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user