From a909b538dc8edc7c9428d1891c260452d35cd558 Mon Sep 17 00:00:00 2001 From: Robert Lubos Date: Wed, 27 Nov 2024 10:19:29 +0100 Subject: [PATCH] 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 --- subsys/net/ip/net_if.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 0fc7933b909..d5902f0f198 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -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; }