This commit is contained in:
Mazeorz
2021-11-17 16:03:47 +08:00
parent 1f3968bd50
commit 900e852525
115 changed files with 9990 additions and 585 deletions

View File

@ -3,16 +3,20 @@ package proxy
import (
"fmt"
"net"
"runtime"
"strconv"
"sync"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/http"
"github.com/Dreamacro/clash/listener/mixed"
"github.com/Dreamacro/clash/listener/redir"
"github.com/Dreamacro/clash/listener/socks"
"github.com/Dreamacro/clash/listener/tproxy"
"github.com/Dreamacro/clash/listener/tun"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/log"
)
@ -29,6 +33,7 @@ var (
tproxyUDPListener *tproxy.UDPListener
mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener
tunAdapter ipstack.TunAdapter
// lock for recreate function
socksMux sync.Mutex
@ -36,6 +41,7 @@ var (
redirMux sync.Mutex
tproxyMux sync.Mutex
mixedMux sync.Mutex
tunMux sync.Mutex
)
type Ports struct {
@ -58,6 +64,18 @@ func SetAllowLan(al bool) {
allowLan = al
}
func Tun() config.Tun {
if tunAdapter == nil {
return config.Tun{}
}
return config.Tun{
Enable: true,
Stack: tunAdapter.Stack(),
DNSListen: tunAdapter.DNSListen(),
AutoRoute: tunAdapter.AutoRoute(),
}
}
func SetBindAddress(host string) {
bindAddress = host
}
@ -275,6 +293,25 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
return nil
}
func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error {
tunMux.Lock()
defer tunMux.Unlock()
if tunAdapter != nil {
tunAdapter.Close()
tunAdapter = nil
}
if !conf.Enable {
return nil
}
var err error
tunAdapter, err = tun.New(conf, tcpIn, udpIn)
return err
}
// GetPorts return the ports of proxy servers
func GetPorts() *Ports {
ports := &Ports{}
@ -330,3 +367,12 @@ func genAddr(host string, port int, allowLan bool) string {
return fmt.Sprintf("127.0.0.1:%d", port)
}
// CleanUp clean up something
func CleanUp() {
if runtime.GOOS == "windows" {
if tunAdapter != nil {
tunAdapter.Close()
}
}
}

View File

@ -0,0 +1,186 @@
package tproxy
import (
"errors"
"fmt"
"os/exec"
U "os/user"
"runtime"
"strings"
"github.com/Dreamacro/clash/log"
)
var (
interfaceName = ""
tproxyPort = 0
dnsPort = 0
)
const (
PROXY_FWMARK = "0x2d0"
PROXY_ROUTE_TABLE = "0x2d0"
USERNAME = "clash"
)
func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error {
var err error
if _, err = execCmd("iptables -V"); err != nil {
return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS)
}
user, err := U.Lookup(USERNAME)
if err != nil {
return fmt.Errorf("the user \" %s\" does not exist, please create it", USERNAME)
}
if ifname == "" {
return errors.New("the 'interface-name' can not be empty")
}
ownerUid := user.Uid
interfaceName = ifname
tproxyPort = tport
dnsPort = dport
// add route
execCmd(fmt.Sprintf("ip -f inet rule add fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE))
execCmd(fmt.Sprintf("ip -f inet route add local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE))
// set FORWARD
execCmd("sysctl -w net.ipv4.ip_forward=1")
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName))
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -o %s -j ACCEPT", interfaceName))
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName))
execCmd(fmt.Sprintf("iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName))
// set clash divert
execCmd("iptables -t mangle -N clash_divert")
execCmd("iptables -t mangle -F clash_divert")
execCmd(fmt.Sprintf("iptables -t mangle -A clash_divert -j MARK --set-mark %s", PROXY_FWMARK))
execCmd("iptables -t mangle -A clash_divert -j ACCEPT")
// set pre routing
execCmd("iptables -t mangle -N clash_prerouting")
execCmd("iptables -t mangle -F clash_prerouting")
execCmd("iptables -t mangle -A clash_prerouting -s 172.17.0.0/16 -j RETURN")
execCmd("iptables -t mangle -A clash_prerouting -p udp --dport 53 -j ACCEPT")
execCmd("iptables -t mangle -A clash_prerouting -p tcp --dport 53 -j ACCEPT")
execCmd("iptables -t mangle -A clash_prerouting -m addrtype --dst-type LOCAL -j RETURN")
addLocalnetworkToChain("clash_prerouting")
execCmd("iptables -t mangle -A clash_prerouting -p tcp -m socket -j clash_divert")
execCmd("iptables -t mangle -A clash_prerouting -p udp -m socket -j clash_divert")
execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK))
execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK))
execCmd("iptables -t mangle -A PREROUTING -j clash_prerouting")
execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort))
execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort))
// set post routing
execCmd(fmt.Sprintf("iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName))
// set output
execCmd("iptables -t mangle -N clash_output")
execCmd("iptables -t mangle -F clash_output")
execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -m owner --uid-owner %s -j RETURN", ownerUid))
execCmd("iptables -t mangle -A clash_output -p udp -m multiport --dports 53,123,137 -j ACCEPT")
execCmd("iptables -t mangle -A clash_output -p tcp --dport 53 -j ACCEPT")
execCmd("iptables -t mangle -A clash_output -m addrtype --dst-type LOCAL -j RETURN")
execCmd("iptables -t mangle -A clash_output -m addrtype --dst-type BROADCAST -j RETURN")
addLocalnetworkToChain("clash_output")
execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -p tcp -j MARK --set-mark %s", PROXY_FWMARK))
execCmd(fmt.Sprintf("iptables -t mangle -A clash_output -p udp -j MARK --set-mark %s", PROXY_FWMARK))
execCmd(fmt.Sprintf("iptables -t mangle -I OUTPUT -o %s -j clash_output", interfaceName))
// set dns output
execCmd("iptables -t nat -N clash_dns_output")
execCmd("iptables -t nat -F clash_dns_output")
execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -m owner --uid-owner %s -j RETURN", ownerUid))
execCmd("iptables -t nat -A clash_dns_output -s 172.17.0.0/16 -j RETURN")
execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort))
execCmd(fmt.Sprintf("iptables -t nat -A clash_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort))
execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j clash_dns_output")
execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j clash_dns_output")
log.Infoln("[TProxy] Setting iptables completed")
return nil
}
func CleanUpTProxyLinuxIPTables() {
if interfaceName == "" || tproxyPort == 0 || dnsPort == 0 {
return
}
log.Warnln("Clean up tproxy linux iptables")
if _, err := execCmd("iptables -t mangle -L clash_divert"); err != nil {
return
}
// clean route
execCmd(fmt.Sprintf("ip -f inet rule del fwmark %s lookup %s", PROXY_FWMARK, PROXY_ROUTE_TABLE))
execCmd(fmt.Sprintf("ip -f inet route del local default dev %s table %s", interfaceName, PROXY_ROUTE_TABLE))
// clean FORWARD
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT", interfaceName, interfaceName))
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT", interfaceName, interfaceName))
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT", interfaceName))
execCmd(fmt.Sprintf("iptables -t filter -D FORWARD -o %s -j ACCEPT", interfaceName))
// clean PREROUTING
execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort))
execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort))
execCmd("iptables -t mangle -D PREROUTING -j clash_prerouting")
// clean POSTROUTING
execCmd(fmt.Sprintf("iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE", interfaceName))
// clean OUTPUT
execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j clash_output", interfaceName))
execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j clash_dns_output")
execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j clash_dns_output")
// clean chain
execCmd("iptables -t mangle -F clash_prerouting")
execCmd("iptables -t mangle -X clash_prerouting")
execCmd("iptables -t mangle -F clash_divert")
execCmd("iptables -t mangle -X clash_divert")
execCmd("iptables -t mangle -F clash_output")
execCmd("iptables -t mangle -X clash_output")
execCmd("iptables -t nat -F clash_dns_output")
execCmd("iptables -t nat -X clash_dns_output")
}
func addLocalnetworkToChain(chain string) {
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 0.0.0.0/8 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 10.0.0.0/8 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 100.64.0.0/10 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 127.0.0.0/8 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 169.254.0.0/16 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 172.16.0.0/12 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.0.0/24 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.0.2.0/24 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.88.99.0/24 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 192.168.0.0/16 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 198.51.100.0/24 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 203.0.113.0/24 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 224.0.0.0/4 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 240.0.0.0/4 -j RETURN", chain))
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN", chain))
}
func execCmd(cmdstr string) (string, error) {
log.Debugln("[TProxy] %s", cmdstr)
args := strings.Split(cmdstr, " ")
cmd := exec.Command(args[0], args[1:]...)
out, err := cmd.CombinedOutput()
if err != nil {
log.Errorln("[TProxy] error: %s, %s", err.Error(), string(out))
return "", err
}
return string(out), nil
}

66
listener/tun/dev/dev.go Normal file
View File

@ -0,0 +1,66 @@
package dev
import (
"os/exec"
"runtime"
"github.com/Dreamacro/clash/log"
)
// TunDevice is cross-platform tun interface
type TunDevice interface {
Name() string
URL() string
MTU() (int, error)
IsClose() bool
Close() error
Read(buff []byte) (int, error)
Write(buff []byte) (int, error)
}
func SetLinuxAutoRoute() {
log.Infoln("Tun adapter auto setting global route")
addLinuxSystemRoute("1")
addLinuxSystemRoute("2/7")
addLinuxSystemRoute("4/6")
addLinuxSystemRoute("8/5")
addLinuxSystemRoute("16/4")
addLinuxSystemRoute("32/3")
addLinuxSystemRoute("64/2")
addLinuxSystemRoute("128.0/1")
addLinuxSystemRoute("198.18.0/16")
}
func RemoveLinuxAutoRoute() {
log.Infoln("Tun adapter removing global route")
delLinuxSystemRoute("1")
delLinuxSystemRoute("2/7")
delLinuxSystemRoute("4/6")
delLinuxSystemRoute("8/5")
delLinuxSystemRoute("16/4")
delLinuxSystemRoute("32/3")
delLinuxSystemRoute("64/2")
delLinuxSystemRoute("128.0/1")
delLinuxSystemRoute("198.18.0/16")
}
func addLinuxSystemRoute(net string) {
if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
return
}
cmd := exec.Command("route", "add", "-net", net, "198.18.0.1")
if err := cmd.Run(); err != nil {
log.Errorln("[auto route] Failed to add system route: %s, cmd: %s", err.Error(), cmd.String())
}
}
func delLinuxSystemRoute(net string) {
if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
return
}
cmd := exec.Command("route", "delete", "-net", net, "198.18.0.1")
_ = cmd.Run()
//if err := cmd.Run(); err != nil {
// log.Errorln("[auto route]Failed to delete system route: %s, cmd: %s", err.Error(), cmd.String())
//}
}

View File

@ -0,0 +1,494 @@
//go:build darwin
// +build darwin
package dev
import (
"bytes"
"errors"
"fmt"
"net"
"os"
"os/exec"
"sync"
"syscall"
"unsafe"
"golang.org/x/net/ipv6"
"golang.org/x/sys/unix"
"github.com/Dreamacro/clash/common/pool"
)
const (
utunControlName = "com.apple.net.utun_control"
iocOut = 0x40000000
iocIn = 0x80000000
iocInout = iocIn | iocOut
)
// _CTLIOCGINFO value derived from /usr/include/sys/{kern_control,ioccom}.h
// https://github.com/apple/darwin-xnu/blob/master/bsd/sys/ioccom.h
// #define CTLIOCGINFO _IOWR('N', 3, struct ctl_info) /* get id from name */ = 0xc0644e03
const _CTLIOCGINFO = iocInout | ((100 & 0x1fff) << 16) | uint32(byte('N'))<<8 | 3
// #define SIOCPROTOATTACH_IN6 _IOWR('i', 110, struct in6_aliasreq_64)
const siocprotoattachIn6 = iocInout | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 110
// #define SIOCLL_START _IOWR('i', 130, struct in6Aliasreq)
const siocllStart = iocInout | ((128 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 130
// Following the wireguard-go solution:
// These unix.SYS_* constants were removed from golang.org/x/sys/unix
// so copy them here for now.
// See https://github.com/golang/go/issues/41868
const (
sysIoctl = 54
sysConnect = 98
sysGetsockopt = 118
)
type tunDarwin struct {
name string
tunAddress string
autoRoute bool
tunFile *os.File
errors chan error
closed bool
stopOnce sync.Once
}
// sockaddr_ctl specifeid in /usr/include/sys/kern_control.h
type sockaddrCtl struct {
scLen uint8
scFamily uint8
ssSysaddr uint16
scID uint32
scUnit uint32
scReserved [5]uint32
}
type ctlInfo struct {
ctlID uint32
ctlName [96]byte
}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/sys/sockio.h#L107
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L570-L575
// https://man.openbsd.org/netintro.4#SIOCAIFADDR
type aliasreq struct {
ifraName [unix.IFNAMSIZ]byte
ifraAddr unix.RawSockaddrInet4
ifraDstaddr unix.RawSockaddrInet4
ifraMask unix.RawSockaddrInet4
}
// SIOCAIFADDR_IN6
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L114-L119
// https://opensource.apple.com/source/network_cmds/network_cmds-543.260.3/
type in6Addrlifetime struct{}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6_var.h#L336-L343
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/netinet6/in6.h#L174-L181
type in6Aliasreq struct {
ifraName [unix.IFNAMSIZ]byte
ifraAddr unix.RawSockaddrInet6
ifraDstaddr unix.RawSockaddrInet6
ifraPrefixmask unix.RawSockaddrInet6
ifraFlags int32
ifraLifetime in6Addrlifetime
}
// https://github.com/apple/darwin-xnu/blob/a449c6a3b8014d9406c2ddbdc81795da24aa7443/bsd/net/if.h#L402-L563
//type ifreqAddr struct {
// Name [unix.IFNAMSIZ]byte
// Addr unix.RawSockaddrInet4
// Pad [8]byte
//}
var sockaddrCtlSize uintptr = 32
// OpenTunDevice return a TunDevice according a URL
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
name := "utun"
mtu := 9000
ifIndex := -1
if name != "utun" {
_, err := fmt.Sscanf(name, "utun%d", &ifIndex)
if err != nil || ifIndex < 0 {
return nil, fmt.Errorf("interface name must be utun[0-9]*")
}
}
fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, 2)
if err != nil {
return nil, err
}
ctlInfo1 := &ctlInfo{}
copy(ctlInfo1.ctlName[:], []byte(utunControlName))
_, _, errno := unix.Syscall(
sysIoctl,
uintptr(fd),
uintptr(_CTLIOCGINFO),
uintptr(unsafe.Pointer(ctlInfo1)),
)
if errno != 0 {
return nil, fmt.Errorf("_CTLIOCGINFO: %v", errno)
}
sc := sockaddrCtl{
scLen: uint8(sockaddrCtlSize),
scFamily: unix.AF_SYSTEM,
ssSysaddr: 2,
scID: ctlInfo1.ctlID,
scUnit: uint32(ifIndex) + 1,
}
scPointer := unsafe.Pointer(&sc)
_, _, errno = unix.RawSyscall(
sysConnect,
uintptr(fd),
uintptr(scPointer),
uintptr(sockaddrCtlSize),
)
if errno != 0 {
return nil, fmt.Errorf("SYS_CONNECT: %v", errno)
}
err = syscall.SetNonblock(fd, true)
if err != nil {
return nil, err
}
tun, err := CreateTUNFromFile(os.NewFile(uintptr(fd), ""), mtu, tunAddress, autoRoute)
if err != nil {
return nil, err
}
if autoRoute {
SetLinuxAutoRoute()
}
return tun, nil
}
func CreateTUNFromFile(file *os.File, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) {
tun := &tunDarwin{
tunFile: file,
tunAddress: tunAddress,
autoRoute: autoRoute,
errors: make(chan error, 5),
}
name, err := tun.getName()
if err != nil {
tun.tunFile.Close()
return nil, err
}
tun.name = name
if err != nil {
tun.tunFile.Close()
return nil, err
}
if mtu > 0 {
err = tun.setMTU(mtu)
if err != nil {
tun.Close()
return nil, err
}
}
// This address doesn't mean anything here. NIC just net an IP address to set route upon.
p2pAddress := net.ParseIP(tunAddress)
err = tun.setTunAddress(p2pAddress)
if err != nil {
tun.Close()
return nil, err
}
err = tun.attachLinkLocal()
if err != nil {
tun.Close()
return nil, err
}
return tun, nil
}
func (t *tunDarwin) Name() string {
return t.name
}
func (t *tunDarwin) URL() string {
return fmt.Sprintf("dev://%s", t.Name())
}
func (t *tunDarwin) MTU() (int, error) {
return t.getInterfaceMtu()
}
func (t *tunDarwin) Read(buff []byte) (int, error) {
select {
case err := <-t.errors:
return 0, err
default:
n, err := t.tunFile.Read(buff)
if n < 4 {
return 0, err
}
copy(buff[:], buff[4:])
return n - 4, err
}
}
func (t *tunDarwin) Write(buff []byte) (int, error) {
// reserve space for header
buf := pool.Get(pool.RelayBufferSize)
defer pool.Put(buf[:cap(buf)])
buf[0] = 0x00
buf[1] = 0x00
buf[2] = 0x00
copy(buf[4:], buff)
if buf[4]>>4 == ipv6.Version {
buf[3] = unix.AF_INET6
} else {
buf[3] = unix.AF_INET
}
// write
return t.tunFile.Write(buf[:4+len(buff)])
}
func (t *tunDarwin) IsClose() bool {
return t.closed
}
func (t *tunDarwin) Close() error {
t.stopOnce.Do(func() {
if t.autoRoute {
RemoveLinuxAutoRoute()
}
t.closed = true
t.tunFile.Close()
})
return nil
}
func (t *tunDarwin) getInterfaceMtu() (int, error) {
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return 0, err
}
defer unix.Close(fd)
// do ioctl call
var ifr [64]byte
copy(ifr[:], t.name)
_, _, errno := unix.Syscall(
sysIoctl,
uintptr(fd),
uintptr(unix.SIOCGIFMTU),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return 0, fmt.Errorf("failed to get MTU on %s", t.name)
}
return int(*(*int32)(unsafe.Pointer(&ifr[16]))), nil
}
func (t *tunDarwin) getName() (string, error) {
var ifName struct {
name [16]byte
}
ifNameSize := uintptr(16)
var errno syscall.Errno
t.operateOnFd(func(fd uintptr) {
_, _, errno = unix.Syscall6(
sysGetsockopt,
fd,
2, /* #define SYSPROTO_CONTROL 2 */
2, /* #define UTUN_OPT_IFNAME 2 */
uintptr(unsafe.Pointer(&ifName)),
uintptr(unsafe.Pointer(&ifNameSize)), 0)
})
if errno != 0 {
return "", fmt.Errorf("SYS_GETSOCKOPT: %v", errno)
}
t.name = string(ifName.name[:ifNameSize-1])
return t.name, nil
}
func (t *tunDarwin) setMTU(n int) error {
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer unix.Close(fd)
// do ioctl call
var ifr [32]byte
copy(ifr[:], t.name)
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
_, _, errno := unix.Syscall(
sysIoctl,
uintptr(fd),
uintptr(unix.SIOCSIFMTU),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return fmt.Errorf("failed to set MTU on %s", t.name)
}
return nil
}
func (t *tunDarwin) operateOnFd(fn func(fd uintptr)) {
sysconn, err := t.tunFile.SyscallConn()
// TODO: consume the errors
if err != nil {
t.errors <- fmt.Errorf("unable to find sysconn for tunfile: %s", err.Error())
return
}
err = sysconn.Control(fn)
if err != nil {
t.errors <- fmt.Errorf("unable to control sysconn for tunfile: %s", err.Error())
}
}
func (t *tunDarwin) setTunAddress(addr net.IP) error {
var ifr [unix.IFNAMSIZ]byte
copy(ifr[:], t.name)
// set IPv4 address
fd4, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer syscall.Close(fd4)
var ip4 [4]byte
copy(ip4[:], addr.To4())
ip4mask := [4]byte{255, 255, 0, 0}
ifra4 := aliasreq{
ifraName: ifr,
ifraAddr: unix.RawSockaddrInet4{
Len: unix.SizeofSockaddrInet4,
Family: unix.AF_INET,
Addr: ip4,
},
ifraDstaddr: unix.RawSockaddrInet4{
Len: unix.SizeofSockaddrInet4,
Family: unix.AF_INET,
Addr: ip4,
},
ifraMask: unix.RawSockaddrInet4{
Len: unix.SizeofSockaddrInet4,
Family: unix.AF_INET,
Addr: ip4mask,
},
}
if _, _, errno := unix.Syscall(
sysIoctl,
uintptr(fd4),
uintptr(unix.SIOCAIFADDR),
uintptr(unsafe.Pointer(&ifra4)),
); errno != 0 {
return fmt.Errorf("failed to set ip address on %s: %v", t.name, errno)
}
return nil
}
func (t *tunDarwin) attachLinkLocal() error {
var ifr [unix.IFNAMSIZ]byte
copy(ifr[:], t.name)
// attach link-local address
fd6, err := unix.Socket(
unix.AF_INET6,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer syscall.Close(fd6)
// Attach link-local address
ifra6 := in6Aliasreq{
ifraName: ifr,
}
if _, _, errno := unix.Syscall(
sysIoctl,
uintptr(fd6),
uintptr(siocprotoattachIn6),
uintptr(unsafe.Pointer(&ifra6)),
); errno != 0 {
return fmt.Errorf("failed to attach link-local address on %s: SIOCPROTOATTACH_IN6 %v", t.name, errno)
}
if _, _, errno := unix.Syscall(
sysIoctl,
uintptr(fd6),
uintptr(siocllStart),
uintptr(unsafe.Pointer(&ifra6)),
); errno != 0 {
return fmt.Errorf("failed to set ipv6 address on %s: SIOCLL_START %v", t.name, errno)
}
return nil
}
// GetAutoDetectInterface get ethernet interface
func GetAutoDetectInterface() (string, error) {
cmd := exec.Command("bash", "-c", "netstat -rnf inet | grep 'default' | awk -F ' ' 'NR==1{print $6}' | xargs echo -n")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return "", err
}
if out.Len() == 0 {
return "", errors.New("interface not found by default route")
}
return out.String(), nil
}

View File

@ -0,0 +1,255 @@
//go:build linux || android
// +build linux android
package dev
import (
"bytes"
"errors"
"fmt"
"net/url"
"os"
"os/exec"
"strconv"
"sync"
"syscall"
"unsafe"
"golang.org/x/sys/unix"
)
const (
cloneDevicePath = "/dev/net/tun"
ifReqSize = unix.IFNAMSIZ + 64
)
type tunLinux struct {
url string
name string
tunAddress string
autoRoute bool
tunFile *os.File
mtu int
closed bool
stopOnce sync.Once
}
// OpenTunDevice return a TunDevice according a URL
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
deviceURL, _ := url.Parse("dev://clash0")
mtu, _ := strconv.ParseInt(deviceURL.Query().Get("mtu"), 0, 32)
t := &tunLinux{
url: deviceURL.String(),
mtu: int(mtu),
tunAddress: tunAddress,
autoRoute: autoRoute,
}
switch deviceURL.Scheme {
case "dev":
var err error
var dev TunDevice
dev, err = t.openDeviceByName(deviceURL.Host)
if err != nil {
return nil, err
}
if autoRoute {
SetLinuxAutoRoute()
}
return dev, nil
case "fd":
fd, err := strconv.ParseInt(deviceURL.Host, 10, 32)
if err != nil {
return nil, err
}
var dev TunDevice
dev, err = t.openDeviceByFd(int(fd))
if err != nil {
return nil, err
}
if autoRoute {
SetLinuxAutoRoute()
}
return dev, nil
}
return nil, fmt.Errorf("unsupported device type `%s`", deviceURL.Scheme)
}
func (t *tunLinux) Name() string {
return t.name
}
func (t *tunLinux) URL() string {
return t.url
}
func (t *tunLinux) Write(buff []byte) (int, error) {
return t.tunFile.Write(buff)
}
func (t *tunLinux) Read(buff []byte) (int, error) {
return t.tunFile.Read(buff)
}
func (t *tunLinux) IsClose() bool {
return t.closed
}
func (t *tunLinux) Close() error {
t.stopOnce.Do(func() {
if t.autoRoute {
RemoveLinuxAutoRoute()
}
t.closed = true
t.tunFile.Close()
})
return nil
}
func (t *tunLinux) MTU() (int, error) {
// Sometime, we can't read MTU by SIOCGIFMTU. Then we should return the preset MTU
if t.mtu > 0 {
return t.mtu, nil
}
mtu, err := t.getInterfaceMtu()
return int(mtu), err
}
func (t *tunLinux) openDeviceByName(name string) (TunDevice, error) {
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
if err != nil {
return nil, err
}
var ifr [ifReqSize]byte
var flags uint16 = unix.IFF_TUN | unix.IFF_NO_PI
nameBytes := []byte(name)
if len(nameBytes) >= unix.IFNAMSIZ {
return nil, errors.New("interface name too long")
}
copy(ifr[:], nameBytes)
*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(nfd),
uintptr(unix.TUNSETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return nil, errno
}
err = unix.SetNonblock(nfd, true)
if err != nil {
return nil, err
}
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
t.tunFile = os.NewFile(uintptr(nfd), cloneDevicePath)
t.name, err = t.getName()
if err != nil {
t.tunFile.Close()
return nil, err
}
return t, nil
}
func (t *tunLinux) openDeviceByFd(fd int) (TunDevice, error) {
var ifr struct {
name [16]byte
flags uint16
_ [22]byte
}
fd, err := syscall.Dup(fd)
if err != nil {
return nil, err
}
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TUNGETIFF, uintptr(unsafe.Pointer(&ifr)))
if errno != 0 {
return nil, errno
}
if ifr.flags&syscall.IFF_TUN == 0 || ifr.flags&syscall.IFF_NO_PI == 0 {
return nil, errors.New("only tun device and no pi mode supported")
}
nullStr := ifr.name[:]
i := bytes.IndexByte(nullStr, 0)
if i != -1 {
nullStr = nullStr[:i]
}
t.name = string(nullStr)
t.tunFile = os.NewFile(uintptr(fd), "/dev/tun")
return t, nil
}
func (t *tunLinux) getInterfaceMtu() (uint32, error) {
fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_DGRAM, 0)
if err != nil {
return 0, err
}
defer syscall.Close(fd)
var ifreq struct {
name [16]byte
mtu int32
_ [20]byte
}
copy(ifreq.name[:], t.name)
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
if errno != 0 {
return 0, errno
}
return uint32(ifreq.mtu), nil
}
func (t *tunLinux) getName() (string, error) {
sysconn, err := t.tunFile.SyscallConn()
if err != nil {
return "", err
}
var ifr [ifReqSize]byte
var errno syscall.Errno
err = sysconn.Control(func(fd uintptr) {
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
fd,
uintptr(unix.TUNGETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
})
if err != nil {
return "", errors.New("failed to get name of TUN device: " + err.Error())
}
if errno != 0 {
return "", errors.New("failed to get name of TUN device: " + errno.Error())
}
nullStr := ifr[:]
i := bytes.IndexByte(nullStr, 0)
if i != -1 {
nullStr = nullStr[:i]
}
t.name = string(nullStr)
return t.name, nil
}
// GetAutoDetectInterface get ethernet interface
func GetAutoDetectInterface() (string, error) {
cmd := exec.Command("bash", "-c", "ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return "", err
}
return out.String(), nil
}

View File

@ -0,0 +1,18 @@
//go:build !linux && !android && !darwin && !windows
// +build !linux,!android,!darwin,!windows
package dev
import (
"errors"
"runtime"
)
func OpenTunDevice(tunAddress string, autoRute bool) (TunDevice, error) {
return nil, errors.New("Unsupported platform " + runtime.GOOS + "/" + runtime.GOARCH)
}
// GetAutoDetectInterface get ethernet interface
func GetAutoDetectInterface() (string, error) {
return "", nil
}

View File

@ -0,0 +1,162 @@
//go:build windows
// +build windows
// Modified from: https://git.zx2c4.com/wireguard-go/tree/tun/tun_windows.go and https://git.zx2c4.com/wireguard-windows/tree/tunnel/addressconfig.go
// SPDX-License-Identifier: MIT
package dev
import (
"errors"
"fmt"
"os"
"sync/atomic"
"time"
_ "unsafe"
"github.com/Dreamacro/clash/listener/tun/dev/wintun"
"golang.org/x/sys/windows"
)
const (
rateMeasurementGranularity = uint64((time.Second / 2) / time.Nanosecond)
spinloopRateThreshold = 800000000 / 8 // 800mbps
spinloopDuration = uint64(time.Millisecond / 80 / time.Nanosecond) // ~1gbit/s
)
type rateJuggler struct {
current uint64
nextByteCount uint64
nextStartTime int64
changing int32
}
var WintunTunnelType = "Clash"
var WintunStaticRequestedGUID *windows.GUID
//go:linkname procyield runtime.procyield
func procyield(cycles uint32)
//go:linkname nanotime runtime.nanotime
func nanotime() int64
func (tun *tunWindows) Close() error {
var err error
tun.closeOnce.Do(func() {
atomic.StoreInt32(&tun.close, 1)
windows.SetEvent(tun.readWait)
//tun.running.Wait()
tun.session.End()
if tun.wt != nil {
err = tun.wt.Close()
}
})
return err
}
func (tun *tunWindows) MTU() (int, error) {
return tun.forcedMTU, nil
}
// TODO: This is a temporary hack. We really need to be monitoring the interface in real time and adapting to MTU changes.
func (tun *tunWindows) ForceMTU(mtu int) {
tun.forcedMTU = mtu
}
// Note: Read() and Write() assume the caller comes only from a single thread; there's no locking.
func (tun *tunWindows) Read0(buff []byte, offset int) (int, error) {
tun.running.Add(1)
defer tun.running.Done()
retry:
if atomic.LoadInt32(&tun.close) == 1 {
return 0, os.ErrClosed
}
start := nanotime()
shouldSpin := atomic.LoadUint64(&tun.rate.current) >= spinloopRateThreshold && uint64(start-atomic.LoadInt64(&tun.rate.nextStartTime)) <= rateMeasurementGranularity*2
for {
if atomic.LoadInt32(&tun.close) == 1 {
return 0, os.ErrClosed
}
packet, err := tun.session.ReceivePacket()
switch err {
case nil:
packetSize := len(packet)
copy(buff[offset:], packet)
tun.session.ReleaseReceivePacket(packet)
tun.rate.update(uint64(packetSize))
return packetSize, nil
case windows.ERROR_NO_MORE_ITEMS:
if !shouldSpin || uint64(nanotime()-start) >= spinloopDuration {
windows.WaitForSingleObject(tun.readWait, windows.INFINITE)
goto retry
}
procyield(1)
continue
case windows.ERROR_HANDLE_EOF:
return 0, os.ErrClosed
case windows.ERROR_INVALID_DATA:
return 0, errors.New("Send ring corrupt")
}
return 0, fmt.Errorf("Read failed: %w", err)
}
}
func (tun *tunWindows) Flush() error {
return nil
}
func (tun *tunWindows) Write0(buff []byte, offset int) (int, error) {
tun.running.Add(1)
defer tun.running.Done()
if atomic.LoadInt32(&tun.close) == 1 {
return 0, os.ErrClosed
}
packetSize := len(buff) - offset
tun.rate.update(uint64(packetSize))
packet, err := tun.session.AllocateSendPacket(packetSize)
if err == nil {
copy(packet, buff[offset:])
tun.session.SendPacket(packet)
return packetSize, nil
}
switch err {
case windows.ERROR_HANDLE_EOF:
return 0, os.ErrClosed
case windows.ERROR_BUFFER_OVERFLOW:
return 0, nil // Dropping when ring is full.
}
return 0, fmt.Errorf("Write failed: %w", err)
}
// LUID returns Windows interface instance ID.
func (tun *tunWindows) LUID() uint64 {
tun.running.Add(1)
defer tun.running.Done()
if atomic.LoadInt32(&tun.close) == 1 {
return 0
}
return tun.wt.LUID()
}
// RunningVersion returns the running version of the Wintun driver.
func (tun *tunWindows) RunningVersion() (version uint32, err error) {
return wintun.RunningVersion()
}
func (rate *rateJuggler) update(packetLen uint64) {
now := nanotime()
total := atomic.AddUint64(&rate.nextByteCount, packetLen)
period := uint64(now - atomic.LoadInt64(&rate.nextStartTime))
if period >= rateMeasurementGranularity {
if !atomic.CompareAndSwapInt32(&rate.changing, 0, 1) {
return
}
atomic.StoreInt64(&rate.nextStartTime, now)
atomic.StoreUint64(&rate.current, total*uint64(time.Second/time.Nanosecond)/period)
atomic.StoreUint64(&rate.nextByteCount, 0)
atomic.StoreInt32(&rate.changing, 0)
}
}

View File

@ -0,0 +1,398 @@
//go:build windows
// +build windows
package dev
import (
"bytes"
"errors"
"fmt"
"net"
"sort"
"sync"
"sync/atomic"
"time"
"github.com/Dreamacro/clash/listener/tun/dev/wintun"
"github.com/Dreamacro/clash/log"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
const messageTransportHeaderSize = 0 // size of data preceding content in transport message
type tunWindows struct {
wt *wintun.Adapter
handle windows.Handle
close int32
running sync.WaitGroup
forcedMTU int
rate rateJuggler
session wintun.Session
readWait windows.Handle
closeOnce sync.Once
url string
name string
tunAddress string
autoRoute bool
}
// OpenTunDevice return a TunDevice according a URL
func OpenTunDevice(tunAddress string, autoRoute bool) (TunDevice, error) {
requestedGUID, err := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}")
if err == nil {
WintunStaticRequestedGUID = &requestedGUID
log.Debugln("Generate GUID: %s", WintunStaticRequestedGUID.String())
} else {
log.Warnln("Error parese GUID from string: %v", err)
}
interfaceName := "Clash"
mtu := 9000
tun, err := CreateTUN(interfaceName, mtu, tunAddress, autoRoute)
if err != nil {
return nil, err
}
return tun, nil
}
//
// CreateTUN creates a Wintun interface with the given name. Should a Wintun
// interface with the same name exist, it is reused.
//
func CreateTUN(ifname string, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) {
return CreateTUNWithRequestedGUID(ifname, WintunStaticRequestedGUID, mtu, tunAddress, autoRoute)
}
//
// CreateTUNWithRequestedGUID creates a Wintun interface with the given name and
// a requested GUID. Should a Wintun interface with the same name exist, it is reused.
//
func CreateTUNWithRequestedGUID(ifname string, requestedGUID *windows.GUID, mtu int, tunAddress string, autoRoute bool) (TunDevice, error) {
wt, err := wintun.CreateAdapter(ifname, WintunTunnelType, requestedGUID)
if err != nil {
return nil, fmt.Errorf("Error creating interface: %w", err)
}
forcedMTU := 1420
if mtu > 0 {
forcedMTU = mtu
}
tun := &tunWindows{
name: ifname,
wt: wt,
handle: windows.InvalidHandle,
forcedMTU: forcedMTU,
tunAddress: tunAddress,
autoRoute: autoRoute,
}
// config tun ip
err = tun.configureInterface()
if err != nil {
tun.wt.Close()
return nil, fmt.Errorf("error configure interface: %w", err)
}
tun.session, err = wt.StartSession(0x800000) // Ring capacity, 8 MiB
if err != nil {
tun.wt.Close()
return nil, fmt.Errorf("error starting session: %w", err)
}
tun.readWait = tun.session.ReadWaitEvent()
return tun, nil
}
func (tun *tunWindows) Name() string {
return tun.name
}
func (tun *tunWindows) IsClose() bool {
return atomic.LoadInt32(&tun.close) == 1
}
func (tun *tunWindows) Read(buff []byte) (int, error) {
return tun.Read0(buff, messageTransportHeaderSize)
}
func (tun *tunWindows) Write(buff []byte) (int, error) {
return tun.Write0(buff, messageTransportHeaderSize)
}
func (tun *tunWindows) URL() string {
return fmt.Sprintf("dev://%s", tun.Name())
}
func (tun *tunWindows) configureInterface() error {
retryOnFailure := wintun.StartedAtBoot()
tryTimes := 0
startOver:
var err error
if tryTimes > 0 {
log.Infoln("Retrying interface configuration after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err)
time.Sleep(time.Second)
retryOnFailure = retryOnFailure && tryTimes < 15
}
tryTimes++
luid := winipcfg.LUID(tun.LUID())
log.Infoln("[wintun]: tun adapter LUID: %d", luid)
mtu, err := tun.MTU()
if err != nil {
return errors.New("unable to get device mtu")
}
family := winipcfg.AddressFamily(windows.AF_INET)
familyV6 := winipcfg.AddressFamily(windows.AF_INET6)
tunAddress := wintun.ParseIPCidr(tun.tunAddress + "/16")
addresses := []net.IPNet{tunAddress.IPNet()}
err = luid.FlushIPAddresses(familyV6)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
err = luid.FlushDNS(family)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
err = luid.FlushDNS(familyV6)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
err = luid.FlushRoutes(familyV6)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
foundDefault4 := false
foundDefault6 := false
if tun.autoRoute {
allowedIPs := []*wintun.IPCidr{
//wintun.ParseIPCidr("0.0.0.0/0"),
wintun.ParseIPCidr("1.0.0.0/8"),
wintun.ParseIPCidr("2.0.0.0/7"),
wintun.ParseIPCidr("4.0.0.0/6"),
wintun.ParseIPCidr("8.0.0.0/5"),
wintun.ParseIPCidr("16.0.0.0/4"),
wintun.ParseIPCidr("32.0.0.0/3"),
wintun.ParseIPCidr("64.0.0.0/2"),
wintun.ParseIPCidr("128.0.0.0/1"),
wintun.ParseIPCidr("224.0.0.0/4"),
wintun.ParseIPCidr("255.255.255.255/32"),
}
estimatedRouteCount := len(allowedIPs)
routes := make([]winipcfg.RouteData, 0, estimatedRouteCount)
var haveV4Address, haveV6Address bool = true, false
for _, allowedip := range allowedIPs {
allowedip.MaskSelf()
if (allowedip.Bits() == 32 && !haveV4Address) || (allowedip.Bits() == 128 && !haveV6Address) {
continue
}
route := winipcfg.RouteData{
Destination: allowedip.IPNet(),
Metric: 0,
}
if allowedip.Bits() == 32 {
if allowedip.Cidr == 0 {
foundDefault4 = true
}
route.NextHop = net.IPv4zero
} else if allowedip.Bits() == 128 {
if allowedip.Cidr == 0 {
foundDefault6 = true
}
route.NextHop = net.IPv6zero
}
routes = append(routes, route)
}
deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes))
sort.Slice(routes, func(i, j int) bool {
if routes[i].Metric != routes[j].Metric {
return routes[i].Metric < routes[j].Metric
}
if c := bytes.Compare(routes[i].NextHop, routes[j].NextHop); c != 0 {
return c < 0
}
if c := bytes.Compare(routes[i].Destination.IP, routes[j].Destination.IP); c != 0 {
return c < 0
}
if c := bytes.Compare(routes[i].Destination.Mask, routes[j].Destination.Mask); c != 0 {
return c < 0
}
return false
})
for i := 0; i < len(routes); i++ {
if i > 0 && routes[i].Metric == routes[i-1].Metric &&
bytes.Equal(routes[i].NextHop, routes[i-1].NextHop) &&
bytes.Equal(routes[i].Destination.IP, routes[i-1].Destination.IP) &&
bytes.Equal(routes[i].Destination.Mask, routes[i-1].Destination.Mask) {
continue
}
deduplicatedRoutes = append(deduplicatedRoutes, &routes[i])
}
err = luid.SetRoutesForFamily(family, deduplicatedRoutes)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set routes: %w", err)
}
}
err = luid.SetIPAddressesForFamily(family, addresses)
if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
cleanupAddressesOnDisconnectedInterfaces(family, addresses)
err = luid.SetIPAddressesForFamily(family, addresses)
}
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set ips: %w", err)
}
var ipif *winipcfg.MibIPInterfaceRow
ipif, err = luid.IPInterface(family)
if err != nil {
return err
}
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
ipif.DadTransmits = 0
ipif.ManagedAddressConfigurationSupported = false
ipif.OtherStatefulConfigurationSupported = false
if mtu > 0 {
ipif.NLMTU = uint32(mtu)
}
if (family == windows.AF_INET && foundDefault4) || (family == windows.AF_INET6 && foundDefault6) {
ipif.UseAutomaticMetric = false
ipif.Metric = 0
}
err = ipif.Set()
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set metric and MTU: %w", err)
}
var ipif6 *winipcfg.MibIPInterfaceRow
ipif6, err = luid.IPInterface(familyV6)
if err != nil {
return err
}
ipif6.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
ipif6.DadTransmits = 0
ipif6.ManagedAddressConfigurationSupported = false
ipif6.OtherStatefulConfigurationSupported = false
err = ipif6.Set()
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set v6 metric and MTU: %w", err)
}
err = luid.SetDNS(family, []net.IP{net.ParseIP("198.18.0.2")}, nil)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set DNS %s %s: %w", "198.18.0.2", "nil", err)
}
return nil
}
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []net.IPNet) {
if len(addresses) == 0 {
return
}
addrToStr := func(ip *net.IP) string {
if ip4 := ip.To4(); ip4 != nil {
return string(ip4)
}
return string(*ip)
}
addrHash := make(map[string]bool, len(addresses))
for i := range addresses {
addrHash[addrToStr(&addresses[i].IP)] = true
}
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagDefault)
if err != nil {
return
}
for _, iface := range interfaces {
if iface.OperStatus == winipcfg.IfOperStatusUp {
continue
}
for address := iface.FirstUnicastAddress; address != nil; address = address.Next {
ip := address.Address.IP()
if addrHash[addrToStr(&ip)] {
ipnet := net.IPNet{IP: ip, Mask: net.CIDRMask(int(address.OnLinkPrefixLength), 8*len(ip))}
log.Infoln("Cleaning up stale address %s from interface %s", ipnet.String(), iface.FriendlyName())
iface.LUID.DeleteIPAddress(ipnet)
}
}
}
}
// GetAutoDetectInterface get ethernet interface
func GetAutoDetectInterface() (string, error) {
ifname, err := getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET))
if err == nil {
return ifname, err
}
return getAutoDetectInterfaceByFamily(winipcfg.AddressFamily(windows.AF_INET6))
}
func getAutoDetectInterfaceByFamily(family winipcfg.AddressFamily) (string, error) {
interfaces, err := winipcfg.GetAdaptersAddresses(family, winipcfg.GAAFlagIncludeGateways)
if err != nil {
return "", fmt.Errorf("get ethernet interface failure. %w", err)
}
for _, iface := range interfaces {
if iface.OperStatus != winipcfg.IfOperStatusUp {
continue
}
ifname := iface.FriendlyName()
if ifname == "Clash" {
continue
}
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
nextHop := gatewayAddress.Address.IP()
var ipnet net.IPNet
if family == windows.AF_INET {
ipnet = net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}
} else {
ipnet = net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}
}
if _, err = iface.LUID.Route(ipnet, nextHop); err == nil {
return ifname, nil
}
}
}
return "", errors.New("ethernet interface not found")
}

View File

@ -0,0 +1,41 @@
//go:build windows
// +build windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2019-2021 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"errors"
"log"
"sync"
"time"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
)
var (
startedAtBoot bool
startedAtBootOnce sync.Once
)
func StartedAtBoot() bool {
startedAtBootOnce.Do(func() {
if isService, err := svc.IsWindowsService(); err == nil && !isService {
return
}
if reason, err := svc.DynamicStartReason(); err == nil {
startedAtBoot = (reason&svc.StartReasonAuto) != 0 || (reason&svc.StartReasonDelayedAuto) != 0
} else if errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
// TODO: Below this line is Windows 7 compatibility code, which hopefully we can delete at some point.
startedAtBoot = windows.DurationSinceBoot() < time.Minute*10
} else {
log.Printf("Unable to determine service start reason: %v", err)
}
})
return startedAtBoot
}

View File

@ -0,0 +1,57 @@
//go:build windows
// +build windows
package wintun
import (
"fmt"
"net"
"strconv"
"strings"
)
type IPCidr struct {
IP net.IP
Cidr uint8
}
func (r *IPCidr) String() string {
return fmt.Sprintf("%s/%d", r.IP.String(), r.Cidr)
}
func (r *IPCidr) Bits() uint8 {
if r.IP.To4() != nil {
return 32
}
return 128
}
func (r *IPCidr) IPNet() net.IPNet {
return net.IPNet{
IP: r.IP,
Mask: net.CIDRMask(int(r.Cidr), int(r.Bits())),
}
}
func (r *IPCidr) MaskSelf() {
bits := int(r.Bits())
mask := net.CIDRMask(int(r.Cidr), bits)
for i := 0; i < bits/8; i++ {
r.IP[i] &= mask[i]
}
}
func ParseIPCidr(ipcidr string) *IPCidr {
s := strings.Split(ipcidr, "/")
if len(s) != 2 {
return nil
}
cidr, err := strconv.Atoi(s[1])
if err != nil {
return nil
}
return &IPCidr{
IP: net.ParseIP(s[0]),
Cidr: uint8(cidr),
}
}

View File

@ -0,0 +1,130 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
C "github.com/Dreamacro/clash/constant"
"golang.org/x/sys/windows"
)
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
return &lazyDLL{Name: name, onLoad: onLoad}
}
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 %v DLL: %w", p.dll.Name, err)
}
addr, err := p.nameToAddr()
if err != nil {
return fmt.Errorf("Error getting %v 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
}
type lazyDLL struct {
Name string
mu sync.Mutex
module windows.Handle
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 != 0 {
return nil
}
//const (
// LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
// LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
//)
//module, err := windows.LoadLibraryEx(d.Name, 0, LOAD_LIBRARY_SEARCH_APPLICATION_DIR|LOAD_LIBRARY_SEARCH_SYSTEM32)
module, err := windows.LoadLibraryEx(C.Path.GetAssetLocation(d.Name), 0, windows.LOAD_WITH_ALTERED_SEARCH_PATH)
if err != nil {
return fmt.Errorf("Unable to load library: %w", err)
}
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 windows.GetProcAddress(p.dll.module, p.Name)
}
// Version returns the version of the Wintun DLL.
func Version() string {
if modwintun.Load() != nil {
return "unknown"
}
resInfo, err := windows.FindResource(modwintun.module, windows.ResourceID(1), windows.RT_VERSION)
if err != nil {
return "unknown"
}
data, err := windows.LoadResourceData(modwintun.module, resInfo)
if err != nil {
return "unknown"
}
var fixedInfo *windows.VS_FIXEDFILEINFO
fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
err = windows.VerQueryValue(unsafe.Pointer(&data[0]), `\`, unsafe.Pointer(&fixedInfo), &fixedInfoLen)
if err != nil {
return "unknown"
}
version := fmt.Sprintf("%d.%d", (fixedInfo.FileVersionMS>>16)&0xff, (fixedInfo.FileVersionMS>>0)&0xff)
if nextNibble := (fixedInfo.FileVersionLS >> 16) & 0xff; nextNibble != 0 {
version += fmt.Sprintf(".%d", nextNibble)
}
if nextNibble := (fixedInfo.FileVersionLS >> 0) & 0xff; nextNibble != 0 {
version += fmt.Sprintf(".%d", nextNibble)
}
return version
}

View File

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

View File

@ -0,0 +1,90 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type Session struct {
handle uintptr
}
const (
PacketSizeMax = 0xffff // Maximum packet size
RingCapacityMin = 0x20000 // Minimum ring capacity (128 kiB)
RingCapacityMax = 0x4000000 // Maximum ring capacity (64 MiB)
)
// Packet with data
type Packet struct {
Next *Packet // Pointer to next packet in queue
Size uint32 // Size of packet (max WINTUN_MAX_IP_PACKET_SIZE)
Data *[PacketSizeMax]byte // Pointer to layer 3 IPv4 or IPv6 packet
}
var (
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 (wintun *Adapter) StartSession(capacity uint32) (session Session, err error) {
r0, _, e1 := syscall.Syscall(procWintunStartSession.Addr(), 2, uintptr(wintun.handle), uintptr(capacity), 0)
if r0 == 0 {
err = e1
} else {
session = Session{r0}
}
return
}
func (session Session) End() {
syscall.Syscall(procWintunEndSession.Addr(), 1, session.handle, 0, 0)
session.handle = 0
}
func (session Session) ReadWaitEvent() (handle windows.Handle) {
r0, _, _ := syscall.Syscall(procWintunGetReadWaitEvent.Addr(), 1, session.handle, 0, 0)
handle = windows.Handle(r0)
return
}
func (session Session) ReceivePacket() (packet []byte, err error) {
var packetSize uint32
r0, _, e1 := syscall.Syscall(procWintunReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packetSize)), 0)
if r0 == 0 {
err = e1
return
}
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
return
}
func (session Session) ReleaseReceivePacket(packet []byte) {
syscall.Syscall(procWintunReleaseReceivePacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
}
func (session Session) AllocateSendPacket(packetSize int) (packet []byte, err error) {
r0, _, e1 := syscall.Syscall(procWintunAllocateSendPacket.Addr(), 2, session.handle, uintptr(packetSize), 0)
if r0 == 0 {
err = e1
return
}
packet = unsafe.Slice((*byte)(unsafe.Pointer(r0)), packetSize)
return
}
func (session Session) SendPacket(packet []byte) {
syscall.Syscall(procWintunSendPacket.Addr(), 2, session.handle, uintptr(unsafe.Pointer(&packet[0])), 0)
}

View File

@ -0,0 +1,153 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"runtime"
"syscall"
"unsafe"
"github.com/Dreamacro/clash/log"
"golang.org/x/sys/windows"
)
type loggerLevel int
const (
logInfo loggerLevel = iota
logWarn
logErr
)
const AdapterNameMax = 128
type Adapter struct {
handle uintptr
}
var (
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")
)
func logMessage(level loggerLevel, _ uint64, msg *uint16) int {
var lv log.LogLevel
switch level {
case logInfo:
lv = log.INFO
case logWarn:
lv = log.WARNING
case logErr:
lv = log.ERROR
default:
lv = log.INFO
}
log.PrintLog(lv, "[Wintun] %s", windows.UTF16PtrToString(msg))
return 0
}
func setupLogger(dll *lazyDLL) {
var callback uintptr
if runtime.GOARCH == "386" {
callback = windows.NewCallback(func(level loggerLevel, timestampLow, timestampHigh uint32, msg *uint16) int {
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
})
} else if runtime.GOARCH == "arm" {
callback = windows.NewCallback(func(level loggerLevel, _, timestampLow, timestampHigh uint32, msg *uint16) int {
return logMessage(level, uint64(timestampHigh)<<32|uint64(timestampLow), msg)
})
} else if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
callback = windows.NewCallback(logMessage)
}
syscall.Syscall(dll.NewProc("WintunSetLogger").Addr(), 1, callback, 0, 0)
}
func closeAdapter(wintun *Adapter) {
syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
}
// CreateAdapter creates a Wintun adapter. name is the cosmetic name of the adapter.
// tunnelType represents the type of adapter and should be "Wintun". requestedGUID is
// the GUID of the created network adapter, which then influences NLA generation
// deterministically. If it is set to nil, the GUID is chosen by the system at random,
// and hence a new NLA entry is created for each new adapter.
func CreateAdapter(name string, tunnelType string, requestedGUID *windows.GUID) (wintun *Adapter, err error) {
var name16 *uint16
name16, err = windows.UTF16PtrFromString(name)
if err != nil {
return
}
var tunnelType16 *uint16
tunnelType16, err = windows.UTF16PtrFromString(tunnelType)
if err != nil {
return
}
r0, _, e1 := syscall.Syscall(procWintunCreateAdapter.Addr(), 3, uintptr(unsafe.Pointer(name16)), uintptr(unsafe.Pointer(tunnelType16)), uintptr(unsafe.Pointer(requestedGUID)))
if r0 == 0 {
err = e1
return
}
wintun = &Adapter{handle: r0}
runtime.SetFinalizer(wintun, closeAdapter)
return
}
// OpenAdapter opens an existing Wintun adapter by name.
func OpenAdapter(name string) (wintun *Adapter, err error) {
var name16 *uint16
name16, err = windows.UTF16PtrFromString(name)
if err != nil {
return
}
r0, _, e1 := syscall.Syscall(procWintunOpenAdapter.Addr(), 1, uintptr(unsafe.Pointer(name16)), 0, 0)
if r0 == 0 {
err = e1
return
}
wintun = &Adapter{handle: r0}
runtime.SetFinalizer(wintun, closeAdapter)
return
}
// Close closes a Wintun adapter.
func (wintun *Adapter) Close() (err error) {
runtime.SetFinalizer(wintun, nil)
r1, _, e1 := syscall.Syscall(procWintunCloseAdapter.Addr(), 1, wintun.handle, 0, 0)
if r1 == 0 {
err = e1
}
return
}
// Uninstall removes the driver from the system if no drivers are currently in use.
func Uninstall() (err error) {
r1, _, e1 := syscall.Syscall(procWintunDeleteDriver.Addr(), 0, 0, 0, 0)
if r1 == 0 {
err = e1
}
return
}
// RunningVersion returns the version of the loaded driver.
func RunningVersion() (version uint32, err error) {
r0, _, e1 := syscall.Syscall(procWintunGetRunningDriverVersion.Addr(), 0, 0, 0, 0)
version = uint32(r0)
if version == 0 {
err = e1
}
return
}
// LUID returns the LUID of the adapter.
func (wintun *Adapter) LUID() (luid uint64) {
syscall.Syscall(procWintunGetAdapterLUID.Addr(), 2, uintptr(wintun.handle), uintptr(unsafe.Pointer(&luid)), 0)
return
}

View File

@ -0,0 +1,30 @@
package commons
import (
"github.com/Dreamacro/clash/component/resolver"
D "github.com/miekg/dns"
)
func RelayDnsPacket(payload []byte) ([]byte, error) {
msg := &D.Msg{}
if err := msg.Unpack(payload); err != nil {
return nil, err
}
r, err := resolver.ServeMsg(msg)
if err != nil {
return nil, err
}
for _, ans := range r.Answer {
header := ans.Header()
if header.Class == D.ClassINET && (header.Rrtype == D.TypeA || header.Rrtype == D.TypeAAAA) {
header.Ttl = 1
}
}
r.SetRcode(msg, r.Rcode)
r.Compress = true
return r.Pack()
}

View File

@ -0,0 +1,263 @@
package gvisor
import (
"encoding/binary"
"errors"
"fmt"
"net"
"strings"
"sync"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/listener/tun/dev"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
"gvisor.dev/gvisor/pkg/waiter"
)
const nicID tcpip.NICID = 1
type gvisorAdapter struct {
device dev.TunDevice
ipstack *stack.Stack
dnsserver *DNSServer
udpIn chan<- *inbound.PacketAdapter
stackName string
autoRoute bool
linkCache *channel.Endpoint
wg sync.WaitGroup // wait for goroutines to stop
writeHandle *channel.NotificationHandle
}
// GvisorAdapter create GvisorAdapter
func NewAdapter(device dev.TunDevice, conf config.Tun, tunAddress string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
ipstack := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
})
adapter := &gvisorAdapter{
device: device,
ipstack: ipstack,
udpIn: udpIn,
stackName: conf.Stack,
autoRoute: conf.AutoRoute,
}
linkEP, err := adapter.AsLinkEndpoint()
if err != nil {
return nil, fmt.Errorf("unable to create virtual endpoint: %v", err)
}
if err := ipstack.CreateNIC(nicID, linkEP); err != nil {
return nil, fmt.Errorf("fail to create NIC in ipstack: %v", err)
}
ipstack.SetPromiscuousMode(nicID, true) // Accept all the traffice from this NIC
ipstack.SetSpoofing(nicID, true) // Otherwise our TCP connection can not find the route backward
// Add route for ipv4 & ipv6
// So FindRoute will return correct route to tun NIC
subnet, _ := tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 4)), tcpip.AddressMask(strings.Repeat("\x00", 4)))
ipstack.AddRoute(tcpip.Route{Destination: subnet, Gateway: "", NIC: nicID})
subnet, _ = tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 16)), tcpip.AddressMask(strings.Repeat("\x00", 16)))
ipstack.AddRoute(tcpip.Route{Destination: subnet, Gateway: "", NIC: nicID})
// TCP handler
// maximum number of half-open tcp connection set to 1024
// receive buffer size set to 20k
tcpFwd := tcp.NewForwarder(ipstack, pool.RelayBufferSize, 1024, func(r *tcp.ForwarderRequest) {
var wq waiter.Queue
ep, err := r.CreateEndpoint(&wq)
if err != nil {
log.Warnln("Can't create TCP Endpoint in ipstack: %v", err)
r.Complete(true)
return
}
r.Complete(false)
conn := gonet.NewTCPConn(&wq, ep)
// if the endpoint is not in connected state, conn.RemoteAddr() will return nil
// this protection may be not enough, but will help us debug the panic
if conn.RemoteAddr() == nil {
log.Warnln("TCP endpoint is not connected, current state: %v", tcp.EndpointState(ep.State()))
conn.Close()
return
}
target := getAddr(ep.Info().(*stack.TransportEndpointInfo).ID)
tcpIn <- inbound.NewSocket(target, conn, C.TUN)
})
ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpFwd.HandlePacket)
// UDP handler
ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, adapter.udpHandlePacket)
if resolver.DefaultResolver != nil {
err = adapter.ReCreateDNSServer(resolver.DefaultResolver.(*dns.Resolver), resolver.DefaultHostMapper.(*dns.ResolverEnhancer), conf.DNSListen)
if err != nil {
return nil, err
}
}
return adapter, nil
}
func (t *gvisorAdapter) Stack() string {
return t.stackName
}
func (t *gvisorAdapter) AutoRoute() bool {
return t.autoRoute
}
// Close close the TunAdapter
func (t *gvisorAdapter) Close() {
if t.dnsserver != nil {
t.dnsserver.Stop()
}
if t.ipstack != nil {
t.ipstack.Close()
}
if t.device != nil {
_ = t.device.Close()
}
}
func (t *gvisorAdapter) udpHandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
// ref: gvisor pkg/tcpip/transport/udp/endpoint.go HandlePacket
hdr := header.UDP(pkt.TransportHeader().View())
if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize {
// Malformed packet.
t.ipstack.Stats().UDP.MalformedPacketsReceived.Increment()
return true
}
target := getAddr(id)
packet := &fakeConn{
id: id,
pkt: pkt,
s: t.ipstack,
payload: pkt.Data().AsRange().ToOwnedView(),
}
select {
case t.udpIn <- inbound.NewPacket(target, packet, C.TUN):
default:
}
return true
}
// Wait wait goroutines to exit
func (t *gvisorAdapter) Wait() {
t.wg.Wait()
}
func (t *gvisorAdapter) AsLinkEndpoint() (result stack.LinkEndpoint, err error) {
if t.linkCache != nil {
return t.linkCache, nil
}
mtu, err := t.device.MTU()
if err != nil {
return nil, errors.New("unable to get device mtu")
}
linkEP := channel.New(512, uint32(mtu), "")
// start Read loop. read ip packet from tun and write it to ipstack
t.wg.Add(1)
go func() {
for !t.device.IsClose() {
packet := make([]byte, mtu)
n, err := t.device.Read(packet)
if err != nil && !t.device.IsClose() {
log.Errorln("can not read from tun: %v", err)
continue
}
var p tcpip.NetworkProtocolNumber
switch header.IPVersion(packet) {
case header.IPv4Version:
p = header.IPv4ProtocolNumber
case header.IPv6Version:
p = header.IPv6ProtocolNumber
}
if linkEP.IsAttached() {
linkEP.InjectInbound(p, stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.View(packet[:n]).ToVectorisedView(),
}))
} else {
log.Debugln("received packet from tun when %s is not attached to any dispatcher.", t.device.Name())
}
}
t.wg.Done()
t.Close()
log.Debugln("%v stop read loop", t.device.Name())
}()
// start write notification
t.writeHandle = linkEP.AddNotify(t)
t.linkCache = linkEP
return t.linkCache, nil
}
// WriteNotify implements channel.Notification.WriteNotify.
func (t *gvisorAdapter) WriteNotify() {
packet, ok := t.linkCache.Read()
if ok {
var vv buffer.VectorisedView
// Append upper headers.
vv.AppendView(packet.Pkt.NetworkHeader().View())
vv.AppendView(packet.Pkt.TransportHeader().View())
// Append data payload.
vv.Append(packet.Pkt.Data().ExtractVV())
_, err := t.device.Write(vv.ToView())
if err != nil && !t.device.IsClose() {
log.Errorln("can not write to tun: %v", err)
}
}
}
func getAddr(id stack.TransportEndpointID) socks5.Addr {
ipv4 := id.LocalAddress.To4()
// get the big-endian binary represent of port
port := make([]byte, 2)
binary.BigEndian.PutUint16(port, id.LocalPort)
if ipv4 != "" {
addr := make([]byte, 1+net.IPv4len+2)
addr[0] = socks5.AtypIPv4
copy(addr[1:1+net.IPv4len], []byte(ipv4))
addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1]
return addr
} else {
addr := make([]byte, 1+net.IPv6len+2)
addr[0] = socks5.AtypIPv6
copy(addr[1:1+net.IPv6len], []byte(id.LocalAddress))
addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1]
return addr
}
}

View File

@ -0,0 +1,291 @@
package gvisor
import (
"fmt"
"net"
"github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/log"
D "github.com/miekg/dns"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/ports"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
var (
ipv4Zero = tcpip.Address(net.IPv4zero.To4())
ipv6Zero = tcpip.Address(net.IPv6zero.To16())
)
// DNSServer is DNS Server listening on tun devcice
type DNSServer struct {
*dns.Server
resolver *dns.Resolver
stack *stack.Stack
tcpListener net.Listener
udpEndpoint *dnsEndpoint
udpEndpointID *stack.TransportEndpointID
tcpip.NICID
}
// dnsEndpoint is a TransportEndpoint that will register to stack
type dnsEndpoint struct {
stack.TransportEndpoint
stack *stack.Stack
uniqueID uint64
server *dns.Server
}
// Keep track of the source of DNS request
type dnsResponseWriter struct {
s *stack.Stack
pkt *stack.PacketBuffer // The request packet
id stack.TransportEndpointID
}
func (e *dnsEndpoint) UniqueID() uint64 {
return e.uniqueID
}
func (e *dnsEndpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) {
hdr := header.UDP(pkt.TransportHeader().View())
if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize {
// Malformed packet.
e.stack.Stats().UDP.MalformedPacketsReceived.Increment()
return
}
// server DNS
var msg D.Msg
msg.Unpack(pkt.Data().AsRange().ToOwnedView())
writer := dnsResponseWriter{s: e.stack, pkt: pkt, id: id}
go e.server.ServeDNS(&writer, &msg)
}
func (e *dnsEndpoint) Close() {
}
func (e *dnsEndpoint) Wait() {
}
func (e *dnsEndpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) {
log.Warnln("DNS endpoint get a transport error: %v", transErr)
log.Debugln("DNS endpoint transport error packet : %v", pkt)
}
// Abort implements stack.TransportEndpoint.Abort.
func (e *dnsEndpoint) Abort() {
e.Close()
}
func (w *dnsResponseWriter) LocalAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(w.id.LocalAddress), Port: int(w.id.LocalPort)}
}
func (w *dnsResponseWriter) RemoteAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(w.id.RemoteAddress), Port: int(w.id.RemotePort)}
}
func (w *dnsResponseWriter) WriteMsg(msg *D.Msg) error {
b, err := msg.Pack()
if err != nil {
return err
}
_, err = w.Write(b)
return err
}
func (w *dnsResponseWriter) TsigStatus() error {
// Unsupported
return nil
}
func (w *dnsResponseWriter) TsigTimersOnly(bool) {
// Unsupported
}
func (w *dnsResponseWriter) Hijack() {
// Unsupported
}
func (w *dnsResponseWriter) Write(b []byte) (int, error) {
v := buffer.NewView(len(b))
copy(v, b)
data := v.ToVectorisedView()
// w.id.LocalAddress is the source ip of DNS response
r, _ := w.s.FindRoute(w.pkt.NICID, w.id.LocalAddress, w.id.RemoteAddress, w.pkt.NetworkProtocolNumber, false /* multicastLoop */)
return writeUDP(r, data, w.id.LocalPort, w.id.RemotePort)
}
func (w *dnsResponseWriter) Close() error {
return nil
}
// CreateDNSServer create a dns server on given netstack
func CreateDNSServer(s *stack.Stack, resolver *dns.Resolver, mapper *dns.ResolverEnhancer, ip net.IP, port int, nicID tcpip.NICID) (*DNSServer, error) {
var v4 bool
var err error
address := tcpip.FullAddress{NIC: nicID, Port: uint16(port)}
var protocol tcpip.NetworkProtocolNumber
if ip.To4() != nil {
v4 = true
address.Addr = tcpip.Address(ip.To4())
protocol = ipv4.ProtocolNumber
} else {
v4 = false
address.Addr = tcpip.Address(ip.To16())
protocol = ipv6.ProtocolNumber
}
protocolAddr := tcpip.ProtocolAddress{
Protocol: protocol,
AddressWithPrefix: address.Addr.WithPrefix(),
}
// netstack will only reassemble IP fragments when its' dest ip address is registered in NIC.endpoints
if err := s.AddProtocolAddress(nicID, protocolAddr, stack.AddressProperties{}); err != nil {
log.Errorln("AddProtocolAddress(%d, %+v, {}): %s", nicID, protocolAddr, err)
}
if address.Addr == ipv4Zero || address.Addr == ipv6Zero {
address.Addr = ""
}
handler := dns.NewHandler(resolver, mapper)
serverIn := &dns.Server{}
serverIn.SetHandler(handler)
// UDP DNS
id := &stack.TransportEndpointID{
LocalAddress: address.Addr,
LocalPort: uint16(port),
RemotePort: 0,
RemoteAddress: "",
}
// TransportEndpoint for DNS
endpoint := &dnsEndpoint{
stack: s,
uniqueID: s.UniqueID(),
server: serverIn,
}
if tcpiperr := s.RegisterTransportEndpoint(
[]tcpip.NetworkProtocolNumber{
ipv4.ProtocolNumber,
ipv6.ProtocolNumber,
},
udp.ProtocolNumber,
*id,
endpoint,
ports.Flags{LoadBalanced: true}, // it's actually the SO_REUSEPORT. Not sure it take effect.
nicID); tcpiperr != nil {
log.Errorln("Unable to start UDP DNS on tun: %v", tcpiperr.String())
}
// TCP DNS
var tcpListener net.Listener
if v4 {
tcpListener, err = gonet.ListenTCP(s, address, ipv4.ProtocolNumber)
} else {
tcpListener, err = gonet.ListenTCP(s, address, ipv6.ProtocolNumber)
}
if err != nil {
return nil, fmt.Errorf("can not listen on tun: %v", err)
}
server := &DNSServer{
Server: serverIn,
resolver: resolver,
stack: s,
tcpListener: tcpListener,
udpEndpoint: endpoint,
udpEndpointID: id,
NICID: nicID,
}
server.SetHandler(handler)
server.Server.Server = &D.Server{Listener: tcpListener, Handler: server}
go func() {
server.ActivateAndServe()
}()
return server, err
}
// Stop stop the DNS Server on tun
func (s *DNSServer) Stop() {
// shutdown TCP DNS Server
s.Server.Shutdown()
// remove TCP endpoint from stack
if s.Listener != nil {
s.Listener.Close()
}
// remove udp endpoint from stack
s.stack.UnregisterTransportEndpoint(
[]tcpip.NetworkProtocolNumber{
ipv4.ProtocolNumber,
ipv6.ProtocolNumber,
},
udp.ProtocolNumber,
*s.udpEndpointID,
s.udpEndpoint,
ports.Flags{LoadBalanced: true}, // should match the RegisterTransportEndpoint
s.NICID)
}
// DNSListen return the listening address of DNS Server
func (t *gvisorAdapter) DNSListen() string {
if t.dnsserver != nil {
id := t.dnsserver.udpEndpointID
return fmt.Sprintf("%s:%d", id.LocalAddress.String(), id.LocalPort)
}
return ""
}
// Stop stop the DNS Server on tun
func (t *gvisorAdapter) ReCreateDNSServer(resolver *dns.Resolver, mapper *dns.ResolverEnhancer, addr string) error {
if addr == "" && t.dnsserver == nil {
return nil
}
if addr == t.DNSListen() && t.dnsserver != nil && t.dnsserver.resolver == resolver {
return nil
}
if t.dnsserver != nil {
t.dnsserver.Stop()
t.dnsserver = nil
log.Debugln("tun DNS server stoped")
}
var err error
_, port, err := net.SplitHostPort(addr)
if port == "0" || port == "" || err != nil {
return nil
}
if resolver == nil {
return fmt.Errorf("failed to create DNS server on tun: resolver not provided")
}
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return err
}
server, err := CreateDNSServer(t.ipstack, resolver, mapper, udpAddr.IP, udpAddr.Port, nicID)
if err != nil {
return err
}
t.dnsserver = server
log.Infoln("Tun DNS server listening at: %s, fake ip enabled: %v", addr, mapper.FakeIPEnabled())
return nil
}

View File

@ -0,0 +1,108 @@
package gvisor
import (
"fmt"
"net"
"github.com/Dreamacro/clash/component/resolver"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
type fakeConn struct {
id stack.TransportEndpointID // The endpoint of incomming packet, it's remote address is the source address it sent from
pkt *stack.PacketBuffer // The original packet comming from tun
s *stack.Stack
payload []byte
fakeip *bool
}
func (c *fakeConn) Data() []byte {
return c.payload
}
func (c *fakeConn) WriteBack(b []byte, addr net.Addr) (n int, err error) {
v := buffer.View(b)
data := v.ToVectorisedView()
var localAddress tcpip.Address
var localPort uint16
// if addr is not provided, write back use original dst Addr as src Addr
if c.FakeIP() || addr == nil {
localAddress = c.id.LocalAddress
localPort = c.id.LocalPort
} else {
udpaddr, _ := addr.(*net.UDPAddr)
localAddress = tcpip.Address(udpaddr.IP)
localPort = uint16(udpaddr.Port)
}
r, _ := c.s.FindRoute(c.pkt.NICID, localAddress, c.id.RemoteAddress, c.pkt.NetworkProtocolNumber, false /* multicastLoop */)
return writeUDP(r, data, localPort, c.id.RemotePort)
}
func (c *fakeConn) LocalAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(c.id.RemoteAddress), Port: int(c.id.RemotePort)}
}
func (c *fakeConn) Close() error {
return nil
}
func (c *fakeConn) Drop() {
}
func (c *fakeConn) FakeIP() bool {
if c.fakeip != nil {
return *c.fakeip
}
fakeip := resolver.IsFakeIP(net.IP(c.id.LocalAddress.To4()))
c.fakeip = &fakeip
return fakeip
}
func writeUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16) (int, error) {
const protocol = udp.ProtocolNumber
// Allocate a buffer for the UDP header.
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()),
Data: data,
})
// Initialize the header.
udp := header.UDP(pkt.TransportHeader().Push(header.UDPMinimumSize))
length := uint16(pkt.Size())
udp.Encode(&header.UDPFields{
SrcPort: localPort,
DstPort: remotePort,
Length: length,
})
// Set the checksum field unless TX checksum offload is enabled.
// On IPv4, UDP checksum is optional, and a zero value indicates the
// transmitter skipped the checksum generation (RFC768).
// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1).
if r.RequiresTXTransportChecksum() {
xsum := r.PseudoHeaderChecksum(protocol, length)
for _, v := range data.Views() {
xsum = header.Checksum(v, xsum)
}
udp.SetChecksum(^udp.CalculateChecksum(xsum))
}
ttl := r.DefaultTTL()
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: protocol, TTL: ttl, TOS: 0 /* default */}, pkt); err != nil {
r.Stats().UDP.PacketSendErrors.Increment()
return 0, fmt.Errorf("%v", err)
}
// Track count of packets sent.
r.Stats().UDP.PacketsSent.Increment()
return data.Size(), nil
}

View File

@ -0,0 +1,87 @@
package lwip
import (
"encoding/binary"
"io"
"net"
"time"
"github.com/Dreamacro/clash/component/resolver"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/log"
"github.com/yaling888/go-lwip"
)
const defaultDnsReadTimeout = time.Second * 8
func shouldHijackDns(dnsIP net.IP, targetIp net.IP, targetPort int) bool {
if targetPort != 53 {
return false
}
return dnsIP.Equal(net.IPv4zero) || dnsIP.Equal(targetIp)
}
func hijackUDPDns(conn golwip.UDPConn, pkt []byte, addr *net.UDPAddr) {
go func() {
defer func(conn golwip.UDPConn) {
_ = conn.Close()
}(conn)
answer, err := D.RelayDnsPacket(pkt)
if err != nil {
return
}
_, _ = conn.WriteFrom(answer, addr)
}()
}
func hijackTCPDns(conn net.Conn) {
go func() {
defer func(conn net.Conn) {
_ = conn.Close()
}(conn)
if err := conn.SetDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
return
}
for {
var length uint16
if binary.Read(conn, binary.BigEndian, &length) != nil {
return
}
data := make([]byte, length)
_, err := io.ReadFull(conn, data)
if err != nil {
return
}
rb, err := D.RelayDnsPacket(data)
if err != nil {
continue
}
if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
return
}
if _, err = conn.Write(rb); err != nil {
return
}
}
}()
}
type dnsHandler struct{}
func newDnsHandler() golwip.DnsHandler {
return &dnsHandler{}
}
func (d dnsHandler) ResolveIP(host string) (net.IP, error) {
log.Debugln("[TUN] lwip resolve ip for host: %s", host)
return resolver.ResolveIP(host)
}

View File

@ -0,0 +1,61 @@
package lwip
import (
"net"
"strconv"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
"github.com/Dreamacro/clash/log"
"github.com/yaling888/go-lwip"
)
type tcpHandler struct {
dnsIP net.IP
tcpIn chan<- C.ConnContext
}
func newTCPHandler(dnsIP net.IP, tcpIn chan<- C.ConnContext) golwip.TCPConnHandler {
return &tcpHandler{dnsIP, tcpIn}
}
func (h *tcpHandler) Handle(conn net.Conn, target *net.TCPAddr) error {
if shouldHijackDns(h.dnsIP, target.IP, target.Port) {
hijackTCPDns(conn)
log.Debugln("[TUN] hijack dns tcp: %s:%d", target.IP.String(), target.Port)
return nil
}
if conn.RemoteAddr() == nil {
_ = conn.Close()
return nil
}
src, _ := conn.LocalAddr().(*net.TCPAddr)
dst, _ := conn.RemoteAddr().(*net.TCPAddr)
addrType := C.AtypIPv4
if dst.IP.To4() == nil {
addrType = C.AtypIPv6
}
metadata := &C.Metadata{
NetWork: C.TCP,
Type: C.TUN,
SrcIP: src.IP,
DstIP: dst.IP,
SrcPort: strconv.Itoa(src.Port),
DstPort: strconv.Itoa(dst.Port),
AddrType: addrType,
Host: "",
}
go func(conn net.Conn, metadata *C.Metadata) {
//if c, ok := conn.(*net.TCPConn); ok {
// c.SetKeepAlive(true)
//}
h.tcpIn <- context.NewConnContext(conn, metadata)
}(conn, metadata)
return nil
}

View File

@ -0,0 +1,121 @@
package lwip
import (
"io"
"net"
"sync"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/dev"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/log"
"github.com/yaling888/go-lwip"
)
type lwipAdapter struct {
device dev.TunDevice
lwipStack golwip.LWIPStack
lock sync.Mutex
mtu int
stackName string
dnsListen string
autoRoute bool
}
func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
adapter := &lwipAdapter{
device: device,
mtu: mtu,
stackName: conf.Stack,
dnsListen: conf.DNSListen,
autoRoute: conf.AutoRoute,
}
adapter.lock.Lock()
defer adapter.lock.Unlock()
dnsHost, _, err := net.SplitHostPort(conf.DNSListen)
if err != nil {
return nil, err
}
dnsIP := net.ParseIP(dnsHost)
// Register output function, write packets from lwip stack to tun device
golwip.RegisterOutputFn(func(data []byte) (int, error) {
return device.Write(data)
})
// Set custom buffer pool
golwip.SetPoolAllocator(newLWIPPool())
// Setup TCP/IP stack.
lwipStack, err := golwip.NewLWIPStack(mtu)
if err != nil {
return nil, err
}
adapter.lwipStack = lwipStack
golwip.RegisterDnsHandler(newDnsHandler())
golwip.RegisterTCPConnHandler(newTCPHandler(dnsIP, tcpIn))
golwip.RegisterUDPConnHandler(newUDPHandler(dnsIP, udpIn))
// Copy packets from tun device to lwip stack, it's the loop.
go func(lwipStack golwip.LWIPStack, device dev.TunDevice, mtu int) {
_, err := io.CopyBuffer(lwipStack.(io.Writer), device, make([]byte, mtu))
if err != nil {
log.Debugln("copying data failed: %v", err)
}
}(lwipStack, device, mtu)
return adapter, nil
}
func (l *lwipAdapter) Stack() string {
return l.stackName
}
func (l *lwipAdapter) AutoRoute() bool {
return l.autoRoute
}
func (l *lwipAdapter) DNSListen() string {
return l.dnsListen
}
func (l *lwipAdapter) Close() {
l.lock.Lock()
defer l.lock.Unlock()
l.stopLocked()
}
func (l *lwipAdapter) stopLocked() {
if l.lwipStack != nil {
_ = l.lwipStack.Close()
}
if l.device != nil {
_ = l.device.Close()
}
l.lwipStack = nil
l.device = nil
}
type lwipPool struct{}
func (p lwipPool) Get(size int) []byte {
return pool.Get(size)
}
func (p lwipPool) Put(buf []byte) error {
return pool.Put(buf)
}
func newLWIPPool() golwip.LWIPPool {
return &lwipPool{}
}

View File

@ -0,0 +1,74 @@
package lwip
import (
"io"
"net"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
"github.com/yaling888/go-lwip"
)
type udpPacket struct {
source *net.UDPAddr
payload []byte
sender golwip.UDPConn
}
func (u *udpPacket) Data() []byte {
return u.payload
}
func (u *udpPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {
_, ok := addr.(*net.UDPAddr)
if !ok {
return 0, io.ErrClosedPipe
}
return u.sender.WriteFrom(b, u.source)
}
func (u *udpPacket) Drop() {
}
func (u *udpPacket) LocalAddr() net.Addr {
return u.source
}
type udpHandler struct {
dnsIP net.IP
udpIn chan<- *inbound.PacketAdapter
}
func newUDPHandler(dnsIP net.IP, udpIn chan<- *inbound.PacketAdapter) golwip.UDPConnHandler {
return &udpHandler{dnsIP, udpIn}
}
func (h *udpHandler) Connect(golwip.UDPConn, *net.UDPAddr) error {
return nil
}
func (h *udpHandler) ReceiveTo(conn golwip.UDPConn, data []byte, addr *net.UDPAddr) error {
if shouldHijackDns(h.dnsIP, addr.IP, addr.Port) {
hijackUDPDns(conn, data, addr)
log.Debugln("[TUN] hijack dns udp: %s:%d", addr.IP.String(), addr.Port)
return nil
}
packet := &udpPacket{
source: conn.LocalAddr(),
payload: data,
sender: conn,
}
go func(addr *net.UDPAddr, packet *udpPacket) {
select {
case h.udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(addr), packet, C.TUN):
default:
}
}(addr, packet)
return nil
}

View File

@ -0,0 +1,9 @@
package ipstack
// TunAdapter hold the state of tun/tap interface
type TunAdapter interface {
Close()
Stack() string
DNSListen() string
AutoRoute() bool
}

View File

@ -0,0 +1,75 @@
package system
import (
"encoding/binary"
"io"
"net"
"time"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"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 {
return false
}
return dnsAddr.IP.Equal(net.IPv4zero) || dnsAddr.IP.Equal(targetAddr.IP)
}
func hijackUDPDns(pkt []byte, ep *binding.Endpoint, sender redirect.UDPSender) {
go func() {
answer, err := D.RelayDnsPacket(pkt)
if err != nil {
return
}
_ = sender(answer, &binding.Endpoint{
Source: ep.Target,
Target: ep.Source,
})
}()
}
func hijackTCPDns(conn net.Conn) {
go func() {
defer func(conn net.Conn) {
_ = conn.Close()
}(conn)
if err := conn.SetReadDeadline(time.Now().Add(defaultDnsReadTimeout)); err != nil {
return
}
for {
var length uint16
if binary.Read(conn, binary.BigEndian, &length) != nil {
return
}
data := make([]byte, length)
_, err := io.ReadFull(conn, data)
if err != nil {
return
}
rb, err := D.RelayDnsPacket(data)
if err != nil {
continue
}
if binary.Write(conn, binary.BigEndian, uint16(len(rb))) != nil {
return
}
if _, err = conn.Write(rb); err != nil {
return
}
}
}()
}

View File

@ -0,0 +1,21 @@
package system
import "github.com/Dreamacro/clash/log"
type logger struct{}
func (l *logger) D(format string, args ...interface{}) {
log.Debugln("[TUN] "+format, args...)
}
func (l *logger) I(format string, args ...interface{}) {
log.Infoln("[TUN] "+format, args...)
}
func (l *logger) W(format string, args ...interface{}) {
log.Warnln("[TUN] "+format, args...)
}
func (l *logger) E(format string, args ...interface{}) {
log.Errorln("[TUN] "+format, args...)
}

View File

@ -0,0 +1,46 @@
package system
import (
"net"
"strconv"
"github.com/kr328/tun2socket/binding"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
)
func handleTCP(conn net.Conn, endpoint *binding.Endpoint, tcpIn chan<- C.ConnContext) {
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: "",
}
addrType := C.AtypIPv4
if dst.IP.To4() == nil {
addrType = C.AtypIPv6
}
metadata := &C.Metadata{
NetWork: C.TCP,
Type: C.TUN,
SrcIP: src.IP,
DstIP: dst.IP,
SrcPort: strconv.Itoa(src.Port),
DstPort: strconv.Itoa(dst.Port),
AddrType: addrType,
Host: "",
}
//if c, ok := conn.(*net.TCPConn); ok {
// c.SetKeepAlive(true)
//}
tcpIn <- context.NewConnContext(conn, metadata)
}

View File

@ -0,0 +1,116 @@
package system
import (
"net"
"strconv"
"sync"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/dev"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/log"
"github.com/kr328/tun2socket"
"github.com/kr328/tun2socket/binding"
"github.com/kr328/tun2socket/redirect"
)
type systemAdapter struct {
device dev.TunDevice
tun *tun2socket.Tun2Socket
lock sync.Mutex
stackName string
dnsListen string
autoRoute bool
}
func NewAdapter(device dev.TunDevice, conf config.Tun, mtu int, gateway, mirror string, onStop func(), tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
adapter := &systemAdapter{
device: device,
stackName: conf.Stack,
dnsListen: conf.DNSListen,
autoRoute: conf.AutoRoute,
}
adapter.lock.Lock()
defer adapter.lock.Unlock()
dnsHost, dnsPort, err := net.SplitHostPort(conf.DNSListen)
if err != nil {
return nil, err
}
dnsP, err := strconv.Atoi(dnsPort)
if err != nil {
return nil, err
}
dnsAddr := binding.Address{
IP: net.ParseIP(dnsHost),
Port: uint16(dnsP),
}
t := tun2socket.NewTun2Socket(device, mtu, net.ParseIP(gateway), net.ParseIP(mirror))
t.SetAllocator(allocUDP)
t.SetClosedHandler(onStop)
t.SetLogger(&logger{})
t.SetTCPHandler(func(conn net.Conn, endpoint *binding.Endpoint) {
if shouldHijackDns(dnsAddr, endpoint.Target) {
hijackTCPDns(conn)
log.Debugln("[TUN] hijack dns tcp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
return
}
handleTCP(conn, endpoint, tcpIn)
})
t.SetUDPHandler(func(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender) {
if shouldHijackDns(dnsAddr, endpoint.Target) {
hijackUDPDns(payload, endpoint, sender)
log.Debugln("[TUN] hijack dns udp: %s:%d", endpoint.Target.IP.String(), endpoint.Target.Port)
return
}
handleUDP(payload, endpoint, sender, udpIn)
})
t.Start()
adapter.tun = t
return adapter, nil
}
func (t *systemAdapter) Stack() string {
return t.stackName
}
func (t *systemAdapter) AutoRoute() bool {
return t.autoRoute
}
func (t *systemAdapter) DNSListen() string {
return t.dnsListen
}
func (t *systemAdapter) Close() {
t.lock.Lock()
defer t.lock.Unlock()
t.stopLocked()
}
func (t *systemAdapter) stopLocked() {
if t.tun != nil {
t.tun.Close()
}
if t.device != nil {
_ = t.device.Close()
}
t.tun = nil
t.device = nil
}

View File

@ -0,0 +1,74 @@
package system
import (
"io"
"net"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5"
"github.com/kr328/tun2socket/binding"
"github.com/kr328/tun2socket/redirect"
)
type udpPacket struct {
source binding.Address
data []byte
send redirect.UDPSender
}
func (u *udpPacket) Data() []byte {
return u.data
}
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,
})
}
func (u *udpPacket) Drop() {
recycleUDP(u.data)
}
func (u *udpPacket) LocalAddr() net.Addr {
return &net.UDPAddr{
IP: u.source.IP,
Port: int(u.source.Port),
Zone: "",
}
}
func handleUDP(payload []byte, endpoint *binding.Endpoint, sender redirect.UDPSender, udpIn chan<- *inbound.PacketAdapter) {
pkt := &udpPacket{
source: endpoint.Source,
data: payload,
send: sender,
}
rAddr := &net.UDPAddr{
IP: endpoint.Target.IP,
Port: int(endpoint.Target.Port),
Zone: "",
}
select {
case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN):
default:
}
}
func allocUDP(size int) []byte {
return pool.Get(size)
}
func recycleUDP(payload []byte) {
_ = pool.Put(payload)
}

View File

@ -0,0 +1,54 @@
package tun
import (
"errors"
"fmt"
"strings"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/dev"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
"github.com/Dreamacro/clash/listener/tun/ipstack/lwip"
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
"github.com/Dreamacro/clash/log"
)
// New create TunAdapter
func New(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.TunAdapter, error) {
tunAddress := "198.18.0.1"
autoRoute := conf.AutoRoute
stack := conf.Stack
var tunAdapter ipstack.TunAdapter
device, err := dev.OpenTunDevice(tunAddress, autoRoute)
if err != nil {
return nil, fmt.Errorf("can't open tun: %v", err)
}
mtu, err := device.MTU()
if err != nil {
_ = device.Close()
return nil, errors.New("unable to get device mtu")
}
if strings.EqualFold(stack, "lwip") {
tunAdapter, err = lwip.NewAdapter(device, conf, mtu, tcpIn, udpIn)
} else if strings.EqualFold(stack, "system") {
tunAdapter, err = system.NewAdapter(device, conf, mtu, tunAddress, tunAddress, func() {}, tcpIn, udpIn)
} else if strings.EqualFold(stack, "gvisor") {
tunAdapter, err = gvisor.NewAdapter(device, conf, tunAddress, tcpIn, udpIn)
} else {
err = fmt.Errorf("can not support tun ip stack: %s, only support \"lwip\" \"system\" and \"gvisor\"", stack)
}
if err != nil {
_ = device.Close()
return nil, err
}
log.Infoln("Tun adapter listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", device.Name(), tunAddress, mtu, autoRoute, stack)
return tunAdapter, nil
}