From aef83fce14ce005850b6557cf04bca0ac6991501 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 23 Apr 2025 17:41:07 +0300 Subject: [PATCH] net: if: Allow selecting deprecated IPv6 address as src addr This adjust the IPv6 source address selection so that it is possible to select deprecated IPv6 address if no better preferred address is found. From RFC 6724 chapter 5: Rule 3: Avoid deprecated addresses. If one of the two source addresses is "preferred" and one of them is "deprecated" (in the RFC 4862 sense), then prefer the one that is "preferred". Rule 8: Use longest matching prefix. If CommonPrefixLen(SA, D) > CommonPrefixLen(SB, D), then prefer SA. Similarly, if CommonPrefixLen(SB, D) > CommonPrefixLen(SA, D), then prefer SB. So the fix allows deprecated address to be selected if it is a better match than the preferred one. The reasoning here is that an address with a longer matching prefix is generally considered topologically closer to the destination. Using such a source address can lead to more efficient routing, as it's more likely that the source and destination are within the same network segment or a closely related one. Signed-off-by: Jukka Rissanen --- subsys/net/ip/net_if.c | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/subsys/net/ip/net_if.c b/subsys/net/ip/net_if.c index 3a56b606e01..198d44acd7b 100644 --- a/subsys/net/ip/net_if.c +++ b/subsys/net/ip/net_if.c @@ -3085,10 +3085,12 @@ static uint8_t get_diff_ipv6(const struct in6_addr *src, static inline bool is_proper_ipv6_address(struct net_if_addr *addr) { - if (addr->is_used && addr->addr_state == NET_ADDR_PREFERRED && - addr->address.family == AF_INET6 && + if (addr->is_used && addr->address.family == AF_INET6 && !net_ipv6_is_ll_addr(&addr->address.in6_addr)) { - return true; + if (addr->addr_state == NET_ADDR_PREFERRED || + addr->addr_state == NET_ADDR_DEPRECATED) { + return true; + } } return false; @@ -3122,6 +3124,7 @@ static struct in6_addr *net_if_ipv6_get_best_match(struct net_if *iface, uint8_t *best_so_far, int flags) { + enum net_addr_state addr_state = NET_ADDR_ANY_STATE; struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6; struct net_if_addr *public_addr = NULL; struct in6_addr *src = NULL; @@ -3179,6 +3182,25 @@ static struct in6_addr *net_if_ipv6_get_best_match(struct net_if *iface, continue; } + if (len == *best_so_far && + ipv6->unicast[i].addr_state == NET_ADDR_DEPRECATED && + addr_state == NET_ADDR_PREFERRED) { + /* We have a preferred address and a deprecated + * address. We prefer the preferred address if the + * prefix lengths are the same. + * See RFC 6724 chapter 5. + */ + continue; + } + + addr_state = ipv6->unicast[i].addr_state; + + NET_DBG("[%zd] Checking %s (%s) dst %s/%d", i, + net_sprint_ipv6_addr(&ipv6->unicast[i].address.in6_addr), + addr_state == NET_ADDR_PREFERRED ? "preferred" : + addr_state == NET_ADDR_DEPRECATED ? "deprecated" : "?", + net_sprint_ipv6_addr(dst), prefix_len); + ret = use_public_address(iface->pe_prefer_public, ipv6->unicast[i].is_temporary, flags); @@ -3228,6 +3250,14 @@ use_public: out: net_if_unlock(iface); + if (src != NULL) { + NET_DBG("Selected %s (%s) dst %s/%d", + net_sprint_ipv6_addr(src), + addr_state == NET_ADDR_PREFERRED ? "preferred" : + addr_state == NET_ADDR_DEPRECATED ? "deprecated" : "?", + net_sprint_ipv6_addr(dst), prefix_len); + } + return src; }