mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-08-08 05:29:47 +00:00
fix: support android shared tun fd groups
Android previously treated setTunFd as a single-instance update, and the VpnService plugin could only expose one IPv4 address. That made shared TUN members disable each other or leave only one address configured. Group enabled Android TUN instances by shared dev_name, send the fd to every compatible member, and only disable incompatible TUN users. Build the Android VPN request from the whole running shared group and pass every IPv4 address to VpnService. The shared mobile dispatcher now owns current fd device state on a process-level runtime. New setTunFd calls replace that state even when the raw fd number is reused, and mobile TUN read/write/create failures rebuild with backoff while preserving member registrations. Protect shared member cleanup with per-registration ownership tokens, so old async cleanup cannot unregister a recreated member or remove its source claims. Mobile source addresses are registered in the dispatcher without applying OS ifcfg changes, so Android-originated packets return through the owning instance. When one shared member stops while another remains, notify the frontend to recalculate the VpnService config instead of leaving stale addresses and routes. Serialize Android VpnService config recalculation so stale async events cannot overwrite newer shared-group state. If one shared member is not ready, rebuild from the healthy members and retry the missing member later. If no healthy member remains, stop the Android VPN service instead of keeping stale routes active.
This commit is contained in:
@@ -15,10 +15,12 @@ class TauriVpnService : VpnService() {
|
||||
@JvmField var triggerCallback: (String, JSObject) -> Unit = { _, _ -> }
|
||||
@JvmField var self: TauriVpnService? = null
|
||||
@JvmField var ipv4Addr: String? = null
|
||||
@JvmField var ipv4Addrs: Array<String> = emptyArray()
|
||||
@JvmField var routes: Array<String> = emptyArray()
|
||||
@JvmField var dns: String? = null
|
||||
|
||||
const val IPV4_ADDR = "IPV4_ADDR"
|
||||
const val IPV4_ADDRS = "IPV4_ADDRS"
|
||||
const val ROUTES = "ROUTES"
|
||||
const val DNS = "DNS"
|
||||
const val DISALLOWED_APPLICATIONS = "DISALLOWED_APPLICATIONS"
|
||||
@@ -30,7 +32,8 @@ class TauriVpnService : VpnService() {
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
println("vpn on start command ${intent?.getExtras()} $intent")
|
||||
var args = intent?.getExtras()
|
||||
ipv4Addr = args?.getString(IPV4_ADDR)
|
||||
ipv4Addrs = getIpv4Addrs(args)
|
||||
ipv4Addr = ipv4Addrs.firstOrNull()
|
||||
routes = args?.getStringArray(ROUTES) ?: emptyArray()
|
||||
dns = args?.getString(DNS)
|
||||
|
||||
@@ -74,28 +77,44 @@ class TauriVpnService : VpnService() {
|
||||
|
||||
private fun clearStatus() {
|
||||
ipv4Addr = null
|
||||
ipv4Addrs = emptyArray()
|
||||
routes = emptyArray()
|
||||
dns = null
|
||||
}
|
||||
|
||||
private fun getIpv4Addrs(args: Bundle?): Array<String> {
|
||||
val ipv4Addrs = args
|
||||
?.getStringArray(IPV4_ADDRS)
|
||||
?.filter { it.isNotBlank() }
|
||||
?.toTypedArray()
|
||||
?: emptyArray()
|
||||
if (ipv4Addrs.isNotEmpty()) {
|
||||
return ipv4Addrs
|
||||
}
|
||||
|
||||
return arrayOf(args?.getString(IPV4_ADDR) ?: "10.126.126.1/24")
|
||||
}
|
||||
|
||||
private fun createVpnInterface(args: Bundle?): ParcelFileDescriptor {
|
||||
var builder = Builder()
|
||||
.setSession("TauriVpnService")
|
||||
.setBlocking(false)
|
||||
|
||||
var mtu = args?.getInt(MTU) ?: 1500
|
||||
var ipv4Addr = args?.getString(IPV4_ADDR) ?: "10.126.126.1/24"
|
||||
var ipv4Addrs = getIpv4Addrs(args)
|
||||
var dns: String? = args?.getString(DNS)
|
||||
var routes = args?.getStringArray(ROUTES) ?: emptyArray()
|
||||
var disallowedApplications = args?.getStringArray(DISALLOWED_APPLICATIONS) ?: emptyArray()
|
||||
|
||||
println("vpn create vpn interface. mtu: $mtu, ipv4Addr: $ipv4Addr, dns:" +
|
||||
println("vpn create vpn interface. mtu: $mtu, ipv4Addrs: ${java.util.Arrays.toString(ipv4Addrs)}, dns:" +
|
||||
"$dns, routes: ${java.util.Arrays.toString(routes)}," +
|
||||
"disallowedApplications: ${java.util.Arrays.toString(disallowedApplications)}")
|
||||
|
||||
val ipParts = ipv4Addr.split("/")
|
||||
if (ipParts.size != 2) throw IllegalArgumentException("Invalid IP addr string")
|
||||
builder.addAddress(ipParts[0], ipParts[1].toInt())
|
||||
for (ipv4Addr in ipv4Addrs) {
|
||||
val ipParts = ipv4Addr.split("/")
|
||||
if (ipParts.size != 2) throw IllegalArgumentException("Invalid IP addr string")
|
||||
builder.addAddress(ipParts[0], ipParts[1].toInt())
|
||||
}
|
||||
builder.addAddress("fd00::1", 128)
|
||||
|
||||
builder.setMtu(mtu)
|
||||
|
||||
@@ -12,6 +12,7 @@ import app.tauri.plugin.Invoke
|
||||
import app.tauri.plugin.JSObject
|
||||
import app.tauri.plugin.Plugin
|
||||
import android.webkit.WebView
|
||||
import org.json.JSONArray
|
||||
|
||||
@InvokeArg
|
||||
class PingArgs {
|
||||
@@ -21,6 +22,7 @@ class PingArgs {
|
||||
@InvokeArg
|
||||
class StartVpnArgs {
|
||||
var ipv4Addr: String? = null
|
||||
var ipv4Addrs: Array<String> = emptyArray()
|
||||
var routes: Array<String> = emptyArray()
|
||||
var dns: String? = null
|
||||
var disallowedApplications: Array<String> = emptyArray()
|
||||
@@ -85,6 +87,7 @@ class VpnServicePlugin(private val activity: Activity) : Plugin(activity) {
|
||||
} else {
|
||||
val intent = Intent(activity, TauriVpnService::class.java)
|
||||
intent.putExtra(TauriVpnService.IPV4_ADDR, args.ipv4Addr)
|
||||
intent.putExtra(TauriVpnService.IPV4_ADDRS, args.ipv4Addrs)
|
||||
intent.putExtra(TauriVpnService.ROUTES, args.routes)
|
||||
intent.putExtra(TauriVpnService.DNS, args.dns)
|
||||
intent.putExtra(TauriVpnService.DISALLOWED_APPLICATIONS, args.disallowedApplications)
|
||||
@@ -112,7 +115,8 @@ class VpnServicePlugin(private val activity: Activity) : Plugin(activity) {
|
||||
val ret = JSObject()
|
||||
ret.put("running", TauriVpnService.self != null)
|
||||
ret.put("ipv4Addr", TauriVpnService.ipv4Addr)
|
||||
ret.put("routes", TauriVpnService.routes)
|
||||
ret.put("ipv4Addrs", JSONArray(TauriVpnService.ipv4Addrs))
|
||||
ret.put("routes", JSONArray(TauriVpnService.routes))
|
||||
ret.put("dns", TauriVpnService.dns)
|
||||
invoke.resolve(ret)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user