net: ipv4: Rework source address matching for LL addresses

Currently we blindly return the LL address found on the default
interface or else on the first interface that has a valid LL address
configured.

This doesn't work well, if different interfaces have LL addresses
configured with a different subnet mask. Therefore, instead of blindly
selecting the address based on the first LL address encountered, use
net_if_ipv4_get_best_match() function for LL addresses to find the best
match for the given interface.

The rework takes into account current behavior, i. e. default interface
still gets the preference if there is no better candidate.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2024-11-27 10:19:29 +01:00 committed by Benjamin Cabé
commit a909b538dc

View file

@ -3561,8 +3561,7 @@ static uint8_t get_diff_ipv4(const struct in_addr *src,
static inline bool is_proper_ipv4_address(struct net_if_addr *addr)
{
if (addr->is_used && addr->addr_state == NET_ADDR_PREFERRED &&
addr->address.family == AF_INET &&
!net_ipv4_is_ll_addr(&addr->address.in_addr)) {
addr->address.family == AF_INET) {
return true;
}
@ -3571,7 +3570,7 @@ static inline bool is_proper_ipv4_address(struct net_if_addr *addr)
static struct in_addr *net_if_ipv4_get_best_match(struct net_if *iface,
const struct in_addr *dst,
uint8_t *best_so_far)
uint8_t *best_so_far, bool ll)
{
struct net_if_ipv4 *ipv4;
struct in_addr *src = NULL;
@ -3589,6 +3588,10 @@ static struct in_addr *net_if_ipv4_get_best_match(struct net_if *iface,
continue;
}
if (net_ipv4_is_ll_addr(&ipv4->unicast[i].ipv4.address.in_addr) != ll) {
continue;
}
len = get_diff_ipv4(dst, &ipv4->unicast[i].ipv4.address.in_addr);
if (len >= *best_so_far) {
*best_so_far = len;
@ -3672,13 +3675,14 @@ const struct in_addr *net_if_ipv4_select_src_addr(struct net_if *dst_iface,
/* If caller has supplied interface, then use that */
if (dst_iface) {
src = net_if_ipv4_get_best_match(dst_iface, dst,
&best_match);
&best_match, false);
} else {
STRUCT_SECTION_FOREACH(net_if, iface) {
struct in_addr *addr;
addr = net_if_ipv4_get_best_match(iface, dst,
&best_match);
&best_match,
false);
if (addr) {
src = addr;
}
@ -3691,20 +3695,25 @@ const struct in_addr *net_if_ipv4_select_src_addr(struct net_if *dst_iface,
} else {
struct in_addr *addr;
addr = net_if_ipv4_get_ll(net_if_get_default(), NET_ADDR_PREFERRED);
if (addr) {
src = addr;
goto out;
}
STRUCT_SECTION_FOREACH(net_if, iface) {
addr = net_if_ipv4_get_ll(iface,
NET_ADDR_PREFERRED);
addr = net_if_ipv4_get_best_match(iface, dst,
&best_match,
true);
if (addr) {
src = addr;
break;
}
}
/* Check the default interface again. It will only
* be used if it has a valid LL address, and there was
* no better match on any other interface.
*/
addr = net_if_ipv4_get_best_match(net_if_get_default(),
dst, &best_match,
true);
if (addr) {
src = addr;
}
}
}
@ -3724,7 +3733,6 @@ const struct in_addr *net_if_ipv4_select_src_addr(struct net_if *dst_iface,
}
}
out:
return src;
}