chore: use sing-tun to replace old tun_adapter
This commit is contained in:
parent
347e5e9606
commit
94246104b8
93 changed files with 739 additions and 5101 deletions
2
Makefile
2
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='
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -13,9 +13,9 @@ const (
|
|||
)
|
||||
|
||||
func Get(size int) []byte {
|
||||
return DefaultAllocator.Get(size)
|
||||
return defaultAllocator.Get(size)
|
||||
}
|
||||
|
||||
func Put(buf []byte) error {
|
||||
return DefaultAllocator.Put(buf)
|
||||
return defaultAllocator.Put(buf)
|
||||
}
|
||||
|
|
7
common/pool/sing.go
Normal file
7
common/pool/sing.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package pool
|
||||
|
||||
import "github.com/sagernet/sing/common/buf"
|
||||
|
||||
func init() {
|
||||
buf.DefaultAllocator = defaultAllocator
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
101
config/config.go
101
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
|
||||
}
|
||||
|
||||
|
|
13
go.mod
13
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
|
||||
)
|
||||
|
|
20
go.sum
20
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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
41
listener/sing/log.go
Normal file
41
listener/sing/log.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package sing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
L "github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type logger struct{}
|
||||
|
||||
func (l logger) Trace(args ...any) {
|
||||
log.Debugln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l logger) Debug(args ...any) {
|
||||
log.Debugln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l logger) Info(args ...any) {
|
||||
log.Infoln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l logger) Warn(args ...any) {
|
||||
log.Warnln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l logger) Error(args ...any) {
|
||||
log.Errorln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l logger) Fatal(args ...any) {
|
||||
log.Fatalln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (l logger) Panic(args ...any) {
|
||||
log.Fatalln(fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
var Logger L.Logger = logger{}
|
143
listener/sing/sing.go
Normal file
143
listener/sing/sing.go
Normal file
|
@ -0,0 +1,143 @@
|
|||
package sing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
|
||||
vmess "github.com/sagernet/sing-vmess"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/uot"
|
||||
)
|
||||
|
||||
type ListenerHandler struct {
|
||||
TcpIn chan<- C.ConnContext
|
||||
UdpIn chan<- *inbound.PacketAdapter
|
||||
Type C.Type
|
||||
}
|
||||
|
||||
type waitCloseConn struct {
|
||||
net.Conn
|
||||
wg *sync.WaitGroup
|
||||
close sync.Once
|
||||
}
|
||||
|
||||
func (c *waitCloseConn) Close() error { // call from handleTCPConn(connCtx C.ConnContext)
|
||||
c.close.Do(func() {
|
||||
c.wg.Done()
|
||||
})
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
switch metadata.Destination.Fqdn {
|
||||
case vmess.MuxDestination.Fqdn:
|
||||
return vmess.HandleMuxConnection(ctx, conn, h)
|
||||
case uot.UOTMagicAddress:
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
return h.NewPacketConnection(ctx, uot.NewClientConn(conn), metadata)
|
||||
}
|
||||
target := socks5.ParseAddr(metadata.Destination.String())
|
||||
wg := &sync.WaitGroup{}
|
||||
defer wg.Wait() // this goroutine must exit after conn.Close()
|
||||
wg.Add(1)
|
||||
h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg}, h.Type)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
||||
defer func() { _ = conn.Close() }()
|
||||
mutex := sync.Mutex{}
|
||||
conn2 := conn // a new interface to set nil in defer
|
||||
defer func() {
|
||||
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
|
||||
defer mutex.Unlock()
|
||||
conn2 = nil
|
||||
}()
|
||||
for {
|
||||
buff := buf.NewPacket() // do not use stack buffer
|
||||
dest, err := conn.ReadPacket(buff)
|
||||
if err != nil {
|
||||
buff.Release()
|
||||
if E.IsClosed(err) {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
target := socks5.ParseAddr(dest.String())
|
||||
packet := &packet{
|
||||
conn: &conn2,
|
||||
mutex: &mutex,
|
||||
rAddr: metadata.Source.UDPAddr(),
|
||||
lAddr: conn.LocalAddr(),
|
||||
buff: buff,
|
||||
}
|
||||
select {
|
||||
case h.UdpIn <- inbound.NewPacket(target, packet, h.Type):
|
||||
default:
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) NewError(ctx context.Context, err error) {
|
||||
log.Warnln("%s listener get error: %+v", h.Type.String(), err)
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
conn *network.PacketConn
|
||||
mutex *sync.Mutex
|
||||
rAddr net.Addr
|
||||
lAddr net.Addr
|
||||
buff *buf.Buffer
|
||||
}
|
||||
|
||||
func (c *packet) Data() []byte {
|
||||
return c.buff.Bytes()
|
||||
}
|
||||
|
||||
// WriteBack wirtes UDP packet with source(ip, port) = `addr`
|
||||
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
if addr == nil {
|
||||
err = errors.New("address is invalid")
|
||||
return
|
||||
}
|
||||
buff := buf.NewPacket()
|
||||
defer buff.Release()
|
||||
n, err = buff.Write(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
conn := *c.conn
|
||||
if conn == nil {
|
||||
err = errors.New("writeBack to closed connection")
|
||||
return
|
||||
}
|
||||
err = conn.WritePacket(buff, M.ParseSocksaddr(addr.String()))
|
||||
return
|
||||
}
|
||||
|
||||
// LocalAddr returns the source IP/Port of UDP Packet
|
||||
func (c *packet) LocalAddr() net.Addr {
|
||||
return c.rAddr
|
||||
}
|
||||
|
||||
func (c *packet) Drop() {
|
||||
c.buff.Release()
|
||||
}
|
||||
|
||||
func (c *packet) InAddr() net.Addr {
|
||||
return c.lAddr
|
||||
}
|
165
listener/sing_tun/dns.go
Normal file
165
listener/sing_tun/dns.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
package sing_tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/pool"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
"github.com/Dreamacro/clash/listener/sing"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
const DefaultDnsReadTimeout = time.Second * 10
|
||||
|
||||
type ListenerHandler struct {
|
||||
sing.ListenerHandler
|
||||
DnsAdds []netip.AddrPort
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool {
|
||||
if targetAddr.Addr().IsLoopback() && targetAddr.Port() == 53 { // cause by system stack
|
||||
return true
|
||||
}
|
||||
for _, addrPort := range h.DnsAdds {
|
||||
if addrPort == targetAddr || (addrPort.Addr().IsUnspecified() && targetAddr.Port() == 53) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||||
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
||||
log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String())
|
||||
buff := pool.Get(pool.UDPBufferSize)
|
||||
defer func() {
|
||||
_ = pool.Put(buff)
|
||||
_ = conn.Close()
|
||||
}()
|
||||
for {
|
||||
if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil {
|
||||
break
|
||||
}
|
||||
|
||||
length := uint16(0)
|
||||
if err := binary.Read(conn, binary.BigEndian, &length); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if int(length) > len(buff) {
|
||||
break
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(conn, buff[:length])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
inData := buff[:n]
|
||||
msg, err := RelayDnsPacket(inData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = binary.Write(conn, binary.BigEndian, uint16(len(msg)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = conn.Write(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return h.ListenerHandler.NewConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {
|
||||
if h.ShouldHijackDns(metadata.Destination.AddrPort()) {
|
||||
log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String())
|
||||
defer func() { _ = conn.Close() }()
|
||||
mutex := sync.Mutex{}
|
||||
conn2 := conn // a new interface to set nil in defer
|
||||
defer func() {
|
||||
mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running
|
||||
defer mutex.Unlock()
|
||||
conn2 = nil
|
||||
}()
|
||||
for {
|
||||
buff := buf.NewPacket()
|
||||
dest, err := conn.ReadPacket(buff)
|
||||
if err != nil {
|
||||
buff.Release()
|
||||
if E.IsClosed(err) {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
inData := buff.Bytes()
|
||||
msg, err := RelayDnsPacket(inData)
|
||||
if err != nil {
|
||||
buff.Release()
|
||||
return
|
||||
}
|
||||
buff.Reset()
|
||||
_, err = buff.Write(msg)
|
||||
if err != nil {
|
||||
buff.Release()
|
||||
return
|
||||
}
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
conn := conn2
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
err = conn.WritePacket(buff, dest) // WritePacket will release buff
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)
|
||||
}
|
||||
|
||||
func RelayDnsPacket(payload []byte) ([]byte, error) {
|
||||
msg := &D.Msg{}
|
||||
if err := msg.Unpack(payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := resolver.ServeMsg(msg)
|
||||
if err != nil {
|
||||
m := new(D.Msg)
|
||||
m.SetRcode(msg, D.RcodeServerFailure)
|
||||
return m.Pack()
|
||||
}
|
||||
|
||||
r.SetRcode(msg, r.Rcode)
|
||||
r.Compress = true
|
||||
return r.Pack()
|
||||
}
|
237
listener/sing_tun/server.go
Normal file
237
listener/sing_tun/server.go
Normal file
|
@ -0,0 +1,237 @@
|
|||
package sing_tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/iface"
|
||||
"github.com/Dreamacro/clash/config"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/listener/sing"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
tun "github.com/sagernet/sing-tun"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/ranges"
|
||||
)
|
||||
|
||||
var InterfaceName = "Meta"
|
||||
|
||||
type Listener struct {
|
||||
closed bool
|
||||
options config.Tun
|
||||
handler tun.Handler
|
||||
|
||||
tunIf tun.Tun
|
||||
tunStack tun.Stack
|
||||
|
||||
networkUpdateMonitor tun.NetworkUpdateMonitor
|
||||
defaultInterfaceMonitor tun.DefaultInterfaceMonitor
|
||||
}
|
||||
|
||||
func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) {
|
||||
tunName := options.Device
|
||||
if tunName == "" {
|
||||
tunName = tun.CalculateInterfaceName(InterfaceName)
|
||||
}
|
||||
tunMTU := options.MTU
|
||||
if tunMTU == 0 {
|
||||
tunMTU = 9000
|
||||
}
|
||||
var udpTimeout int64
|
||||
if options.UDPTimeout != 0 {
|
||||
udpTimeout = options.UDPTimeout
|
||||
} else {
|
||||
udpTimeout = int64(C.DefaultUDPTimeout.Seconds())
|
||||
}
|
||||
includeUID := uidToRange(options.IncludeUID)
|
||||
if len(options.IncludeUIDRange) > 0 {
|
||||
var err error
|
||||
includeUID, err = parseRange(includeUID, options.IncludeUIDRange)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse include_uid_range")
|
||||
}
|
||||
}
|
||||
excludeUID := uidToRange(options.ExcludeUID)
|
||||
if len(options.ExcludeUIDRange) > 0 {
|
||||
var err error
|
||||
excludeUID, err = parseRange(excludeUID, options.ExcludeUIDRange)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse exclude_uid_range")
|
||||
}
|
||||
}
|
||||
|
||||
var dnsAdds []netip.AddrPort
|
||||
|
||||
for _, d := range options.DNSHijack {
|
||||
dnsAdds = append(dnsAdds, d)
|
||||
}
|
||||
for _, a := range options.Inet4Address {
|
||||
addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53)
|
||||
dnsAdds = append(dnsAdds, addrPort)
|
||||
}
|
||||
for _, a := range options.Inet6Address {
|
||||
addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53)
|
||||
dnsAdds = append(dnsAdds, addrPort)
|
||||
}
|
||||
|
||||
handler := &ListenerHandler{
|
||||
ListenerHandler: sing.ListenerHandler{
|
||||
TcpIn: tcpIn,
|
||||
UdpIn: udpIn,
|
||||
Type: C.TUN,
|
||||
},
|
||||
DnsAdds: dnsAdds,
|
||||
}
|
||||
l = &Listener{
|
||||
closed: false,
|
||||
options: options,
|
||||
handler: handler,
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
l.Close()
|
||||
l = nil
|
||||
}
|
||||
}()
|
||||
|
||||
networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(handler)
|
||||
if err != nil {
|
||||
err = E.Cause(err, "create NetworkUpdateMonitor")
|
||||
return
|
||||
}
|
||||
err = networkUpdateMonitor.Start()
|
||||
if err != nil {
|
||||
err = E.Cause(err, "start NetworkUpdateMonitor")
|
||||
return
|
||||
}
|
||||
l.networkUpdateMonitor = networkUpdateMonitor
|
||||
|
||||
defaultInterfaceMonitor, err := tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, tun.DefaultInterfaceMonitorOptions{})
|
||||
if err != nil {
|
||||
err = E.Cause(err, "create DefaultInterfaceMonitor")
|
||||
return
|
||||
}
|
||||
defaultInterfaceMonitor.RegisterCallback(func(event int) error {
|
||||
targetInterface := dialer.DefaultInterface.Load()
|
||||
autoDetectInterfaceName := defaultInterfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified())
|
||||
if autoDetectInterfaceName != "" && autoDetectInterfaceName != "<nil>" {
|
||||
targetInterface = autoDetectInterfaceName
|
||||
} else {
|
||||
log.Warnln("Auto detect interface name is empty.")
|
||||
}
|
||||
if old := dialer.DefaultInterface.Load(); old != targetInterface {
|
||||
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, targetInterface)
|
||||
|
||||
dialer.DefaultInterface.Store(targetInterface)
|
||||
|
||||
iface.FlushCache()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
err = defaultInterfaceMonitor.Start()
|
||||
if err != nil {
|
||||
err = E.Cause(err, "start DefaultInterfaceMonitor")
|
||||
return
|
||||
}
|
||||
l.defaultInterfaceMonitor = defaultInterfaceMonitor
|
||||
|
||||
tunOptions := tun.Options{
|
||||
Name: tunName,
|
||||
MTU: tunMTU,
|
||||
Inet4Address: common.Map(options.Inet4Address, config.ListenPrefix.Build),
|
||||
Inet6Address: common.Map(options.Inet6Address, config.ListenPrefix.Build),
|
||||
AutoRoute: options.AutoRoute,
|
||||
StrictRoute: options.StrictRoute,
|
||||
IncludeUID: includeUID,
|
||||
ExcludeUID: excludeUID,
|
||||
IncludeAndroidUser: options.IncludeAndroidUser,
|
||||
IncludePackage: options.IncludePackage,
|
||||
ExcludePackage: options.ExcludePackage,
|
||||
InterfaceMonitor: defaultInterfaceMonitor,
|
||||
TableIndex: 2022,
|
||||
}
|
||||
|
||||
//if C.IsAndroid {
|
||||
// t.tunOptions.BuildAndroidRules(t.router.PackageManager(), t)
|
||||
//}
|
||||
tunIf, err := tun.Open(tunOptions)
|
||||
if err != nil {
|
||||
err = E.Cause(err, "configure tun interface")
|
||||
return
|
||||
}
|
||||
l.tunIf = tunIf
|
||||
l.tunStack, err = tun.NewStack(options.Stack, tun.StackOptions{
|
||||
Context: context.TODO(),
|
||||
Tun: tunIf,
|
||||
MTU: tunOptions.MTU,
|
||||
Name: tunOptions.Name,
|
||||
Inet4Address: tunOptions.Inet4Address,
|
||||
Inet6Address: tunOptions.Inet6Address,
|
||||
EndpointIndependentNat: options.EndpointIndependentNat,
|
||||
UDPTimeout: udpTimeout,
|
||||
Handler: handler,
|
||||
Logger: sing.Logger,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = l.tunStack.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Infoln("Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s",
|
||||
tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack)
|
||||
return
|
||||
}
|
||||
|
||||
func uidToRange(uidList []uint32) []ranges.Range[uint32] {
|
||||
return common.Map(uidList, func(uid uint32) ranges.Range[uint32] {
|
||||
return ranges.NewSingle(uid)
|
||||
})
|
||||
}
|
||||
|
||||
func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.Range[uint32], error) {
|
||||
for _, uidRange := range rangeList {
|
||||
if !strings.Contains(uidRange, ":") {
|
||||
return nil, E.New("missing ':' in range: ", uidRange)
|
||||
}
|
||||
subIndex := strings.Index(uidRange, ":")
|
||||
if subIndex == 0 {
|
||||
return nil, E.New("missing range start: ", uidRange)
|
||||
} else if subIndex == len(uidRange)-1 {
|
||||
return nil, E.New("missing range end: ", uidRange)
|
||||
}
|
||||
var start, end uint64
|
||||
var err error
|
||||
start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse range start")
|
||||
}
|
||||
end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse range end")
|
||||
}
|
||||
uidRanges = append(uidRanges, ranges.New(uint32(start), uint32(end)))
|
||||
}
|
||||
return uidRanges, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() {
|
||||
l.closed = true
|
||||
_ = common.Close(
|
||||
l.tunStack,
|
||||
l.tunIf,
|
||||
l.defaultInterfaceMonitor,
|
||||
l.networkUpdateMonitor,
|
||||
)
|
||||
}
|
||||
|
||||
func (l *Listener) Config() config.Tun {
|
||||
return l.options
|
||||
}
|
7
listener/sing_tun/server_windows.go
Normal file
7
listener/sing_tun/server_windows.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package sing_tun
|
||||
|
||||
import tun "github.com/sagernet/sing-tun"
|
||||
|
||||
func init() {
|
||||
tun.TunnelType = InterfaceName
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package fdbased
|
||||
|
||||
const Driver = "fd"
|
||||
|
||||
const defaultMTU = 1500
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
//go:build no_gvisor
|
||||
|
||||
package fdbased
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type FD struct {
|
||||
fd int
|
||||
mtu uint32
|
||||
|
||||
file *os.File
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package fdbased
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Dreamacro/clash/listener/tun/device"
|
||||
)
|
||||
|
||||
func Open(name string, mtu uint32) (device.Device, error) {
|
||||
return nil, errors.New("not supported")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package iobased
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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)
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Binary file not shown.
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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{}{}
|
||||
}
|
||||
}
|
|
@ -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{}{}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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() {}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
|
@ -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:
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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),
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
package ipstack
|
||||
|
||||
import "io"
|
||||
|
||||
type Stack io.Closer
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()),
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
//go:build !linux
|
||||
|
||||
package nat
|
||||
|
||||
import "net"
|
||||
|
||||
func addition(*net.TCPConn) {}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue