chore: use sing-tun to replace old tun_adapter

This commit is contained in:
wwqgtxx 2022-10-06 19:23:38 +08:00
parent 347e5e9606
commit 94246104b8
93 changed files with 739 additions and 5101 deletions

View File

@ -12,7 +12,7 @@ VERSION=$(shell git rev-parse --short HEAD)
endif
BUILDTIME=$(shell date -u)
GOBUILD=CGO_ENABLED=0 go build -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \
GOBUILD=CGO_ENABLED=0 go build -tags with_gvisor -trimpath -ldflags '-X "github.com/Dreamacro/clash/constant.Version=$(VERSION)" \
-X "github.com/Dreamacro/clash/constant.BuildTime=$(BUILDTIME)" \
-w -s -buildid='

View File

@ -7,7 +7,6 @@ import (
"net"
"strconv"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
@ -16,16 +15,11 @@ import (
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
"github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowimpl"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/uot"
)
func init() {
buf.DefaultAllocator = pool.DefaultAllocator
}
type ShadowSocks struct {
*Base
method shadowsocks.Method

View File

@ -8,7 +8,7 @@ import (
"sync"
)
var DefaultAllocator = NewAllocator()
var defaultAllocator = NewAllocator()
// Allocator for incoming frames, optimized to prevent overwriting after zeroing
type Allocator struct {

View File

@ -13,9 +13,9 @@ const (
)
func Get(size int) []byte {
return DefaultAllocator.Get(size)
return defaultAllocator.Get(size)
}
func Put(buf []byte) error {
return DefaultAllocator.Put(buf)
return defaultAllocator.Put(buf)
}

7
common/pool/sing.go Normal file
View File

@ -0,0 +1,7 @@
package pool
import "github.com/sagernet/sing/common/buf"
func init() {
buf.DefaultAllocator = defaultAllocator
}

View File

@ -15,6 +15,30 @@ import (
C "github.com/Dreamacro/clash/constant"
)
func GetAutoDetectInterface() (string, error) {
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return "", err
}
for _, route := range routes {
if route.Dst == nil {
lk, err := netlink.LinkByIndex(route.LinkIndex)
if err != nil {
return "", err
}
if lk.Type() == "tuntap" {
continue
}
return lk.Attrs().Name, nil
}
}
return "", fmt.Errorf("interface not found")
}
// NewTcEBpfProgram new redirect to tun ebpf program
func NewTcEBpfProgram(ifaceNames []string, tunName string) (*TcEBpfProgram, error) {
tunIface, err := netlink.LinkByName(tunName)

View File

@ -15,3 +15,7 @@ func NewTcEBpfProgram(_ []string, _ string) (*TcEBpfProgram, error) {
func NewRedirEBpfProgram(_ []string, _ uint16, _ string) (*TcEBpfProgram, error) {
return nil, fmt.Errorf("system not supported")
}
func GetAutoDetectInterface() (string, error) {
return "", fmt.Errorf("system not supported")
}

View File

@ -2,10 +2,9 @@ package config
import (
"container/list"
"encoding/json"
"errors"
"fmt"
"github.com/Dreamacro/clash/constant/sniffer"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"net"
"net/netip"
"net/url"
@ -31,6 +30,7 @@ import (
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
providerTypes "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/constant/sniffer"
snifferTypes "github.com/Dreamacro/clash/constant/sniffer"
"github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/log"
@ -114,12 +114,75 @@ type Profile struct {
type Tun struct {
Enable bool `yaml:"enable" json:"enable"`
Device string `yaml:"device" json:"device"`
Stack C.TUNStack `yaml:"stack" json:"stack"`
Stack string `yaml:"stack" json:"stack"`
DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"`
TunAddressPrefix netip.Prefix `yaml:"-" json:"-"`
RedirectToTun []string `yaml:"-" json:"-"`
MTU uint32 `yaml:"mtu" json:"mtu,omitempty"`
Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"`
Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"`
StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"`
IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"`
IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"`
ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"`
ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"`
IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"`
IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"`
ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"`
EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"`
UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"`
}
type ListenPrefix netip.Prefix
func (p ListenPrefix) MarshalJSON() ([]byte, error) {
prefix := netip.Prefix(p)
if !prefix.IsValid() {
return json.Marshal(nil)
}
return json.Marshal(prefix.String())
}
func (p ListenPrefix) MarshalYAML() (interface{}, error) {
prefix := netip.Prefix(p)
if !prefix.IsValid() {
return nil, nil
}
return prefix.String(), nil
}
func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error {
var value string
err := json.Unmarshal(bytes, &value)
if err != nil {
return err
}
prefix, err := netip.ParsePrefix(value)
if err != nil {
return err
}
*p = ListenPrefix(prefix)
return nil
}
func (p *ListenPrefix) UnmarshalYAML(node *yaml.Node) error {
var value string
err := node.Decode(&value)
if err != nil {
return err
}
prefix, err := netip.ParsePrefix(value)
if err != nil {
return err
}
*p = ListenPrefix(prefix)
return nil
}
func (p ListenPrefix) Build() netip.Prefix {
return netip.Prefix(p)
}
// IPTables config
@ -189,7 +252,7 @@ type RawFallbackFilter struct {
type RawTun struct {
Enable bool `yaml:"enable" json:"enable"`
Device string `yaml:"device" json:"device"`
Stack C.TUNStack `yaml:"stack" json:"stack"`
Stack string `yaml:"stack" json:"stack"`
DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"`
AutoDetectInterface bool `yaml:"auto-detect-interface"`
@ -294,7 +357,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Tun: RawTun{
Enable: false,
Device: "",
Stack: C.TunGvisor,
Stack: "gvisor",
DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query
AutoRoute: false,
AutoDetectInterface: false,
@ -1039,17 +1102,6 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser {
}
func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) {
if rawTun.Enable && rawTun.AutoDetectInterface {
autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
if err != nil {
log.Warnln("Can not find auto detect interface.[%s]", err)
} else {
log.Warnln("Auto detect interface: %s", autoDetectInterfaceName)
}
general.Interface = autoDetectInterfaceName
}
var dnsHijack []netip.AddrPort
for _, d := range rawTun.DNSHijack {
@ -1079,8 +1131,9 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) {
DNSHijack: dnsHijack,
AutoRoute: rawTun.AutoRoute,
AutoDetectInterface: rawTun.AutoDetectInterface,
TunAddressPrefix: tunAddressPrefix,
RedirectToTun: rawTun.RedirectToTun,
Inet4Address: []ListenPrefix{ListenPrefix(tunAddressPrefix)},
Inet6Address: []ListenPrefix{ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))},
}, nil
}

13
go.mod
View File

@ -19,8 +19,9 @@ require (
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/miekg/dns v1.1.50
github.com/oschwald/geoip2-golang v1.8.0
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f
github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3
github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
@ -34,12 +35,8 @@ require (
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e
google.golang.org/protobuf v1.28.1
gopkg.in/yaml.v3 v3.0.1
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
)
@ -64,13 +61,17 @@ require (
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
golang.org/x/tools v0.1.12 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect
lukechampine.com/blake3 v1.1.7 // indirect
)

20
go.sum
View File

@ -178,10 +178,19 @@ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7q
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f h1:GX416thAwyc0vHBOal/qplvdhFgYO2dHD5GqADCJ0Ig=
github.com/sagernet/sing v0.0.0-20220921101604-86d7d510231f/go.mod h1:x3NHUeJBQwV75L51zwmLKQdLtRvR+M4PmXkfQtU1vIY=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34=
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 h1:ZDlgH6dTozS3ODaYq1GxCj+H8NvYESaex90iX72gadw=
github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4=
github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM=
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3 h1:9Igu/lgB1na+YTSEX6/YtPugAlMRyxLCDb7X+I0gdAE=
github.com/sagernet/sing-tun v0.0.0-20221005115555-9a556307f6a3/go.mod h1:qbqV9lwcXJnj1Tw4we7oA6Z8zGE/kCXQBCzuhzRWVw8=
github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE=
github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@ -344,6 +353,7 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -378,12 +388,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c h1:Okh6a1xpnJslG9Mn84pId1Mn+Q8cvpo4HCeeFWHo0cA=
golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg=
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e h1:yV04h6Tx19uDR6LvuEbR19cDU+3QrB9LuGjtF7F5G0w=
golang.zx2c4.com/wireguard/windows v0.5.4-0.20220328111914-004c22c5647e/go.mod h1:1CeiatTZwcwSFA3cAtMm8CQoroviTldnxd7DOgM/vI4=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=

View File

@ -2,10 +2,7 @@ package proxy
import (
"fmt"
"github.com/Dreamacro/clash/component/ebpf"
"github.com/Dreamacro/clash/listener/autoredir"
"github.com/Dreamacro/clash/listener/inner"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/sing_tun"
"golang.org/x/exp/slices"
"net"
"sort"
@ -13,15 +10,16 @@ import (
"sync"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/component/ebpf"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/autoredir"
"github.com/Dreamacro/clash/listener/http"
"github.com/Dreamacro/clash/listener/inner"
"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"
)
@ -40,7 +38,7 @@ var (
tproxyUDPListener *tproxy.UDPListener
mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener
tunStackListener ipstack.Stack
tunLister *sing_tun.Listener
autoRedirListener *autoredir.Listener
autoRedirProgram *ebpf.TcEBpfProgram
tcProgram *ebpf.TcEBpfProgram
@ -359,7 +357,7 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *
return
}
tunStackListener, err = tun.New(tunConf, tcpIn, udpIn)
tunLister, err = sing_tun.New(*tunConf, tcpIn, udpIn)
lastTunConf = tunConf
}
@ -429,7 +427,7 @@ func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<-
return
}
defaultRouteInterfaceName, err := commons.GetAutoDetectInterface()
defaultRouteInterfaceName, err := ebpf.GetAutoDetectInterface()
if err != nil {
return
}
@ -538,7 +536,7 @@ func hasTunConfigChange(tunConf *config.Tun) bool {
return true
}
if tunConf.TunAddressPrefix.String() != lastTunConf.TunAddressPrefix.String() {
if slices.Equal(tunConf.Inet4Address, lastTunConf.Inet4Address) && slices.Equal(tunConf.Inet6Address, lastTunConf.Inet6Address) {
return true
}
@ -546,16 +544,9 @@ func hasTunConfigChange(tunConf *config.Tun) bool {
}
func Cleanup(wait bool) {
if tunStackListener != nil {
_ = tunStackListener.Close()
commons.StopDefaultInterfaceChangeMonitor()
if wait {
commons.WaitForTunClose(lastTunConf.Device)
if tunLister != nil {
tunLister.Close()
tunLister = nil
}
commons.CleanupRule()
}
tunStackListener = nil
lastTunConf = nil
}

41
listener/sing/log.go Normal file
View File

@ -0,0 +1,41 @@
package sing
import (
"fmt"
"github.com/Dreamacro/clash/log"
L "github.com/sagernet/sing/common/logger"
)
type logger struct{}
func (l logger) Trace(args ...any) {
log.Debugln(fmt.Sprint(args...))
}
func (l logger) Debug(args ...any) {
log.Debugln(fmt.Sprint(args...))
}
func (l logger) Info(args ...any) {
log.Infoln(fmt.Sprint(args...))
}
func (l logger) Warn(args ...any) {
log.Warnln(fmt.Sprint(args...))
}
func (l logger) Error(args ...any) {
log.Errorln(fmt.Sprint(args...))
}
func (l logger) Fatal(args ...any) {
log.Fatalln(fmt.Sprint(args...))
}
func (l logger) Panic(args ...any) {
log.Fatalln(fmt.Sprint(args...))
}
var Logger L.Logger = logger{}

143
listener/sing/sing.go Normal file
View File

@ -0,0 +1,143 @@
package sing
import (
"context"
"errors"
"net"
"sync"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
vmess "github.com/sagernet/sing-vmess"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/uot"
)
type ListenerHandler struct {
TcpIn chan<- C.ConnContext
UdpIn chan<- *inbound.PacketAdapter
Type C.Type
}
type waitCloseConn struct {
net.Conn
wg *sync.WaitGroup
close sync.Once
}
func (c *waitCloseConn) Close() error { // call from handleTCPConn(connCtx C.ConnContext)
c.close.Do(func() {
c.wg.Done()
})
return c.Conn.Close()
}
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
switch metadata.Destination.Fqdn {
case vmess.MuxDestination.Fqdn:
return vmess.HandleMuxConnection(ctx, conn, h)
case uot.UOTMagicAddress:
metadata.Destination = M.Socksaddr{}
return h.NewPacketConnection(ctx, uot.NewClientConn(conn), metadata)
}
target := socks5.ParseAddr(metadata.Destination.String())
wg := &sync.WaitGroup{}
defer wg.Wait() // this goroutine must exit after conn.Close()
wg.Add(1)
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg}, h.Type)
return nil
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := conn // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
}()
for {
buff := buf.NewPacket() // do not use stack buffer
dest, err := conn.ReadPacket(buff)
if err != nil {
buff.Release()
if E.IsClosed(err) {
break
}
return err
}
target := socks5.ParseAddr(dest.String())
packet := &packet{
conn: &conn2,
mutex: &mutex,
rAddr: metadata.Source.UDPAddr(),
lAddr: conn.LocalAddr(),
buff: buff,
}
select {
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type):
default:
}
}
return nil
}
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
}
type packet struct {
conn *network.PacketConn
mutex *sync.Mutex
rAddr net.Addr
lAddr net.Addr
buff *buf.Buffer
}
func (c *packet) Data() []byte {
return c.buff.Bytes()
}
// WriteBack wirtes UDP packet with source(ip, port) = `addr`
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
if addr == nil {
err = errors.New("address is invalid")
return
}
buff := buf.NewPacket()
defer buff.Release()
n, err = buff.Write(b)
if err != nil {
return
}
c.mutex.Lock()
defer c.mutex.Unlock()
conn := *c.conn
if conn == nil {
err = errors.New("writeBack to closed connection")
return
}
err = conn.WritePacket(buff, M.ParseSocksaddr(addr.String()))
return
}
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.rAddr
}
func (c *packet) Drop() {
c.buff.Release()
}
func (c *packet) InAddr() net.Addr {
return c.lAddr
}

165
listener/sing_tun/dns.go Normal file
View File

@ -0,0 +1,165 @@
package sing_tun
import (
"context"
"encoding/binary"
"io"
"net"
"net/netip"
"sync"
"time"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
D "github.com/miekg/dns"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/common/network"
)
const DefaultDnsReadTimeout = time.Second * 10
type ListenerHandler struct {
sing.ListenerHandler
DnsAdds []netip.AddrPort
}
func (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool {
if targetAddr.Addr().IsLoopback() && targetAddr.Port() == 53 { // cause by system stack
return true
}
for _, addrPort := range h.DnsAdds {
if addrPort == targetAddr || (addrPort.Addr().IsUnspecified() && targetAddr.Port() == 53) {
return true
}
}
return false
}
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String())
buff := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buff)
_ = conn.Close()
}()
for {
if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil {
break
}
length := uint16(0)
if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
break
}
if int(length) > len(buff) {
break
}
n, err := io.ReadFull(conn, buff[:length])
if err != nil {
break
}
err = func() error {
inData := buff[:n]
msg, err := RelayDnsPacket(inData)
if err != nil {
return err
}
err = binary.Write(conn, binary.BigEndian, uint16(len(msg)))
if err != nil {
return err
}
_, err = conn.Write(msg)
if err != nil {
return err
}
return nil
}()
if err != nil {
return err
}
}
return nil
}
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
}
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
defer func() { _ = conn.Close() }()
mutex := sync.Mutex{}
conn2 := conn // a new interface to set nil in defer
defer func() {
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
defer mutex.Unlock()
conn2 = nil
}()
for {
buff := buf.NewPacket()
dest, err := conn.ReadPacket(buff)
if err != nil {
buff.Release()
if E.IsClosed(err) {
break
}
return err
}
go func() {
inData := buff.Bytes()
msg, err := RelayDnsPacket(inData)
if err != nil {
buff.Release()
return
}
buff.Reset()
_, err = buff.Write(msg)
if err != nil {
buff.Release()
return
}
mutex.Lock()
defer mutex.Unlock()
conn := conn2
if conn == nil {
return
}
err = conn.WritePacket(buff, dest) // WritePacket will release buff
if err != nil {
return
}
}()
}
return nil
}
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
}
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 {
m := new(D.Msg)
m.SetRcode(msg, D.RcodeServerFailure)
return m.Pack()
}
r.SetRcode(msg, r.Rcode)
r.Compress = true
return r.Pack()
}

237
listener/sing_tun/server.go Normal file
View File

@ -0,0 +1,237 @@
package sing_tun
import (
"context"
"net/netip"
"strconv"
"strings"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/sing"
"github.com/Dreamacro/clash/log"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/ranges"
)
var InterfaceName = "Meta"
type Listener struct {
closed bool
options config.Tun
handler tun.Handler
tunIf tun.Tun
tunStack tun.Stack
networkUpdateMonitor tun.NetworkUpdateMonitor
defaultInterfaceMonitor tun.DefaultInterfaceMonitor
}
func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) {
tunName := options.Device
if tunName == "" {
tunName = tun.CalculateInterfaceName(InterfaceName)
}
tunMTU := options.MTU
if tunMTU == 0 {
tunMTU = 9000
}
var udpTimeout int64
if options.UDPTimeout != 0 {
udpTimeout = options.UDPTimeout
} else {
udpTimeout = int64(C.DefaultUDPTimeout.Seconds())
}
includeUID := uidToRange(options.IncludeUID)
if len(options.IncludeUIDRange) > 0 {
var err error
includeUID, err = parseRange(includeUID, options.IncludeUIDRange)
if err != nil {
return nil, E.Cause(err, "parse include_uid_range")
}
}
excludeUID := uidToRange(options.ExcludeUID)
if len(options.ExcludeUIDRange) > 0 {
var err error
excludeUID, err = parseRange(excludeUID, options.ExcludeUIDRange)
if err != nil {
return nil, E.Cause(err, "parse exclude_uid_range")
}
}
var dnsAdds []netip.AddrPort
for _, d := range options.DNSHijack {
dnsAdds = append(dnsAdds, d)
}
for _, a := range options.Inet4Address {
addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53)
dnsAdds = append(dnsAdds, addrPort)
}
for _, a := range options.Inet6Address {
addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53)
dnsAdds = append(dnsAdds, addrPort)
}
handler := &ListenerHandler{
ListenerHandler: sing.ListenerHandler{
TcpIn: tcpIn,
UdpIn: udpIn,
Type: C.TUN,
},
DnsAdds: dnsAdds,
}
l = &Listener{
closed: false,
options: options,
handler: handler,
}
defer func() {
if err != nil {
l.Close()
l = nil
}
}()
networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(handler)
if err != nil {
err = E.Cause(err, "create NetworkUpdateMonitor")
return
}
err = networkUpdateMonitor.Start()
if err != nil {
err = E.Cause(err, "start NetworkUpdateMonitor")
return
}
l.networkUpdateMonitor = networkUpdateMonitor
defaultInterfaceMonitor, err := tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, tun.DefaultInterfaceMonitorOptions{})
if err != nil {
err = E.Cause(err, "create DefaultInterfaceMonitor")
return
}
defaultInterfaceMonitor.RegisterCallback(func(event int) error {
targetInterface := dialer.DefaultInterface.Load()
autoDetectInterfaceName := defaultInterfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified())
if autoDetectInterfaceName != "" && autoDetectInterfaceName != "<nil>" {
targetInterface = autoDetectInterfaceName
} else {
log.Warnln("Auto detect interface name is empty.")
}
if old := dialer.DefaultInterface.Load(); old != targetInterface {
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, targetInterface)
dialer.DefaultInterface.Store(targetInterface)
iface.FlushCache()
}
return nil
})
err = defaultInterfaceMonitor.Start()
if err != nil {
err = E.Cause(err, "start DefaultInterfaceMonitor")
return
}
l.defaultInterfaceMonitor = defaultInterfaceMonitor
tunOptions := tun.Options{
Name: tunName,
MTU: tunMTU,
Inet4Address: common.Map(options.Inet4Address, config.ListenPrefix.Build),
Inet6Address: common.Map(options.Inet6Address, config.ListenPrefix.Build),
AutoRoute: options.AutoRoute,
StrictRoute: options.StrictRoute,
IncludeUID: includeUID,
ExcludeUID: excludeUID,
IncludeAndroidUser: options.IncludeAndroidUser,
IncludePackage: options.IncludePackage,
ExcludePackage: options.ExcludePackage,
InterfaceMonitor: defaultInterfaceMonitor,
TableIndex: 2022,
}
//if C.IsAndroid {
// t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
//}
tunIf, err := tun.Open(tunOptions)
if err != nil {
err = E.Cause(err, "configure tun interface")
return
}
l.tunIf = tunIf
l.tunStack, err = tun.NewStack(options.Stack, tun.StackOptions{
Context: context.TODO(),
Tun: tunIf,
MTU: tunOptions.MTU,
Name: tunOptions.Name,
Inet4Address: tunOptions.Inet4Address,
Inet6Address: tunOptions.Inet6Address,
EndpointIndependentNat: options.EndpointIndependentNat,
UDPTimeout: udpTimeout,
Handler: handler,
Logger: sing.Logger,
})
if err != nil {
return
}
err = l.tunStack.Start()
if err != nil {
return
}
log.Infoln("Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s",
tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack)
return
}
func uidToRange(uidList []uint32) []ranges.Range[uint32] {
return common.Map(uidList, func(uid uint32) ranges.Range[uint32] {
return ranges.NewSingle(uid)
})
}
func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.Range[uint32], error) {
for _, uidRange := range rangeList {
if !strings.Contains(uidRange, ":") {
return nil, E.New("missing ':' in range: ", uidRange)
}
subIndex := strings.Index(uidRange, ":")
if subIndex == 0 {
return nil, E.New("missing range start: ", uidRange)
} else if subIndex == len(uidRange)-1 {
return nil, E.New("missing range end: ", uidRange)
}
var start, end uint64
var err error
start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32)
if err != nil {
return nil, E.Cause(err, "parse range start")
}
end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32)
if err != nil {
return nil, E.Cause(err, "parse range end")
}
uidRanges = append(uidRanges, ranges.New(uint32(start), uint32(end)))
}
return uidRanges, nil
}
func (l *Listener) Close() {
l.closed = true
_ = common.Close(
l.tunStack,
l.tunIf,
l.defaultInterfaceMonitor,
l.networkUpdateMonitor,
)
}
func (l *Listener) Config() config.Tun {
return l.options
}

View File

@ -0,0 +1,7 @@
package sing_tun
import tun "github.com/sagernet/sing-tun"
func init() {
tun.TunnelType = InterfaceName
}

View File

@ -1,34 +0,0 @@
//go:build !no_gvisor
package device
import (
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
// Device is the interface that implemented by network layer devices (e.g. tun),
// and easy to use as stack.LinkEndpoint.
type Device interface {
stack.LinkEndpoint
// Name returns the current name of the device.
Name() string
// Type returns the driver type of the device.
Type() string
// Read packets from tun device
Read(packet []byte) (int, error)
// Write packets to tun device
Write(packet []byte) (int, error)
// Close stops and closes the device.
Close() error
// UseEndpoint work for gVisor stack
UseEndpoint() error
// UseIOBased work for other ip stack
UseIOBased() error
}

View File

@ -1,29 +0,0 @@
//go:build no_gvisor
package device
// Device is the interface that implemented by network layer devices (e.g. tun),
// and easy to use as stack.LinkEndpoint.
type Device interface {
// Name returns the current name of the device.
Name() string
// Type returns the driver type of the device.
Type() string
// Read packets from tun device
Read(packet []byte) (int, error)
// Write packets to tun device
Write(packet []byte) (int, error)
// Close stops and closes the device.
Close() error
// UseEndpoint work for gVisor stack
UseEndpoint() error
// UseIOBased work for other ip stack
UseIOBased() error
}

View File

@ -1,5 +0,0 @@
package fdbased
const Driver = "fd"
const defaultMTU = 1500

View File

@ -1,49 +0,0 @@
//go:build !windows
package fdbased
import (
"fmt"
"strconv"
"github.com/Dreamacro/clash/listener/tun/device"
"golang.org/x/sys/unix"
)
func Open(name string, mtu uint32) (device.Device, error) {
fd, err := strconv.Atoi(name)
if err != nil {
return nil, fmt.Errorf("cannot open fd: %s", name)
}
if mtu == 0 {
mtu = defaultMTU
}
return open(fd, mtu)
}
func (f *FD) Type() string {
return Driver
}
func (f *FD) Name() string {
return strconv.Itoa(f.fd)
}
func (f *FD) Close() error {
err := unix.Close(f.fd)
if f.file != nil {
_ = f.file.Close()
}
return err
}
func (f *FD) UseEndpoint() error {
return f.useEndpoint()
}
func (f *FD) UseIOBased() error {
return f.useIOBased()
}
var _ device.Device = (*FD)(nil)

View File

@ -1,17 +0,0 @@
//go:build !no_gvisor
package fdbased
import (
"gvisor.dev/gvisor/pkg/tcpip/stack"
"os"
)
type FD struct {
stack.LinkEndpoint
fd int
mtu uint32
file *os.File
}

View File

@ -1,14 +0,0 @@
//go:build no_gvisor
package fdbased
import (
"os"
)
type FD struct {
fd int
mtu uint32
file *os.File
}

View File

@ -1,11 +0,0 @@
package fdbased
import (
"errors"
"github.com/Dreamacro/clash/listener/tun/device"
)
func Open(name string, mtu uint32) (device.Device, error) {
return nil, errors.New("not supported")
}

View File

@ -1,27 +0,0 @@
package fdbased
import (
"github.com/Dreamacro/clash/listener/tun/device"
)
func open(fd int, mtu uint32) (device.Device, error) {
f := &FD{fd: fd, mtu: mtu}
return f, nil
}
func (f *FD) useEndpoint() error {
return f.newLinuxEp()
}
func (f *FD) useIOBased() error {
return nil
}
func (f *FD) Read(packet []byte) (int, error) {
return f.read(packet)
}
func (f *FD) Write(packet []byte) (int, error) {
return f.write(packet)
}

View File

@ -1,45 +0,0 @@
//go:build !no_gvisor && (linux || android)
package fdbased
import (
"fmt"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
)
func (f *FD) newLinuxEp() error {
ep, err := fdbased.New(&fdbased.Options{
FDs: []int{f.fd},
MTU: f.mtu,
// TUN only, ignore ethernet header.
EthernetHeader: false,
})
if err != nil {
return fmt.Errorf("create endpoint: %w", err)
}
f.LinkEndpoint = ep
return nil
}
func (f *FD) read(packet []byte) (int, error) {
n, gvErr := rawfile.BlockingRead(f.fd, packet)
if gvErr != nil {
return 0, fmt.Errorf("read error: %s", gvErr.String())
}
return n, nil
}
func (f *FD) write(packet []byte) (int, error) {
n := len(packet)
if n == 0 {
return 0, nil
}
gvErr := rawfile.NonBlockingWrite(f.fd, packet)
if gvErr != nil {
return 0, fmt.Errorf("write error: %s", gvErr.String())
}
return n, nil
}

View File

@ -1,19 +0,0 @@
//go:build no_gvisor
package fdbased
import (
"fmt"
)
func (f *FD) newLinuxEp() error {
return fmt.Errorf("unsupported gvisor on the build")
}
func (f *FD) read(packet []byte) (int, error) {
return 0, fmt.Errorf("unsupported gvisor on the build")
}
func (f *FD) write(packet []byte) (int, error) {
return 0, fmt.Errorf("unsupported gvisor on the build")
}

View File

@ -1,36 +0,0 @@
//go:build !linux && !windows
package fdbased
import (
"fmt"
"os"
"github.com/Dreamacro/clash/listener/tun/device"
)
func open(fd int, mtu uint32) (device.Device, error) {
f := &FD{fd: fd, mtu: mtu}
return f, nil
}
func (f *FD) useEndpoint() error {
return f.newEpOther()
}
func (f *FD) useIOBased() error {
f.file = os.NewFile(uintptr(f.fd), f.Name())
if f.file == nil {
return fmt.Errorf("create IOBased failed, can not open file: %s", f.Name())
}
return nil
}
func (f *FD) Read(packet []byte) (int, error) {
return f.file.Read(packet)
}
func (f *FD) Write(packet []byte) (int, error) {
return f.file.Write(packet)
}

View File

@ -1,19 +0,0 @@
//go:build !no_gvisor && !linux && !windows
package fdbased
import (
"fmt"
"os"
"github.com/Dreamacro/clash/listener/tun/device/iobased"
)
func (f *FD) newEpOther() error {
ep, err := iobased.New(os.NewFile(uintptr(f.fd), f.Name()), f.mtu, 0)
if err != nil {
return fmt.Errorf("create endpoint: %w", err)
}
f.LinkEndpoint = ep
return nil
}

View File

@ -1,11 +0,0 @@
//go:build no_gvisor && !linux && !windows
package fdbased
import (
"fmt"
)
func (f *FD) newEpOther() error {
return fmt.Errorf("unsupported gvisor on the build")
}

View File

@ -1,166 +0,0 @@
//go:build !no_gvisor
// Package iobased provides the implementation of io.ReadWriter
// based data-link layer endpoints.
package iobased
import (
"context"
"errors"
"gvisor.dev/gvisor/pkg/bufferv2"
"io"
"sync"
"github.com/Dreamacro/clash/common/pool"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// Queue length for outbound packet, arriving for read. Overflow
// causes packet drops.
defaultOutQueueLen = 1 << 10
)
// Endpoint implements the interface of stack.LinkEndpoint from io.ReadWriter.
type Endpoint struct {
*channel.Endpoint
// rw is the io.ReadWriter for reading and writing packets.
rw io.ReadWriter
// mtu (maximum transmission unit) is the maximum size of a packet.
mtu uint32
// offset can be useful when perform TUN device I/O with TUN_PI enabled.
offset int
// once is used to perform the init action once when attaching.
once sync.Once
// wg keeps track of running goroutines.
wg sync.WaitGroup
}
// New returns stack.LinkEndpoint(.*Endpoint) and error.
func New(rw io.ReadWriter, mtu uint32, offset int) (*Endpoint, error) {
if mtu == 0 {
return nil, errors.New("MTU size is zero")
}
if rw == nil {
return nil, errors.New("RW interface is nil")
}
if offset < 0 {
return nil, errors.New("offset must be non-negative")
}
return &Endpoint{
Endpoint: channel.New(defaultOutQueueLen, mtu, ""),
rw: rw,
mtu: mtu,
offset: offset,
}, nil
}
func (e *Endpoint) Wait() {
e.wg.Wait()
}
// Attach launches the goroutine that reads packets from io.Reader and
// dispatches them via the provided dispatcher.
func (e *Endpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.Endpoint.Attach(dispatcher)
e.once.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
e.wg.Add(2)
go func() {
e.outboundLoop(ctx)
e.wg.Done()
}()
go func() {
e.dispatchLoop(cancel)
e.wg.Done()
}()
})
}
// dispatchLoop dispatches packets to upper layer.
func (e *Endpoint) dispatchLoop(cancel context.CancelFunc) {
// Call cancel() to ensure (*Endpoint).outboundLoop(context.Context) exits
// gracefully after (*Endpoint).dispatchLoop(context.CancelFunc) returns.
defer cancel()
mtu := int(e.mtu)
for {
data := pool.Get(mtu)
n, err := e.rw.Read(data)
if err != nil {
_ = pool.Put(data)
break
}
if n == 0 || n > mtu {
_ = pool.Put(data)
continue
}
if !e.IsAttached() {
_ = pool.Put(data)
continue /* unattached, drop packet */
}
var p tcpip.NetworkProtocolNumber
switch header.IPVersion(data) {
case header.IPv4Version:
p = header.IPv4ProtocolNumber
case header.IPv6Version:
p = header.IPv6ProtocolNumber
default:
_ = pool.Put(data)
continue
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: bufferv2.MakeWithData(data),
OnRelease: func() {
_ = pool.Put(data)
},
})
e.InjectInbound(p, pkt)
pkt.DecRef()
}
}
// outboundLoop reads outbound packets from channel, and then it calls
// writePacket to send those packets back to lower layer.
func (e *Endpoint) outboundLoop(ctx context.Context) {
for {
pkt := e.ReadContext(ctx)
if pkt == nil {
break
}
e.writePacket(pkt)
}
}
// writePacket writes outbound packets to the io.Writer.
func (e *Endpoint) writePacket(pkt *stack.PacketBuffer) tcpip.Error {
pktView := pkt.ToView()
defer func() {
pktView.Release()
pkt.DecRef()
}()
if _, err := e.rw.Write(pktView.AsSlice()); err != nil {
return &tcpip.ErrInvalidEndpointState{}
}
return nil
}

View File

@ -1 +0,0 @@
package iobased

View File

@ -1,233 +0,0 @@
/* 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(dllContent)
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

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

View File

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

View File

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

View File

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

View File

@ -1,10 +0,0 @@
//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

View File

@ -1,14 +0,0 @@
// Package tun provides TUN which implemented device.Device interface.
package tun
import (
"github.com/Dreamacro/clash/listener/tun/device"
)
const Driver = "tun"
func (t *TUN) Type() string {
return Driver
}
var _ device.Device = (*TUN)(nil)

View File

@ -1,145 +0,0 @@
//go:build linux
package tun
import (
"fmt"
"unsafe"
"github.com/Dreamacro/clash/listener/tun/device"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
type TUN struct {
stack.LinkEndpoint
fd int
mtu uint32
name string
}
func Open(name string, mtu uint32) (device.Device, error) {
t := &TUN{name: name, mtu: mtu}
if len(t.name) >= unix.IFNAMSIZ {
return nil, fmt.Errorf("interface name too long: %s", t.name)
}
fd, err := tun.Open(t.name)
if err != nil {
return nil, fmt.Errorf("create tun: %w", err)
}
t.fd = fd
if t.mtu > 0 {
if err := setMTU(t.name, t.mtu); err != nil {
return nil, fmt.Errorf("set mtu: %w", err)
}
}
_mtu, err := rawfile.GetMTU(t.name)
if err != nil {
return nil, fmt.Errorf("get mtu: %w", err)
}
t.mtu = _mtu
return t, nil
}
func (t *TUN) Name() string {
return t.name
}
func (t *TUN) Read(packet []byte) (int, error) {
n, gvErr := rawfile.BlockingRead(t.fd, packet)
if gvErr != nil {
return 0, fmt.Errorf("read error: %s", gvErr.String())
}
return n, nil
}
func (t *TUN) Write(packet []byte) (int, error) {
n := len(packet)
if n == 0 {
return 0, nil
}
gvErr := rawfile.NonBlockingWrite(t.fd, packet)
if gvErr != nil {
return 0, fmt.Errorf("write error: %s", gvErr.String())
}
return n, nil
}
func (t *TUN) Close() error {
return unix.Close(t.fd)
}
func (t *TUN) UseEndpoint() error {
ep, err := fdbased.New(&fdbased.Options{
FDs: []int{t.fd},
MTU: t.mtu,
// TUN only, ignore ethernet header.
EthernetHeader: false,
// SYS_READV support only for TUN fd.
PacketDispatchMode: fdbased.Readv,
// TAP/TUN fd's are not sockets and using the WritePackets calls results
// in errors as it always defaults to using SendMMsg which is not supported
// for tap/tun device fds.
//
// This CL changes WritePackets to gracefully degrade to using writev instead
// of sendmmsg if the underlying fd is not a socket.
//
// Fixed: https://github.com/google/gvisor/commit/f33d034fecd7723a1e560ccc62aeeba328454fd0
MaxSyscallHeaderBytes: 0x00,
})
if err != nil {
return fmt.Errorf("create endpoint: %w", err)
}
t.LinkEndpoint = ep
return nil
}
func (t *TUN) UseIOBased() error {
return nil
}
// Ref: wireguard tun/tun_linux.go setMTU.
func setMTU(name string, n uint32) error {
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer unix.Close(fd)
const ifReqSize = unix.IFNAMSIZ + 64
// do ioctl call
var ifr [ifReqSize]byte
copy(ifr[:], name)
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = n
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.SIOCSIFMTU),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return fmt.Errorf("failed to set MTU: %w", errno)
}
return nil
}

View File

@ -1,97 +0,0 @@
//go:build !linux
package tun
import (
"fmt"
"os"
"runtime"
"github.com/Dreamacro/clash/listener/tun/device"
"golang.zx2c4.com/wireguard/tun"
)
func Open(name string, mtu uint32) (_ device.Device, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("open tun: %v", r)
}
}()
t := &TUN{
name: name,
mtu: mtu,
offset: offset,
}
forcedMTU := defaultMTU
if t.mtu > 0 {
forcedMTU = int(t.mtu)
}
nt, err := newDevice(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way
// retry if abnormal exit at last time on Windows
if err != nil && runtime.GOOS == "windows" && os.IsExist(err) {
nt, err = newDevice(t.name, forcedMTU)
}
if err != nil {
return nil, fmt.Errorf("create tun: %w", err)
}
t.nt = nt.(*tun.NativeTun)
tunMTU, err := nt.MTU()
if err != nil {
return nil, fmt.Errorf("get mtu: %w", err)
}
t.mtu = uint32(tunMTU)
if t.offset > 0 {
t.cache = make([]byte, 65535)
}
return t, nil
}
func (t *TUN) Read(packet []byte) (int, error) {
if t.offset == 0 {
return t.nt.Read(packet, t.offset)
}
n, err := t.nt.Read(t.cache, t.offset)
copy(packet, t.cache[t.offset:t.offset+n])
return n, err
}
func (t *TUN) Write(packet []byte) (int, error) {
if t.offset == 0 {
return t.nt.Write(packet, t.offset)
}
packet = append(t.cache[:t.offset], packet...)
return t.nt.Write(packet, t.offset)
}
func (t *TUN) Close() error {
defer closeIO(t)
return t.nt.Close()
}
func (t *TUN) Name() string {
name, _ := t.nt.Name()
return name
}
func (t *TUN) UseEndpoint() error {
return newEq(t)
}
func (t *TUN) UseIOBased() error {
return nil
}

View File

@ -1,34 +0,0 @@
//go:build !linux && !no_gvisor
package tun
import (
"fmt"
"github.com/Dreamacro/clash/listener/tun/device/iobased"
"golang.zx2c4.com/wireguard/tun"
)
type TUN struct {
*iobased.Endpoint
nt *tun.NativeTun
mtu uint32
name string
offset int
cache []byte
}
func closeIO(t *TUN) {
if t.Endpoint != nil {
t.Endpoint.Close()
}
}
func newEq(t *TUN) error {
ep, err := iobased.New(t, t.mtu, t.offset)
if err != nil {
return fmt.Errorf("create endpoint: %w", err)
}
t.Endpoint = ep
return nil
}

View File

@ -1,24 +0,0 @@
//go:build !linux && no_gvisor
package tun
import (
"golang.zx2c4.com/wireguard/tun"
)
type TUN struct {
nt *tun.NativeTun
mtu uint32
name string
offset int
cache []byte
}
func closeIO(t *TUN) {
}
func newEq(t *TUN) error {
return nil
}

View File

@ -1,14 +0,0 @@
//go:build !linux && !windows
package tun
import "golang.zx2c4.com/wireguard/tun"
const (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)
func newDevice(name string, mtu int) (tun.Device, error) {
return tun.CreateTUN(name, mtu)
}

View File

@ -1,32 +0,0 @@
package tun
import (
"github.com/Dreamacro/clash/listener/tun/device/tun/driver"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun"
)
const (
offset = 0
defaultMTU = 0 /* auto */
)
func init() {
guid, _ := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}")
tun.WintunTunnelType = "Meta"
tun.WintunStaticRequestedGUID = &guid
}
func (t *TUN) LUID() uint64 {
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)
}

View File

@ -1,90 +0,0 @@
package commons
import (
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/log"
"github.com/vishvananda/netlink"
"go.uber.org/atomic"
"time"
)
var (
monitorStarted = atomic.NewBool(false)
monitorStop = make(chan struct{}, 2)
)
func StartDefaultInterfaceChangeMonitor() {
go func() {
if monitorStarted.Load() {
return
}
monitorStarted.Store(true)
done := make(chan struct{})
ch := make(chan netlink.RouteUpdate, 2)
err := netlink.RouteSubscribe(ch, done)
if err != nil {
log.Warnln("[TUN] auto detect interface fail: %s", err)
return
}
log.Debugln("[TUN] start auto detect interface monitor")
defer func() {
close(done)
monitorStarted.Store(false)
log.Debugln("[TUN] stop auto detect interface monitor")
}()
select {
case <-monitorStop:
default:
}
for {
select {
case <-monitorStop:
return
case <-ch:
}
interfaceName, err := GetAutoDetectInterface()
if err != nil {
t := time.NewTicker(2 * time.Second)
for {
select {
case ch <- <-ch:
break
case <-t.C:
interfaceName, err = GetAutoDetectInterface()
if err != nil {
continue
}
}
break
}
t.Stop()
}
if err != nil {
log.Debugln("[TUN] detect interface: %s", err)
continue
}
old := dialer.DefaultInterface.Load()
if interfaceName == old {
continue
}
dialer.DefaultInterface.Store(interfaceName)
iface.FlushCache()
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
}
}()
}
func StopDefaultInterfaceChangeMonitor() {
if monitorStarted.Load() {
monitorStop <- struct{}{}
}
}

View File

@ -1,67 +0,0 @@
//go:build !linux
package commons
import (
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/iface"
"github.com/Dreamacro/clash/log"
"go.uber.org/atomic"
"time"
)
var (
monitorDuration = 3 * time.Second
monitorStarted = atomic.NewBool(false)
monitorStop = make(chan struct{}, 2)
)
func StartDefaultInterfaceChangeMonitor() {
go func() {
if monitorStarted.Load() {
return
}
monitorStarted.Store(true)
t := time.NewTicker(monitorDuration)
log.Debugln("[TUN] start auto detect interface monitor")
defer func() {
monitorStarted.Store(false)
t.Stop()
log.Debugln("[TUN] stop auto detect interface monitor")
}()
select {
case <-monitorStop:
default:
}
for {
select {
case <-t.C:
interfaceName, err := GetAutoDetectInterface()
if err != nil {
log.Warnln("[TUN] default interface monitor err: %v", err)
continue
}
old := dialer.DefaultInterface.Load()
if interfaceName == old {
continue
}
dialer.DefaultInterface.Store(interfaceName)
iface.FlushCache()
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, interfaceName)
case <-monitorStop:
break
}
}
}()
}
func StopDefaultInterfaceChangeMonitor() {
if monitorStarted.Load() {
monitorStop <- struct{}{}
}
}

View File

@ -1,38 +0,0 @@
package commons
import (
"net/netip"
"time"
"github.com/Dreamacro/clash/component/resolver"
D "github.com/miekg/dns"
)
const DefaultDnsReadTimeout = time.Second * 10
func ShouldHijackDns(dnsAdds []netip.AddrPort, targetAddr netip.AddrPort) bool {
for _, addrPort := range dnsAdds {
if addrPort == targetAddr || (addrPort.Addr().IsUnspecified() && targetAddr.Port() == 53) {
return true
}
}
return false
}
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 {
m := new(D.Msg)
m.SetRcode(msg, D.RcodeServerFailure)
return m.Pack()
}
r.SetRcode(msg, r.Rcode)
r.Compress = true
return r.Pack()
}

View File

@ -1,46 +0,0 @@
package commons
import (
"fmt"
"github.com/Dreamacro/clash/log"
"net"
"time"
)
var (
defaultRoutes = []string{"1.0.0.0/8", "2.0.0.0/7", "4.0.0.0/6", "8.0.0.0/5", "16.0.0.0/4", "32.0.0.0/3", "64.0.0.0/2", "128.0.0.0/1"}
)
func ipv4MaskString(bits int) string {
m := net.CIDRMask(bits, 32)
if len(m) != 4 {
panic("ipv4Mask: len must be 4 bytes")
}
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
}
func WaitForTunClose(deviceName string) {
t := time.NewTicker(600 * time.Millisecond)
defer t.Stop()
log.Debugln("[TUN] waiting for device close")
for {
<-t.C
interfaces, err := net.Interfaces()
if err != nil {
break
}
found := false
for i := len(interfaces) - 1; i > -1; i-- {
if interfaces[i].Name == deviceName {
found = true
break
}
}
if !found {
break
}
}
}

View File

@ -1,140 +0,0 @@
package commons
import (
"fmt"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/log"
"github.com/vishvananda/netlink"
"net"
"net/netip"
)
func GetAutoDetectInterface() (ifn string, err error) {
routes, err := netlink.RouteGetWithOptions(
net.ParseIP("1.1.1.1"),
&netlink.RouteGetOptions{
Uid: &netlink.UID{Uid: 4294967295},
})
if err != nil {
return "", err
}
for _, route := range routes {
if lk, err := netlink.LinkByIndex(route.LinkIndex); err == nil {
return lk.Attrs().Name, nil
}
}
return "", fmt.Errorf("interface not found")
}
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
var (
interfaceName = dev.Name()
ip = addr.Masked().Addr().Next()
)
metaLink, err := netlink.LinkByName(interfaceName)
if err != nil {
return err
}
naddr, err := netlink.ParseAddr(addr.String())
if err != nil {
return err
}
if err = netlink.AddrAdd(metaLink, naddr); err != nil && err.Error() != "file exists" {
return err
}
if err = netlink.LinkSetUp(metaLink); err != nil {
return err
}
if autoRoute {
err = configInterfaceRouting(metaLink.Attrs().Index, interfaceName, ip)
}
return err
}
func configInterfaceRouting(index int, interfaceName string, ip netip.Addr) error {
const tableId = 1981801
var pref = 9000
for _, route := range defaultRoutes {
_, ipn, err := net.ParseCIDR(route)
if err != nil {
return err
}
if err := netlink.RouteAdd(&netlink.Route{
LinkIndex: index,
Scope: netlink.SCOPE_LINK,
Protocol: 2,
Src: ip.AsSlice(),
Dst: ipn,
Table: tableId,
}); err != nil {
return err
}
}
logIfErr := func(e error) {
if e != nil {
log.Warnln("[TOUTE] config route rule: %s", e)
}
}
var r *netlink.Rule
r = netlink.NewRule()
r.Table = 254
r.Priority = pref
logIfErr(netlink.RuleAdd(r))
pref += 10
r = netlink.NewRule()
_, nl, _ := net.ParseCIDR("0.0.0.0/32")
r.Table = tableId
r.Priority = pref
r.Src = nl
r.IifName = "lo"
r.UID = netlink.NewRuleUIDRange(0, 4294967294)
logIfErr(netlink.RuleAdd(r))
pref += 10
_, nl, _ = net.ParseCIDR(ip.String())
r.Priority = pref
r.Src = nl
logIfErr(netlink.RuleAdd(r))
pref += 10
r = netlink.NewRule()
r.Table = 254
r.Priority = pref
r.IifName = interfaceName
r.SuppressPrefixlen = 0
logIfErr(netlink.RuleAdd(r))
pref += 10
r = netlink.NewRule()
r.Table = tableId
r.Priority = pref
r.IifName = "lo"
r.SuppressPrefixlen = 0
r.Invert = true
logIfErr(netlink.RuleAdd(r))
return nil
}
func CleanupRule() {
r := netlink.NewRule()
for i := 0; i < 5; i++ {
r.Priority = 9000 + i*10
err := netlink.RuleDel(r)
if err != nil {
log.Warnln("[TOUTE] cleanup route rule: %s", err)
}
}
}

View File

@ -1,65 +0,0 @@
package commons
import (
"fmt"
"net/netip"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/listener/tun/device"
)
func GetAutoDetectInterface() (string, error) {
return cmd.ExecCmd("bash -c route -n get default | grep 'interface:' | awk -F ' ' 'NR==1{print $2}' | xargs echo -n")
}
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
if !addr.Addr().Is4() {
return fmt.Errorf("supported ipv4 only")
}
var (
interfaceName = dev.Name()
ip = addr.Masked().Addr().Next()
gw = ip.Next()
netmask = ipv4MaskString(addr.Bits())
)
cmdStr := fmt.Sprintf("ifconfig %s inet %s netmask %s %s", interfaceName, ip, netmask, gw)
_, err := cmd.ExecCmd(cmdStr)
if err != nil {
return err
}
_, err = cmd.ExecCmd(fmt.Sprintf("ipconfig set %s automatic-v6", interfaceName))
if err != nil {
return err
}
if autoRoute {
err = configInterfaceRouting(interfaceName, addr)
}
return err
}
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
var (
routes = append(defaultRoutes, addr.String())
gateway = addr.Masked().Addr().Next()
)
for _, destination := range routes {
if _, err := cmd.ExecCmd(fmt.Sprintf("route add -net %s %s", destination, gateway)); err != nil {
return err
}
}
return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
}
func execRouterCmd(action, inet, route string, interfaceName string) error {
_, err := cmd.ExecCmd(fmt.Sprintf("route %s %s %s -interface %s", action, inet, route, interfaceName))
return err
}
func CleanupRule() {}

View File

@ -1,89 +0,0 @@
//go:build !android
package commons
import (
"fmt"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/vishvananda/netlink"
"net"
"net/netip"
)
func GetAutoDetectInterface() (string, error) {
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
if err != nil {
return "", err
}
for _, route := range routes {
if route.Dst == nil {
lk, err := netlink.LinkByIndex(route.LinkIndex)
if err != nil {
return "", err
}
if lk.Type() == "tuntap" {
continue
}
return lk.Attrs().Name, nil
}
}
return "", fmt.Errorf("interface not found")
}
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
var (
interfaceName = dev.Name()
ip = addr.Masked().Addr().Next()
)
metaLink, err := netlink.LinkByName(interfaceName)
if err != nil {
return err
}
nlAddr, err := netlink.ParseAddr(addr.String())
if err != nil {
return err
}
if err = netlink.AddrAdd(metaLink, nlAddr); err != nil && err.Error() != "file exists" {
return err
}
if err = netlink.LinkSetUp(metaLink); err != nil {
return err
}
if autoRoute {
_ = configInterfaceRouting(metaLink.Attrs().Index, ip)
}
return nil
}
func configInterfaceRouting(index int, ip netip.Addr) error {
for _, route := range defaultRoutes {
_, ipn, err := net.ParseCIDR(route)
if err != nil {
return err
}
if err := netlink.RouteAdd(&netlink.Route{
LinkIndex: index,
Scope: netlink.SCOPE_LINK,
Protocol: 2,
Src: ip.AsSlice(),
Dst: ipn,
Table: 254,
}); err != nil {
return err
}
}
return nil
}
func CleanupRule() {}

View File

@ -1,21 +0,0 @@
//go:build !darwin && !linux && !windows
package commons
import (
"fmt"
"net/netip"
"runtime"
"github.com/Dreamacro/clash/listener/tun/device"
)
func GetAutoDetectInterface() (string, error) {
return "", fmt.Errorf("can not auto detect interface-name on this OS: %s, you must be detecting interface-name by manual", runtime.GOOS)
}
func ConfigInterfaceAddress(device.Device, netip.Prefix, int, bool) error {
return fmt.Errorf("unsupported on this OS: %s", runtime.GOOS)
}
func CleanupRule() {}

View File

@ -1,273 +0,0 @@
package commons
import (
"errors"
"fmt"
"net/netip"
"time"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/device/tun"
"github.com/Dreamacro/clash/log"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/windows/services"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
)
var wintunInterfaceName string
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 ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
retryOnFailure := services.StartedAtBoot()
tryTimes := 0
var err error
startOver:
if tryTimes > 0 {
log.Infoln("[TUN] retrying interface configuration after failure because system just booted (T+%v): %v", windows.DurationSinceBoot(), err)
time.Sleep(time.Second)
retryOnFailure = retryOnFailure && tryTimes < 15
}
tryTimes++
var (
luid = winipcfg.LUID(dev.(*tun.TUN).LUID())
ip = addr.Masked().Addr().Next()
gw = ip.Next()
addresses = []netip.Prefix{netip.PrefixFrom(ip, addr.Bits())}
dnsAddress = []netip.Addr{gw}
family4 = winipcfg.AddressFamily(windows.AF_INET)
familyV6 = winipcfg.AddressFamily(windows.AF_INET6)
currentFamily = winipcfg.AddressFamily(windows.AF_INET6)
)
if addr.Addr().Is4() {
currentFamily = winipcfg.AddressFamily(windows.AF_INET)
}
err = luid.FlushRoutes(windows.AF_INET6)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
err = luid.FlushIPAddresses(windows.AF_INET6)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
err = luid.FlushDNS(windows.AF_INET6)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
err = luid.FlushDNS(windows.AF_INET)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return err
}
foundDefault4 := false
foundDefault6 := false
if autoRoute {
var (
allowedIPs []netip.Prefix
// add default
routeArr = []string{"0.0.0.0/1"}
)
for _, route := range routeArr {
allowedIPs = append(allowedIPs, netip.MustParsePrefix(route))
}
estimatedRouteCount := len(allowedIPs)
routes := make(map[winipcfg.RouteData]bool, estimatedRouteCount)
for _, allowedip := range allowedIPs {
route := winipcfg.RouteData{
Destination: allowedip.Masked(),
Metric: 0,
}
if allowedip.Addr().Is4() {
if allowedip.Bits() == 0 {
foundDefault4 = true
}
route.NextHop = netip.IPv4Unspecified()
} else if allowedip.Addr().Is6() {
if allowedip.Bits() == 0 {
foundDefault6 = true
}
route.NextHop = netip.IPv6Unspecified()
}
routes[route] = true
}
deduplicatedRoutes := make([]*winipcfg.RouteData, 0, len(routes))
for route := range routes {
r := route
deduplicatedRoutes = append(deduplicatedRoutes, &r)
}
// add gateway
deduplicatedRoutes = append(deduplicatedRoutes, &winipcfg.RouteData{
Destination: addr.Masked(),
NextHop: gw,
Metric: 0,
})
err = luid.SetRoutesForFamily(currentFamily, 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(currentFamily, addresses)
if err == windows.ERROR_OBJECT_ALREADY_EXISTS {
cleanupAddressesOnDisconnectedInterfaces(currentFamily, addresses)
err = luid.SetIPAddressesForFamily(currentFamily, 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(family4)
if err != nil {
return err
}
ipif.ForwardingEnabled = true
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
ipif.DadTransmits = 0
ipif.ManagedAddressConfigurationSupported = false
ipif.OtherStatefulConfigurationSupported = false
if forceMTU > 0 {
ipif.NLMTU = uint32(forceMTU)
}
if foundDefault4 {
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
if forceMTU > 0 {
ipif6.NLMTU = uint32(forceMTU)
}
if foundDefault6 {
ipif6.UseAutomaticMetric = false
ipif6.Metric = 0
}
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(family4, dnsAddress, nil)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set DNS %s %s: %w", dnsAddress[0].String(), "nil", err)
}
wintunInterfaceName = dev.Name()
return nil
}
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []netip.Prefix) {
if len(addresses) == 0 {
return
}
addrHash := make(map[netip.Addr]bool, len(addresses))
for i := range addresses {
addrHash[addresses[i].Addr()] = 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 {
if ip := nnip.IpToAddr(address.Address.IP()); addrHash[ip] {
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength))
log.Infoln("[TUN] cleaning up stale address %s from interface %s", prefix.String(), iface.FriendlyName())
_ = iface.LUID.DeleteIPAddress(prefix)
}
}
}
}
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)
}
var destination netip.Prefix
if family == windows.AF_INET {
destination = netip.PrefixFrom(netip.IPv4Unspecified(), 0)
} else {
destination = netip.PrefixFrom(netip.IPv6Unspecified(), 0)
}
for _, iface := range interfaces {
if iface.OperStatus != winipcfg.IfOperStatusUp {
continue
}
ifname := iface.FriendlyName()
if wintunInterfaceName == ifname {
continue
}
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
nextHop := nnip.IpToAddr(gatewayAddress.Address.IP())
if _, err = iface.LUID.Route(destination, nextHop); err == nil {
return ifname, nil
}
}
}
return "", errors.New("ethernet interface not found")
}
func CleanupRule() {}

View File

@ -1,26 +0,0 @@
//go:build !no_gvisor
package adapter
import (
"net"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
// TCPConn implements the net.Conn interface.
type TCPConn interface {
net.Conn
// ID returns the transport endpoint id of TCPConn.
ID() *stack.TransportEndpointID
}
// UDPConn implements net.Conn and net.PacketConn.
type UDPConn interface {
net.Conn
net.PacketConn
// ID returns the transport endpoint id of UDPConn.
ID() *stack.TransportEndpointID
}

View File

@ -1,16 +0,0 @@
//go:build !no_gvisor
package adapter
// Handler is a TCP/UDP connection handler that implements
// HandleTCPConn and HandleUDPConn methods.
type Handler interface {
HandleTCP(TCPConn)
HandleUDP(UDPConn)
}
// TCPHandleFunc handles incoming TCP connection.
type TCPHandleFunc func(TCPConn)
// UDPHandleFunc handles incoming UDP connection.
type UDPHandleFunc func(UDPConn)

View File

@ -1,148 +0,0 @@
//go:build !no_gvisor
package gvisor
import (
"encoding/binary"
"io"
"net"
"net/netip"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/transport/socks5"
)
var _ adapter.Handler = (*gvHandler)(nil)
type gvHandler struct {
gateway netip.Addr
dnsHijack []netip.AddrPort
tcpIn chan<- C.ConnContext
udpIn chan<- *inbound.PacketAdapter
}
func (gh *gvHandler) HandleTCP(tunConn adapter.TCPConn) {
id := tunConn.ID()
rAddr := &net.TCPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
Zone: "",
}
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
go func() {
buf := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buf)
_ = tunConn.Close()
}()
for {
if tunConn.SetReadDeadline(time.Now().Add(D.DefaultDnsReadTimeout)) != nil {
break
}
length := uint16(0)
if err := binary.Read(tunConn, binary.BigEndian, &length); err != nil {
break
}
if int(length) > len(buf) {
break
}
n, err := io.ReadFull(tunConn, buf[:length])
if err != nil {
break
}
msg, err := D.RelayDnsPacket(buf[:n])
if err != nil {
break
}
err = binary.Write(tunConn, binary.BigEndian, uint16(len(msg)))
if err != nil {
break
}
_, _ = tunConn.Write(msg)
}
}()
return
}
gh.tcpIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
}
func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
id := tunConn.ID()
rAddr := &net.UDPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
Zone: "",
}
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), id.LocalPort)
if rAddrPort.Addr() == gh.gateway {
_ = tunConn.Close()
return
}
target := socks5.ParseAddrToSocksAddr(rAddr)
go func() {
for {
buf := pool.Get(pool.UDPBufferSize)
n, addr, err := tunConn.ReadFrom(buf)
if err != nil {
_ = pool.Put(buf)
break
}
payload := buf[:n]
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
go func() {
defer func() {
_ = pool.Put(buf)
}()
msg, err1 := D.RelayDnsPacket(payload)
if err1 != nil {
return
}
_, _ = tunConn.WriteTo(msg, addr)
}()
continue
}
gvPacket := &packet{
pc: tunConn,
rAddr: addr,
payload: payload,
}
select {
case gh.udpIn <- inbound.NewPacket(target, gvPacket, C.TUN):
default:
}
}
}()
}

View File

@ -1,60 +0,0 @@
//go:build !no_gvisor
package gvisor
import (
"fmt"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// nicPromiscuousModeEnabled is the value used by stack to enable
// or disable NIC's promiscuous mode.
nicPromiscuousModeEnabled = true
// nicSpoofingEnabled is the value used by stack to enable or disable
// NIC's spoofing.
nicSpoofingEnabled = true
)
// withCreatingNIC creates NIC for stack.
func withCreatingNIC(nicID tcpip.NICID, ep stack.LinkEndpoint) option.Option {
return func(s *stack.Stack) error {
if err := s.CreateNICWithOptions(nicID, ep,
stack.NICOptions{
Disabled: false,
// If no queueing discipline was specified
// provide a stub implementation that just
// delegates to the lower link endpoint.
QDisc: nil,
}); err != nil {
return fmt.Errorf("create NIC: %s", err)
}
return nil
}
}
// withPromiscuousMode sets promiscuous mode in the given NICs.
func withPromiscuousMode(nicID tcpip.NICID, v bool) option.Option {
return func(s *stack.Stack) error {
if err := s.SetPromiscuousMode(nicID, v); err != nil {
return fmt.Errorf("set promiscuous mode: %s", err)
}
return nil
}
}
// withSpoofing sets address spoofing in the given NICs, allowing
// endpoints to bind to any address in the NIC.
func withSpoofing(nicID tcpip.NICID, v bool) option.Option {
return func(s *stack.Stack) error {
if err := s.SetSpoofing(nicID, v); err != nil {
return fmt.Errorf("set spoofing: %s", err)
}
return nil
}
}

View File

@ -1,260 +0,0 @@
package option
import (
"fmt"
"golang.org/x/time/rate"
"gvisor.dev/gvisor/pkg/tcpip"
"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"
"github.com/Dreamacro/clash/common/pool"
)
const (
// defaultTimeToLive specifies the default TTL used by stack.
defaultTimeToLive uint8 = 64
// ipForwardingEnabled is the value used by stack to enable packet
// forwarding between NICs.
ipForwardingEnabled = true
// icmpBurst is the default number of ICMP messages that can be sent in
// a single burst.
icmpBurst = 50
// icmpLimit is the default maximum number of ICMP messages permitted
// by this rate limiter.
icmpLimit rate.Limit = 1000
// tcpCongestionControl is the congestion control algorithm used by
// stack. ccReno is the default option in gVisor stack.
tcpCongestionControlAlgorithm = "reno" // "reno" or "cubic"
// tcpDelayEnabled is the value used by stack to enable or disable
// tcp delay option. Disable Nagle's algorithm here by default.
tcpDelayEnabled = false
// tcpModerateReceiveBufferEnabled is the value used by stack to
// enable or disable tcp receive buffer auto-tuning option.
tcpModerateReceiveBufferEnabled = false
// tcpSACKEnabled is the value used by stack to enable or disable
// tcp selective ACK.
tcpSACKEnabled = true
// tcpRecovery is the loss detection algorithm used by TCP.
tcpRecovery = tcpip.TCPRACKLossDetection
// tcpMinBufferSize is the smallest size of a send/recv buffer.
tcpMinBufferSize = tcp.MinBufferSize
// tcpMaxBufferSize is the maximum permitted size of a send/recv buffer.
tcpMaxBufferSize = pool.RelayBufferSize
// tcpDefaultBufferSize is the default size of the send buffer for
// a transport endpoint.
tcpDefaultSendBufferSize = pool.RelayBufferSize
// tcpDefaultReceiveBufferSize is the default size of the receive buffer
// for a transport endpoint.
tcpDefaultReceiveBufferSize = pool.RelayBufferSize
)
type Option func(*stack.Stack) error
// WithDefault sets all default values for stack.
func WithDefault() Option {
return func(s *stack.Stack) error {
opts := []Option{
WithDefaultTTL(defaultTimeToLive),
WithForwarding(ipForwardingEnabled),
// Config default stack ICMP settings.
WithICMPBurst(icmpBurst), WithICMPLimit(icmpLimit),
// We expect no packet loss, therefore we can bump buffers.
// Too large buffers thrash cache, so there is little point
// in too large buffers.
//
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
WithTCPSendBufferSizeRange(tcpMinBufferSize, tcpDefaultSendBufferSize, tcpMaxBufferSize),
WithTCPReceiveBufferSizeRange(tcpMinBufferSize, tcpDefaultReceiveBufferSize, tcpMaxBufferSize),
WithTCPCongestionControl(tcpCongestionControlAlgorithm),
WithTCPDelay(tcpDelayEnabled),
// Receive Buffer Auto-Tuning Option, see:
// https://github.com/google/gvisor/issues/1666
WithTCPModerateReceiveBuffer(tcpModerateReceiveBufferEnabled),
// TCP selective ACK Option, see:
// https://tools.ietf.org/html/rfc2018
WithTCPSACKEnabled(tcpSACKEnabled),
// TCPRACKLossDetection: indicates RACK is used for loss detection and
// recovery.
//
// TCPRACKStaticReoWnd: indicates the reordering window should not be
// adjusted when DSACK is received.
//
// TCPRACKNoDupTh: indicates RACK should not consider the classic three
// duplicate acknowledgements rule to mark the segments as lost. This
// is used when reordering is not detected.
WithTCPRecovery(tcpRecovery),
}
for _, opt := range opts {
if err := opt(s); err != nil {
return err
}
}
return nil
}
}
// WithDefaultTTL sets the default TTL used by stack.
func WithDefaultTTL(ttl uint8) Option {
return func(s *stack.Stack) error {
opt := tcpip.DefaultTTLOption(ttl)
if err := s.SetNetworkProtocolOption(ipv4.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set ipv4 default TTL: %s", err)
}
if err := s.SetNetworkProtocolOption(ipv6.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set ipv6 default TTL: %s", err)
}
return nil
}
}
// WithForwarding sets packet forwarding between NICs for IPv4 & IPv6.
func WithForwarding(v bool) Option {
return func(s *stack.Stack) error {
if err := s.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, v); err != nil {
return fmt.Errorf("set ipv4 forwarding: %s", err)
}
if err := s.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, v); err != nil {
return fmt.Errorf("set ipv6 forwarding: %s", err)
}
return nil
}
}
// WithICMPBurst sets the number of ICMP messages that can be sent
// in a single burst.
func WithICMPBurst(burst int) Option {
return func(s *stack.Stack) error {
s.SetICMPBurst(burst)
return nil
}
}
// WithICMPLimit sets the maximum number of ICMP messages permitted
// by rate limiter.
func WithICMPLimit(limit rate.Limit) Option {
return func(s *stack.Stack) error {
s.SetICMPLimit(limit)
return nil
}
}
// WithTCPSendBufferSize sets default the send buffer size for TCP.
func WithTCPSendBufferSize(size int) Option {
return func(s *stack.Stack) error {
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: tcpMinBufferSize, Default: size, Max: tcpMaxBufferSize}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
}
return nil
}
}
// WithTCPSendBufferSizeRange sets the send buffer size range for TCP.
func WithTCPSendBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error {
sndOpt := tcpip.TCPSendBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &sndOpt); err != nil {
return fmt.Errorf("set TCP send buffer size range: %s", err)
}
return nil
}
}
// WithTCPReceiveBufferSize sets the default receive buffer size for TCP.
func WithTCPReceiveBufferSize(size int) Option {
return func(s *stack.Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: tcpMinBufferSize, Default: size, Max: tcpMaxBufferSize}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err)
}
return nil
}
}
// WithTCPReceiveBufferSizeRange sets the receive buffer size range for TCP.
func WithTCPReceiveBufferSizeRange(a, b, c int) Option {
return func(s *stack.Stack) error {
rcvOpt := tcpip.TCPReceiveBufferSizeRangeOption{Min: a, Default: b, Max: c}
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &rcvOpt); err != nil {
return fmt.Errorf("set TCP receive buffer size range: %s", err)
}
return nil
}
}
// WithTCPCongestionControl sets the current congestion control algorithm.
func WithTCPCongestionControl(cc string) Option {
return func(s *stack.Stack) error {
opt := tcpip.CongestionControlOption(cc)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP congestion control algorithm: %s", err)
}
return nil
}
}
// WithTCPDelay enables or disables Nagle's algorithm in TCP.
func WithTCPDelay(v bool) Option {
return func(s *stack.Stack) error {
opt := tcpip.TCPDelayEnabled(v)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP delay: %s", err)
}
return nil
}
}
// WithTCPModerateReceiveBuffer sets receive buffer moderation for TCP.
func WithTCPModerateReceiveBuffer(v bool) Option {
return func(s *stack.Stack) error {
opt := tcpip.TCPModerateReceiveBufferOption(v)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP moderate receive buffer: %s", err)
}
return nil
}
}
// WithTCPSACKEnabled sets the SACK option for TCP.
func WithTCPSACKEnabled(v bool) Option {
return func(s *stack.Stack) error {
opt := tcpip.TCPSACKEnabled(v)
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
return fmt.Errorf("set TCP SACK: %s", err)
}
return nil
}
}
// WithTCPRecovery sets the recovery option for TCP.
func WithTCPRecovery(v tcpip.TCPRecovery) Option {
return func(s *stack.Stack) error {
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &v); err != nil {
return fmt.Errorf("set TCP Recovery: %s", err)
}
return nil
}
}

View File

@ -1,27 +0,0 @@
//go:build !no_gvisor
package gvisor
import (
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
func withRouteTable(nicID tcpip.NICID) option.Option {
return func(s *stack.Stack) error {
s.SetRouteTable([]tcpip.Route{
{
Destination: header.IPv4EmptySubnet,
NIC: nicID,
},
{
Destination: header.IPv6EmptySubnet,
NIC: nicID,
},
})
return nil
}
}

View File

@ -1,112 +0,0 @@
//go:build !no_gvisor
// Package gvisor provides a thin wrapper around a gVisor's stack.
package gvisor
import (
"net/netip"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"gvisor.dev/gvisor/pkg/tcpip"
"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/icmp"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
type gvStack struct {
*stack.Stack
device device.Device
}
func (s *gvStack) Close() error {
var err error
if s.device != nil {
err = s.device.Close()
}
if s.Stack != nil {
s.Stack.Close()
s.Stack.Wait()
}
return err
}
// New allocates a new *gvStack with given options.
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter, opts ...option.Option) (ipstack.Stack, error) {
s := &gvStack{
Stack: stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
ipv4.NewProtocol,
ipv6.NewProtocol,
},
TransportProtocols: []stack.TransportProtocolFactory{
tcp.NewProtocol,
udp.NewProtocol,
icmp.NewProtocol4,
icmp.NewProtocol6,
},
}),
device: device,
}
handler := &gvHandler{
gateway: tunAddress.Masked().Addr().Next(),
dnsHijack: dnsHijack,
tcpIn: tcpIn,
udpIn: udpIn,
}
// Generate unique NIC id.
nicID := tcpip.NICID(s.Stack.UniqueID())
defaultOpts := []option.Option{option.WithDefault()}
defaultOpts = append(defaultOpts, opts...)
opts = append(defaultOpts,
// Create stack NIC and then bind link endpoint to it.
withCreatingNIC(nicID, device),
// In the past we did s.AddAddressRange to assign 0.0.0.0/0
// onto the interface. We need that to be able to terminate
// all the incoming connections - to any ip. AddressRange API
// has been removed and the suggested workaround is to use
// Promiscuous mode. https://github.com/google/gvisor/issues/3876
//
// Ref: https://github.com/cloudflare/slirpnetstack/blob/master/stack.go
withPromiscuousMode(nicID, nicPromiscuousModeEnabled),
// Enable spoofing if a stack may send packets from unowned
// addresses. This change required changes to some netgophers
// since previously, promiscuous mode was enough to let the
// netstack respond to all incoming packets regardless of the
// packet's destination address. Now that a stack.Route is not
// held for each incoming packet, finding a route may fail with
// local addresses we don't own but accepted packets for while
// in promiscuous mode. Since we also want to be able to send
// from any address (in response the received promiscuous mode
// packets), we need to enable spoofing.
//
// Ref: https://github.com/google/gvisor/commit/8c0701462a84ff77e602f1626aec49479c308127
withSpoofing(nicID, nicSpoofingEnabled),
// Add default route table for IPv4 and IPv6. This will handle
// all incoming ICMP packets.
withRouteTable(nicID),
// Initiate transport protocol (TCP/UDP) with given handler.
withTCPHandler(handler.HandleTCP), withUDPHandler(handler.HandleUDP),
)
for _, opt := range opts {
if err := opt(s.Stack); err != nil {
return nil, err
}
}
return s, nil
}

View File

@ -1,19 +0,0 @@
//go:build no_gvisor
package gvisor
import (
"fmt"
"github.com/Dreamacro/clash/adapter/inbound"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/log"
"net/netip"
)
// New allocates a new *gvStack with given options.
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
log.Fatalln("unsupported gvisor stack on the build")
return nil, fmt.Errorf("unsupported gvisor stack on the build")
}

View File

@ -1,151 +0,0 @@
//go:build !no_gvisor
package gvisor
import (
"net"
"time"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"github.com/Dreamacro/clash/log"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
)
const (
// defaultWndSize if set to zero, the default
// receive window buffer size is used instead.
defaultWndSize = pool.RelayBufferSize
// maxConnAttempts specifies the maximum number
// of in-flight tcp connection attempts.
maxConnAttempts = 1 << 10
// tcpKeepaliveCount is the maximum number of
// TCP keep-alive probes to send before giving up
// and killing the connection if no response is
// obtained from the other end.
tcpKeepaliveCount = 8
// tcpKeepaliveIdle specifies the time a connection
// must remain idle before the first TCP keepalive
// packet is sent. Once this time is reached,
// tcpKeepaliveInterval option is used instead.
tcpKeepaliveIdle = 60 * time.Second
// tcpKeepaliveInterval specifies the interval
// time between sending TCP keepalive packets.
tcpKeepaliveInterval = 30 * time.Second
)
func withTCPHandler(handle adapter.TCPHandleFunc) option.Option {
return func(s *stack.Stack) error {
tcpForwarder := tcp.NewForwarder(s, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
var (
wq waiter.Queue
ep tcpip.Endpoint
err tcpip.Error
id = r.ID()
)
defer func() {
if err != nil {
log.Warnln("[STACK] forward tcp request %s:%d->%s:%d: %s", id.RemoteAddress, id.RemotePort, id.LocalAddress, id.LocalPort, err)
}
}()
// Perform a TCP three-way handshake.
ep, err = r.CreateEndpoint(&wq)
if err != nil {
// RST: prevent potential half-open TCP connection leak.
r.Complete(true)
return
}
err = setSocketOptions(s, ep)
if err != nil {
ep.Close()
r.Complete(true)
return
}
defer r.Complete(false)
conn := &tcpConn{
TCPConn: gonet.NewTCPConn(&wq, ep),
id: id,
}
if conn.RemoteAddr() == nil {
log.Warnln("[STACK] endpoint is not connected, current state: %v", tcp.EndpointState(ep.State()))
_ = conn.Close()
return
}
handle(conn)
})
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
return nil
}
}
func setSocketOptions(s *stack.Stack, ep tcpip.Endpoint) tcpip.Error {
{ /* TCP keepalive options */
ep.SocketOptions().SetKeepAlive(true)
idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
if err := ep.SetSockOpt(&idle); err != nil {
return err
}
interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval)
if err := ep.SetSockOpt(&interval); err != nil {
return err
}
if err := ep.SetSockOptInt(tcpip.KeepaliveCountOption, tcpKeepaliveCount); err != nil {
return err
}
}
{ /* TCP recv/send buffer size */
var ss tcpip.TCPSendBufferSizeRangeOption
if err := s.TransportProtocolOption(header.TCPProtocolNumber, &ss); err == nil {
ep.SocketOptions().SetReceiveBufferSize(int64(ss.Default), false)
}
var rs tcpip.TCPReceiveBufferSizeRangeOption
if err := s.TransportProtocolOption(header.TCPProtocolNumber, &rs); err == nil {
ep.SocketOptions().SetReceiveBufferSize(int64(rs.Default), false)
}
}
return nil
}
type tcpConn struct {
*gonet.TCPConn
id stack.TransportEndpointID
}
func (c *tcpConn) ID() *stack.TransportEndpointID {
return &c.id
}
func (c *tcpConn) LocalAddr() net.Addr {
return &net.TCPAddr{
IP: net.IP(c.id.LocalAddress),
Port: int(c.id.LocalPort),
}
}
func (c *tcpConn) RemoteAddr() net.Addr {
return &net.TCPAddr{
IP: net.IP(c.id.RemoteAddress),
Port: int(c.id.RemotePort),
}
}

View File

@ -1,88 +0,0 @@
//go:build !no_gvisor
package gvisor
import (
"net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"github.com/Dreamacro/clash/log"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
"gvisor.dev/gvisor/pkg/waiter"
)
func withUDPHandler(handle adapter.UDPHandleFunc) option.Option {
return func(s *stack.Stack) error {
udpForwarder := udp.NewForwarder(s, func(r *udp.ForwarderRequest) {
var (
wq waiter.Queue
id = r.ID()
)
ep, err := r.CreateEndpoint(&wq)
if err != nil {
log.Warnln("[STACK] udp forwarder request %s:%d->%s:%d: %s", id.RemoteAddress, id.RemotePort, id.LocalAddress, id.LocalPort, err)
return
}
conn := &udpConn{
UDPConn: gonet.NewUDPConn(s, &wq, ep),
id: id,
}
handle(conn)
})
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
return nil
}
}
type udpConn struct {
*gonet.UDPConn
id stack.TransportEndpointID
}
func (c *udpConn) ID() *stack.TransportEndpointID {
return &c.id
}
func (c *udpConn) LocalAddr() net.Addr {
return &net.UDPAddr{
IP: net.IP(c.id.LocalAddress),
Port: int(c.id.LocalPort),
}
}
func (c *udpConn) RemoteAddr() net.Addr {
return &net.UDPAddr{
IP: net.IP(c.id.RemoteAddress),
Port: int(c.id.RemotePort),
}
}
type packet struct {
pc adapter.UDPConn
rAddr net.Addr
payload []byte
}
func (c *packet) Data() []byte {
return c.payload
}
// WriteBack write UDP packet with source(ip, port) = `addr`
func (c *packet) WriteBack(b []byte, _ net.Addr) (n int, err error) {
return c.pc.WriteTo(b, c.rAddr)
}
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.rAddr
}
func (c *packet) Drop() {
_ = pool.Put(c.payload)
}

View File

@ -1,5 +0,0 @@
package ipstack
import "io"
type Stack io.Closer

View File

@ -1,40 +0,0 @@
package mars
import (
"io"
"net/netip"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/nat"
)
type StackListener struct {
device io.Closer
tcp *nat.TCP
udp *nat.UDP
}
func StartListener(device io.ReadWriteCloser, gateway, portal, broadcast netip.Addr) (*StackListener, error) {
tcp, udp, err := nat.Start(device, gateway, portal, broadcast)
if err != nil {
return nil, err
}
return &StackListener{
device: device,
tcp: tcp,
udp: udp,
}, nil
}
func (t *StackListener) Close() error {
_ = t.udp.Close()
return t.tcp.Close()
}
func (t *StackListener) TCP() *nat.TCP {
return t.tcp
}
func (t *StackListener) UDP() *nat.UDP {
return t.udp
}

View File

@ -1,195 +0,0 @@
package nat
import (
log "github.com/sirupsen/logrus"
"io"
"net"
"net/netip"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
)
func Start(device io.ReadWriter, gateway, portal, broadcast netip.Addr) (*TCP, *UDP, error) {
if !portal.Is4() || !gateway.Is4() {
return nil, nil, net.InvalidAddrError("only ipv4 supported")
}
listener, err := net.ListenTCP("tcp4", nil)
if err != nil {
return nil, nil, err
}
tab := newTable()
udp := &UDP{
device: device,
buf: [pool.UDPBufferSize]byte{},
}
tcp := &TCP{
listener: listener,
portal: portal,
table: tab,
}
gatewayPort := uint16(listener.Addr().(*net.TCPAddr).Port)
go func() {
defer func() {
_ = tcp.Close()
_ = udp.Close()
}()
buf := make([]byte, pool.RelayBufferSize)
for {
n, err := device.Read(buf)
if err != nil {
log.Errorf("system error:%s", err.Error())
return
}
raw := buf[:n]
var (
ipVersion int
ip tcpip.IP
)
ipVersion = tcpip.IPVersion(raw)
switch ipVersion {
case tcpip.IPv4Version:
ipv4 := tcpip.IPv4Packet(raw)
if !ipv4.Valid() {
continue
}
if ipv4.TimeToLive() == 0x00 {
continue
}
if ipv4.Flags()&tcpip.FlagMoreFragment != 0 {
continue
}
if ipv4.FragmentOffset() != 0 {
continue
}
ip = ipv4
case tcpip.IPv6Version:
ipv6 := tcpip.IPv6Packet(raw)
if !ipv6.Valid() {
continue
}
if ipv6.HopLimit() == 0x00 {
continue
}
ip = ipv6
default:
continue
}
destinationIP := ip.DestinationIP()
if !destinationIP.IsGlobalUnicast() || destinationIP == broadcast {
continue
}
switch ip.Protocol() {
case tcpip.TCP:
t := tcpip.TCPPacket(ip.Payload())
if !t.Valid() {
continue
}
if destinationIP == portal {
if ip.SourceIP() == gateway && t.SourcePort() == gatewayPort {
tup := tab.tupleOf(t.DestinationPort())
if tup == zeroTuple {
continue
}
ip.SetSourceIP(tup.DestinationAddr.Addr())
t.SetSourcePort(tup.DestinationAddr.Port())
ip.SetDestinationIP(tup.SourceAddr.Addr())
t.SetDestinationPort(tup.SourceAddr.Port())
ip.DecTimeToLive()
ip.ResetChecksum()
t.ResetChecksum(ip.PseudoSum())
_, _ = device.Write(raw)
}
} else {
tup := tuple{
SourceAddr: netip.AddrPortFrom(ip.SourceIP(), t.SourcePort()),
DestinationAddr: netip.AddrPortFrom(destinationIP, t.DestinationPort()),
}
port := tab.portOf(tup)
if port == 0 {
if t.Flags() != tcpip.TCPSyn {
continue
}
port = tab.newConn(tup)
}
ip.SetSourceIP(portal)
ip.SetDestinationIP(gateway)
t.SetSourcePort(port)
t.SetDestinationPort(gatewayPort)
ip.ResetChecksum()
t.ResetChecksum(ip.PseudoSum())
_, _ = device.Write(raw)
}
case tcpip.UDP:
u := tcpip.UDPPacket(ip.Payload())
if !u.Valid() {
continue
}
udp.handleUDPPacket(ip, u)
case tcpip.ICMP:
i := tcpip.ICMPPacket(ip.Payload())
if i.Type() != tcpip.ICMPTypePingRequest || i.Code() != 0 {
continue
}
i.SetType(tcpip.ICMPTypePingResponse)
ip.SetDestinationIP(ip.SourceIP())
ip.SetSourceIP(destinationIP)
ip.ResetChecksum()
i.ResetChecksum()
_, _ = device.Write(raw)
case tcpip.ICMPv6:
i := tcpip.ICMPv6Packet(ip.Payload())
if i.Type() != tcpip.ICMPv6EchoRequest || i.Code() != 0 {
continue
}
i.SetType(tcpip.ICMPv6EchoReply)
ip.SetDestinationIP(ip.SourceIP())
ip.SetSourceIP(destinationIP)
ip.ResetChecksum()
i.ResetChecksum(ip.PseudoSum())
_, _ = device.Write(raw)
}
}
}()
return tcp, udp, nil
}

View File

@ -1,84 +0,0 @@
package nat
import (
"net/netip"
"github.com/Dreamacro/clash/common/generics/list"
)
const (
portBegin = 30000
portLength = 10240
)
var zeroTuple = tuple{}
type tuple struct {
SourceAddr netip.AddrPort
DestinationAddr netip.AddrPort
}
type binding struct {
tuple tuple
offset uint16
}
type table struct {
tuples map[tuple]*list.Element[*binding]
ports [portLength]*list.Element[*binding]
available *list.List[*binding]
}
func (t *table) tupleOf(port uint16) tuple {
offset := port - portBegin
if offset > portLength {
return zeroTuple
}
elm := t.ports[offset]
t.available.MoveToFront(elm)
return elm.Value.tuple
}
func (t *table) portOf(tuple tuple) uint16 {
elm := t.tuples[tuple]
if elm == nil {
return 0
}
t.available.MoveToFront(elm)
return portBegin + elm.Value.offset
}
func (t *table) newConn(tuple tuple) uint16 {
elm := t.available.Back()
b := elm.Value
delete(t.tuples, b.tuple)
t.tuples[tuple] = elm
b.tuple = tuple
t.available.MoveToFront(elm)
return portBegin + b.offset
}
func newTable() *table {
result := &table{
tuples: make(map[tuple]*list.Element[*binding], portLength),
ports: [portLength]*list.Element[*binding]{},
available: list.New[*binding](),
}
for idx := range result.ports {
result.ports[idx] = result.available.PushFront(&binding{
tuple: tuple{},
offset: uint16(idx),
})
}
return result
}

View File

@ -1,67 +0,0 @@
package nat
import (
"net"
"net/netip"
"time"
)
type TCP struct {
listener *net.TCPListener
portal netip.Addr
table *table
}
type conn struct {
net.Conn
tuple tuple
}
func (t *TCP) Accept() (net.Conn, error) {
c, err := t.listener.AcceptTCP()
if err != nil {
return nil, err
}
addr := c.RemoteAddr().(*net.TCPAddr)
tup := t.table.tupleOf(uint16(addr.Port))
if !addr.IP.Equal(t.portal.AsSlice()) || tup == zeroTuple {
_ = c.Close()
return nil, net.InvalidAddrError("unknown remote addr")
}
addition(c)
return &conn{
Conn: c,
tuple: tup,
}, nil
}
func (t *TCP) Close() error {
return t.listener.Close()
}
func (t *TCP) Addr() net.Addr {
return t.listener.Addr()
}
func (t *TCP) SetDeadline(time time.Time) error {
return t.listener.SetDeadline(time)
}
func (c *conn) LocalAddr() net.Addr {
return &net.TCPAddr{
IP: c.tuple.SourceAddr.Addr().AsSlice(),
Port: int(c.tuple.SourceAddr.Port()),
}
}
func (c *conn) RemoteAddr() net.Addr {
return &net.TCPAddr{
IP: c.tuple.DestinationAddr.Addr().AsSlice(),
Port: int(c.tuple.DestinationAddr.Port()),
}
}

View File

@ -1,15 +0,0 @@
package nat
import (
"net"
"syscall"
)
func addition(c *net.TCPConn) {
sys, err := c.SyscallConn()
if err == nil {
_ = sys.Control(func(fd uintptr) {
_ = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_NO_CHECK, 1)
})
}
}

View File

@ -1,7 +0,0 @@
//go:build !linux
package nat
import "net"
func addition(*net.TCPConn) {}

View File

@ -1,145 +0,0 @@
package nat
import (
"io"
"math/rand"
"net"
"net/netip"
"sync"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/tcpip"
)
type call struct {
cond *sync.Cond
buf []byte
n int
source net.Addr
destination net.Addr
}
type UDP struct {
closed bool
device io.Writer
queueLock sync.Mutex
queue []*call
bufLock sync.Mutex
buf [pool.UDPBufferSize]byte
}
func (u *UDP) ReadFrom(buf []byte) (int, net.Addr, net.Addr, error) {
u.queueLock.Lock()
defer u.queueLock.Unlock()
for !u.closed {
c := &call{
cond: sync.NewCond(&u.queueLock),
buf: buf,
n: -1,
source: nil,
destination: nil,
}
u.queue = append(u.queue, c)
c.cond.Wait()
if c.n >= 0 {
return c.n, c.source, c.destination, nil
}
}
return -1, nil, nil, net.ErrClosed
}
func (u *UDP) WriteTo(buf []byte, local net.Addr, remote net.Addr) (int, error) {
if u.closed {
return 0, net.ErrClosed
}
u.bufLock.Lock()
defer u.bufLock.Unlock()
if len(buf) > 0xffff {
return 0, net.InvalidAddrError("invalid ip version")
}
srcAddr, srcOk := local.(*net.UDPAddr)
dstAddr, dstOk := remote.(*net.UDPAddr)
if !srcOk || !dstOk {
return 0, net.InvalidAddrError("invalid addr")
}
srcAddrPort := netip.AddrPortFrom(nnip.IpToAddr(srcAddr.IP), uint16(srcAddr.Port))
dstAddrPort := netip.AddrPortFrom(nnip.IpToAddr(dstAddr.IP), uint16(dstAddr.Port))
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
return 0, net.InvalidAddrError("invalid ip version")
}
tcpip.SetIPv4(u.buf[:])
ip := tcpip.IPv4Packet(u.buf[:])
ip.SetHeaderLen(tcpip.IPv4HeaderSize)
ip.SetTotalLength(tcpip.IPv4HeaderSize + tcpip.UDPHeaderSize + uint16(len(buf)))
ip.SetTypeOfService(0)
ip.SetIdentification(uint16(rand.Uint32()))
ip.SetFragmentOffset(0)
ip.SetTimeToLive(64)
ip.SetProtocol(tcpip.UDP)
ip.SetSourceIP(srcAddrPort.Addr())
ip.SetDestinationIP(dstAddrPort.Addr())
udp := tcpip.UDPPacket(ip.Payload())
udp.SetLength(tcpip.UDPHeaderSize + uint16(len(buf)))
udp.SetSourcePort(srcAddrPort.Port())
udp.SetDestinationPort(dstAddrPort.Port())
copy(udp.Payload(), buf)
ip.ResetChecksum()
udp.ResetChecksum(ip.PseudoSum())
return u.device.Write(u.buf[:ip.TotalLen()])
}
func (u *UDP) Close() error {
u.queueLock.Lock()
defer u.queueLock.Unlock()
u.closed = true
for _, c := range u.queue {
c.cond.Signal()
}
return nil
}
func (u *UDP) handleUDPPacket(ip tcpip.IP, pkt tcpip.UDPPacket) {
var c *call
u.queueLock.Lock()
if len(u.queue) > 0 {
idx := len(u.queue) - 1
c = u.queue[idx]
u.queue = u.queue[:idx]
}
u.queueLock.Unlock()
if c != nil {
c.source = &net.UDPAddr{
IP: ip.SourceIP().AsSlice(),
Port: int(pkt.SourcePort()),
}
c.destination = &net.UDPAddr{
IP: ip.DestinationIP().AsSlice(),
Port: int(pkt.DestinationPort()),
}
c.n = copy(c.buf, pkt.Payload())
c.cond.Signal()
}
}

View File

@ -1,40 +0,0 @@
package tcpip
import (
"encoding/binary"
)
type ICMPType = byte
const (
ICMPTypePingRequest byte = 0x8
ICMPTypePingResponse byte = 0x0
)
type ICMPPacket []byte
func (p ICMPPacket) Type() ICMPType {
return p[0]
}
func (p ICMPPacket) SetType(v ICMPType) {
p[0] = v
}
func (p ICMPPacket) Code() byte {
return p[1]
}
func (p ICMPPacket) Checksum() uint16 {
return binary.BigEndian.Uint16(p[2:])
}
func (p ICMPPacket) SetChecksum(sum [2]byte) {
p[2] = sum[0]
p[3] = sum[1]
}
func (p ICMPPacket) ResetChecksum() {
p.SetChecksum(zeroChecksum)
p.SetChecksum(Checksum(0, p))
}

View File

@ -1,172 +0,0 @@
package tcpip
import (
"encoding/binary"
)
type ICMPv6Packet []byte
const (
ICMPv6HeaderSize = 4
ICMPv6MinimumSize = 8
ICMPv6PayloadOffset = 8
ICMPv6EchoMinimumSize = 8
ICMPv6ErrorHeaderSize = 8
ICMPv6DstUnreachableMinimumSize = ICMPv6MinimumSize
ICMPv6PacketTooBigMinimumSize = ICMPv6MinimumSize
ICMPv6ChecksumOffset = 2
icmpv6PointerOffset = 4
icmpv6MTUOffset = 4
icmpv6IdentOffset = 4
icmpv6SequenceOffset = 6
NDPHopLimit = 255
)
type ICMPv6Type byte
const (
ICMPv6DstUnreachable ICMPv6Type = 1
ICMPv6PacketTooBig ICMPv6Type = 2
ICMPv6TimeExceeded ICMPv6Type = 3
ICMPv6ParamProblem ICMPv6Type = 4
ICMPv6EchoRequest ICMPv6Type = 128
ICMPv6EchoReply ICMPv6Type = 129
ICMPv6RouterSolicit ICMPv6Type = 133
ICMPv6RouterAdvert ICMPv6Type = 134
ICMPv6NeighborSolicit ICMPv6Type = 135
ICMPv6NeighborAdvert ICMPv6Type = 136
ICMPv6RedirectMsg ICMPv6Type = 137
ICMPv6MulticastListenerQuery ICMPv6Type = 130
ICMPv6MulticastListenerReport ICMPv6Type = 131
ICMPv6MulticastListenerDone ICMPv6Type = 132
)
func (typ ICMPv6Type) IsErrorType() bool {
return typ&0x80 == 0
}
type ICMPv6Code byte
const (
ICMPv6NetworkUnreachable ICMPv6Code = 0
ICMPv6Prohibited ICMPv6Code = 1
ICMPv6BeyondScope ICMPv6Code = 2
ICMPv6AddressUnreachable ICMPv6Code = 3
ICMPv6PortUnreachable ICMPv6Code = 4
ICMPv6Policy ICMPv6Code = 5
ICMPv6RejectRoute ICMPv6Code = 6
)
const (
ICMPv6HopLimitExceeded ICMPv6Code = 0
ICMPv6ReassemblyTimeout ICMPv6Code = 1
)
const (
ICMPv6ErroneousHeader ICMPv6Code = 0
ICMPv6UnknownHeader ICMPv6Code = 1
ICMPv6UnknownOption ICMPv6Code = 2
)
const ICMPv6UnusedCode ICMPv6Code = 0
func (b ICMPv6Packet) Type() ICMPv6Type {
return ICMPv6Type(b[0])
}
func (b ICMPv6Packet) SetType(t ICMPv6Type) {
b[0] = byte(t)
}
func (b ICMPv6Packet) Code() ICMPv6Code {
return ICMPv6Code(b[1])
}
func (b ICMPv6Packet) SetCode(c ICMPv6Code) {
b[1] = byte(c)
}
func (b ICMPv6Packet) TypeSpecific() uint32 {
return binary.BigEndian.Uint32(b[icmpv6PointerOffset:])
}
func (b ICMPv6Packet) SetTypeSpecific(val uint32) {
binary.BigEndian.PutUint32(b[icmpv6PointerOffset:], val)
}
func (b ICMPv6Packet) Checksum() uint16 {
return binary.BigEndian.Uint16(b[ICMPv6ChecksumOffset:])
}
func (b ICMPv6Packet) SetChecksum(sum [2]byte) {
_ = b[ICMPv6ChecksumOffset+1]
b[ICMPv6ChecksumOffset] = sum[0]
b[ICMPv6ChecksumOffset+1] = sum[1]
}
func (ICMPv6Packet) SourcePort() uint16 {
return 0
}
func (ICMPv6Packet) DestinationPort() uint16 {
return 0
}
func (ICMPv6Packet) SetSourcePort(uint16) {
}
func (ICMPv6Packet) SetDestinationPort(uint16) {
}
func (b ICMPv6Packet) MTU() uint32 {
return binary.BigEndian.Uint32(b[icmpv6MTUOffset:])
}
func (b ICMPv6Packet) SetMTU(mtu uint32) {
binary.BigEndian.PutUint32(b[icmpv6MTUOffset:], mtu)
}
func (b ICMPv6Packet) Ident() uint16 {
return binary.BigEndian.Uint16(b[icmpv6IdentOffset:])
}
func (b ICMPv6Packet) SetIdent(ident uint16) {
binary.BigEndian.PutUint16(b[icmpv6IdentOffset:], ident)
}
func (b ICMPv6Packet) Sequence() uint16 {
return binary.BigEndian.Uint16(b[icmpv6SequenceOffset:])
}
func (b ICMPv6Packet) SetSequence(sequence uint16) {
binary.BigEndian.PutUint16(b[icmpv6SequenceOffset:], sequence)
}
func (b ICMPv6Packet) MessageBody() []byte {
return b[ICMPv6HeaderSize:]
}
func (b ICMPv6Packet) Payload() []byte {
return b[ICMPv6PayloadOffset:]
}
func (b ICMPv6Packet) ResetChecksum(psum uint32) {
b.SetChecksum(zeroChecksum)
b.SetChecksum(Checksum(psum, b))
}

View File

@ -1,209 +0,0 @@
package tcpip
import (
"encoding/binary"
"errors"
"net/netip"
)
type IPProtocol = byte
type IP interface {
Payload() []byte
SourceIP() netip.Addr
DestinationIP() netip.Addr
SetSourceIP(ip netip.Addr)
SetDestinationIP(ip netip.Addr)
Protocol() IPProtocol
DecTimeToLive()
ResetChecksum()
PseudoSum() uint32
}
// IPProtocol type
const (
ICMP IPProtocol = 0x01
TCP IPProtocol = 0x06
UDP IPProtocol = 0x11
ICMPv6 IPProtocol = 0x3a
)
const (
FlagDontFragment = 1 << 1
FlagMoreFragment = 1 << 2
)
const (
IPv4HeaderSize = 20
IPv4Version = 4
IPv4OptionsOffset = 20
IPv4PacketMinLength = IPv4OptionsOffset
)
var (
ErrInvalidLength = errors.New("invalid packet length")
ErrInvalidIPVersion = errors.New("invalid ip version")
ErrInvalidChecksum = errors.New("invalid checksum")
)
type IPv4Packet []byte
func (p IPv4Packet) TotalLen() uint16 {
return binary.BigEndian.Uint16(p[2:])
}
func (p IPv4Packet) SetTotalLength(length uint16) {
binary.BigEndian.PutUint16(p[2:], length)
}
func (p IPv4Packet) HeaderLen() uint16 {
return uint16(p[0]&0xf) * 4
}
func (p IPv4Packet) SetHeaderLen(length uint16) {
p[0] &= 0xF0
p[0] |= byte(length / 4)
}
func (p IPv4Packet) TypeOfService() byte {
return p[1]
}
func (p IPv4Packet) SetTypeOfService(tos byte) {
p[1] = tos
}
func (p IPv4Packet) Identification() uint16 {
return binary.BigEndian.Uint16(p[4:])
}
func (p IPv4Packet) SetIdentification(id uint16) {
binary.BigEndian.PutUint16(p[4:], id)
}
func (p IPv4Packet) FragmentOffset() uint16 {
return binary.BigEndian.Uint16([]byte{p[6] & 0x7, p[7]}) * 8
}
func (p IPv4Packet) SetFragmentOffset(offset uint32) {
flags := p.Flags()
binary.BigEndian.PutUint16(p[6:], uint16(offset/8))
p.SetFlags(flags)
}
func (p IPv4Packet) DataLen() uint16 {
return p.TotalLen() - p.HeaderLen()
}
func (p IPv4Packet) Payload() []byte {
return p[p.HeaderLen():p.TotalLen()]
}
func (p IPv4Packet) Protocol() IPProtocol {
return p[9]
}
func (p IPv4Packet) SetProtocol(protocol IPProtocol) {
p[9] = protocol
}
func (p IPv4Packet) Flags() byte {
return p[6] >> 5
}
func (p IPv4Packet) SetFlags(flags byte) {
p[6] &= 0x1F
p[6] |= flags << 5
}
func (p IPv4Packet) SourceIP() netip.Addr {
return netip.AddrFrom4([4]byte{p[12], p[13], p[14], p[15]})
}
func (p IPv4Packet) SetSourceIP(ip netip.Addr) {
if ip.Is4() {
copy(p[12:16], ip.AsSlice())
}
}
func (p IPv4Packet) DestinationIP() netip.Addr {
return netip.AddrFrom4([4]byte{p[16], p[17], p[18], p[19]})
}
func (p IPv4Packet) SetDestinationIP(ip netip.Addr) {
if ip.Is4() {
copy(p[16:20], ip.AsSlice())
}
}
func (p IPv4Packet) Checksum() uint16 {
return binary.BigEndian.Uint16(p[10:])
}
func (p IPv4Packet) SetChecksum(sum [2]byte) {
p[10] = sum[0]
p[11] = sum[1]
}
func (p IPv4Packet) TimeToLive() uint8 {
return p[8]
}
func (p IPv4Packet) SetTimeToLive(ttl uint8) {
p[8] = ttl
}
func (p IPv4Packet) DecTimeToLive() {
p[8] = p[8] - uint8(1)
}
func (p IPv4Packet) ResetChecksum() {
p.SetChecksum(zeroChecksum)
p.SetChecksum(Checksum(0, p[:p.HeaderLen()]))
}
// PseudoSum for tcp checksum
func (p IPv4Packet) PseudoSum() uint32 {
sum := Sum(p[12:20])
sum += uint32(p.Protocol())
sum += uint32(p.DataLen())
return sum
}
func (p IPv4Packet) Valid() bool {
return len(p) >= IPv4HeaderSize && uint16(len(p)) >= p.TotalLen()
}
func (p IPv4Packet) Verify() error {
if len(p) < IPv4PacketMinLength {
return ErrInvalidLength
}
checksum := []byte{p[10], p[11]}
headerLength := uint16(p[0]&0xF) * 4
packetLength := binary.BigEndian.Uint16(p[2:])
if p[0]>>4 != 4 {
return ErrInvalidIPVersion
}
if uint16(len(p)) < packetLength || packetLength < headerLength {
return ErrInvalidLength
}
p[10] = 0
p[11] = 0
defer copy(p[10:12], checksum)
answer := Checksum(0, p[:headerLength])
if answer[0] != checksum[0] || answer[1] != checksum[1] {
return ErrInvalidChecksum
}
return nil
}
var _ IP = (*IPv4Packet)(nil)

View File

@ -1,141 +0,0 @@
package tcpip
import (
"encoding/binary"
"net/netip"
)
const (
versTCFL = 0
IPv6PayloadLenOffset = 4
IPv6NextHeaderOffset = 6
hopLimit = 7
v6SrcAddr = 8
v6DstAddr = v6SrcAddr + IPv6AddressSize
IPv6FixedHeaderSize = v6DstAddr + IPv6AddressSize
)
const (
versIHL = 0
tos = 1
ipVersionShift = 4
ipIHLMask = 0x0f
IPv4IHLStride = 4
)
type IPv6Packet []byte
const (
IPv6MinimumSize = IPv6FixedHeaderSize
IPv6AddressSize = 16
IPv6Version = 6
IPv6MinimumMTU = 1280
)
func (b IPv6Packet) PayloadLength() uint16 {
return binary.BigEndian.Uint16(b[IPv6PayloadLenOffset:])
}
func (b IPv6Packet) HopLimit() uint8 {
return b[hopLimit]
}
func (b IPv6Packet) NextHeader() byte {
return b[IPv6NextHeaderOffset]
}
func (b IPv6Packet) Protocol() IPProtocol {
return b.NextHeader()
}
func (b IPv6Packet) Payload() []byte {
return b[IPv6MinimumSize:][:b.PayloadLength()]
}
func (b IPv6Packet) SourceIP() netip.Addr {
addr, _ := netip.AddrFromSlice(b[v6SrcAddr:][:IPv6AddressSize])
return addr
}
func (b IPv6Packet) DestinationIP() netip.Addr {
addr, _ := netip.AddrFromSlice(b[v6DstAddr:][:IPv6AddressSize])
return addr
}
func (IPv6Packet) Checksum() uint16 {
return 0
}
func (b IPv6Packet) TOS() (uint8, uint32) {
v := binary.BigEndian.Uint32(b[versTCFL:])
return uint8(v >> 20), v & 0xfffff
}
func (b IPv6Packet) SetTOS(t uint8, l uint32) {
vtf := (6 << 28) | (uint32(t) << 20) | (l & 0xfffff)
binary.BigEndian.PutUint32(b[versTCFL:], vtf)
}
func (b IPv6Packet) SetPayloadLength(payloadLength uint16) {
binary.BigEndian.PutUint16(b[IPv6PayloadLenOffset:], payloadLength)
}
func (b IPv6Packet) SetSourceIP(addr netip.Addr) {
if addr.Is6() {
copy(b[v6SrcAddr:][:IPv6AddressSize], addr.AsSlice())
}
}
func (b IPv6Packet) SetDestinationIP(addr netip.Addr) {
if addr.Is6() {
copy(b[v6DstAddr:][:IPv6AddressSize], addr.AsSlice())
}
}
func (b IPv6Packet) SetHopLimit(v uint8) {
b[hopLimit] = v
}
func (b IPv6Packet) SetNextHeader(v byte) {
b[IPv6NextHeaderOffset] = v
}
func (b IPv6Packet) SetProtocol(p IPProtocol) {
b.SetNextHeader(p)
}
func (b IPv6Packet) DecTimeToLive() {
b[hopLimit] = b[hopLimit] - uint8(1)
}
func (IPv6Packet) SetChecksum(uint16) {
}
func (IPv6Packet) ResetChecksum() {
}
func (b IPv6Packet) PseudoSum() uint32 {
sum := Sum(b[v6SrcAddr:IPv6FixedHeaderSize])
sum += uint32(b.Protocol())
sum += uint32(b.PayloadLength())
return sum
}
func (b IPv6Packet) Valid() bool {
return len(b) >= IPv6MinimumSize && len(b) >= int(b.PayloadLength())+IPv6MinimumSize
}
func IPVersion(b []byte) int {
if len(b) < versIHL+1 {
return -1
}
return int(b[versIHL] >> ipVersionShift)
}
var _ IP = (*IPv6Packet)(nil)

View File

@ -1,90 +0,0 @@
package tcpip
import (
"encoding/binary"
"net"
)
const (
TCPFin uint16 = 1 << 0
TCPSyn uint16 = 1 << 1
TCPRst uint16 = 1 << 2
TCPPuh uint16 = 1 << 3
TCPAck uint16 = 1 << 4
TCPUrg uint16 = 1 << 5
TCPEce uint16 = 1 << 6
TCPEwr uint16 = 1 << 7
TCPNs uint16 = 1 << 8
)
const TCPHeaderSize = 20
type TCPPacket []byte
func (p TCPPacket) SourcePort() uint16 {
return binary.BigEndian.Uint16(p)
}
func (p TCPPacket) SetSourcePort(port uint16) {
binary.BigEndian.PutUint16(p, port)
}
func (p TCPPacket) DestinationPort() uint16 {
return binary.BigEndian.Uint16(p[2:])
}
func (p TCPPacket) SetDestinationPort(port uint16) {
binary.BigEndian.PutUint16(p[2:], port)
}
func (p TCPPacket) Flags() uint16 {
return uint16(p[13] | (p[12] & 0x1))
}
func (p TCPPacket) Checksum() uint16 {
return binary.BigEndian.Uint16(p[16:])
}
func (p TCPPacket) SetChecksum(sum [2]byte) {
p[16] = sum[0]
p[17] = sum[1]
}
func (p TCPPacket) ResetChecksum(psum uint32) {
p.SetChecksum(zeroChecksum)
p.SetChecksum(Checksum(psum, p))
}
func (p TCPPacket) Valid() bool {
return len(p) >= TCPHeaderSize
}
func (p TCPPacket) Verify(sourceAddress net.IP, targetAddress net.IP) error {
var checksum [2]byte
checksum[0] = p[16]
checksum[1] = p[17]
// reset checksum
p[16] = 0
p[17] = 0
// restore checksum
defer func() {
p[16] = checksum[0]
p[17] = checksum[1]
}()
// check checksum
s := uint32(0)
s += Sum(sourceAddress)
s += Sum(targetAddress)
s += uint32(TCP)
s += uint32(len(p))
check := Checksum(s, p)
if checksum[0] != check[0] || checksum[1] != check[1] {
return ErrInvalidChecksum
}
return nil
}

View File

@ -1,24 +0,0 @@
package tcpip
var zeroChecksum = [2]byte{0x00, 0x00}
var SumFnc = SumCompat
func Sum(b []byte) uint32 {
return SumFnc(b)
}
// Checksum for Internet Protocol family headers
func Checksum(sum uint32, b []byte) (answer [2]byte) {
sum += Sum(b)
sum = (sum >> 16) + (sum & 0xffff)
sum += sum >> 16
sum = ^sum
answer[0] = byte(sum >> 8)
answer[1] = byte(sum)
return
}
func SetIPv4(packet []byte) {
packet[0] = (packet[0] & 0x0f) | (4 << 4)
}

View File

@ -1,26 +0,0 @@
//go:build !noasm
package tcpip
import (
"unsafe"
"golang.org/x/sys/cpu"
)
//go:noescape
func sumAsmAvx2(data unsafe.Pointer, length uintptr) uintptr
func SumAVX2(data []byte) uint32 {
if len(data) == 0 {
return 0
}
return uint32(sumAsmAvx2(unsafe.Pointer(&data[0]), uintptr(len(data))))
}
func init() {
if cpu.X86.HasAVX2 {
SumFnc = SumAVX2
}
}

View File

@ -1,140 +0,0 @@
#include "textflag.h"
DATA endian_swap_mask<>+0(SB)/8, $0x607040502030001
DATA endian_swap_mask<>+8(SB)/8, $0xE0F0C0D0A0B0809
DATA endian_swap_mask<>+16(SB)/8, $0x607040502030001
DATA endian_swap_mask<>+24(SB)/8, $0xE0F0C0D0A0B0809
GLOBL endian_swap_mask<>(SB), RODATA, $32
// func sumAsmAvx2(data unsafe.Pointer, length uintptr) uintptr
//
// args (8 bytes aligned):
// data unsafe.Pointer - 8 bytes - 0 offset
// length uintptr - 8 bytes - 8 offset
// result uintptr - 8 bytes - 16 offset
#define PDATA AX
#define LENGTH CX
#define RESULT BX
TEXT ·sumAsmAvx2(SB),NOSPLIT,$0-24
MOVQ data+0(FP), PDATA
MOVQ length+8(FP), LENGTH
XORQ RESULT, RESULT
#define VSUM Y0
#define ENDIAN_SWAP_MASK Y1
BEGIN:
VMOVDQU endian_swap_mask<>(SB), ENDIAN_SWAP_MASK
VPXOR VSUM, VSUM, VSUM
#define LOADED_0 Y2
#define LOADED_1 Y3
#define LOADED_2 Y4
#define LOADED_3 Y5
BATCH_64:
CMPQ LENGTH, $64
JB BATCH_32
VPMOVZXWD (PDATA), LOADED_0
VPMOVZXWD 16(PDATA), LOADED_1
VPMOVZXWD 32(PDATA), LOADED_2
VPMOVZXWD 48(PDATA), LOADED_3
VPSHUFB ENDIAN_SWAP_MASK, LOADED_0, LOADED_0
VPSHUFB ENDIAN_SWAP_MASK, LOADED_1, LOADED_1
VPSHUFB ENDIAN_SWAP_MASK, LOADED_2, LOADED_2
VPSHUFB ENDIAN_SWAP_MASK, LOADED_3, LOADED_3
VPADDD LOADED_0, VSUM, VSUM
VPADDD LOADED_1, VSUM, VSUM
VPADDD LOADED_2, VSUM, VSUM
VPADDD LOADED_3, VSUM, VSUM
ADDQ $-64, LENGTH
ADDQ $64, PDATA
JMP BATCH_64
#undef LOADED_0
#undef LOADED_1
#undef LOADED_2
#undef LOADED_3
#define LOADED_0 Y2
#define LOADED_1 Y3
BATCH_32:
CMPQ LENGTH, $32
JB BATCH_16
VPMOVZXWD (PDATA), LOADED_0
VPMOVZXWD 16(PDATA), LOADED_1
VPSHUFB ENDIAN_SWAP_MASK, LOADED_0, LOADED_0
VPSHUFB ENDIAN_SWAP_MASK, LOADED_1, LOADED_1
VPADDD LOADED_0, VSUM, VSUM
VPADDD LOADED_1, VSUM, VSUM
ADDQ $-32, LENGTH
ADDQ $32, PDATA
JMP BATCH_32
#undef LOADED_0
#undef LOADED_1
#define LOADED Y2
BATCH_16:
CMPQ LENGTH, $16
JB COLLECT
VPMOVZXWD (PDATA), LOADED
VPSHUFB ENDIAN_SWAP_MASK, LOADED, LOADED
VPADDD LOADED, VSUM, VSUM
ADDQ $-16, LENGTH
ADDQ $16, PDATA
JMP BATCH_16
#undef LOADED
#define EXTRACTED Y2
#define EXTRACTED_128 X2
#define TEMP_64 DX
COLLECT:
VEXTRACTI128 $0, VSUM, EXTRACTED_128
VPEXTRD $0, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VPEXTRD $1, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VPEXTRD $2, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VPEXTRD $3, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VEXTRACTI128 $1, VSUM, EXTRACTED_128
VPEXTRD $0, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VPEXTRD $1, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VPEXTRD $2, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
VPEXTRD $3, EXTRACTED_128, TEMP_64
ADDL TEMP_64, RESULT
#undef EXTRACTED
#undef EXTRACTED_128
#undef TEMP_64
#define TEMP DX
#define TEMP2 SI
BATCH_2:
CMPQ LENGTH, $2
JB BATCH_1
XORQ TEMP, TEMP
MOVW (PDATA), TEMP
MOVQ TEMP, TEMP2
SHRW $8, TEMP2
SHLW $8, TEMP
ORW TEMP2, TEMP
ADDL TEMP, RESULT
ADDQ $-2, LENGTH
ADDQ $2, PDATA
JMP BATCH_2
#undef TEMP
#define TEMP DX
BATCH_1:
CMPQ LENGTH, $0
JZ RETURN
XORQ TEMP, TEMP
MOVB (PDATA), TEMP
SHLW $8, TEMP
ADDL TEMP, RESULT
#undef TEMP
RETURN:
MOVQ RESULT, result+16(FP)
RET

View File

@ -1,51 +0,0 @@
package tcpip
import (
"crypto/rand"
"testing"
"golang.org/x/sys/cpu"
)
func Test_SumAVX2(t *testing.T) {
if !cpu.X86.HasAVX2 {
t.Skipf("AVX2 unavailable")
}
bytes := make([]byte, chunkSize)
for size := 0; size <= chunkSize; size++ {
for count := 0; count < chunkCount; count++ {
_, err := rand.Reader.Read(bytes[:size])
if err != nil {
t.Skipf("Rand read failed: %v", err)
}
compat := SumCompat(bytes[:size])
avx := SumAVX2(bytes[:size])
if compat != avx {
t.Errorf("Sum of length=%d mismatched", size)
}
}
}
}
func Benchmark_SumAVX2(b *testing.B) {
if !cpu.X86.HasAVX2 {
b.Skipf("AVX2 unavailable")
}
bytes := make([]byte, chunkSize)
_, err := rand.Reader.Read(bytes)
if err != nil {
b.Skipf("Rand read failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
SumAVX2(bytes)
}
}

View File

@ -1,24 +0,0 @@
package tcpip
import (
"unsafe"
"golang.org/x/sys/cpu"
)
//go:noescape
func sumAsmNeon(data unsafe.Pointer, length uintptr) uintptr
func SumNeon(data []byte) uint32 {
if len(data) == 0 {
return 0
}
return uint32(sumAsmNeon(unsafe.Pointer(&data[0]), uintptr(len(data))))
}
func init() {
if cpu.ARM64.HasASIMD {
SumFnc = SumNeon
}
}

View File

@ -1,118 +0,0 @@
#include "textflag.h"
// func sumAsmNeon(data unsafe.Pointer, length uintptr) uintptr
//
// args (8 bytes aligned):
// data unsafe.Pointer - 8 bytes - 0 offset
// length uintptr - 8 bytes - 8 offset
// result uintptr - 8 bytes - 16 offset
#define PDATA R0
#define LENGTH R1
#define RESULT R2
#define VSUM V0
TEXT ·sumAsmNeon(SB),NOSPLIT,$0-24
MOVD data+0(FP), PDATA
MOVD length+8(FP), LENGTH
MOVD $0, RESULT
VMOVQ $0, $0, VSUM
#define LOADED_0 V1
#define LOADED_1 V2
#define LOADED_2 V3
#define LOADED_3 V4
BATCH_32:
CMP $32, LENGTH
BLO BATCH_16
VLD1 (PDATA), [LOADED_0.B8, LOADED_1.B8, LOADED_2.B8, LOADED_3.B8]
VREV16 LOADED_0.B8, LOADED_0.B8
VREV16 LOADED_1.B8, LOADED_1.B8
VREV16 LOADED_2.B8, LOADED_2.B8
VREV16 LOADED_3.B8, LOADED_3.B8
VUSHLL $0, LOADED_0.H4, LOADED_0.S4
VUSHLL $0, LOADED_1.H4, LOADED_1.S4
VUSHLL $0, LOADED_2.H4, LOADED_2.S4
VUSHLL $0, LOADED_3.H4, LOADED_3.S4
VADD LOADED_0.S4, VSUM.S4, VSUM.S4
VADD LOADED_1.S4, VSUM.S4, VSUM.S4
VADD LOADED_2.S4, VSUM.S4, VSUM.S4
VADD LOADED_3.S4, VSUM.S4, VSUM.S4
ADD $-32, LENGTH
ADD $32, PDATA
B BATCH_32
#undef LOADED_0
#undef LOADED_1
#undef LOADED_2
#undef LOADED_3
#define LOADED_0 V1
#define LOADED_1 V2
BATCH_16:
CMP $16, LENGTH
BLO BATCH_8
VLD1 (PDATA), [LOADED_0.B8, LOADED_1.B8]
VREV16 LOADED_0.B8, LOADED_0.B8
VREV16 LOADED_1.B8, LOADED_1.B8
VUSHLL $0, LOADED_0.H4, LOADED_0.S4
VUSHLL $0, LOADED_1.H4, LOADED_1.S4
VADD LOADED_0.S4, VSUM.S4, VSUM.S4
VADD LOADED_1.S4, VSUM.S4, VSUM.S4
ADD $-16, LENGTH
ADD $16, PDATA
B BATCH_16
#undef LOADED_0
#undef LOADED_1
#define LOADED_0 V1
BATCH_8:
CMP $8, LENGTH
BLO BATCH_2
VLD1 (PDATA), [LOADED_0.B8]
VREV16 LOADED_0.B8, LOADED_0.B8
VUSHLL $0, LOADED_0.H4, LOADED_0.S4
VADD LOADED_0.S4, VSUM.S4, VSUM.S4
ADD $-8, LENGTH
ADD $8, PDATA
B BATCH_8
#undef LOADED_0
#define LOADED_L R3
#define LOADED_H R4
BATCH_2:
CMP $2, LENGTH
BLO BATCH_1
MOVBU (PDATA), LOADED_H
MOVBU 1(PDATA), LOADED_L
LSL $8, LOADED_H
ORR LOADED_H, LOADED_L, LOADED_L
ADD LOADED_L, RESULT, RESULT
ADD $2, PDATA
ADD $-2, LENGTH
B BATCH_2
#undef LOADED_H
#undef LOADED_L
#define LOADED R3
BATCH_1:
CMP $1, LENGTH
BLO COLLECT
MOVBU (PDATA), LOADED
LSL $8, LOADED
ADD LOADED, RESULT, RESULT
#define EXTRACTED R3
COLLECT:
VMOV VSUM.S[0], EXTRACTED
ADD EXTRACTED, RESULT
VMOV VSUM.S[1], EXTRACTED
ADD EXTRACTED, RESULT
VMOV VSUM.S[2], EXTRACTED
ADD EXTRACTED, RESULT
VMOV VSUM.S[3], EXTRACTED
ADD EXTRACTED, RESULT
#undef VSUM
#undef PDATA
#undef LENGTH
RETURN:
MOVD RESULT, result+16(FP)
RET

View File

@ -1,51 +0,0 @@
package tcpip
import (
"crypto/rand"
"testing"
"golang.org/x/sys/cpu"
)
func Test_SumNeon(t *testing.T) {
if !cpu.ARM64.HasASIMD {
t.Skipf("Neon unavailable")
}
bytes := make([]byte, chunkSize)
for size := 0; size <= chunkSize; size++ {
for count := 0; count < chunkCount; count++ {
_, err := rand.Reader.Read(bytes[:size])
if err != nil {
t.Skipf("Rand read failed: %v", err)
}
compat := SumCompat(bytes[:size])
neon := SumNeon(bytes[:size])
if compat != neon {
t.Errorf("Sum of length=%d mismatched", size)
}
}
}
}
func Benchmark_SumNeon(b *testing.B) {
if !cpu.ARM64.HasASIMD {
b.Skipf("Neon unavailable")
}
bytes := make([]byte, chunkSize)
_, err := rand.Reader.Read(bytes)
if err != nil {
b.Skipf("Rand read failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
SumNeon(bytes)
}
}

View File

@ -1,14 +0,0 @@
package tcpip
func SumCompat(b []byte) (sum uint32) {
n := len(b)
if n&1 != 0 {
n--
sum += uint32(b[n]) << 8
}
for i := 0; i < n; i += 2 {
sum += (uint32(b[i]) << 8) | uint32(b[i+1])
}
return
}

View File

@ -1,26 +0,0 @@
package tcpip
import (
"crypto/rand"
"testing"
)
const (
chunkSize = 9000
chunkCount = 10
)
func Benchmark_SumCompat(b *testing.B) {
bytes := make([]byte, chunkSize)
_, err := rand.Reader.Read(bytes)
if err != nil {
b.Skipf("Rand read failed: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
SumCompat(bytes)
}
}

View File

@ -1,55 +0,0 @@
package tcpip
import (
"encoding/binary"
)
const UDPHeaderSize = 8
type UDPPacket []byte
func (p UDPPacket) Length() uint16 {
return binary.BigEndian.Uint16(p[4:])
}
func (p UDPPacket) SetLength(length uint16) {
binary.BigEndian.PutUint16(p[4:], length)
}
func (p UDPPacket) SourcePort() uint16 {
return binary.BigEndian.Uint16(p)
}
func (p UDPPacket) SetSourcePort(port uint16) {
binary.BigEndian.PutUint16(p, port)
}
func (p UDPPacket) DestinationPort() uint16 {
return binary.BigEndian.Uint16(p[2:])
}
func (p UDPPacket) SetDestinationPort(port uint16) {
binary.BigEndian.PutUint16(p[2:], port)
}
func (p UDPPacket) Payload() []byte {
return p[UDPHeaderSize:p.Length()]
}
func (p UDPPacket) Checksum() uint16 {
return binary.BigEndian.Uint16(p[6:])
}
func (p UDPPacket) SetChecksum(sum [2]byte) {
p[6] = sum[0]
p[7] = sum[1]
}
func (p UDPPacket) ResetChecksum(psum uint32) {
p.SetChecksum(zeroChecksum)
p.SetChecksum(Checksum(psum, p))
}
func (p UDPPacket) Valid() bool {
return len(p) >= UDPHeaderSize && uint16(len(p)) >= p.Length()
}

View File

@ -1,232 +0,0 @@
package system
import (
"encoding/binary"
"io"
"net"
"net/netip"
"runtime"
"strconv"
"sync"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/nnip"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/context"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/ipstack"
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/nat"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5"
)
type sysStack struct {
stack io.Closer
device device.Device
closed bool
once sync.Once
wg sync.WaitGroup
}
func (s *sysStack) Close() error {
defer func() {
if s.device != nil {
_ = s.device.Close()
}
}()
s.closed = true
err := s.stack.Close()
s.wg.Wait()
return err
}
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
var (
gateway = tunAddress.Masked().Addr().Next()
portal = gateway.Next()
broadcast = nnip.UnMasked(tunAddress)
)
stack, err := mars.StartListener(device, gateway, portal, broadcast)
if err != nil {
_ = device.Close()
return nil, err
}
ipStack := &sysStack{stack: stack, device: device}
dnsAddr := dnsHijack
tcp := func() {
defer func(tcp *nat.TCP) {
_ = tcp.Close()
}(stack.TCP())
for !ipStack.closed {
conn, err := stack.TCP().Accept()
if err != nil {
log.Debugln("[STACK] accept connection error: %v", err)
continue
}
lAddr := conn.LocalAddr().(*net.TCPAddr)
rAddr := conn.RemoteAddr().(*net.TCPAddr)
lAddrPort := netip.AddrPortFrom(nnip.IpToAddr(lAddr.IP), uint16(lAddr.Port))
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() {
_ = conn.Close()
continue
}
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
go func() {
buf := pool.Get(pool.UDPBufferSize)
defer func() {
_ = pool.Put(buf)
_ = conn.Close()
}()
for {
if conn.SetReadDeadline(time.Now().Add(C.DefaultTCPTimeout)) != nil {
break
}
length := uint16(0)
if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
break
}
if int(length) > len(buf) {
break
}
n, err := io.ReadFull(conn, buf[:length])
if err != nil {
break
}
msg, err := D.RelayDnsPacket(buf[:n])
if err != nil {
break
}
err = binary.Write(conn, binary.BigEndian, uint16(len(msg)))
if err != nil {
break
}
_, _ = conn.Write(msg)
}
}()
continue
}
metadata := &C.Metadata{
NetWork: C.TCP,
Type: C.TUN,
SrcIP: lAddrPort.Addr(),
DstIP: rAddrPort.Addr(),
SrcPort: strconv.Itoa(lAddr.Port),
DstPort: strconv.Itoa(rAddr.Port),
AddrType: C.AtypIPv4,
Host: "",
}
tcpIn <- context.NewConnContext(conn, metadata)
}
ipStack.wg.Done()
}
udp := func() {
defer func(udp *nat.UDP) {
_ = udp.Close()
}(stack.UDP())
for !ipStack.closed {
buf := pool.Get(pool.UDPBufferSize)
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
if err != nil {
_ = pool.Put(buf)
break
}
raw := buf[:n]
lAddr := lRAddr.(*net.UDPAddr)
rAddr := rRAddr.(*net.UDPAddr)
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() || rAddrPort.Addr() == gateway {
_ = pool.Put(buf)
continue
}
if D.ShouldHijackDns(dnsAddr, rAddrPort) {
go func() {
msg, err := D.RelayDnsPacket(raw)
if err != nil {
_ = pool.Put(buf)
return
}
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
_ = pool.Put(buf)
}()
continue
}
pkt := &packet{
local: lAddr,
data: raw,
writeBack: func(b []byte, addr net.Addr) (int, error) {
return stack.UDP().WriteTo(b, rAddr, lAddr)
},
drop: func() {
_ = pool.Put(buf)
},
}
select {
case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN):
default:
}
}
ipStack.wg.Done()
}
ipStack.once.Do(func() {
ipStack.wg.Add(1)
go tcp()
numUDPWorkers := 4
if num := runtime.GOMAXPROCS(0); num > numUDPWorkers {
numUDPWorkers = num
}
for i := 0; i < numUDPWorkers; i++ {
ipStack.wg.Add(1)
go udp()
}
})
return ipStack, nil
}

View File

@ -1,26 +0,0 @@
package system
import "net"
type packet struct {
local *net.UDPAddr
data []byte
writeBack func(b []byte, addr net.Addr) (int, error)
drop func()
}
func (pkt *packet) Data() []byte {
return pkt.data
}
func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
return pkt.writeBack(b, addr)
}
func (pkt *packet) Drop() {
pkt.drop()
}
func (pkt *packet) LocalAddr() net.Addr {
return pkt.local
}

View File

@ -1,162 +0,0 @@
package tun
import (
"fmt"
"github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/config"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/device/fdbased"
"github.com/Dreamacro/clash/listener/tun/device/tun"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor"
"github.com/Dreamacro/clash/listener/tun/ipstack/system"
"github.com/Dreamacro/clash/log"
"net/netip"
"net/url"
"runtime"
"strings"
)
// New TunAdapter
func New(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
var (
tunAddress = tunConf.TunAddressPrefix
devName = tunConf.Device
stackType = tunConf.Stack
autoRoute = tunConf.AutoRoute
mtu = 9000
tunDevice device.Device
tunStack ipstack.Stack
err error
)
if devName == "" {
devName = generateDeviceName()
}
if !tunAddress.IsValid() || !tunAddress.Addr().Is4() {
tunAddress = netip.MustParsePrefix("198.18.0.1/16")
}
// open tun device
tunDevice, err = parseDevice(devName, uint32(mtu))
if err != nil {
return nil, fmt.Errorf("can't open tun: %w", err)
}
// new ip stack
switch stackType {
case C.TunGvisor:
err = tunDevice.UseEndpoint()
if err != nil {
_ = tunDevice.Close()
return nil, fmt.Errorf("can't attach endpoint to tun: %w", err)
}
tunStack, err = gvisor.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn)
if err != nil {
_ = tunDevice.Close()
return nil, fmt.Errorf("can't New gvisor stack: %w", err)
}
case C.TunSystem:
err = tunDevice.UseIOBased()
if err != nil {
_ = tunDevice.Close()
return nil, fmt.Errorf("can't New system stack: %w", err)
}
tunStack, err = system.New(tunDevice, tunConf.DNSHijack, tunAddress, tcpIn, udpIn)
if err != nil {
_ = tunDevice.Close()
return nil, fmt.Errorf("can't New system stack: %w", err)
}
default:
// never happen
}
// setting address and routing
err = commons.ConfigInterfaceAddress(tunDevice, tunAddress, mtu, autoRoute)
if err != nil {
_ = tunDevice.Close()
return nil, fmt.Errorf("setting interface address and routing failed: %w", err)
}
if tunConf.AutoDetectInterface {
commons.StartDefaultInterfaceChangeMonitor()
}
setAtLatest(stackType, devName)
log.Infoln("TUN stack listening at: %s(%s), mtu: %d, auto route: %v, ip stack: %s", tunDevice.Name(), tunAddress.Masked().Addr().Next().String(), mtu, autoRoute, stackType)
return tunStack, nil
}
func generateDeviceName() string {
switch runtime.GOOS {
case "darwin":
return tun.Driver + "://utun"
case "windows":
return tun.Driver + "://Meta"
default:
return tun.Driver + "://Meta"
}
}
func parseDevice(s string, mtu uint32) (device.Device, error) {
if !strings.Contains(s, "://") {
s = fmt.Sprintf("%s://%s", tun.Driver /* default driver */, s)
}
u, err := url.Parse(s)
if err != nil {
return nil, err
}
name := u.Host
driver := strings.ToLower(u.Scheme)
switch driver {
case fdbased.Driver:
return fdbased.Open(name, mtu)
case tun.Driver:
return tun.Open(name, mtu)
default:
return nil, fmt.Errorf("unsupported driver: %s", driver)
}
}
func setAtLatest(stackType C.TUNStack, devName string) {
if stackType != C.TunSystem {
return
}
switch runtime.GOOS {
case "darwin":
// _, _ = cmd.ExecCmd("sysctl -w net.inet.ip.forwarding=1")
// _, _ = cmd.ExecCmd("sysctl -w net.inet6.ip6.forwarding=1")
case "windows":
_, _ = cmd.ExecCmd("ipconfig /renew")
case "linux":
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.ip_forward=1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.forwarding = 1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_local = 1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.accept_redirects = 1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.all.rp_filter = 2")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.default.forwarding = 1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.default.accept_local = 1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.default.accept_redirects = 1")
// _, _ = cmd.ExecCmd("sysctl -w net.ipv4.conf.default.rp_filter = 2")
// _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.forwarding = 1", devName))
// _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_local = 1", devName))
// _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.accept_redirects = 1", devName))
// _, _ = cmd.ExecCmd(fmt.Sprintf("sysctl -w net.ipv4.conf.%s.rp_filter = 2", devName))
// _, _ = cmd.ExecCmd("iptables -t filter -P FORWARD ACCEPT")
}
}