Improve: use lwip stack instead of kernel

This commit is contained in:
Kr328
2021-05-21 21:02:22 +08:00
parent eff310ac40
commit 7381b787f3
15 changed files with 198 additions and 155 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)
}()
}

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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()