Feature: make wintun driver embed

This commit is contained in:
yaling888 2022-05-27 09:20:46 +08:00
parent b37e1fb2b9
commit 67905bcf7e
14 changed files with 315 additions and 6 deletions

View File

@ -150,7 +150,6 @@ Then manually create the default route and DNS server. If your device already ha
Enjoy! :) Enjoy! :)
#### For Windows: #### For Windows:
go to [https://www.wintun.net](https://www.wintun.net) and download the latest release, copy the right `wintun.dll` into the system32 directory.
```yaml ```yaml
tun: tun:
enable: true enable: true

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,233 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package driver
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
"syscall"
"unsafe"
"github.com/Dreamacro/clash/log"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/driver/memmod"
)
//go:linkname modwintun golang.zx2c4.com/wintun.modwintun
//go:linkname procWintunCreateAdapter golang.zx2c4.com/wintun.procWintunCreateAdapter
//go:linkname procWintunOpenAdapter golang.zx2c4.com/wintun.procWintunOpenAdapter
//go:linkname procWintunCloseAdapter golang.zx2c4.com/wintun.procWintunCloseAdapter
//go:linkname procWintunDeleteDriver golang.zx2c4.com/wintun.procWintunDeleteDriver
//go:linkname procWintunGetAdapterLUID golang.zx2c4.com/wintun.procWintunGetAdapterLUID
//go:linkname procWintunGetRunningDriverVersion golang.zx2c4.com/wintun.procWintunGetRunningDriverVersion
//go:linkname procWintunAllocateSendPacket golang.zx2c4.com/wintun.procWintunAllocateSendPacket
//go:linkname procWintunEndSession golang.zx2c4.com/wintun.procWintunEndSession
//go:linkname procWintunGetReadWaitEvent golang.zx2c4.com/wintun.procWintunGetReadWaitEvent
//go:linkname procWintunReceivePacket golang.zx2c4.com/wintun.procWintunReceivePacket
//go:linkname procWintunReleaseReceivePacket golang.zx2c4.com/wintun.procWintunReleaseReceivePacket
//go:linkname procWintunSendPacket golang.zx2c4.com/wintun.procWintunSendPacket
//go:linkname procWintunStartSession golang.zx2c4.com/wintun.procWintunStartSession
var (
modwintun *lazyDLL
procWintunCreateAdapter *lazyProc
procWintunOpenAdapter *lazyProc
procWintunCloseAdapter *lazyProc
procWintunDeleteDriver *lazyProc
procWintunGetAdapterLUID *lazyProc
procWintunGetRunningDriverVersion *lazyProc
procWintunAllocateSendPacket *lazyProc
procWintunEndSession *lazyProc
procWintunGetReadWaitEvent *lazyProc
procWintunReceivePacket *lazyProc
procWintunReleaseReceivePacket *lazyProc
procWintunSendPacket *lazyProc
procWintunStartSession *lazyProc
)
type loggerLevel int
const (
logInfo loggerLevel = iota
logWarn
logErr
)
func init() {
modwintun = newLazyDLL("wintun.dll", setupLogger)
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
procWintunOpenAdapter = modwintun.NewProc("WintunOpenAdapter")
procWintunCloseAdapter = modwintun.NewProc("WintunCloseAdapter")
procWintunDeleteDriver = modwintun.NewProc("WintunDeleteDriver")
procWintunGetAdapterLUID = modwintun.NewProc("WintunGetAdapterLUID")
procWintunGetRunningDriverVersion = modwintun.NewProc("WintunGetRunningDriverVersion")
procWintunAllocateSendPacket = modwintun.NewProc("WintunAllocateSendPacket")
procWintunEndSession = modwintun.NewProc("WintunEndSession")
procWintunGetReadWaitEvent = modwintun.NewProc("WintunGetReadWaitEvent")
procWintunReceivePacket = modwintun.NewProc("WintunReceivePacket")
procWintunReleaseReceivePacket = modwintun.NewProc("WintunReleaseReceivePacket")
procWintunSendPacket = modwintun.NewProc("WintunSendPacket")
procWintunStartSession = modwintun.NewProc("WintunStartSession")
}
func InitWintun() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("init wintun.dll error: %v", r)
}
}()
if err = modwintun.Load(); err != nil {
return
}
procWintunCreateAdapter.Addr()
procWintunOpenAdapter.Addr()
procWintunCloseAdapter.Addr()
procWintunDeleteDriver.Addr()
procWintunGetAdapterLUID.Addr()
procWintunGetRunningDriverVersion.Addr()
procWintunAllocateSendPacket.Addr()
procWintunEndSession.Addr()
procWintunGetReadWaitEvent.Addr()
procWintunReceivePacket.Addr()
procWintunReleaseReceivePacket.Addr()
procWintunSendPacket.Addr()
procWintunStartSession.Addr()
return
}
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
return &lazyDLL{Name: name, onLoad: onLoad}
}
func logMessage(level loggerLevel, _ uint64, msg *uint16) int {
switch level {
case logInfo:
log.Infoln("[TUN] %s", windows.UTF16PtrToString(msg))
case logWarn:
log.Warnln("[TUN] %s", windows.UTF16PtrToString(msg))
case logErr:
log.Errorln("[TUN] %s", windows.UTF16PtrToString(msg))
default:
log.Debugln("[TUN] %s", windows.UTF16PtrToString(msg))
}
return 0
}
func setupLogger(dll *lazyDLL) {
var callback uintptr
if runtime.GOARCH == "386" {
callback = windows.NewCallback(func(level loggerLevel, _, _ uint32, msg *uint16) int {
return logMessage(level, 0, msg)
})
} else if runtime.GOARCH == "arm" {
callback = windows.NewCallback(func(level loggerLevel, _, _, _ uint32, msg *uint16) int {
return logMessage(level, 0, msg)
})
} else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
callback = windows.NewCallback(logMessage)
}
_, _, _ = syscall.SyscallN(dll.NewProc("WintunSetLogger").Addr(), callback)
}
func (d *lazyDLL) NewProc(name string) *lazyProc {
return &lazyProc{dll: d, Name: name}
}
type lazyProc struct {
Name string
mu sync.Mutex
dll *lazyDLL
addr uintptr
}
func (p *lazyProc) Find() error {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
return nil
}
p.mu.Lock()
defer p.mu.Unlock()
if p.addr != 0 {
return nil
}
err := p.dll.Load()
if err != nil {
return fmt.Errorf("error loading DLL: %s, MODULE: %s, error: %w", p.dll.Name, p.Name, err)
}
addr, err := p.nameToAddr()
if err != nil {
return fmt.Errorf("error getting %s address: %w", p.Name, err)
}
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
return nil
}
func (p *lazyProc) Addr() uintptr {
err := p.Find()
if err != nil {
panic(err)
}
return p.addr
}
func (p *lazyProc) Load() error {
return p.dll.Load()
}
type lazyDLL struct {
Name string
Base windows.Handle
mu sync.Mutex
module *memmod.Module
onLoad func(d *lazyDLL)
}
func (d *lazyDLL) Load() error {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
return nil
}
d.mu.Lock()
defer d.mu.Unlock()
if d.module != nil {
return nil
}
module, err := memmod.LoadLibrary(dllData)
if err != nil {
return fmt.Errorf("unable to load library: %w", err)
}
d.Base = windows.Handle(module.BaseAddr())
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
if d.onLoad != nil {
d.onLoad(d)
}
return nil
}
func (p *lazyProc) nameToAddr() (uintptr, error) {
return p.dll.module.ProcAddressByName(p.Name)
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package driver
import (
_ "embed"
)
//go:embed x86/wintun.dll
var dllData []byte

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package driver
import (
_ "embed"
)
//go:embed amd64/wintun.dll
var dllData []byte

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package driver
import (
_ "embed"
)
//go:embed arm/wintun.dll
var dllData []byte

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package driver
import (
_ "embed"
)
//go:embed arm64/wintun.dll
var dllData []byte

View File

@ -0,0 +1,10 @@
//go:build windows
// https://git.zx2c4.com/wireguard-go/tree/tun/wintun
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
package driver

Binary file not shown.

View File

@ -3,7 +3,6 @@
package tun package tun
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
@ -43,11 +42,11 @@ func Open(name string, mtu uint32) (_ device.Device, err error) {
forcedMTU = int(t.mtu) forcedMTU = int(t.mtu)
} }
nt, err := tun.CreateTUN(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way nt, err := newDevice(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way
// retry if abnormal exit on Windows at last time // retry if abnormal exit at last time on Windows
if err != nil && runtime.GOOS == "windows" && errors.Is(err, os.ErrExist) { if err != nil && runtime.GOOS == "windows" && os.IsExist(err) {
nt, err = tun.CreateTUN(t.name, forcedMTU) nt, err = newDevice(t.name, forcedMTU)
} }
if err != nil { if err != nil {

View File

@ -2,7 +2,13 @@
package tun package tun
import "golang.zx2c4.com/wireguard/tun"
const ( const (
offset = 4 /* 4 bytes TUN_PI */ offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500 defaultMTU = 1500
) )
func newDevice(name string, mtu int) (tun.Device, error) {
return tun.CreateTUN(name, mtu)
}

View File

@ -1,6 +1,8 @@
package tun package tun
import ( import (
"github.com/Dreamacro/clash/listener/tun/device/tun/driver"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun" "golang.zx2c4.com/wireguard/tun"
) )
@ -20,3 +22,11 @@ func init() {
func (t *TUN) LUID() uint64 { func (t *TUN) LUID() uint64 {
return t.nt.LUID() return t.nt.LUID()
} }
func newDevice(name string, mtu int) (nt tun.Device, err error) {
if err = driver.InitWintun(); err != nil {
return
}
return tun.CreateTUN(name, mtu)
}