1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-10 05:18:17 +03:00

local-addresses: honor RTA_PREFSRC field of gateway

Fixes #34739.
This commit is contained in:
Yu Watanabe 2024-10-13 06:56:38 +09:00
parent 418641f73f
commit 4adf2653e2
2 changed files with 88 additions and 13 deletions

View File

@ -81,7 +81,8 @@ static int add_local_address_full(
uint32_t priority,
uint32_t weight,
int family,
const union in_addr_union *address) {
const union in_addr_union *address,
const union in_addr_union *prefsrc) {
assert(list);
assert(n_list);
@ -99,6 +100,7 @@ static int add_local_address_full(
.weight = weight,
.family = family,
.address = *address,
.prefsrc = prefsrc ? *prefsrc : IN_ADDR_NULL,
};
return 1;
@ -112,7 +114,10 @@ int add_local_address(
int family,
const union in_addr_union *address) {
return add_local_address_full(list, n_list, ifindex, scope, 0, 0, family, address);
return add_local_address_full(
list, n_list, ifindex,
scope, /* priority = */ 0, /* weight = */ 0,
family, address, /* prefsrc = */ NULL);
}
int local_addresses(
@ -235,9 +240,14 @@ static int add_local_gateway(
uint32_t priority,
uint32_t weight,
int family,
const union in_addr_union *address) {
const union in_addr_union *address,
const union in_addr_union *prefsrc) {
return add_local_address_full(list, n_list, ifindex, 0, priority, weight, family, address);
return add_local_address_full(
list, n_list,
ifindex,
/* scope = */ 0, priority, weight,
family, address, prefsrc);
}
static int parse_nexthop_one(
@ -246,6 +256,7 @@ static int parse_nexthop_one(
bool allow_via,
int family,
uint32_t priority,
const union in_addr_union *prefsrc,
const struct rtnexthop *rtnh) {
bool has_gw = false;
@ -268,7 +279,7 @@ static int parse_nexthop_one(
union in_addr_union a;
memcpy(&a, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, family, &a);
r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, family, &a, prefsrc);
if (r < 0)
return r;
@ -294,7 +305,8 @@ static int parse_nexthop_one(
return -EBADMSG; /* gateway address should be always IPv6. */
r = add_local_gateway(list, n_list, rtnh->rtnh_ifindex, priority, rtnh->rtnh_hops, via->family,
&(union in_addr_union) { .in6 = via->address.in6 });
&(union in_addr_union) { .in6 = via->address.in6 },
/* prefsrc = */ NULL);
if (r < 0)
return r;
@ -311,6 +323,7 @@ static int parse_nexthops(
bool allow_via,
int family,
uint32_t priority,
const union in_addr_union *prefsrc,
const struct rtnexthop *rtnh,
size_t size) {
@ -334,7 +347,7 @@ static int parse_nexthops(
if (ifindex > 0 && rtnh->rtnh_ifindex != ifindex)
goto next_nexthop;
r = parse_nexthop_one(list, n_list, allow_via, family, priority, rtnh);
r = parse_nexthop_one(list, n_list, allow_via, family, priority, prefsrc, rtnh);
if (r < 0)
return r;
@ -393,6 +406,7 @@ int local_gateways(
return r;
for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
union in_addr_union prefsrc = IN_ADDR_NULL;
uint16_t type;
unsigned char dst_len, src_len, table;
uint32_t ifi = 0, priority = 0;
@ -439,6 +453,10 @@ int local_gateways(
if (af != AF_UNSPEC && af != family)
continue;
r = netlink_message_read_in_addr_union(m, RTA_PREFSRC, family, &prefsrc);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
if (r < 0 && r != -ENODATA)
return r;
@ -453,7 +471,7 @@ int local_gateways(
if (r < 0 && r != -ENODATA)
return r;
if (r >= 0) {
r = add_local_gateway(&list, &n_list, ifi, priority, 0, family, &gateway);
r = add_local_gateway(&list, &n_list, ifi, priority, 0, family, &gateway, &prefsrc);
if (r < 0)
return r;
@ -474,8 +492,10 @@ int local_gateways(
if (via.family != AF_INET6)
return -EBADMSG;
/* Ignore prefsrc, and let's take the source address by socket command, if necessary. */
r = add_local_gateway(&list, &n_list, ifi, priority, 0, via.family,
&(union in_addr_union) { .in6 = via.address.in6 });
&(union in_addr_union) { .in6 = via.address.in6 },
/* prefsrc = */ NULL);
if (r < 0)
return r;
}
@ -490,7 +510,7 @@ int local_gateways(
if (r < 0 && r != -ENODATA)
return r;
if (r >= 0) {
r = parse_nexthops(&list, &n_list, ifindex, allow_via, family, priority, rta_multipath, rta_len);
r = parse_nexthops(&list, &n_list, ifindex, allow_via, family, priority, &prefsrc, rta_multipath, rta_len);
if (r < 0)
return r;
}
@ -512,7 +532,51 @@ static int add_local_outbound(
int family,
const union in_addr_union *address) {
return add_local_address_full(list, n_list, ifindex, 0, 0, 0, family, address);
return add_local_address_full(
list, n_list, ifindex,
/* scope = */ 0, /* priority = */ 0, /* weight = */ 0,
family, address, /* prefsrc = */ NULL);
}
static int add_local_outbound_by_prefsrc(
struct local_address **list,
size_t *n_list,
const struct local_address *gateway,
const struct local_address *addresses,
size_t n_addresses) {
int r;
assert(list);
assert(n_list);
assert(gateway);
if (!in_addr_is_set(gateway->family, &gateway->prefsrc))
return 0;
/* If the gateway has prefsrc, then let's honor the field. But, check if the address is assigned to
* the same interface, like we do with SO_BINDTOINDEX. */
bool found = false;
FOREACH_ARRAY(a, addresses, n_addresses) {
if (a->ifindex != gateway->ifindex)
continue;
if (a->family != gateway->family)
continue;
if (in_addr_equal(a->family, &a->address, &gateway->prefsrc) <= 0)
continue;
found = true;
break;
}
if (!found)
return -EHOSTUNREACH;
r = add_local_outbound(list, n_list, gateway->ifindex, gateway->family, &gateway->prefsrc);
if (r < 0)
return r;
return 1;
}
int local_outbounds(
@ -521,9 +585,9 @@ int local_outbounds(
int af,
struct local_address **ret) {
_cleanup_free_ struct local_address *list = NULL, *gateways = NULL;
_cleanup_free_ struct local_address *list = NULL, *gateways = NULL, *addresses = NULL;
size_t n_list = 0;
int r, n_gateways;
int r, n_gateways, n_addresses;
/* Determines our default outbound addresses, i.e. the "primary" local addresses we use to talk to IP
* addresses behind the default routes. This is still an address of the local host (i.e. this doesn't
@ -544,11 +608,21 @@ int local_outbounds(
return 0;
}
n_addresses = local_addresses(context, ifindex, af, &addresses);
if (n_addresses < 0)
return n_addresses;
FOREACH_ARRAY(i, gateways, n_gateways) {
_cleanup_close_ int fd = -EBADF;
union sockaddr_union sa;
socklen_t salen;
r = add_local_outbound_by_prefsrc(&list, &n_list, i, addresses, n_addresses);
if (r > 0 || r == -EHOSTUNREACH)
continue;
if (r < 0)
return r;
fd = socket(i->family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;

View File

@ -12,6 +12,7 @@ struct local_address {
uint32_t weight;
int family;
union in_addr_union address;
union in_addr_union prefsrc;
};
bool has_local_address(const struct local_address *addresses, size_t n_addresses, const struct local_address *needle);