diff --git a/core/src/main/cpp/main.c b/core/src/main/cpp/main.c index 0a2c8b51..2ad3f717 100644 --- a/core/src/main/cpp/main.c +++ b/core/src/main/cpp/main.c @@ -111,18 +111,20 @@ Java_com_github_kr328_clash_core_bridge_Bridge_nativeNotifyInstalledAppChanged(J JNIEXPORT void JNICALL Java_com_github_kr328_clash_core_bridge_Bridge_nativeStartTun(JNIEnv *env, jobject thiz, jint fd, + jstring stack, jstring gateway, jstring portal, jstring dns, jobject cb) { TRACE_METHOD(); + scoped_string _stack = get_string(stack); scoped_string _gateway = get_string(gateway); scoped_string _portal = get_string(portal); scoped_string _dns = get_string(dns); jobject _interface = new_global(cb); - startTun(fd, _gateway, _portal, _dns, _interface); + startTun(fd, _stack, _gateway, _portal, _dns, _interface); } JNIEXPORT void JNICALL diff --git a/core/src/main/golang/native/tun.go b/core/src/main/golang/native/tun.go index b09d6f65..860a1fdc 100644 --- a/core/src/main/golang/native/tun.go +++ b/core/src/main/golang/native/tun.go @@ -64,7 +64,7 @@ func (t *remoteTun) close() { } //export startTun -func startTun(fd C.int, gateway, portal, dns C.c_string, callback unsafe.Pointer) C.int { +func startTun(fd C.int, stack, gateway, portal, dns C.c_string, callback unsafe.Pointer) C.int { rTunLock.Lock() defer rTunLock.Unlock() @@ -74,6 +74,7 @@ func startTun(fd C.int, gateway, portal, dns C.c_string, callback unsafe.Pointer } f := int(fd) + s := C.GoString(stack) g := C.GoString(gateway) p := C.GoString(portal) d := C.GoString(dns) @@ -82,7 +83,7 @@ func startTun(fd C.int, gateway, portal, dns C.c_string, callback unsafe.Pointer app.ApplyTunContext(remote.markSocket, remote.querySocketUid) - closer, err := tun.Start(f, g, p, d) + closer, err := tun.Start(f, s, g, p, d) if err != nil { remote.close() diff --git a/core/src/main/golang/native/tun/tun.go b/core/src/main/golang/native/tun/tun.go index 9686ea6f..d686589f 100644 --- a/core/src/main/golang/native/tun/tun.go +++ b/core/src/main/golang/native/tun/tun.go @@ -14,8 +14,13 @@ import ( "github.com/metacubex/mihomo/tunnel" ) -func Start(fd int, gateway, portal, dns string) (io.Closer, error) { - log.Debugln("TUN: fd = %d, gateway = %s, portal = %s, dns = %s", fd, gateway, portal, dns) +func Start(fd int, stack, gateway, portal, dns string) (io.Closer, error) { + log.Debugln("TUN: fd = %d, stack = %s, gateway = %s, portal = %s, dns = %s", fd, stack, gateway, portal, dns) + + tunStack, ok := C.StackTypeMapping[strings.ToLower(stack)] + if !ok { + tunStack = C.TunSystem + } var prefix4 []netip.Prefix var prefix6 []netip.Prefix @@ -49,7 +54,7 @@ func Start(fd int, gateway, portal, dns string) (io.Closer, error) { options := LC.Tun{ Enable: true, Device: sing_tun.InterfaceName, - Stack: C.TunSystem, + Stack: tunStack, DNSHijack: dnsHijack, Inet4Address: prefix4, Inet6Address: prefix6, diff --git a/core/src/main/java/com/github/kr328/clash/core/Clash.kt b/core/src/main/java/com/github/kr328/clash/core/Clash.kt index 37096215..a58dc41f 100644 --- a/core/src/main/java/com/github/kr328/clash/core/Clash.kt +++ b/core/src/main/java/com/github/kr328/clash/core/Clash.kt @@ -64,13 +64,14 @@ object Clash { fun startTun( fd: Int, + stack: String, gateway: String, portal: String, dns: String, markSocket: (Int) -> Boolean, querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int ) { - Bridge.nativeStartTun(fd, gateway, portal, dns, object : TunInterface { + Bridge.nativeStartTun(fd, stack, gateway, portal, dns, object : TunInterface { override fun markSocket(fd: Int) { markSocket(fd) } diff --git a/core/src/main/java/com/github/kr328/clash/core/bridge/Bridge.kt b/core/src/main/java/com/github/kr328/clash/core/bridge/Bridge.kt index 09fa052b..8aa26fb4 100644 --- a/core/src/main/java/com/github/kr328/clash/core/bridge/Bridge.kt +++ b/core/src/main/java/com/github/kr328/clash/core/bridge/Bridge.kt @@ -19,7 +19,7 @@ object Bridge { external fun nativeNotifyDnsChanged(dnsList: String) external fun nativeNotifyTimeZoneChanged(name: String, offset: Int) external fun nativeNotifyInstalledAppChanged(uidList: String) - external fun nativeStartTun(fd: Int, gateway: String, portal: String, dns: String, cb: TunInterface) + external fun nativeStartTun(fd: Int, stack: String, gateway: String, portal: String, dns: String, cb: TunInterface) external fun nativeStopTun() external fun nativeStartHttp(listenAt: String): String? external fun nativeStopHttp() diff --git a/design/src/main/java/com/github/kr328/clash/design/NetworkSettingsDesign.kt b/design/src/main/java/com/github/kr328/clash/design/NetworkSettingsDesign.kt index d2af3ded..2195840b 100644 --- a/design/src/main/java/com/github/kr328/clash/design/NetworkSettingsDesign.kt +++ b/design/src/main/java/com/github/kr328/clash/design/NetworkSettingsDesign.kt @@ -93,6 +93,22 @@ class NetworkSettingsDesign( ) } + selectableList( + value = srvStore::tunStackMode, + values = arrayOf( + "system", + "gvisor", + "mixed" + ), + valuesText = arrayOf( + R.string.tun_stack_system, + R.string.tun_stack_gvisor, + R.string.tun_stack_mixed + ), + title = R.string.tun_stack_mode, + configure = vpnDependencies::add, + ) + selectableList( value = srvStore::accessControlMode, values = AccessControlMode.values(), diff --git a/design/src/main/res/values/strings.xml b/design/src/main/res/values/strings.xml index 0be678d2..b1987c7c 100644 --- a/design/src/main/res/values/strings.xml +++ b/design/src/main/res/values/strings.xml @@ -232,6 +232,11 @@ Always Dark Always Light + Stack Mode + System Stack + Gvisor Stack + Mixed Stack + Allow all apps Allow selected apps Deny selected apps diff --git a/service/src/main/java/com/github/kr328/clash/service/TunService.kt b/service/src/main/java/com/github/kr328/clash/service/TunService.kt index abb50f7c..8550f8ab 100644 --- a/service/src/main/java/com/github/kr328/clash/service/TunService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/TunService.kt @@ -218,6 +218,7 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De TunModule.TunDevice( fd = establish()?.detachFd() ?: throw NullPointerException("Establish VPN rejected by system"), + stack = store.tunStackMode, gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX" + if (store.allowIpv6) ",$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "", portal = TUN_PORTAL + if (store.allowIpv6) ",$TUN_PORTAL6" else "", dns = if (store.dnsHijacking) NET_ANY else (TUN_DNS + if (store.allowIpv6) ",$TUN_DNS6" else ""), diff --git a/service/src/main/java/com/github/kr328/clash/service/clash/module/TunModule.kt b/service/src/main/java/com/github/kr328/clash/service/clash/module/TunModule.kt index e2b38f59..84197d32 100644 --- a/service/src/main/java/com/github/kr328/clash/service/clash/module/TunModule.kt +++ b/service/src/main/java/com/github/kr328/clash/service/clash/module/TunModule.kt @@ -15,6 +15,7 @@ import java.security.SecureRandom class TunModule(private val vpn: VpnService) : Module(vpn) { data class TunDevice( val fd: Int, + var stack: String, val gateway: String, val portal: String, val dns: String, @@ -56,6 +57,7 @@ class TunModule(private val vpn: VpnService) : Module(vpn) { fun attach(device: TunDevice) { Clash.startTun( fd = device.fd, + stack = device.stack, gateway = device.gateway, portal = device.portal, dns = device.dns, diff --git a/service/src/main/java/com/github/kr328/clash/service/store/ServiceStore.kt b/service/src/main/java/com/github/kr328/clash/service/store/ServiceStore.kt index 474f1fac..d361848f 100644 --- a/service/src/main/java/com/github/kr328/clash/service/store/ServiceStore.kt +++ b/service/src/main/java/com/github/kr328/clash/service/store/ServiceStore.kt @@ -56,6 +56,11 @@ class ServiceStore(context: Context) { defaultValue = false ) + var tunStackMode by store.string( + key = "tun_stack_mode", + defaultValue = "system" + ) + var dynamicNotification by store.boolean( key = "dynamic_notification", defaultValue = true