[client] Assign the Android TUN address as a host prefix (#7414)

Android 16+ local network protection derives the blocked prefixes from the interface address prefix. A /16 address turns the whole overlay into a local network, so apps without ACCESS_LOCAL_NETWORK cannot reach any peer. Pass the address as /32 and /128 and add the overlay networks to the route list that the Android side turns into VPN routes, on both the initial create and the renew path.
This commit is contained in:
Zoltan Papp
2026-09-04 15:10:36 +02:00
committed by GitHub
parent 825389818c
commit 5cb6b0d33b
6 changed files with 183 additions and 22 deletions
+6 -1
View File
@@ -63,7 +63,12 @@ func (t *WGTunDevice) Create(routes []string, dns string, searchDomains []string
searchDomainsToString = ""
}
fd, err := t.tunAdapter.ConfigureInterface(t.address.String(), t.address.IPv6String(), int(t.mtu), dns, searchDomainsToString, routesString)
ipv6Host := ""
if t.address.HasIPv6() {
ipv6Host = t.address.IPv6HostPrefix().String()
}
fd, err := t.tunAdapter.ConfigureInterface(t.address.HostPrefix().String(), ipv6Host, int(t.mtu), dns, searchDomainsToString, routesString)
if err != nil {
log.Errorf("failed to create Android interface: %s", err)
return nil, err
+13
View File
@@ -59,6 +59,19 @@ func (addr Address) IPv6Prefix() netip.Prefix {
return netip.PrefixFrom(addr.IPv6, addr.IPv6Net.Bits())
}
// HostPrefix returns the v4 address as a single-host prefix.
func (addr Address) HostPrefix() netip.Prefix {
return netip.PrefixFrom(addr.IP, addr.IP.BitLen())
}
// IPv6HostPrefix returns the v6 address as a single-host prefix, or an invalid prefix when no v6 overlay address is assigned.
func (addr Address) IPv6HostPrefix() netip.Prefix {
if !addr.HasIPv6() {
return netip.Prefix{}
}
return netip.PrefixFrom(addr.IPv6, addr.IPv6.BitLen())
}
// SetIPv6FromCompact decodes a compact prefix (5 or 17 bytes) and sets the IPv6 fields.
// Returns an error if the bytes are invalid. A nil or empty input is a no-op.
//
+24
View File
@@ -0,0 +1,24 @@
package wgaddr
import (
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAddress_HostPrefix(t *testing.T) {
addr := MustParseWGAddress("100.91.96.107/16")
assert.Equal(t, netip.MustParsePrefix("100.91.96.107/32"), addr.HostPrefix(), "v4 host prefix must be a single host")
assert.Equal(t, netip.MustParsePrefix("100.91.0.0/16"), addr.Network, "network must keep the overlay prefix length")
assert.False(t, addr.IPv6HostPrefix().IsValid(), "no v6 overlay means no v6 host prefix")
}
func TestAddress_IPv6HostPrefix(t *testing.T) {
addr := MustParseWGAddress("100.91.96.107/16")
addr.IPv6 = netip.MustParseAddr("fd00:1234::1")
addr.IPv6Net = netip.MustParsePrefix("fd00:1234::/64")
assert.Equal(t, netip.MustParsePrefix("fd00:1234::1/128"), addr.IPv6HostPrefix(), "v6 host prefix must be a single host")
}