mwan3: common.sh: fix src_ip detection for ipv6-PD
authorFabian Groffen <[email protected]>
Tue, 2 Sep 2025 11:20:13 +0000 (13:20 +0200)
committerFlorian Eckert <[email protected]>
Wed, 3 Sep 2025 13:42:20 +0000 (15:42 +0200)
IPv6 via PPPoE often receives a prefix, not an address. In that case
mwan3_get_src_ip would not find an address and fall back to ::.  However
in case of multiple IPv6 upstreams, this no longer means a ping (such as
done by uptime check) succeeds, for there are multiple addresses.

With this, mwan3 can effectively load-balance/fail-over on a PPPoE IPv6
prefix and a secondary (LTE) acquired /64 without immediately disabling
the IPv6 prefix interface as soon as the secondary IPv6 interface comes
up.

I think this change is also a fix to #26690
but I'm not 100% sure of that.

Signed-off-by: Fabian Groffen <[email protected]>
net/mwan3/files/lib/mwan3/common.sh

index 8b4adbdf08ba2cc38d9ea433f86b6f4fcc6f6d36..df42a0a2c988d2a77b7a33625497d63a6a67d97b 100644 (file)
@@ -74,8 +74,33 @@ mwan3_get_src_ip()
 
        $addr_cmd _src_ip "$true_iface"
        if [ -z "$_src_ip" ]; then
-               network_get_device device $true_iface
-               _src_ip=$($IP address ls dev $device 2>/dev/null | sed -ne "$sed_str")
+               if [ "$family" = "ipv6" ]; then
+                       # on IPv6-PD interfaces (like PPPoE interfaces) we don't
+                       # have a real address, just a prefix, that can be delegated
+                       # to interfaces, because using :: (the fallback above) or
+                       # the local address (fe80:... which will be returned from
+                       # the sed_str expression defined above) will not work
+                       # (reliably, if at all) try to find an address which we can
+                       # use instead
+                       network_get_prefix6 _src_ip "$true_iface"
+                       if [ -n "$_src_ip" ]; then
+                               # got a prefix like 2001:xxxx:yyyy::/48, clean it up to
+                               # only contain the prefix -> 2001:xxxx:yyyy
+                               _src_ip=$(echo "$_src_ip" | sed -e 's;:*/.*$;;')
+                               # find an interface with a delegated address, and use
+                               # it, this would be sth like 2001:xxxx:yyyy:zzzz:...
+                               # we just select the first address that matches the prefix
+                               # NOTE: is there a better/more reliable way to get a
+                               #       usable address to use as source for pings here?
+                               local pfx_sed
+                               pfx_sed='s/ *inet6 \('"$_src_ip"':[0-6a-f:]\+\).* scope.*/\1/'
+                               _src_ip=$($IP address ls | sed -ne "${pfx_sed};T;p;q")
+                       fi
+               fi
+               if [ -z "$_src_ip" ]; then
+                       network_get_device device $true_iface
+                       _src_ip=$($IP address ls dev $device 2>/dev/null | sed -ne "$sed_str")
+               fi
                if [ -n "$_src_ip" ]; then
                        LOG warn "no src $family address found from netifd for interface '$true_iface' dev '$device' guessing $_src_ip"
                else