Improve: use lwip stack instead of kernel
This commit is contained in:
@@ -97,17 +97,14 @@ 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,
|
||||
jint mtu, jstring gateway,
|
||||
jstring mirror, jstring dns,
|
||||
jint mtu, jstring dns,
|
||||
jobject cb) {
|
||||
TRACE_METHOD();
|
||||
|
||||
scoped_string _gateway = get_string(gateway);
|
||||
scoped_string _mirror = get_string(mirror);
|
||||
scoped_string _dns = get_string(dns);
|
||||
jobject _interface = new_global(cb);
|
||||
|
||||
startTun(fd, mtu, _gateway, _mirror, _dns, _interface);
|
||||
startTun(fd, mtu, _dns, _interface);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
|
||||
@@ -5,8 +5,8 @@ go 1.16
|
||||
require (
|
||||
cfa/blob v0.0.0 // local generated
|
||||
github.com/Dreamacro/clash v0.0.0 // local
|
||||
github.com/kr328/tun2socket v0.0.0 // local
|
||||
github.com/dlclark/regexp2 v1.4.0
|
||||
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99
|
||||
github.com/miekg/dns v1.1.42
|
||||
github.com/oschwald/geoip2-golang v1.5.0
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
@@ -15,4 +15,6 @@ require (
|
||||
|
||||
replace github.com/Dreamacro/clash => ./clash
|
||||
|
||||
replace github.com/kr328/tun2socket => ./tun2socket
|
||||
|
||||
replace cfa/blob => ../../../build/intermediates/golang_blob
|
||||
|
||||
@@ -56,17 +56,14 @@ func (t *remoteTun) stop() {
|
||||
}
|
||||
|
||||
//export startTun
|
||||
func startTun(fd, mtu C.int, gateway, mirror, dns C.c_string, callback unsafe.Pointer) C.int {
|
||||
func startTun(fd, mtu C.int, dns C.c_string, callback unsafe.Pointer) C.int {
|
||||
f := int(fd)
|
||||
m := int(mtu)
|
||||
|
||||
g := C.GoString(gateway)
|
||||
mr := C.GoString(mirror)
|
||||
d := C.GoString(dns)
|
||||
|
||||
remote := &remoteTun{callback: callback, closed: false, limit: semaphore.NewWeighted(4)}
|
||||
|
||||
if tun.Start(f, m, g, mr, d, remote.stop) != nil {
|
||||
if tun.Start(f, m, d) != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -7,24 +7,22 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/kr328/tun2socket/bridge"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
"github.com/kr328/tun2socket/redirect"
|
||||
)
|
||||
|
||||
const defaultDnsReadTimeout = time.Second * 30
|
||||
|
||||
func shouldHijackDns(dnsAddr binding.Address, targetAddr binding.Address) bool {
|
||||
if targetAddr.Port != 53 {
|
||||
func shouldHijackDns(dns net.IP, target net.IP, targetPort int) bool {
|
||||
if targetPort != 53 {
|
||||
return false
|
||||
}
|
||||
|
||||
return dnsAddr.IP.Equal(net.IPv4zero) || dnsAddr.IP.Equal(targetAddr.IP)
|
||||
return net.IPv4zero.Equal(dns) || target.Equal(dns)
|
||||
}
|
||||
|
||||
func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
|
||||
func hijackUDPDns(pkt []byte, lAddr, rAddr net.Addr, udp bridge.UDP) {
|
||||
go func() {
|
||||
answer, err := relayDnsPacket(pkt)
|
||||
|
||||
@@ -32,10 +30,9 @@ func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
|
||||
return
|
||||
}
|
||||
|
||||
_ = sender(answer, &binding.Endpoint{
|
||||
Source: ep.Target,
|
||||
Target: ep.Source,
|
||||
})
|
||||
_, _ = udp.WriteTo(answer, lAddr, rAddr)
|
||||
|
||||
recycleUDP(pkt)
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,37 +4,24 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/context"
|
||||
CTX "github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
)
|
||||
|
||||
func handleTCP(conn net.Conn, endpoint *binding.Endpoint) {
|
||||
src := &net.TCPAddr{
|
||||
IP: endpoint.Source.IP,
|
||||
Port: int(endpoint.Source.Port),
|
||||
Zone: "",
|
||||
}
|
||||
dst := &net.TCPAddr{
|
||||
IP: endpoint.Target.IP,
|
||||
Port: int(endpoint.Target.Port),
|
||||
Zone: "",
|
||||
}
|
||||
|
||||
func handleTCP(conn net.Conn, source *net.TCPAddr, target *net.TCPAddr) {
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: C.SOCKS,
|
||||
SrcIP: src.IP,
|
||||
DstIP: dst.IP,
|
||||
SrcPort: strconv.Itoa(src.Port),
|
||||
DstPort: strconv.Itoa(dst.Port),
|
||||
SrcIP: source.IP,
|
||||
DstIP: target.IP,
|
||||
SrcPort: strconv.Itoa(source.Port),
|
||||
DstPort: strconv.Itoa(target.Port),
|
||||
AddrType: C.AtypIPv4,
|
||||
Host: "",
|
||||
RawSrcAddr: src,
|
||||
RawDstAddr: dst,
|
||||
RawSrcAddr: source,
|
||||
RawDstAddr: target,
|
||||
}
|
||||
|
||||
tunnel.Add(context.NewConnContext(conn, metadata))
|
||||
tunnel.Add(CTX.NewConnContext(conn, metadata))
|
||||
}
|
||||
|
||||
@@ -3,67 +3,126 @@ package tun
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
"github.com/kr328/tun2socket/redirect"
|
||||
|
||||
"github.com/kr328/tun2socket"
|
||||
)
|
||||
|
||||
var lock sync.Mutex
|
||||
var tun *tun2socket.Tun2Socket
|
||||
type context struct {
|
||||
stack tun2socket.Stack
|
||||
device *os.File
|
||||
}
|
||||
|
||||
func Start(fd, mtu int, gateway, mirror, dns string, onStop func()) error {
|
||||
var lock sync.Mutex
|
||||
var tun *context
|
||||
|
||||
func Start(fd, mtu int, dns string) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
stopLocked()
|
||||
|
||||
dnsHost, dnsPort, err := net.SplitHostPort(dns)
|
||||
dnsIP := net.ParseIP(dns)
|
||||
|
||||
device := os.NewFile(uintptr(fd), "/dev/tun")
|
||||
|
||||
stack, err := tun2socket.NewStack(mtu)
|
||||
if err != nil {
|
||||
_ = device.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
dnsP, err := strconv.Atoi(dnsPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
// device -> lwip
|
||||
|
||||
dnsAddr := binding.Address{
|
||||
IP: net.ParseIP(dnsHost),
|
||||
Port: uint16(dnsP),
|
||||
}
|
||||
defer device.Close()
|
||||
defer stack.Close()
|
||||
|
||||
t := tun2socket.NewTun2Socket(os.NewFile(uintptr(fd), "/dev/tun"), mtu, net.ParseIP(gateway), net.ParseIP(mirror))
|
||||
buf := make([]byte, mtu)
|
||||
|
||||
t.SetAllocator(allocUDP)
|
||||
t.SetClosedHandler(onStop)
|
||||
t.SetLogger(&logger{})
|
||||
for {
|
||||
n, err := device.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) {
|
||||
if shouldHijackDns(dnsAddr, endpoint.Target) {
|
||||
hijackTCPDns(conn)
|
||||
|
||||
return
|
||||
_, _ = stack.Link().Write(buf[:n])
|
||||
}
|
||||
}()
|
||||
|
||||
handleTCP(conn, endpoint)
|
||||
})
|
||||
t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
|
||||
if shouldHijackDns(dnsAddr, endpoint.Target) {
|
||||
hijackUDPDns(payload, endpoint, sender)
|
||||
go func() {
|
||||
// lwip -> device
|
||||
|
||||
return
|
||||
defer device.Close()
|
||||
defer stack.Close()
|
||||
|
||||
buf := make([]byte, mtu)
|
||||
|
||||
for {
|
||||
n, err := stack.Link().Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = device.Write(buf[:n])
|
||||
}
|
||||
}()
|
||||
|
||||
handleUDP(payload, endpoint, sender)
|
||||
})
|
||||
go func() {
|
||||
// lwip tcp
|
||||
|
||||
t.Start()
|
||||
for {
|
||||
conn, err := stack.TCP().Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
tun = t
|
||||
source := conn.LocalAddr().(*net.TCPAddr)
|
||||
target := conn.RemoteAddr().(*net.TCPAddr)
|
||||
|
||||
if shouldHijackDns(dnsIP, target.IP, target.Port) {
|
||||
hijackTCPDns(conn)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
handleTCP(conn, source, target)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
// lwip udp
|
||||
|
||||
for {
|
||||
buf := allocUDP(mtu)
|
||||
|
||||
n, lAddr, rAddr, err := stack.UDP().ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
source := lAddr.(*net.UDPAddr)
|
||||
target := rAddr.(*net.UDPAddr)
|
||||
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if shouldHijackDns(dnsIP, target.IP, target.Port) {
|
||||
hijackUDPDns(buf[:n], source, target, stack.UDP())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
handleUDP(buf[:n], source, target, stack.UDP())
|
||||
}
|
||||
}()
|
||||
|
||||
tun = &context{
|
||||
stack: stack,
|
||||
device: device,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -77,7 +136,8 @@ func Stop() {
|
||||
|
||||
func stopLocked() {
|
||||
if tun != nil {
|
||||
tun.Close()
|
||||
tun.device.Close()
|
||||
tun.stack.Close()
|
||||
}
|
||||
|
||||
tun = nil
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
"github.com/kr328/tun2socket/binding"
|
||||
"github.com/kr328/tun2socket/redirect"
|
||||
"github.com/kr328/tun2socket/bridge"
|
||||
|
||||
adapters "github.com/Dreamacro/clash/adapters/inbound"
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
@@ -15,10 +13,9 @@ import (
|
||||
)
|
||||
|
||||
type udpPacket struct {
|
||||
metadata *C.Metadata
|
||||
source binding.Address
|
||||
source *net.UDPAddr
|
||||
data []byte
|
||||
send redirect.UDPSender
|
||||
udp bridge.UDP
|
||||
}
|
||||
|
||||
func (u *udpPacket) Data() []byte {
|
||||
@@ -26,15 +23,7 @@ func (u *udpPacket) Data() []byte {
|
||||
}
|
||||
|
||||
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
uAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
|
||||
return len(b), u.send(b, &binding.Endpoint{
|
||||
Source: binding.Address{IP: uAddr.IP, Port: uint16(uAddr.Port)},
|
||||
Target: u.source,
|
||||
})
|
||||
return u.udp.WriteTo(b, u.source, addr)
|
||||
}
|
||||
|
||||
func (u *udpPacket) Drop() {
|
||||
@@ -49,20 +38,14 @@ func (u *udpPacket) LocalAddr() net.Addr {
|
||||
}
|
||||
}
|
||||
|
||||
func handleUDP(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
|
||||
func handleUDP(payload []byte, source *net.UDPAddr, target *net.UDPAddr, udp bridge.UDP) {
|
||||
pkt := &udpPacket{
|
||||
source: endpoint.Source,
|
||||
data: payload,
|
||||
send: sender,
|
||||
source: source,
|
||||
data: payload,
|
||||
udp: udp,
|
||||
}
|
||||
|
||||
rAddr := &net.UDPAddr{
|
||||
IP: endpoint.Target.IP,
|
||||
Port: int(endpoint.Target.Port),
|
||||
Zone: "",
|
||||
}
|
||||
|
||||
adapter := adapters.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.SOCKS)
|
||||
adapter := adapters.NewPacket(socks5.ParseAddrToSocksAddr(target), pkt, C.SOCKS)
|
||||
|
||||
tunnel.AddPacket(adapter)
|
||||
}
|
||||
|
||||
1
core/src/main/golang/tun2socket
Submodule
1
core/src/main/golang/tun2socket
Submodule
Submodule core/src/main/golang/tun2socket added at a57caac68e
@@ -61,13 +61,11 @@ object Clash {
|
||||
fun startTun(
|
||||
fd: Int,
|
||||
mtu: Int,
|
||||
gateway: String,
|
||||
mirror: String,
|
||||
dns: String,
|
||||
markSocket: (Int) -> Boolean,
|
||||
querySocketUid: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress) -> Int
|
||||
) {
|
||||
Bridge.nativeStartTun(fd, mtu, gateway, mirror, "$dns:53", object : TunInterface {
|
||||
Bridge.nativeStartTun(fd, mtu, dns, object : TunInterface {
|
||||
override fun markSocket(fd: Int) {
|
||||
markSocket(fd)
|
||||
}
|
||||
|
||||
@@ -17,15 +17,7 @@ object Bridge {
|
||||
external fun nativeQueryTrafficTotal(): Long
|
||||
external fun nativeNotifyDnsChanged(dnsList: String)
|
||||
external fun nativeNotifyInstalledAppChanged(uidList: String)
|
||||
external fun nativeStartTun(
|
||||
fd: Int,
|
||||
mtu: Int,
|
||||
gateway: String,
|
||||
mirror: String,
|
||||
dns: String,
|
||||
cb: TunInterface
|
||||
)
|
||||
|
||||
external fun nativeStartTun(fd: Int, mtu: Int, dns: String, cb: TunInterface)
|
||||
external fun nativeStopTun()
|
||||
external fun nativeStartHttp(listenAt: String): String?
|
||||
external fun nativeStopHttp()
|
||||
|
||||
Reference in New Issue
Block a user