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
+