diff --git a/core/src/main/golang/native/tun/tun.go b/core/src/main/golang/native/tun/tun.go index 91bbf827..9686ea6f 100644 --- a/core/src/main/golang/native/tun/tun.go +++ b/core/src/main/golang/native/tun/tun.go @@ -1,8 +1,11 @@ package tun import ( + "encoding/json" "io" + "net" "net/netip" + "strings" C "github.com/metacubex/mihomo/constant" LC "github.com/metacubex/mihomo/listener/config" @@ -14,16 +17,49 @@ import ( 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) + var prefix4 []netip.Prefix + var prefix6 []netip.Prefix + for _, gatewayStr := range strings.Split(gateway, ",") { // "172.19.0.1/30" or "172.19.0.1/30,fdfe:dcba:9876::1/126" + gatewayStr = strings.TrimSpace(gatewayStr) + if len(gatewayStr) == 0 { + continue + } + prefix, err := netip.ParsePrefix(gatewayStr) + if err != nil { + log.Errorln("TUN:", err) + return nil, err + } + + if prefix.Addr().Is4() { + prefix4 = append(prefix4, prefix) + } else { + prefix6 = append(prefix6, prefix) + } + } + + var dnsHijack []string + for _, dnsStr := range strings.Split(dns, ",") { // "172.19.0.2" or "0.0.0.0" + dnsStr = strings.TrimSpace(dnsStr) + if len(dnsStr) == 0 { + continue + } + dnsHijack = append(dnsHijack, net.JoinHostPort(dnsStr, "53")) + } + options := LC.Tun{ Enable: true, Device: sing_tun.InterfaceName, Stack: C.TunSystem, - DNSHijack: []string{dns + ":53"}, // "172.19.0.2" or "0.0.0.0" - Inet4Address: []netip.Prefix{netip.MustParsePrefix(gateway)}, // "172.19.0.1/30" - MTU: 9000, // private const val TUN_MTU = 9000 in TunService.kt + DNSHijack: dnsHijack, + Inet4Address: prefix4, + Inet6Address: prefix6, + MTU: 9000, // private const val TUN_MTU = 9000 in TunService.kt FileDescriptor: fd, } + tunOptions, _ := json.Marshal(options) + log.Debugln(string(tunOptions)) + listener, err := sing_tun.New(options, tunnel.Tunnel) if err != nil { log.Errorln("TUN:", err) 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 7c1d2d61..d2af3ded 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 @@ -77,6 +77,13 @@ class NetworkSettingsDesign( configure = vpnDependencies::add, ) + switch( + value = srvStore::allowIpv6, + title = R.string.allow_ipv6, + summary = R.string.allow_ipv6_summary, + configure = vpnDependencies::add, + ) + if (Build.VERSION.SDK_INT >= 29) { switch( value = srvStore::systemProxy, diff --git a/design/src/main/res/values-zh/strings.xml b/design/src/main/res/values-zh/strings.xml index dc92cdac..960e5981 100644 --- a/design/src/main/res/values-zh/strings.xml +++ b/design/src/main/res/values-zh/strings.xml @@ -217,6 +217,8 @@ GeoIP Fallback 区域代码 允许应用绕过 允许其他应用绕过 VPN + 允许Ipv6 + 通过 VpnService 代理Ipv6流量 Clash Meta Wiki Meta Features Unified Delay diff --git a/design/src/main/res/values/strings.xml b/design/src/main/res/values/strings.xml index cba90dcf..b6144cd8 100644 --- a/design/src/main/res/values/strings.xml +++ b/design/src/main/res/values/strings.xml @@ -122,6 +122,8 @@ Block loopback connections Allow Bypass Allows all apps to bypass this VPN connection + Allow Ipv6 + Allows ipv6 traffic via VpnService System Proxy Attach http proxy to VpnService Access Control Mode 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 97c5540b..0ade369a 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 @@ -132,17 +132,31 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De val device = with(Builder()) { // Interface address addAddress(TUN_GATEWAY, TUN_SUBNET_PREFIX) + if (store.allowIpv6) { + addAddress(TUN_GATEWAY6, TUN_SUBNET_PREFIX6) + } // Route if (store.bypassPrivateNetwork) { resources.getStringArray(R.array.bypass_private_route).map(::parseCIDR).forEach { addRoute(it.ip, it.prefix) } + if (store.allowIpv6) { + resources.getStringArray(R.array.bypass_private_route6).map(::parseCIDR).forEach { + addRoute(it.ip, it.prefix) + } + } // Route of virtual DNS addRoute(TUN_DNS, 32) + if (store.allowIpv6) { + addRoute(TUN_DNS6, 128) + } } else { addRoute(NET_ANY, 0) + if (store.allowIpv6) { + addRoute(NET_ANY6, 0) + } } // Access Control @@ -171,6 +185,9 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De // Virtual Dns Server addDnsServer(TUN_DNS) + if (store.allowIpv6) { + addDnsServer(TUN_DNS6) + } // Open MainActivity setConfigureIntent( @@ -207,9 +224,9 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De TunModule.TunDevice( fd = establish()?.detachFd() ?: throw NullPointerException("Establish VPN rejected by system"), - gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX", - portal = TUN_PORTAL, - dns = if (store.dnsHijacking) NET_ANY else TUN_DNS, + 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 ""), ) } @@ -220,9 +237,14 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De private const val TUN_MTU = 9000 private const val TUN_SUBNET_PREFIX = 30 private const val TUN_GATEWAY = "172.19.0.1" + private const val TUN_SUBNET_PREFIX6 = 126 + private const val TUN_GATEWAY6 = "fdfe:dcba:9876::1" private const val TUN_PORTAL = "172.19.0.2" + private const val TUN_PORTAL6 = "fdfe:dcba:9876::2" private const val TUN_DNS = TUN_PORTAL + private const val TUN_DNS6 = TUN_PORTAL6 private const val NET_ANY = "0.0.0.0" + private const val NET_ANY6 = "::" private val HTTP_PROXY_LOCAL_LIST: List = listOf( "localhost", 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 648af537..2752a965 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 @@ -51,6 +51,11 @@ class ServiceStore(context: Context) { defaultValue = true ) + var allowIpv6 by store.boolean( + key = "allow_ipv6", + defaultValue = false + ) + var dynamicNotification by store.boolean( key = "dynamic_notification", defaultValue = true diff --git a/service/src/main/res/values/arrays.xml b/service/src/main/res/values/arrays.xml index 2e102729..3017364c 100644 --- a/service/src/main/res/values/arrays.xml +++ b/service/src/main/res/values/arrays.xml @@ -77,4 +77,15 @@ 255.255.255.252/31 255.255.255.254/32 + + + ::/1 + 8000::/2 + c000::/3 + e000::/4 + f000::/5 + f800::/6 + fe00::/9 + fec0::/10 +