Skip to content

Commit 97364a9

Browse files
committed
add ipv6 support for tun
1 parent b661d94 commit 97364a9

File tree

7 files changed

+216
-6
lines changed

7 files changed

+216
-6
lines changed

core/src/main/golang/native/tun/tun.go

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package tun
22

33
import (
4+
"encoding/json"
45
"io"
6+
"net"
57
"net/netip"
8+
"strings"
69

710
C "github.com/metacubex/mihomo/constant"
811
LC "github.com/metacubex/mihomo/listener/config"
@@ -14,16 +17,49 @@ import (
1417
func Start(fd int, gateway, portal, dns string) (io.Closer, error) {
1518
log.Debugln("TUN: fd = %d, gateway = %s, portal = %s, dns = %s", fd, gateway, portal, dns)
1619

20+
var prefix4 []netip.Prefix
21+
var prefix6 []netip.Prefix
22+
for _, gatewayStr := range strings.Split(gateway, ",") { // "172.19.0.1/30" or "172.19.0.1/30,fdfe:dcba:9876::1/126"
23+
gatewayStr = strings.TrimSpace(gatewayStr)
24+
if len(gatewayStr) == 0 {
25+
continue
26+
}
27+
prefix, err := netip.ParsePrefix(gatewayStr)
28+
if err != nil {
29+
log.Errorln("TUN:", err)
30+
return nil, err
31+
}
32+
33+
if prefix.Addr().Is4() {
34+
prefix4 = append(prefix4, prefix)
35+
} else {
36+
prefix6 = append(prefix6, prefix)
37+
}
38+
}
39+
40+
var dnsHijack []string
41+
for _, dnsStr := range strings.Split(dns, ",") { // "172.19.0.2" or "0.0.0.0"
42+
dnsStr = strings.TrimSpace(dnsStr)
43+
if len(dnsStr) == 0 {
44+
continue
45+
}
46+
dnsHijack = append(dnsHijack, net.JoinHostPort(dnsStr, "53"))
47+
}
48+
1749
options := LC.Tun{
1850
Enable: true,
1951
Device: sing_tun.InterfaceName,
2052
Stack: C.TunSystem,
21-
DNSHijack: []string{dns + ":53"}, // "172.19.0.2" or "0.0.0.0"
22-
Inet4Address: []netip.Prefix{netip.MustParsePrefix(gateway)}, // "172.19.0.1/30"
23-
MTU: 9000, // private const val TUN_MTU = 9000 in TunService.kt
53+
DNSHijack: dnsHijack,
54+
Inet4Address: prefix4,
55+
Inet6Address: prefix6,
56+
MTU: 9000, // private const val TUN_MTU = 9000 in TunService.kt
2457
FileDescriptor: fd,
2558
}
2659

60+
tunOptions, _ := json.Marshal(options)
61+
log.Debugln(string(tunOptions))
62+
2763
listener, err := sing_tun.New(options, tunnel.Tunnel)
2864
if err != nil {
2965
log.Errorln("TUN:", err)

design/src/main/java/com/github/kr328/clash/design/NetworkSettingsDesign.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ class NetworkSettingsDesign(
7777
configure = vpnDependencies::add,
7878
)
7979

80+
switch(
81+
value = srvStore::allowIpv6,
82+
title = R.string.allow_ipv6,
83+
summary = R.string.allow_ipv6_summary,
84+
configure = vpnDependencies::add,
85+
)
86+
8087
if (Build.VERSION.SDK_INT >= 29) {
8188
switch(
8289
value = srvStore::systemProxy,

design/src/main/res/values-zh/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,8 @@
217217
<string name="geoip_fallback_code">GeoIP Fallback 区域代码</string>
218218
<string name="allow_bypass">允许应用绕过</string>
219219
<string name="allow_bypass_summary">允许其他应用绕过 VPN</string>
220+
<string name="allow_ipv6">允许Ipv6</string>
221+
<string name="allow_ipv6_summary">通过 VpnService 代理Ipv6流量</string>
220222
<string name="clash_meta_wiki">Clash Meta Wiki</string>
221223
<string name="meta_features">Meta Features</string>
222224
<string name="unified_delay">Unified Delay</string>

design/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@
122122
<string name="block_loopback_summary">Block loopback connections</string>
123123
<string name="allow_bypass">Allow Bypass</string>
124124
<string name="allow_bypass_summary">Allows all apps to bypass this VPN connection</string>
125+
<string name="allow_ipv6">Allow Ipv6</string>
126+
<string name="allow_ipv6_summary">Allows ipv6 traffic via VpnService</string>
125127
<string name="system_proxy">System Proxy</string>
126128
<string name="system_proxy_summary">Attach http proxy to VpnService</string>
127129
<string name="access_control_mode">Access Control Mode</string>

service/src/main/java/com/github/kr328/clash/service/TunService.kt

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,31 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
132132
val device = with(Builder()) {
133133
// Interface address
134134
addAddress(TUN_GATEWAY, TUN_SUBNET_PREFIX)
135+
if (store.allowIpv6) {
136+
addAddress(TUN_GATEWAY6, TUN_SUBNET_PREFIX6)
137+
}
135138

136139
// Route
137140
if (store.bypassPrivateNetwork) {
138141
resources.getStringArray(R.array.bypass_private_route).map(::parseCIDR).forEach {
139142
addRoute(it.ip, it.prefix)
140143
}
144+
if (store.allowIpv6) {
145+
resources.getStringArray(R.array.bypass_private_route6).map(::parseCIDR).forEach {
146+
addRoute(it.ip, it.prefix)
147+
}
148+
}
141149

142150
// Route of virtual DNS
143151
addRoute(TUN_DNS, 32)
152+
if (store.allowIpv6) {
153+
addRoute(TUN_DNS6, 128)
154+
}
144155
} else {
145156
addRoute(NET_ANY, 0)
157+
if (store.allowIpv6) {
158+
addRoute(NET_ANY6, 0)
159+
}
146160
}
147161

148162
// Access Control
@@ -171,6 +185,9 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
171185

172186
// Virtual Dns Server
173187
addDnsServer(TUN_DNS)
188+
if (store.allowIpv6) {
189+
addDnsServer(TUN_DNS6)
190+
}
174191

175192
// Open MainActivity
176193
setConfigureIntent(
@@ -207,9 +224,9 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
207224
TunModule.TunDevice(
208225
fd = establish()?.detachFd()
209226
?: throw NullPointerException("Establish VPN rejected by system"),
210-
gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX",
211-
portal = TUN_PORTAL,
212-
dns = if (store.dnsHijacking) NET_ANY else TUN_DNS,
227+
gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX" + if (store.allowIpv6) ",$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "",
228+
portal = TUN_PORTAL + if (store.allowIpv6) ",$TUN_PORTAL6" else "",
229+
dns = if (store.dnsHijacking) NET_ANY else (TUN_DNS + if (store.allowIpv6) ",$TUN_DNS6" else ""),
213230
)
214231
}
215232

@@ -220,9 +237,14 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
220237
private const val TUN_MTU = 9000
221238
private const val TUN_SUBNET_PREFIX = 30
222239
private const val TUN_GATEWAY = "172.19.0.1"
240+
private const val TUN_SUBNET_PREFIX6 = 126
241+
private const val TUN_GATEWAY6 = "fdfe:dcba:9876::1"
223242
private const val TUN_PORTAL = "172.19.0.2"
243+
private const val TUN_PORTAL6 = "fdfe:dcba:9876::2"
224244
private const val TUN_DNS = TUN_PORTAL
245+
private const val TUN_DNS6 = TUN_PORTAL6
225246
private const val NET_ANY = "0.0.0.0"
247+
private const val NET_ANY6 = "::"
226248

227249
private val HTTP_PROXY_LOCAL_LIST: List<String> = listOf(
228250
"localhost",

service/src/main/java/com/github/kr328/clash/service/store/ServiceStore.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ class ServiceStore(context: Context) {
5151
defaultValue = true
5252
)
5353

54+
var allowIpv6 by store.boolean(
55+
key = "allow_ipv6",
56+
defaultValue = false
57+
)
58+
5459
var dynamicNotification by store.boolean(
5560
key = "dynamic_notification",
5661
defaultValue = true

service/src/main/res/values/arrays.xml

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,140 @@
7777
<item>255.255.255.252/31</item>
7878
<item>255.255.255.254/32</item>
7979
</string-array>
80+
<!-- exclude ::/127, fc00::/7, fe80::/10, ff00::/8 -->
81+
<string-array name="bypass_private_route6" translatable="false">
82+
<item>::2/127</item>
83+
<item>::4/126</item>
84+
<item>::8/125</item>
85+
<item>::10/124</item>
86+
<item>::20/123</item>
87+
<item>::40/122</item>
88+
<item>::80/121</item>
89+
<item>::100/120</item>
90+
<item>::200/119</item>
91+
<item>::400/118</item>
92+
<item>::800/117</item>
93+
<item>::1000/116</item>
94+
<item>::2000/115</item>
95+
<item>::4000/114</item>
96+
<item>::8000/113</item>
97+
<item>::1:0/112</item>
98+
<item>::2:0/111</item>
99+
<item>::4:0/110</item>
100+
<item>::8:0/109</item>
101+
<item>::10:0/108</item>
102+
<item>::20:0/107</item>
103+
<item>::40:0/106</item>
104+
<item>::80:0/105</item>
105+
<item>::100:0/104</item>
106+
<item>::200:0/103</item>
107+
<item>::400:0/102</item>
108+
<item>::800:0/101</item>
109+
<item>::1000:0/100</item>
110+
<item>::2000:0/99</item>
111+
<item>::4000:0/98</item>
112+
<item>::8000:0/97</item>
113+
<item>::1:0:0/96</item>
114+
<item>::2:0:0/95</item>
115+
<item>::4:0:0/94</item>
116+
<item>::8:0:0/93</item>
117+
<item>::10:0:0/92</item>
118+
<item>::20:0:0/91</item>
119+
<item>::40:0:0/90</item>
120+
<item>::80:0:0/89</item>
121+
<item>::100:0:0/88</item>
122+
<item>::200:0:0/87</item>
123+
<item>::400:0:0/86</item>
124+
<item>::800:0:0/85</item>
125+
<item>::1000:0:0/84</item>
126+
<item>::2000:0:0/83</item>
127+
<item>::4000:0:0/82</item>
128+
<item>::8000:0:0/81</item>
129+
<item>::1:0:0:0/80</item>
130+
<item>::2:0:0:0/79</item>
131+
<item>::4:0:0:0/78</item>
132+
<item>::8:0:0:0/77</item>
133+
<item>::10:0:0:0/76</item>
134+
<item>::20:0:0:0/75</item>
135+
<item>::40:0:0:0/74</item>
136+
<item>::80:0:0:0/73</item>
137+
<item>::100:0:0:0/72</item>
138+
<item>::200:0:0:0/71</item>
139+
<item>::400:0:0:0/70</item>
140+
<item>::800:0:0:0/69</item>
141+
<item>::1000:0:0:0/68</item>
142+
<item>::2000:0:0:0/67</item>
143+
<item>::4000:0:0:0/66</item>
144+
<item>::8000:0:0:0/65</item>
145+
<item>0:0:0:1::/64</item>
146+
<item>0:0:0:2::/63</item>
147+
<item>0:0:0:4::/62</item>
148+
<item>0:0:0:8::/61</item>
149+
<item>0:0:0:10::/60</item>
150+
<item>0:0:0:20::/59</item>
151+
<item>0:0:0:40::/58</item>
152+
<item>0:0:0:80::/57</item>
153+
<item>0:0:0:100::/56</item>
154+
<item>0:0:0:200::/55</item>
155+
<item>0:0:0:400::/54</item>
156+
<item>0:0:0:800::/53</item>
157+
<item>0:0:0:1000::/52</item>
158+
<item>0:0:0:2000::/51</item>
159+
<item>0:0:0:4000::/50</item>
160+
<item>0:0:0:8000::/49</item>
161+
<item>0:0:1::/48</item>
162+
<item>0:0:2::/47</item>
163+
<item>0:0:4::/46</item>
164+
<item>0:0:8::/45</item>
165+
<item>0:0:10::/44</item>
166+
<item>0:0:20::/43</item>
167+
<item>0:0:40::/42</item>
168+
<item>0:0:80::/41</item>
169+
<item>0:0:100::/40</item>
170+
<item>0:0:200::/39</item>
171+
<item>0:0:400::/38</item>
172+
<item>0:0:800::/37</item>
173+
<item>0:0:1000::/36</item>
174+
<item>0:0:2000::/35</item>
175+
<item>0:0:4000::/34</item>
176+
<item>0:0:8000::/33</item>
177+
<item>0:1::/32</item>
178+
<item>0:2::/31</item>
179+
<item>0:4::/30</item>
180+
<item>0:8::/29</item>
181+
<item>0:10::/28</item>
182+
<item>0:20::/27</item>
183+
<item>0:40::/26</item>
184+
<item>0:80::/25</item>
185+
<item>0:100::/24</item>
186+
<item>0:200::/23</item>
187+
<item>0:400::/22</item>
188+
<item>0:800::/21</item>
189+
<item>0:1000::/20</item>
190+
<item>0:2000::/19</item>
191+
<item>0:4000::/18</item>
192+
<item>0:8000::/17</item>
193+
<item>1::/16</item>
194+
<item>2::/15</item>
195+
<item>4::/14</item>
196+
<item>8::/13</item>
197+
<item>10::/12</item>
198+
<item>20::/11</item>
199+
<item>40::/10</item>
200+
<item>80::/9</item>
201+
<item>100::/8</item>
202+
<item>200::/7</item>
203+
<item>400::/6</item>
204+
<item>800::/5</item>
205+
<item>1000::/4</item>
206+
<item>2000::/3</item>
207+
<item>4000::/2</item>
208+
<item>8000::/2</item>
209+
<item>c000::/3</item>
210+
<item>e000::/4</item>
211+
<item>f000::/5</item>
212+
<item>f800::/6</item>
213+
<item>fe00::/9</item>
214+
<item>fec0::/10</item>
215+
</string-array>
80216
</resources>

0 commit comments

Comments
 (0)