diff --git a/Makefile b/Makefile index 0df75db0..3c079aa1 100644 --- a/Makefile +++ b/Makefile @@ -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=' diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 60a7c334..6eeacf45 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -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 diff --git a/common/pool/alloc.go b/common/pool/alloc.go index 96fd059e..25f79897 100644 --- a/common/pool/alloc.go +++ b/common/pool/alloc.go @@ -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 { diff --git a/common/pool/pool.go b/common/pool/pool.go index a2beb082..bee4887f 100644 --- a/common/pool/pool.go +++ b/common/pool/pool.go @@ -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) } diff --git a/common/pool/sing.go b/common/pool/sing.go new file mode 100644 index 00000000..c246ae9f --- /dev/null +++ b/common/pool/sing.go @@ -0,0 +1,7 @@ +package pool + +import "github.com/sagernet/sing/common/buf" + +func init() { + buf.DefaultAllocator = defaultAllocator +} diff --git a/component/ebpf/ebpf_linux.go b/component/ebpf/ebpf_linux.go index 712770c6..bf41d6cb 100644 --- a/component/ebpf/ebpf_linux.go +++ b/component/ebpf/ebpf_linux.go @@ -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) diff --git a/component/ebpf/ebpf_others.go b/component/ebpf/ebpf_others.go index 0ecf5caa..44cf1c3a 100644 --- a/component/ebpf/ebpf_others.go +++ b/component/ebpf/ebpf_others.go @@ -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") +} diff --git a/config/config.go b/config/config.go index 7daf1549..ae24d9ac 100644 --- a/config/config.go +++ b/config/config.go @@ -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 @@ -187,13 +250,13 @@ 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"` - DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` - AutoRoute bool `yaml:"auto-route" json:"auto-route"` - AutoDetectInterface bool `yaml:"auto-detect-interface"` - RedirectToTun []string `yaml:"-" json:"-"` + Enable bool `yaml:"enable" json:"enable"` + Device string `yaml:"device" json:"device"` + 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"` + RedirectToTun []string `yaml:"-" json:"-"` } type RawConfig struct { @@ -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 } diff --git a/go.mod b/go.mod index 2a835cdf..6f549c4a 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 8f9d8ac7..93779d06 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/listener/listener.go b/listener/listener.go index debba89d..40d2d3d3 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -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) - } - - commons.CleanupRule() + if tunLister != nil { + tunLister.Close() + tunLister = nil } - tunStackListener = nil lastTunConf = nil } diff --git a/listener/sing/log.go b/listener/sing/log.go new file mode 100644 index 00000000..4847e063 --- /dev/null +++ b/listener/sing/log.go @@ -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{} diff --git a/listener/sing/sing.go b/listener/sing/sing.go new file mode 100644 index 00000000..d22fa44a --- /dev/null +++ b/listener/sing/sing.go @@ -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 +} diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go new file mode 100644 index 00000000..39e2b1e5 --- /dev/null +++ b/listener/sing_tun/dns.go @@ -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() +} diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go new file mode 100644 index 00000000..82ad6138 --- /dev/null +++ b/listener/sing_tun/server.go @@ -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 != "" { + 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 +} diff --git a/listener/sing_tun/server_windows.go b/listener/sing_tun/server_windows.go new file mode 100644 index 00000000..33f3ff19 --- /dev/null +++ b/listener/sing_tun/server_windows.go @@ -0,0 +1,7 @@ +package sing_tun + +import tun "github.com/sagernet/sing-tun" + +func init() { + tun.TunnelType = InterfaceName +} diff --git a/listener/tun/device/device.go b/listener/tun/device/device.go deleted file mode 100644 index f7c3552a..00000000 --- a/listener/tun/device/device.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/device_no_gvisor.go b/listener/tun/device/device_no_gvisor.go deleted file mode 100644 index b2d7225d..00000000 --- a/listener/tun/device/device_no_gvisor.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/fdbased/fd.go b/listener/tun/device/fdbased/fd.go deleted file mode 100644 index 20ae3489..00000000 --- a/listener/tun/device/fdbased/fd.go +++ /dev/null @@ -1,5 +0,0 @@ -package fdbased - -const Driver = "fd" - -const defaultMTU = 1500 diff --git a/listener/tun/device/fdbased/fd_unix.go b/listener/tun/device/fdbased/fd_unix.go deleted file mode 100644 index 63f6e839..00000000 --- a/listener/tun/device/fdbased/fd_unix.go +++ /dev/null @@ -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) diff --git a/listener/tun/device/fdbased/fd_unix_gvisor.go b/listener/tun/device/fdbased/fd_unix_gvisor.go deleted file mode 100644 index cc327c77..00000000 --- a/listener/tun/device/fdbased/fd_unix_gvisor.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/fdbased/fd_unix_no_gvisor.go b/listener/tun/device/fdbased/fd_unix_no_gvisor.go deleted file mode 100644 index a4ecf8b9..00000000 --- a/listener/tun/device/fdbased/fd_unix_no_gvisor.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build no_gvisor - -package fdbased - -import ( - "os" -) - -type FD struct { - fd int - mtu uint32 - - file *os.File -} diff --git a/listener/tun/device/fdbased/fd_windows.go b/listener/tun/device/fdbased/fd_windows.go deleted file mode 100644 index a04f3356..00000000 --- a/listener/tun/device/fdbased/fd_windows.go +++ /dev/null @@ -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") -} diff --git a/listener/tun/device/fdbased/open_linux.go b/listener/tun/device/fdbased/open_linux.go deleted file mode 100644 index 0df81ff3..00000000 --- a/listener/tun/device/fdbased/open_linux.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/device/fdbased/open_linux_gvisor.go b/listener/tun/device/fdbased/open_linux_gvisor.go deleted file mode 100644 index dc250692..00000000 --- a/listener/tun/device/fdbased/open_linux_gvisor.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/fdbased/open_linux_no_gvisor.go b/listener/tun/device/fdbased/open_linux_no_gvisor.go deleted file mode 100644 index ec76d991..00000000 --- a/listener/tun/device/fdbased/open_linux_no_gvisor.go +++ /dev/null @@ -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") -} diff --git a/listener/tun/device/fdbased/open_others.go b/listener/tun/device/fdbased/open_others.go deleted file mode 100644 index e5c98276..00000000 --- a/listener/tun/device/fdbased/open_others.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/device/fdbased/open_others_gvisor.go b/listener/tun/device/fdbased/open_others_gvisor.go deleted file mode 100644 index 9cb00511..00000000 --- a/listener/tun/device/fdbased/open_others_gvisor.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/fdbased/open_others_no_gvisor.go b/listener/tun/device/fdbased/open_others_no_gvisor.go deleted file mode 100644 index ed616f76..00000000 --- a/listener/tun/device/fdbased/open_others_no_gvisor.go +++ /dev/null @@ -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") -} diff --git a/listener/tun/device/iobased/endpoint.go b/listener/tun/device/iobased/endpoint.go deleted file mode 100644 index 35eb74b3..00000000 --- a/listener/tun/device/iobased/endpoint.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/iobased/iobased.go b/listener/tun/device/iobased/iobased.go deleted file mode 100644 index c16ee55c..00000000 --- a/listener/tun/device/iobased/iobased.go +++ /dev/null @@ -1 +0,0 @@ -package iobased diff --git a/listener/tun/device/tun/driver/amd64/wintun.dll b/listener/tun/device/tun/driver/amd64/wintun.dll deleted file mode 100755 index aee04e77..00000000 Binary files a/listener/tun/device/tun/driver/amd64/wintun.dll and /dev/null differ diff --git a/listener/tun/device/tun/driver/arm/wintun.dll b/listener/tun/device/tun/driver/arm/wintun.dll deleted file mode 100755 index 0017794f..00000000 Binary files a/listener/tun/device/tun/driver/arm/wintun.dll and /dev/null differ diff --git a/listener/tun/device/tun/driver/arm64/wintun.dll b/listener/tun/device/tun/driver/arm64/wintun.dll deleted file mode 100755 index dc4e4aee..00000000 Binary files a/listener/tun/device/tun/driver/arm64/wintun.dll and /dev/null differ diff --git a/listener/tun/device/tun/driver/dll_windows.go b/listener/tun/device/tun/driver/dll_windows.go deleted file mode 100644 index 5f4a5dae..00000000 --- a/listener/tun/device/tun/driver/dll_windows.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/device/tun/driver/dll_windows_386.go b/listener/tun/device/tun/driver/dll_windows_386.go deleted file mode 100644 index 12ace4e8..00000000 --- a/listener/tun/device/tun/driver/dll_windows_386.go +++ /dev/null @@ -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 diff --git a/listener/tun/device/tun/driver/dll_windows_amd64.go b/listener/tun/device/tun/driver/dll_windows_amd64.go deleted file mode 100644 index 58f3e01e..00000000 --- a/listener/tun/device/tun/driver/dll_windows_amd64.go +++ /dev/null @@ -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 diff --git a/listener/tun/device/tun/driver/dll_windows_arm.go b/listener/tun/device/tun/driver/dll_windows_arm.go deleted file mode 100644 index bb941068..00000000 --- a/listener/tun/device/tun/driver/dll_windows_arm.go +++ /dev/null @@ -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 diff --git a/listener/tun/device/tun/driver/dll_windows_arm64.go b/listener/tun/device/tun/driver/dll_windows_arm64.go deleted file mode 100644 index 05bd1cbd..00000000 --- a/listener/tun/device/tun/driver/dll_windows_arm64.go +++ /dev/null @@ -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 diff --git a/listener/tun/device/tun/driver/package_info.go b/listener/tun/device/tun/driver/package_info.go deleted file mode 100644 index 0c1bf7e4..00000000 --- a/listener/tun/device/tun/driver/package_info.go +++ /dev/null @@ -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 diff --git a/listener/tun/device/tun/driver/x86/wintun.dll b/listener/tun/device/tun/driver/x86/wintun.dll deleted file mode 100755 index 2ab97dba..00000000 Binary files a/listener/tun/device/tun/driver/x86/wintun.dll and /dev/null differ diff --git a/listener/tun/device/tun/tun.go b/listener/tun/device/tun/tun.go deleted file mode 100644 index bf04f289..00000000 --- a/listener/tun/device/tun/tun.go +++ /dev/null @@ -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) diff --git a/listener/tun/device/tun/tun_netstack.go b/listener/tun/device/tun/tun_netstack.go deleted file mode 100644 index 89dab248..00000000 --- a/listener/tun/device/tun/tun_netstack.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/tun/tun_wireguard.go b/listener/tun/device/tun/tun_wireguard.go deleted file mode 100644 index 828dd56d..00000000 --- a/listener/tun/device/tun/tun_wireguard.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/tun/tun_wireguard_gvisor.go b/listener/tun/device/tun/tun_wireguard_gvisor.go deleted file mode 100644 index 400b4219..00000000 --- a/listener/tun/device/tun/tun_wireguard_gvisor.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/tun/tun_wireguard_no_gvisor.go b/listener/tun/device/tun/tun_wireguard_no_gvisor.go deleted file mode 100644 index c55487c7..00000000 --- a/listener/tun/device/tun/tun_wireguard_no_gvisor.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/device/tun/tun_wireguard_unix.go b/listener/tun/device/tun/tun_wireguard_unix.go deleted file mode 100644 index b6d3addf..00000000 --- a/listener/tun/device/tun/tun_wireguard_unix.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/device/tun/tun_wireguard_windows.go b/listener/tun/device/tun/tun_wireguard_windows.go deleted file mode 100644 index c5c16a1d..00000000 --- a/listener/tun/device/tun/tun_wireguard_windows.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/ipstack/commons/auto_linux.go b/listener/tun/ipstack/commons/auto_linux.go deleted file mode 100644 index 36be5a66..00000000 --- a/listener/tun/ipstack/commons/auto_linux.go +++ /dev/null @@ -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{}{} - } -} diff --git a/listener/tun/ipstack/commons/auto_others.go b/listener/tun/ipstack/commons/auto_others.go deleted file mode 100644 index 23772c5e..00000000 --- a/listener/tun/ipstack/commons/auto_others.go +++ /dev/null @@ -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{}{} - } -} diff --git a/listener/tun/ipstack/commons/dns.go b/listener/tun/ipstack/commons/dns.go deleted file mode 100644 index 06049708..00000000 --- a/listener/tun/ipstack/commons/dns.go +++ /dev/null @@ -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() -} diff --git a/listener/tun/ipstack/commons/router.go b/listener/tun/ipstack/commons/router.go deleted file mode 100644 index 57c99502..00000000 --- a/listener/tun/ipstack/commons/router.go +++ /dev/null @@ -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 - } - } -} diff --git a/listener/tun/ipstack/commons/router_android.go b/listener/tun/ipstack/commons/router_android.go deleted file mode 100644 index 139dbca9..00000000 --- a/listener/tun/ipstack/commons/router_android.go +++ /dev/null @@ -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) - } - } -} diff --git a/listener/tun/ipstack/commons/router_darwin.go b/listener/tun/ipstack/commons/router_darwin.go deleted file mode 100644 index 3132fced..00000000 --- a/listener/tun/ipstack/commons/router_darwin.go +++ /dev/null @@ -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() {} diff --git a/listener/tun/ipstack/commons/router_linux.go b/listener/tun/ipstack/commons/router_linux.go deleted file mode 100644 index 37a92d2e..00000000 --- a/listener/tun/ipstack/commons/router_linux.go +++ /dev/null @@ -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() {} diff --git a/listener/tun/ipstack/commons/router_others.go b/listener/tun/ipstack/commons/router_others.go deleted file mode 100644 index dfc409f4..00000000 --- a/listener/tun/ipstack/commons/router_others.go +++ /dev/null @@ -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() {} diff --git a/listener/tun/ipstack/commons/router_windows.go b/listener/tun/ipstack/commons/router_windows.go deleted file mode 100644 index e2296173..00000000 --- a/listener/tun/ipstack/commons/router_windows.go +++ /dev/null @@ -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() {} diff --git a/listener/tun/ipstack/gvisor/adapter/adapter.go b/listener/tun/ipstack/gvisor/adapter/adapter.go deleted file mode 100644 index 08d0e780..00000000 --- a/listener/tun/ipstack/gvisor/adapter/adapter.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/gvisor/adapter/handler.go b/listener/tun/ipstack/gvisor/adapter/handler.go deleted file mode 100644 index 913922d6..00000000 --- a/listener/tun/ipstack/gvisor/adapter/handler.go +++ /dev/null @@ -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) diff --git a/listener/tun/ipstack/gvisor/handler.go b/listener/tun/ipstack/gvisor/handler.go deleted file mode 100644 index 5d7d84f4..00000000 --- a/listener/tun/ipstack/gvisor/handler.go +++ /dev/null @@ -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: - } - } - }() -} diff --git a/listener/tun/ipstack/gvisor/nic.go b/listener/tun/ipstack/gvisor/nic.go deleted file mode 100644 index 77a1a349..00000000 --- a/listener/tun/ipstack/gvisor/nic.go +++ /dev/null @@ -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 - } -} diff --git a/listener/tun/ipstack/gvisor/option/option.go b/listener/tun/ipstack/gvisor/option/option.go deleted file mode 100644 index 88c26e62..00000000 --- a/listener/tun/ipstack/gvisor/option/option.go +++ /dev/null @@ -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 - } -} diff --git a/listener/tun/ipstack/gvisor/route.go b/listener/tun/ipstack/gvisor/route.go deleted file mode 100644 index 8e1ae94e..00000000 --- a/listener/tun/ipstack/gvisor/route.go +++ /dev/null @@ -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 - } -} diff --git a/listener/tun/ipstack/gvisor/stack.go b/listener/tun/ipstack/gvisor/stack.go deleted file mode 100644 index c79bd0df..00000000 --- a/listener/tun/ipstack/gvisor/stack.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/gvisor/stack_no_gvisor.go b/listener/tun/ipstack/gvisor/stack_no_gvisor.go deleted file mode 100644 index 7a424e74..00000000 --- a/listener/tun/ipstack/gvisor/stack_no_gvisor.go +++ /dev/null @@ -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") -} diff --git a/listener/tun/ipstack/gvisor/tcp.go b/listener/tun/ipstack/gvisor/tcp.go deleted file mode 100644 index 4798cb7c..00000000 --- a/listener/tun/ipstack/gvisor/tcp.go +++ /dev/null @@ -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), - } -} diff --git a/listener/tun/ipstack/gvisor/udp.go b/listener/tun/ipstack/gvisor/udp.go deleted file mode 100644 index 7abdd046..00000000 --- a/listener/tun/ipstack/gvisor/udp.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/ipstack/stack.go b/listener/tun/ipstack/stack.go deleted file mode 100644 index 4aa2bcb9..00000000 --- a/listener/tun/ipstack/stack.go +++ /dev/null @@ -1,5 +0,0 @@ -package ipstack - -import "io" - -type Stack io.Closer diff --git a/listener/tun/ipstack/system/mars/mars.go b/listener/tun/ipstack/system/mars/mars.go deleted file mode 100644 index a553c2d6..00000000 --- a/listener/tun/ipstack/system/mars/mars.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/system/mars/nat/nat.go b/listener/tun/ipstack/system/mars/nat/nat.go deleted file mode 100644 index 7954ace1..00000000 --- a/listener/tun/ipstack/system/mars/nat/nat.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/system/mars/nat/table.go b/listener/tun/ipstack/system/mars/nat/table.go deleted file mode 100644 index 38b7d6c6..00000000 --- a/listener/tun/ipstack/system/mars/nat/table.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/system/mars/nat/tcp.go b/listener/tun/ipstack/system/mars/nat/tcp.go deleted file mode 100644 index cc0abe7d..00000000 --- a/listener/tun/ipstack/system/mars/nat/tcp.go +++ /dev/null @@ -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()), - } -} diff --git a/listener/tun/ipstack/system/mars/nat/tcp_linux.go b/listener/tun/ipstack/system/mars/nat/tcp_linux.go deleted file mode 100644 index 9f433a3a..00000000 --- a/listener/tun/ipstack/system/mars/nat/tcp_linux.go +++ /dev/null @@ -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) - }) - } -} diff --git a/listener/tun/ipstack/system/mars/nat/tcp_others.go b/listener/tun/ipstack/system/mars/nat/tcp_others.go deleted file mode 100644 index cc46bdbd..00000000 --- a/listener/tun/ipstack/system/mars/nat/tcp_others.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !linux - -package nat - -import "net" - -func addition(*net.TCPConn) {} diff --git a/listener/tun/ipstack/system/mars/nat/udp.go b/listener/tun/ipstack/system/mars/nat/udp.go deleted file mode 100644 index 6cc7faee..00000000 --- a/listener/tun/ipstack/system/mars/nat/udp.go +++ /dev/null @@ -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() - } -} diff --git a/listener/tun/ipstack/system/mars/tcpip/icmp.go b/listener/tun/ipstack/system/mars/tcpip/icmp.go deleted file mode 100644 index d473c25f..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/icmp.go +++ /dev/null @@ -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)) -} diff --git a/listener/tun/ipstack/system/mars/tcpip/icmpv6.go b/listener/tun/ipstack/system/mars/tcpip/icmpv6.go deleted file mode 100644 index a87f965f..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/icmpv6.go +++ /dev/null @@ -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)) -} diff --git a/listener/tun/ipstack/system/mars/tcpip/ip.go b/listener/tun/ipstack/system/mars/tcpip/ip.go deleted file mode 100644 index 88237b01..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/ip.go +++ /dev/null @@ -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) diff --git a/listener/tun/ipstack/system/mars/tcpip/ipv6.go b/listener/tun/ipstack/system/mars/tcpip/ipv6.go deleted file mode 100644 index fd7502d6..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/ipv6.go +++ /dev/null @@ -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) diff --git a/listener/tun/ipstack/system/mars/tcpip/tcp.go b/listener/tun/ipstack/system/mars/tcpip/tcp.go deleted file mode 100644 index d3d08878..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcp.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip.go b/listener/tun/ipstack/system/mars/tcpip/tcpip.go deleted file mode 100644 index 87811ea2..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip.go +++ /dev/null @@ -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) -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64.go deleted file mode 100644 index c7d5dcaa..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64.go +++ /dev/null @@ -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 - } -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64.s b/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64.s deleted file mode 100644 index 100820ba..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64.s +++ /dev/null @@ -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 diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64_test.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64_test.go deleted file mode 100644 index 7133845a..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_amd64_test.go +++ /dev/null @@ -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) - } -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64.go deleted file mode 100644 index c16c8fac..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64.go +++ /dev/null @@ -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 - } -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64.s b/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64.s deleted file mode 100644 index f6d57cf0..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64.s +++ /dev/null @@ -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 diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go deleted file mode 100644 index 20a89095..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_arm64_test.go +++ /dev/null @@ -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) - } -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_compat.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_compat.go deleted file mode 100644 index 0b9dbb01..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_compat.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/system/mars/tcpip/tcpip_compat_test.go b/listener/tun/ipstack/system/mars/tcpip/tcpip_compat_test.go deleted file mode 100644 index f8595aa5..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/tcpip_compat_test.go +++ /dev/null @@ -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) - } -} diff --git a/listener/tun/ipstack/system/mars/tcpip/udp.go b/listener/tun/ipstack/system/mars/tcpip/udp.go deleted file mode 100644 index d2cb7f46..00000000 --- a/listener/tun/ipstack/system/mars/tcpip/udp.go +++ /dev/null @@ -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() -} diff --git a/listener/tun/ipstack/system/stack.go b/listener/tun/ipstack/system/stack.go deleted file mode 100644 index 405eeb71..00000000 --- a/listener/tun/ipstack/system/stack.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/ipstack/system/udp.go b/listener/tun/ipstack/system/udp.go deleted file mode 100644 index cb2761e8..00000000 --- a/listener/tun/ipstack/system/udp.go +++ /dev/null @@ -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 -} diff --git a/listener/tun/tun_adapter.go b/listener/tun/tun_adapter.go deleted file mode 100644 index 24d61949..00000000 --- a/listener/tun/tun_adapter.go +++ /dev/null @@ -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") - } -}