Merge remote-tracking branch 'upstream/Alpha' into Alpha

This commit is contained in:
admin 2022-03-17 23:40:51 +08:00
commit 68345b6a19
140 changed files with 5354 additions and 4136 deletions

29
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: CodeQL
on:
push:
branches: [ rm ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['go']
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

27
.github/workflows/linter.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Linter
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get latest go version
id: version
run: |
echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g')
- name: Setup Go
uses: actions/setup-go@v2
#with:
# go-version: ${{ steps.version.outputs.go_version }}
with:
go-version: 1.18.x
- name: golangci-lint
#uses: golangci/golangci-lint-action@v3
#with:
# version: latest
run: |
go install github.com/golangci/golangci-lint/cmd/golangci-lint@ec95236
golangci-lint run ./...

View File

@ -4,7 +4,9 @@ linters:
- gofumpt - gofumpt
- staticcheck - staticcheck
- govet - govet
- gci # - gci
- gofmt
- goimports
linters-settings: linters-settings:
gci: gci:

View File

@ -14,7 +14,7 @@ PLATFORM_LIST = \
linux-armv5 \ linux-armv5 \
linux-armv6 \ linux-armv6 \
linux-armv7 \ linux-armv7 \
linux-armv8 \ linux-arm64 \
linux-mips64 \ linux-mips64 \
linux-mips64le \ linux-mips64le \
linux-mips-softfloat \ linux-mips-softfloat \
@ -26,20 +26,15 @@ PLATFORM_LIST = \
freebsd-amd64 \ freebsd-amd64 \
freebsd-arm64 freebsd-arm64
WINDOWS_ARCH_LIST = \ WINDOWS_ARCH_LIST = \
windows-386 \ windows-386 \
windows-amd64 \ windows-amd64 \
windows-arm64 \ windows-arm64 \
windows-arm32v7 windows-arm32v7
all:linux-amd64 linux-arm64\
all:linux-amd64\
linux-armv7\
darwin-amd64 darwin-arm64\ darwin-amd64 darwin-arm64\
windows-amd64 windows-386 \ windows-amd64 windows-arm64 \
linux-mips-hardfloat linux-mips-softfloat linux-mips64 linux-mips64le linux-mipsle-hardfloat linux-mipsle-softfloat# Most used
docker: docker:
GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
@ -55,9 +50,6 @@ linux-386:
linux-amd64: linux-amd64:
GOARCH=amd64 GOOS=linux GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOARCH=amd64 GOOS=linux GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
linux-amd64-AutoIptables:
GOARCH=amd64 GOOS=linux $(GOBUILDOP) -o $(BINDIR)/$(NAME)-$@
linux-arm64: linux-arm64:
GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
@ -70,7 +62,7 @@ linux-armv6:
linux-armv7: linux-armv7:
GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
linux-armv8: linux-arm64:
GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
linux-mips-softfloat: linux-mips-softfloat:
@ -91,7 +83,7 @@ linux-mips64:
linux-mips64le: linux-mips64le:
GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
android-armv8: android-arm64:
GOARCH=arm64 GOOS=android $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ GOARCH=arm64 GOOS=android $(GOBUILD) -o $(BINDIR)/$(NAME)-$@
freebsd-386: freebsd-386:
@ -128,5 +120,12 @@ $(zip_releases): %.zip : %
all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST) all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST)
releases: $(gz_releases) $(zip_releases) releases: $(gz_releases) $(zip_releases)
vet:
go vet ./...
lint:
golangci-lint run ./...
clean: clean:
rm $(BINDIR)/* rm $(BINDIR)/*

View File

@ -17,6 +17,7 @@ import (
"github.com/Dreamacro/clash/transport/gun" "github.com/Dreamacro/clash/transport/gun"
"github.com/Dreamacro/clash/transport/vless" "github.com/Dreamacro/clash/transport/vless"
"github.com/Dreamacro/clash/transport/vmess" "github.com/Dreamacro/clash/transport/vmess"
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )

View File

@ -47,6 +47,10 @@ type VmessOption struct {
HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"` HTTP2Opts HTTP2Options `proxy:"h2-opts,omitempty"`
GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"`
WSOpts WSOptions `proxy:"ws-opts,omitempty"` WSOpts WSOptions `proxy:"ws-opts,omitempty"`
// TODO: compatible with VMESS WS older version configurations
WSHeaders map[string]string `proxy:"ws-headers,omitempty"`
WSPath string `proxy:"ws-path,omitempty"`
} }
type HTTPOptions struct { type HTTPOptions struct {
@ -76,6 +80,13 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
var err error var err error
switch v.option.Network { switch v.option.Network {
case "ws": case "ws":
if v.option.WSOpts.Path == "" {
v.option.WSOpts.Path = v.option.WSPath
}
if len(v.option.WSOpts.Headers) == 0 {
v.option.WSOpts.Headers = v.option.WSHeaders
}
host, port, _ := net.SplitHostPort(v.addr) host, port, _ := net.SplitHostPort(v.addr)
wsOpts := &vmess.WebsocketConfig{ wsOpts := &vmess.WebsocketConfig{
Host: host, Host: host,

35
common/cmd/cmd.go Normal file
View File

@ -0,0 +1,35 @@
package cmd
import (
"fmt"
"os/exec"
"strings"
)
func ExecCmd(cmdStr string) (string, error) {
args := splitArgs(cmdStr)
var cmd *exec.Cmd
if len(args) == 1 {
cmd = exec.Command(args[0])
} else {
cmd = exec.Command(args[0], args[1:]...)
}
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("%v, %s", err, string(out))
}
return string(out), nil
}
func splitArgs(cmd string) []string {
args := strings.Split(cmd, " ")
// use in pipeline
if len(args) > 2 && strings.ContainsAny(cmd, "|") {
suffix := strings.Join(args[2:], " ")
args = append(args[:2], suffix)
}
return args
}

40
common/cmd/cmd_test.go Normal file
View File

@ -0,0 +1,40 @@
package cmd
import (
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplitArgs(t *testing.T) {
args := splitArgs("ls")
args1 := splitArgs("ls -la")
args2 := splitArgs("bash -c ls")
args3 := splitArgs("bash -c ls -lahF | grep 'cmd'")
assert.Equal(t, 1, len(args))
assert.Equal(t, 2, len(args1))
assert.Equal(t, 3, len(args2))
assert.Equal(t, 3, len(args3))
}
func TestExecCmd(t *testing.T) {
if runtime.GOOS == "windows" {
_, err := ExecCmd("dir")
assert.Nil(t, err)
return
}
_, err := ExecCmd("ls")
_, err1 := ExecCmd("ls -la")
_, err2 := ExecCmd("bash -c ls")
_, err3 := ExecCmd("bash -c ls -la")
_, err4 := ExecCmd("bash -c ls -la | grep 'cmd'")
assert.Nil(t, err)
assert.Nil(t, err1)
assert.Nil(t, err2)
assert.Nil(t, err3)
assert.Nil(t, err4)
}

View File

@ -0,0 +1,28 @@
package dialer
import (
"context"
"net"
)
func init() {
// We must use this DialContext to query DNS
// when using net default resolver.
net.DefaultResolver.PreferGo = true
net.DefaultResolver.Dial = resolverDialContext
}
func resolverDialContext(ctx context.Context, network, address string) (net.Conn, error) {
d := &net.Dialer{}
interfaceName := DefaultInterface.Load()
if interfaceName != "" {
dstIP := net.ParseIP(address)
if dstIP != nil {
bindIfaceToDialer(interfaceName, d, network, dstIP)
}
}
return d.DialContext(ctx, network, address)
}

View File

@ -25,6 +25,7 @@ type Pool struct {
max uint32 max uint32
min uint32 min uint32
gateway uint32 gateway uint32
broadcast uint32
offset uint32 offset uint32
mux sync.Mutex mux sync.Mutex
host *trie.DomainTrie host *trie.DomainTrie
@ -82,6 +83,11 @@ func (p *Pool) Gateway() net.IP {
return uintToIP(p.gateway) return uintToIP(p.gateway)
} }
// Broadcast return broadcast ip
func (p *Pool) Broadcast() net.IP {
return uintToIP(p.broadcast)
}
// IPNet return raw ipnet // IPNet return raw ipnet
func (p *Pool) IPNet() *net.IPNet { func (p *Pool) IPNet() *net.IPNet {
return p.ipnet return p.ipnet
@ -144,7 +150,7 @@ func New(options Options) (*Pool, error) {
min := ipToUint(options.IPNet.IP) + 2 min := ipToUint(options.IPNet.IP) + 2
ones, bits := options.IPNet.Mask.Size() ones, bits := options.IPNet.Mask.Size()
total := 1<<uint(bits-ones) - 2 total := 1<<uint(bits-ones) - 3
if total <= 0 { if total <= 0 {
return nil, errors.New("ipnet don't have valid ip") return nil, errors.New("ipnet don't have valid ip")
@ -155,6 +161,7 @@ func New(options Options) (*Pool, error) {
min: min, min: min,
max: max, max: max,
gateway: min - 1, gateway: min - 1,
broadcast: max + 1,
host: options.Host, host: options.Host,
ipnet: options.IPNet, ipnet: options.IPNet,
} }

View File

@ -87,7 +87,7 @@ func TestPool_CycleUsed(t *testing.T) {
for _, pool := range pools { for _, pool := range pools {
foo := pool.Lookup("foo.com") foo := pool.Lookup("foo.com")
bar := pool.Lookup("bar.com") bar := pool.Lookup("bar.com")
for i := 0; i < 3; i++ { for i := 0; i < 2; i++ {
pool.Lookup(fmt.Sprintf("%d.com", i)) pool.Lookup(fmt.Sprintf("%d.com", i))
} }
baz := pool.Lookup("baz.com") baz := pool.Lookup("baz.com")

View File

@ -1,8 +1,10 @@
package router package router
import ( import (
"encoding/binary"
"fmt" "fmt"
"net" "net"
"sort"
"strings" "strings"
"github.com/Dreamacro/clash/component/geodata/strmatcher" "github.com/Dreamacro/clash/component/geodata/strmatcher"
@ -71,6 +73,241 @@ func (m *DomainMatcher) ApplyDomain(domain string) bool {
return len(m.matchers.Match(strings.ToLower(domain))) > 0 return len(m.matchers.Match(strings.ToLower(domain))) > 0
} }
// CIDRList is an alias of []*CIDR to provide sort.Interface.
type CIDRList []*CIDR
// Len implements sort.Interface.
func (l *CIDRList) Len() int {
return len(*l)
}
// Less implements sort.Interface.
func (l *CIDRList) Less(i int, j int) bool {
ci := (*l)[i]
cj := (*l)[j]
if len(ci.Ip) < len(cj.Ip) {
return true
}
if len(ci.Ip) > len(cj.Ip) {
return false
}
for k := 0; k < len(ci.Ip); k++ {
if ci.Ip[k] < cj.Ip[k] {
return true
}
if ci.Ip[k] > cj.Ip[k] {
return false
}
}
return ci.Prefix < cj.Prefix
}
// Swap implements sort.Interface.
func (l *CIDRList) Swap(i int, j int) {
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
}
type ipv6 struct {
a uint64
b uint64
}
type GeoIPMatcher struct {
countryCode string
reverseMatch bool
ip4 []uint32
prefix4 []uint8
ip6 []ipv6
prefix6 []uint8
}
func normalize4(ip uint32, prefix uint8) uint32 {
return (ip >> (32 - prefix)) << (32 - prefix)
}
func normalize6(ip ipv6, prefix uint8) ipv6 {
if prefix <= 64 {
ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
ip.b = 0
} else {
ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
}
return ip
}
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
ip4Count := 0
ip6Count := 0
for _, cidr := range cidrs {
ip := cidr.Ip
switch len(ip) {
case 4:
ip4Count++
case 16:
ip6Count++
default:
return fmt.Errorf("unexpect ip length: %d", len(ip))
}
}
cidrList := CIDRList(cidrs)
sort.Sort(&cidrList)
m.ip4 = make([]uint32, 0, ip4Count)
m.prefix4 = make([]uint8, 0, ip4Count)
m.ip6 = make([]ipv6, 0, ip6Count)
m.prefix6 = make([]uint8, 0, ip6Count)
for _, cidr := range cidrs {
ip := cidr.Ip
prefix := uint8(cidr.Prefix)
switch len(ip) {
case 4:
m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
m.prefix4 = append(m.prefix4, prefix)
case 16:
ip6 := ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
}
ip6 = normalize6(ip6, prefix)
m.ip6 = append(m.ip6, ip6)
m.prefix6 = append(m.prefix6, prefix)
}
}
return nil
}
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
m.reverseMatch = isReverseMatch
}
func (m *GeoIPMatcher) match4(ip uint32) bool {
if len(m.ip4) == 0 {
return false
}
if ip < m.ip4[0] {
return false
}
size := uint32(len(m.ip4))
l := uint32(0)
r := size
for l < r {
x := ((l + r) >> 1)
if ip < m.ip4[x] {
r = x
continue
}
nip := normalize4(ip, m.prefix4[x])
if nip == m.ip4[x] {
return true
}
l = x + 1
}
return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
}
func less6(a ipv6, b ipv6) bool {
return a.a < b.a || (a.a == b.a && a.b < b.b)
}
func (m *GeoIPMatcher) match6(ip ipv6) bool {
if len(m.ip6) == 0 {
return false
}
if less6(ip, m.ip6[0]) {
return false
}
size := uint32(len(m.ip6))
l := uint32(0)
r := size
for l < r {
x := (l + r) / 2
if less6(ip, m.ip6[x]) {
r = x
continue
}
if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
return true
}
l = x + 1
}
return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
}
// Match returns true if the given ip is included by the GeoIP.
func (m *GeoIPMatcher) Match(ip net.IP) bool {
switch len(ip) {
case 4:
if m.reverseMatch {
return !m.match4(binary.BigEndian.Uint32(ip))
}
return m.match4(binary.BigEndian.Uint32(ip))
case 16:
if m.reverseMatch {
return !m.match6(ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
})
}
return m.match6(ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
})
default:
return false
}
}
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
type GeoIPMatcherContainer struct {
matchers []*GeoIPMatcher
}
// Add adds a new GeoIP set into the container.
// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
if len(geoip.CountryCode) > 0 {
for _, m := range c.matchers {
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
return m, nil
}
}
}
m := &GeoIPMatcher{
countryCode: geoip.CountryCode,
reverseMatch: geoip.ReverseMatch,
}
if err := m.Init(geoip.Cidr); err != nil {
return nil, err
}
if len(geoip.CountryCode) > 0 {
c.matchers = append(c.matchers, m)
}
return m, nil
}
var globalGeoIPContainer GeoIPMatcherContainer
type MultiGeoIPMatcher struct { type MultiGeoIPMatcher struct {
matchers []*GeoIPMatcher matchers []*GeoIPMatcher
} }

View File

@ -1,243 +0,0 @@
package router
import (
"encoding/binary"
"fmt"
"net"
"sort"
)
// CIDRList is an alias of []*CIDR to provide sort.Interface.
type CIDRList []*CIDR
// Len implements sort.Interface.
func (l *CIDRList) Len() int {
return len(*l)
}
// Less implements sort.Interface.
func (l *CIDRList) Less(i int, j int) bool {
ci := (*l)[i]
cj := (*l)[j]
if len(ci.Ip) < len(cj.Ip) {
return true
}
if len(ci.Ip) > len(cj.Ip) {
return false
}
for k := 0; k < len(ci.Ip); k++ {
if ci.Ip[k] < cj.Ip[k] {
return true
}
if ci.Ip[k] > cj.Ip[k] {
return false
}
}
return ci.Prefix < cj.Prefix
}
// Swap implements sort.Interface.
func (l *CIDRList) Swap(i int, j int) {
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
}
type ipv6 struct {
a uint64
b uint64
}
type GeoIPMatcher struct {
countryCode string
reverseMatch bool
ip4 []uint32
prefix4 []uint8
ip6 []ipv6
prefix6 []uint8
}
func normalize4(ip uint32, prefix uint8) uint32 {
return (ip >> (32 - prefix)) << (32 - prefix)
}
func normalize6(ip ipv6, prefix uint8) ipv6 {
if prefix <= 64 {
ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
ip.b = 0
} else {
ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
}
return ip
}
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
ip4Count := 0
ip6Count := 0
for _, cidr := range cidrs {
ip := cidr.Ip
switch len(ip) {
case 4:
ip4Count++
case 16:
ip6Count++
default:
return fmt.Errorf("unexpect ip length: %d", len(ip))
}
}
cidrList := CIDRList(cidrs)
sort.Sort(&cidrList)
m.ip4 = make([]uint32, 0, ip4Count)
m.prefix4 = make([]uint8, 0, ip4Count)
m.ip6 = make([]ipv6, 0, ip6Count)
m.prefix6 = make([]uint8, 0, ip6Count)
for _, cidr := range cidrs {
ip := cidr.Ip
prefix := uint8(cidr.Prefix)
switch len(ip) {
case 4:
m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
m.prefix4 = append(m.prefix4, prefix)
case 16:
ip6 := ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
}
ip6 = normalize6(ip6, prefix)
m.ip6 = append(m.ip6, ip6)
m.prefix6 = append(m.prefix6, prefix)
}
}
return nil
}
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
m.reverseMatch = isReverseMatch
}
func (m *GeoIPMatcher) match4(ip uint32) bool {
if len(m.ip4) == 0 {
return false
}
if ip < m.ip4[0] {
return false
}
size := uint32(len(m.ip4))
l := uint32(0)
r := size
for l < r {
x := ((l + r) >> 1)
if ip < m.ip4[x] {
r = x
continue
}
nip := normalize4(ip, m.prefix4[x])
if nip == m.ip4[x] {
return true
}
l = x + 1
}
return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
}
func less6(a ipv6, b ipv6) bool {
return a.a < b.a || (a.a == b.a && a.b < b.b)
}
func (m *GeoIPMatcher) match6(ip ipv6) bool {
if len(m.ip6) == 0 {
return false
}
if less6(ip, m.ip6[0]) {
return false
}
size := uint32(len(m.ip6))
l := uint32(0)
r := size
for l < r {
x := (l + r) / 2
if less6(ip, m.ip6[x]) {
r = x
continue
}
if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
return true
}
l = x + 1
}
return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
}
// Match returns true if the given ip is included by the GeoIP.
func (m *GeoIPMatcher) Match(ip net.IP) bool {
switch len(ip) {
case 4:
if m.reverseMatch {
return !m.match4(binary.BigEndian.Uint32(ip))
}
return m.match4(binary.BigEndian.Uint32(ip))
case 16:
if m.reverseMatch {
return !m.match6(ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
})
}
return m.match6(ipv6{
a: binary.BigEndian.Uint64(ip[0:8]),
b: binary.BigEndian.Uint64(ip[8:16]),
})
default:
return false
}
}
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
type GeoIPMatcherContainer struct {
matchers []*GeoIPMatcher
}
// Add adds a new GeoIP set into the container.
// If the country code of GeoIP is not empty, GeoIPMatcherContainer will try to find an existing one, instead of adding a new one.
func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
if len(geoip.CountryCode) > 0 {
for _, m := range c.matchers {
if m.countryCode == geoip.CountryCode && m.reverseMatch == geoip.ReverseMatch {
return m, nil
}
}
}
m := &GeoIPMatcher{
countryCode: geoip.CountryCode,
reverseMatch: geoip.ReverseMatch,
}
if err := m.Init(geoip.Cidr); err != nil {
return nil, err
}
if len(geoip.CountryCode) > 0 {
c.matchers = append(c.matchers, m)
}
return m, nil
}
var globalGeoIPContainer GeoIPMatcherContainer

View File

@ -9,6 +9,7 @@ import (
"github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/geodata"
"github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/geodata/router"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )

View File

@ -34,7 +34,7 @@ func TestPool_MaxSize(t *testing.T) {
size := 5 size := 5
pool := New(g, WithSize(size)) pool := New(g, WithSize(size))
items := []any{} var items []any
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
item, _ := pool.Get() item, _ := pool.Get()

View File

@ -3,6 +3,8 @@ package process
import ( import (
"errors" "errors"
"net" "net"
C "github.com/Dreamacro/clash/constant"
) )
var ( var (
@ -19,3 +21,49 @@ const (
func FindProcessName(network string, srcIP net.IP, srcPort int) (string, error) { func FindProcessName(network string, srcIP net.IP, srcPort int) (string, error) {
return findProcessName(network, srcIP, srcPort) return findProcessName(network, srcIP, srcPort)
} }
func ShouldFindProcess(metadata *C.Metadata) bool {
if metadata.Process != "" {
return false
}
for _, ip := range localIPs {
if ip.Equal(metadata.SrcIP) {
return true
}
}
return false
}
func AppendLocalIPs(ip ...net.IP) {
localIPs = append(ip, localIPs...)
}
func getLocalIPs() []net.IP {
ips := []net.IP{net.IPv4zero, net.IPv6zero}
netInterfaces, err := net.Interfaces()
if err != nil {
ips = append(ips, net.IPv4(127, 0, 0, 1), net.IPv6loopback)
return ips
}
for i := 0; i < len(netInterfaces); i++ {
if (netInterfaces[i].Flags & net.FlagUp) != 0 {
adds, _ := netInterfaces[i].Addrs()
for _, address := range adds {
if ipNet, ok := address.(*net.IPNet); ok {
ips = append(ips, ipNet.IP)
}
}
}
}
return ips
}
var localIPs []net.IP
func init() {
localIPs = getLocalIPs()
}

View File

@ -69,7 +69,7 @@ func findProcessName(network string, ip net.IP, port int) (string, error) {
continue continue
} }
if !ip.Equal(srcIP) { if !ip.Equal(srcIP) && (network == TCP || !srcIP.IsUnspecified()) {
continue continue
} }

View File

@ -110,7 +110,7 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3
return 0, 0, fmt.Errorf("invalid uid(%d) or inode(%d)", uid, inode) return 0, 0, fmt.Errorf("invalid uid(%d) or inode(%d)", uid, inode)
} }
return uid, inode, nil return inode, uid, nil
} }
func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte { func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte {

View File

@ -10,6 +10,7 @@ type Enhancer interface {
FakeIPEnabled() bool FakeIPEnabled() bool
MappingEnabled() bool MappingEnabled() bool
IsFakeIP(net.IP) bool IsFakeIP(net.IP) bool
IsFakeBroadcastIP(net.IP) bool
IsExistFakeIP(net.IP) bool IsExistFakeIP(net.IP) bool
FindHostByIP(net.IP) (string, bool) FindHostByIP(net.IP) (string, bool)
} }
@ -38,6 +39,14 @@ func IsFakeIP(ip net.IP) bool {
return false return false
} }
func IsFakeBroadcastIP(ip net.IP) bool {
if mapper := DefaultHostMapper; mapper != nil {
return mapper.IsFakeBroadcastIP(ip)
}
return false
}
func IsExistFakeIP(ip net.IP) bool { func IsExistFakeIP(ip net.IP) bool {
if mapper := DefaultHostMapper; mapper != nil { if mapper := DefaultHostMapper; mapper != nil {
return mapper.IsExistFakeIP(ip) return mapper.IsExistFakeIP(ip)

View File

@ -7,6 +7,7 @@ import (
R "github.com/Dreamacro/clash/rule" R "github.com/Dreamacro/clash/rule"
RP "github.com/Dreamacro/clash/rule/provider" RP "github.com/Dreamacro/clash/rule/provider"
"net" "net"
"net/netip"
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
@ -19,6 +20,7 @@ import (
"github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/adapter/outboundgroup"
"github.com/Dreamacro/clash/adapter/provider" "github.com/Dreamacro/clash/adapter/provider"
"github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/auth"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/geodata" "github.com/Dreamacro/clash/component/geodata"
"github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/geodata/router"
@ -26,6 +28,7 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
providerTypes "github.com/Dreamacro/clash/constant/provider" providerTypes "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
T "github.com/Dreamacro/clash/tunnel" T "github.com/Dreamacro/clash/tunnel"
@ -47,7 +50,7 @@ type General struct {
AutoIptables bool `json:"auto-iptables"` AutoIptables bool `json:"auto-iptables"`
} }
// Inbound // Inbound config
type Inbound struct { type Inbound struct {
Port int `json:"port"` Port int `json:"port"`
SocksPort int `json:"socks-port"` SocksPort int `json:"socks-port"`
@ -59,7 +62,7 @@ type Inbound struct {
BindAddress string `json:"bind-address"` BindAddress string `json:"bind-address"`
} }
// Controller // Controller config
type Controller struct { type Controller struct {
ExternalController string `json:"-"` ExternalController string `json:"-"`
ExternalUI string `json:"-"` ExternalUI string `json:"-"`
@ -105,8 +108,9 @@ type Profile struct {
// Tun config // Tun config
type Tun struct { type Tun struct {
Enable bool `yaml:"enable" json:"enable"` Enable bool `yaml:"enable" json:"enable"`
Stack string `yaml:"stack" json:"stack"` Device string `yaml:"device" json:"device"`
DnsHijack []string `yaml:"dns-hijack" json:"dns-hijack"` Stack C.TUNStack `yaml:"stack" json:"stack"`
DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoRoute bool `yaml:"auto-route" json:"auto-route"`
} }
@ -157,6 +161,14 @@ type RawFallbackFilter struct {
GeoSite []string `yaml:"geosite"` GeoSite []string `yaml:"geosite"`
} }
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"`
}
type RawConfig struct { type RawConfig struct {
Port int `yaml:"port"` Port int `yaml:"port"`
SocksPort int `yaml:"socks-port"` SocksPort int `yaml:"socks-port"`
@ -179,11 +191,11 @@ type RawConfig struct {
GeodataLoader string `yaml:"geodata-loader"` GeodataLoader string `yaml:"geodata-loader"`
AutoIptables bool `yaml:"auto-iptables"` AutoIptables bool `yaml:"auto-iptables"`
ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
RuleProvider map[string]map[string]interface{} `yaml:"rule-providers"` RuleProvider map[string]map[string]any `yaml:"rule-providers"`
Hosts map[string]string `yaml:"hosts"` Hosts map[string]string `yaml:"hosts"`
DNS RawDNS `yaml:"dns"` DNS RawDNS `yaml:"dns"`
Tun Tun `yaml:"tun"` Tun RawTun `yaml:"tun"`
Experimental Experimental `yaml:"experimental"` Experimental Experimental `yaml:"experimental"`
Profile Profile `yaml:"profile"` Profile Profile `yaml:"profile"`
Proxy []map[string]any `yaml:"proxies"` Proxy []map[string]any `yaml:"proxies"`
@ -218,15 +230,17 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
Rule: []string{}, Rule: []string{},
Proxy: []map[string]any{}, Proxy: []map[string]any{},
ProxyGroup: []map[string]any{}, ProxyGroup: []map[string]any{},
Tun: Tun{ Tun: RawTun{
Enable: false, Enable: false,
Stack: "gvisor", Device: "",
DnsHijack: []string{"198.18.0.2:53"}, Stack: C.TunGvisor,
DNSHijack: []string{"198.18.0.2:53"}, // default hijack all dns query
AutoRoute: true, AutoRoute: true,
}, },
DNS: RawDNS{ DNS: RawDNS{
Enable: false, Enable: false,
UseHosts: true, UseHosts: true,
EnhancedMode: C.DNSMapping,
FakeIPRange: "198.18.0.1/16", FakeIPRange: "198.18.0.1/16",
FallbackFilter: RawFallbackFilter{ FallbackFilter: RawFallbackFilter{
GeoIP: true, GeoIP: true,
@ -243,6 +257,8 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
NameServer: []string{ NameServer: []string{
"223.5.5.5", "223.5.5.5",
"119.29.29.29", "119.29.29.29",
"https://doh.pub/dns-query",
"tls://223.5.5.5:853",
}, },
FakeIPFilter: []string{ FakeIPFilter: []string{
"dns.msftnsci.com", "dns.msftnsci.com",
@ -279,7 +295,13 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
} }
config.General = general config.General = general
config.Tun = &rawCfg.Tun tunCfg, err := parseTun(rawCfg.Tun, config.General)
if err != nil {
return nil, err
}
config.Tun = tunCfg
dialer.DefaultInterface.Store(config.General.Interface)
proxies, providers, err := parseProxies(rawCfg) proxies, providers, err := parseProxies(rawCfg)
if err != nil { if err != nil {
@ -361,13 +383,14 @@ func parseGeneral(cfg *RawConfig) (*General, error) {
func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]providerTypes.ProxyProvider, err error) { func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]providerTypes.ProxyProvider, err error) {
proxies = make(map[string]C.Proxy) proxies = make(map[string]C.Proxy)
providersMap = make(map[string]providerTypes.ProxyProvider) providersMap = make(map[string]providerTypes.ProxyProvider)
var proxyList []string
_proxiesList := list.New()
_groupsList := list.New()
proxiesConfig := cfg.Proxy proxiesConfig := cfg.Proxy
groupsConfig := cfg.ProxyGroup groupsConfig := cfg.ProxyGroup
providersConfig := cfg.ProxyProvider providersConfig := cfg.ProxyProvider
var proxyList []string
_proxiesList := list.New()
_groupsList := list.New()
proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect()) proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
proxies["REJECT"] = adapter.NewProxy(outbound.NewReject()) proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible()) proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible())
@ -417,6 +440,13 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
providersMap[name] = pd providersMap[name] = pd
} }
for _, proxyProvider := range providersMap {
log.Infoln("Start initial provider %s", proxyProvider.Name())
if err := proxyProvider.Initial(); err != nil {
return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", proxyProvider.Name(), err)
}
}
// parse proxy group // parse proxy group
for idx, mapping := range groupsConfig { for idx, mapping := range groupsConfig {
group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap) group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap)
@ -444,7 +474,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
} }
} }
ps := []C.Proxy{} var ps []C.Proxy
for _, v := range proxyList { for _, v := range proxyList {
ps = append(ps, proxies[v]) ps = append(ps, proxies[v])
} }
@ -543,29 +573,27 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
continue continue
} }
l := len(rule)
if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" { if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" {
payload = strings.Join(rule[1:len(rule)-1], ",") target = rule[l-1]
target = rule[len(rule)-1] payload = strings.Join(rule[1:l-1], ",")
} else { } else {
switch l := len(rule); { if l < 2 {
case l == 2:
target = rule[1]
case l == 3:
if ruleName == "MATCH" {
payload = ""
target = rule[1]
params = rule[2:]
break
}
payload = rule[1]
target = rule[2]
case l >= 4:
payload = rule[1]
target = rule[2]
params = rule[3:]
default:
return nil, nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line) return nil, nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line)
} }
if l < 4 {
rule = append(rule, make([]string, 4-l)...)
}
if ruleName == "MATCH" {
l = 2
}
if l >= 3 {
l = 3
payload = rule[1]
}
target = rule[l-1]
params = rule[l:]
} }
if _, ok := proxies[target]; mode != T.Script && !ok { if _, ok := proxies[target]; mode != T.Script && !ok {
@ -681,6 +709,7 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) {
Net: dnsNetType, Net: dnsNetType,
Addr: addr, Addr: addr,
ProxyAdapter: u.Fragment, ProxyAdapter: u.Fragment,
Interface: dialer.DefaultInterface.Load(),
}, },
) )
} }
@ -879,3 +908,37 @@ func cleanPyKeywords(code string) string {
} }
return code return code
} }
func parseTun(rawTun RawTun, general *General) (*Tun, error) {
if (rawTun.Enable || general.TProxyPort != 0) && general.Interface == "" {
autoDetectInterfaceName, err := commons.GetAutoDetectInterface()
if err != nil || autoDetectInterfaceName == "" {
return nil, fmt.Errorf("can not find auto detect interface: %w. you must be detect `interface-name` if tun set to enable or `tproxy-port` isn't zore", err)
}
general.Interface = autoDetectInterfaceName
}
var dnsHijack []netip.AddrPort
for _, d := range rawTun.DNSHijack {
if _, after, ok := strings.Cut(d, "://"); ok {
d = after
}
addrPort, err := netip.ParseAddrPort(d)
if err != nil {
return nil, fmt.Errorf("parse dns-hijack url error: %w", err)
}
dnsHijack = append(dnsHijack, addrPort)
}
return &Tun{
Enable: rawTun.Enable,
Device: rawTun.Device,
Stack: rawTun.Stack,
DNSHijack: dnsHijack,
AutoRoute: rawTun.AutoRoute,
}, nil
}

View File

@ -67,7 +67,7 @@ func downloadGeoSite(path string) (err error) {
func initGeoSite() error { func initGeoSite() error {
if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) { if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
log.Infoln("Need GeoSite but can't find GeoSite.dat, start download") log.Infoln("Can't find GeoSite.dat, start download")
if err := downloadGeoSite(C.Path.GeoSite()); err != nil { if err := downloadGeoSite(C.Path.GeoSite()); err != nil {
return fmt.Errorf("can't download GeoSite.dat: %s", err.Error()) return fmt.Errorf("can't download GeoSite.dat: %s", err.Error())
} }
@ -90,7 +90,7 @@ func initGeoSite() error {
func initGeoIP() error { func initGeoIP() error {
if C.GeodataMode { if C.GeodataMode {
if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) { if _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) {
log.Infoln("Need GeoIP but can't find GeoIP.dat, start download") log.Infoln("Can't find GeoIP.dat, start download")
if err := downloadGeoIP(C.Path.GeoIP()); err != nil { if err := downloadGeoIP(C.Path.GeoIP()); err != nil {
return fmt.Errorf("can't download GeoIP.dat: %s", err.Error()) return fmt.Errorf("can't download GeoIP.dat: %s", err.Error())
} }

View File

@ -81,8 +81,8 @@ type Metadata struct {
DstPort string `json:"destinationPort"` DstPort string `json:"destinationPort"`
AddrType int `json:"-"` AddrType int `json:"-"`
Host string `json:"host"` Host string `json:"host"`
Process string `json:"process"`
DNSMode DNSMode `json:"dnsMode"` DNSMode DNSMode `json:"dnsMode"`
Process string `json:"process"`
ProcessPath string `json:"processPath"` ProcessPath string `json:"processPath"`
} }

View File

@ -75,4 +75,5 @@ type Rule interface {
ShouldResolveIP() bool ShouldResolveIP() bool
ShouldFindProcess() bool ShouldFindProcess() bool
RuleExtra() *RuleExtra RuleExtra() *RuleExtra
SetRuleExtra(re *RuleExtra)
} }

View File

@ -7,8 +7,6 @@ import (
"github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/geodata/router"
) )
var TunBroadcastAddr = net.IPv4(198, 18, 255, 255)
type RuleExtra struct { type RuleExtra struct {
Network NetWork Network NetWork
SourceIPs []*net.IPNet SourceIPs []*net.IPNet

66
constant/tun.go Normal file
View File

@ -0,0 +1,66 @@
package constant
import (
"encoding/json"
"errors"
"strings"
)
var StackTypeMapping = map[string]TUNStack{
strings.ToUpper(TunGvisor.String()): TunGvisor,
strings.ToUpper(TunSystem.String()): TunSystem,
}
const (
TunGvisor TUNStack = iota
TunSystem
)
type TUNStack int
// UnmarshalYAML unserialize TUNStack with yaml
func (e *TUNStack) UnmarshalYAML(unmarshal func(any) error) error {
var tp string
if err := unmarshal(&tp); err != nil {
return err
}
mode, exist := StackTypeMapping[strings.ToUpper(tp)]
if !exist {
return errors.New("invalid tun stack")
}
*e = mode
return nil
}
// MarshalYAML serialize TUNStack with yaml
func (e TUNStack) MarshalYAML() (any, error) {
return e.String(), nil
}
// UnmarshalJSON unserialize TUNStack with json
func (e *TUNStack) UnmarshalJSON(data []byte) error {
var tp string
json.Unmarshal(data, &tp)
mode, exist := StackTypeMapping[strings.ToUpper(tp)]
if !exist {
return errors.New("invalid tun stack")
}
*e = mode
return nil
}
// MarshalJSON serialize TUNStack with json
func (e TUNStack) MarshalJSON() ([]byte, error) {
return json.Marshal(e.String())
}
func (e TUNStack) String() string {
switch e {
case TunGvisor:
return "gVisor"
case TunSystem:
return "System"
default:
return "unknown"
}
}

View File

@ -31,7 +31,6 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
ip net.IP ip net.IP
err error err error
) )
if ip = net.ParseIP(c.host); ip == nil { if ip = net.ParseIP(c.host); ip == nil {
if c.r == nil { if c.r == nil {
return nil, fmt.Errorf("dns %s not a valid ip", c.host) return nil, fmt.Errorf("dns %s not a valid ip", c.host)

View File

@ -40,7 +40,19 @@ func (h *ResolverEnhancer) IsFakeIP(ip net.IP) bool {
} }
if pool := h.fakePool; pool != nil { if pool := h.fakePool; pool != nil {
return pool.IPNet().Contains(ip) && !pool.Gateway().Equal(ip) return pool.IPNet().Contains(ip) && !pool.Gateway().Equal(ip) && !pool.Broadcast().Equal(ip)
}
return false
}
func (h *ResolverEnhancer) IsFakeBroadcastIP(ip net.IP) bool {
if !h.FakeIPEnabled() {
return false
}
if pool := h.fakePool; pool != nil {
return pool.Broadcast().Equal(ip)
} }
return false return false

View File

@ -150,7 +150,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
return return
} }
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (*D.Msg, error) { func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout) fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout)
for _, client := range clients { for _, client := range clients {
r := client r := client
@ -174,8 +174,8 @@ func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.
return nil, err return nil, err
} }
msg := elm.(*D.Msg) msg = elm.(*D.Msg)
return msg, nil return
} }
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient { func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
@ -216,7 +216,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
return false return false
} }
func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (*D.Msg, error) { func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
if matched := r.matchPolicy(m); len(matched) != 0 { if matched := r.matchPolicy(m); len(matched) != 0 {
res := <-r.asyncExchange(ctx, matched, m) res := <-r.asyncExchange(ctx, matched, m)
return res.Msg, res.Error return res.Msg, res.Error
@ -233,20 +233,23 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (*D.Msg, error) {
if r.fallback == nil || len(r.fallback) == 0 { // directly return if no fallback servers are available if r.fallback == nil || len(r.fallback) == 0 { // directly return if no fallback servers are available
res := <-msgCh res := <-msgCh
return res.Msg, res.Error msg, err = res.Msg, res.Error
return
} }
res := <-msgCh res := <-msgCh
if res.Error == nil { if res.Error == nil {
if ips := msgToIP(res.Msg); len(ips) != 0 { if ips := msgToIP(res.Msg); len(ips) != 0 {
if !r.shouldIPFallback(ips[0]) { if !r.shouldIPFallback(ips[0]) {
return res.Msg, res.Error // no need to wait for fallback result msg, err = res.Msg, res.Error // no need to wait for fallback result
return
} }
} }
} }
res = <-r.asyncExchange(ctx, r.fallback, m) res = <-r.asyncExchange(ctx, r.fallback, m)
return res.Msg, res.Error msg, err = res.Msg, res.Error
return
} }
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) { func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) {

14
go.mod
View File

@ -11,12 +11,11 @@ require (
github.com/gofrs/uuid v4.2.0+incompatible github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99
github.com/lucas-clemente/quic-go v0.25.0 github.com/lucas-clemente/quic-go v0.25.0
github.com/miekg/dns v1.1.47 github.com/miekg/dns v1.1.47
github.com/oschwald/geoip2-golang v1.6.1 github.com/oschwald/geoip2-golang v1.6.1
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.1
github.com/xtls/go v0.0.0-20210920065950-d4af136d3672 github.com/xtls/go v0.0.0-20210920065950-d4af136d3672
go.etcd.io/bbolt v1.3.6 go.etcd.io/bbolt v1.3.6
go.uber.org/atomic v1.9.0 go.uber.org/atomic v1.9.0
@ -25,10 +24,12 @@ require (
golang.org/x/net v0.0.0-20220225172249-27dd8689420f golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
golang.zx2c4.com/wireguard/windows v0.5.1 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
golang.zx2c4.com/wireguard v0.0.0-20220310012736-ae6bc4dd64e1
golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/protobuf v1.27.1 google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
gvisor.dev/gvisor v0.0.0-20220129032118-ed00636ef990 gvisor.dev/gvisor v0.0.0-20220315202956-f1399ecf1672
) )
require ( require (
@ -47,11 +48,10 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/go118/netip v0.0.0-20211106132939-9d41d90554dd // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
) )

25
go.sum
View File

@ -104,8 +104,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99 h1:dkEFEnGUg2z/FAPywWr4yfR/sWDQK76qn3J4Y5H2hJs=
github.com/kr328/tun2socket v0.0.0-20210412191540-3d56c47e2d99/go.mod h1:FWfSixjrLgtK+dHkDoN6lHMNhvER24gnjUZd/wt8Z9o=
github.com/lucas-clemente/quic-go v0.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc= github.com/lucas-clemente/quic-go v0.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc=
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg= github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
@ -196,8 +194,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME=
@ -323,8 +322,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y= golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
@ -352,12 +351,12 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/go118/netip v0.0.0-20211106132939-9d41d90554dd h1:gUHae7sCd+tFJLcCximWeBFD2b6Jg3O7UaNaPvjIJHc= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/go118/netip v0.0.0-20211106132939-9d41d90554dd/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/windows v0.5.1 h1:OnYw96PF+CsIMrqWo5QP3Q59q5hY1rFErk/yN3cS+JQ= golang.zx2c4.com/wireguard v0.0.0-20220310012736-ae6bc4dd64e1 h1:iuQdvJn3LrXxz3Iony1qBGVS7kEy2uHYnnjHsVbzq/s=
golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk= golang.zx2c4.com/wireguard v0.0.0-20220310012736-ae6bc4dd64e1/go.mod h1:TjUWrnD5ATh7bFvmm/ALEJZQ4ivKbETb6pmyj1vUoNI=
golang.zx2c4.com/wireguard/windows v0.5.2 h1:C3kUahG6QPhXUYSMesW0t1mDo1XCwls2c8Bz16umS44= golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.2/go.mod h1:KptYG5fXTsP1sHafw0BnWHxobtR6QZHWY6SVWq5R0PM= golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 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.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
@ -400,8 +399,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20220129032118-ed00636ef990 h1:fTgWAYpliP19U3FX8+tI2TZGXnnk45g18frOuZxKay4= gvisor.dev/gvisor v0.0.0-20220315202956-f1399ecf1672 h1:aXIFpjZYl3zv2rQyr4rSit5Uq0k7BVXC8lJaDa4Cg7M=
gvisor.dev/gvisor v0.0.0-20220129032118-ed00636ef990/go.mod h1:vmN0Pug/s8TJmpnt30DvrEfZ5vDl52psGLU04tFuK2U= gvisor.dev/gvisor v0.0.0-20220315202956-f1399ecf1672/go.mod h1:V4WNP2Uwtx69eOhvLDSQ734EaTJTaBI3P8KgRAlROsg=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -7,7 +7,6 @@ import (
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"github.com/Dreamacro/clash/listener/tproxy" "github.com/Dreamacro/clash/listener/tproxy"
@ -28,7 +27,6 @@ import (
"github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/dns"
P "github.com/Dreamacro/clash/listener" P "github.com/Dreamacro/clash/listener"
authStore "github.com/Dreamacro/clash/listener/auth" authStore "github.com/Dreamacro/clash/listener/auth"
"github.com/Dreamacro/clash/listener/tun/dev"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel" "github.com/Dreamacro/clash/tunnel"
) )
@ -76,18 +74,21 @@ func ApplyConfig(cfg *config.Config, force bool) {
mux.Lock() mux.Lock()
defer mux.Unlock() defer mux.Unlock()
log.SetLevel(log.DEBUG)
updateUsers(cfg.Users) updateUsers(cfg.Users)
updateHosts(cfg.Hosts) updateHosts(cfg.Hosts)
updateGeneral(cfg.General, cfg.Tun, force) updateGeneral(cfg.General, cfg.Tun, force)
updateProxies(cfg.Proxies, cfg.Providers) updateProxies(cfg.Proxies, cfg.Providers)
updateRules(cfg.Rules, cfg.RuleProviders) updateRules(cfg.Rules, cfg.RuleProviders)
updateIPTables(cfg.DNS, cfg.General, cfg.Tun) updateIPTables(cfg.DNS, cfg.General.TProxyPort, cfg.General.Interface, cfg.Tun.Enable)
updateDNS(cfg.DNS, cfg.Tun) updateDNS(cfg.DNS, cfg.Tun)
updateTun(cfg.Tun) updateTun(cfg.Tun, cfg.DNS.FakeIPRange.IPNet().String())
updateExperimental(cfg) updateExperimental(cfg)
loadProvider(cfg.RuleProviders, cfg.Providers) loadProvider(cfg.RuleProviders, cfg.Providers)
updateProfile(cfg) updateProfile(cfg)
log.SetLevel(cfg.General.LogLevel)
} }
func GetGeneral() *config.General { func GetGeneral() *config.General {
@ -119,15 +120,7 @@ func GetGeneral() *config.General {
func updateExperimental(c *config.Config) {} func updateExperimental(c *config.Config) {}
func updateDNS(c *config.DNS, Tun *config.Tun) { func updateDNS(c *config.DNS, t *config.Tun) {
if !c.Enable && !Tun.Enable {
resolver.DefaultResolver = nil
resolver.MainResolver = nil
resolver.DefaultHostMapper = nil
dns.ReCreateServer("", nil, nil)
return
}
cfg := dns.Config{ cfg := dns.Config{
Main: c.NameServer, Main: c.NameServer,
Fallback: c.Fallback, Fallback: c.Fallback,
@ -158,14 +151,21 @@ func updateDNS(c *config.DNS, Tun *config.Tun) {
resolver.DefaultResolver = r resolver.DefaultResolver = r
resolver.MainResolver = mr resolver.MainResolver = mr
resolver.DefaultHostMapper = m resolver.DefaultHostMapper = m
if Tun.Enable && !strings.EqualFold(Tun.Stack, "gVisor") {
if t.Enable {
resolver.DefaultLocalServer = dns.NewLocalServer(r, m) resolver.DefaultLocalServer = dns.NewLocalServer(r, m)
} else {
resolver.DefaultLocalServer = nil
} }
if c.Enable { if c.Enable {
dns.ReCreateServer(c.Listen, r, m) dns.ReCreateServer(c.Listen, r, m)
} else {
if !t.Enable {
resolver.DefaultResolver = nil
resolver.MainResolver = nil
resolver.DefaultHostMapper = nil
resolver.DefaultLocalServer = nil
}
dns.ReCreateServer("", nil, nil)
} }
} }
@ -213,33 +213,30 @@ func loadProvider(ruleProviders map[string]*provider.RuleProvider, proxyProvider
} }
} }
func updateTun(tun *config.Tun, tunAddressPrefix string) {
P.ReCreateTun(tun, tunAddressPrefix, tunnel.TCPIn(), tunnel.UDPIn())
}
func updateGeneral(general *config.General, Tun *config.Tun, force bool) { func updateGeneral(general *config.General, Tun *config.Tun, force bool) {
tunnel.SetMode(general.Mode) tunnel.SetMode(general.Mode)
resolver.DisableIPv6 = !general.IPv6 resolver.DisableIPv6 = !general.IPv6
adapter.UnifiedDelay.Store(general.UnifiedDelay) adapter.UnifiedDelay.Store(general.UnifiedDelay)
if (Tun.Enable || general.TProxyPort != 0) && general.Interface == "" {
autoDetectInterfaceName, err := dev.GetAutoDetectInterface()
if err == nil {
if autoDetectInterfaceName != "" && autoDetectInterfaceName != "<nil>" {
general.Interface = autoDetectInterfaceName
} else {
log.Debugln("Auto detect interface name is empty.")
}
} else {
log.Debugln("Can not find auto detect interface. %s", err.Error())
}
}
dialer.DefaultInterface.Store(general.Interface) dialer.DefaultInterface.Store(general.Interface)
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark)) if dialer.DefaultInterface.Load() != "" {
log.Infoln("Use interface name: %s", general.Interface) log.Infoln("Use interface name: %s", general.Interface)
}
if general.RoutingMark > 0 || (general.RoutingMark == 0 && general.TProxyPort == 0) {
dialer.DefaultRoutingMark.Store(int32(general.RoutingMark))
if general.RoutingMark > 0 {
log.Infoln("Use routing mark: %#x", general.RoutingMark)
}
}
iface.FlushCache() iface.FlushCache()
if !force { if !force {
log.SetLevel(general.LogLevel)
return return
} }
@ -260,18 +257,6 @@ func updateGeneral(general *config.General, Tun *config.Tun, force bool) {
P.ReCreateRedir(general.RedirPort, tcpIn, udpIn) P.ReCreateRedir(general.RedirPort, tcpIn, udpIn)
P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn) P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn)
P.ReCreateMixed(general.MixedPort, tcpIn, udpIn) P.ReCreateMixed(general.MixedPort, tcpIn, udpIn)
log.SetLevel(general.LogLevel)
}
func updateTun(Tun *config.Tun) {
if Tun == nil {
return
}
tcpIn := tunnel.TCPIn()
udpIn := tunnel.UDPIn()
P.ReCreateTun(*Tun, tcpIn, udpIn)
} }
func updateUsers(users []auth.AuthUser) { func updateUsers(users []auth.AuthUser) {
@ -317,8 +302,28 @@ func patchSelectGroup(proxies map[string]C.Proxy) {
} }
} }
func updateIPTables(dns *config.DNS, general *config.General, tun *config.Tun) { func updateIPTables(dns *config.DNS, tProxyPort int, interfaceName string, tunEnable bool) {
if runtime.GOOS != "linux" || dns.Listen == "" || general.TProxyPort == 0 || tun.Enable || !general.AutoIptables { tproxy.CleanUpTProxyLinuxIPTables()
if runtime.GOOS != "linux" || tProxyPort == 0 {
return
}
var err error
defer func() {
if err != nil {
log.Errorln("Setting iptables failed: %s", err.Error())
os.Exit(2)
}
}()
if !dns.Enable || dns.Listen == "" {
err = fmt.Errorf("DNS server must be enable")
return
}
if tunEnable {
err = fmt.Errorf("TUN device must be disabe")
return return
} }
@ -332,19 +337,15 @@ func updateIPTables(dns *config.DNS, general *config.General, tun *config.Tun) {
return return
} }
tproxy.CleanUpTProxyLinuxIPTables() if dialer.DefaultRoutingMark.Load() == 0 {
dialer.DefaultRoutingMark.Store(2158) dialer.DefaultRoutingMark.Store(2158)
err = tproxy.SetTProxyLinuxIPTables(general.Interface, general.TProxyPort, dnsPort)
if err != nil {
log.Errorln("Can not setting iptables for TProxy on linux, %s", err.Error())
os.Exit(2)
}
} }
func CleanUp() { err = tproxy.SetTProxyLinuxIPTables(interfaceName, tProxyPort, dnsPort)
P.CleanUp() }
func Cleanup() {
P.Cleanup()
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
tproxy.CleanUpTProxyLinuxIPTables() tproxy.CleanUpTProxyLinuxIPTables()
} }

View File

@ -49,6 +49,6 @@ func Parse(options ...Option) error {
return nil return nil
} }
func CleanUp() { func Cleanup() {
executor.CleanUp() executor.Cleanup()
} }

View File

@ -78,10 +78,6 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) {
P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn) P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn)
P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn) P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn)
if general.Tun != nil {
P.ReCreateTun(*general.Tun, tcpIn, udpIn)
}
if general.Mode != nil { if general.Mode != nil {
tunnel.SetMode(*general.Mode) tunnel.SetMode(*general.Mode)
} }

View File

@ -5,7 +5,6 @@ import (
"github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/inner"
"net" "net"
"os" "os"
"runtime"
"strconv" "strconv"
"sync" "sync"
@ -35,7 +34,7 @@ var (
tproxyUDPListener *tproxy.UDPListener tproxyUDPListener *tproxy.UDPListener
mixedListener *mixed.Listener mixedListener *mixed.Listener
mixedUDPLister *socks.UDPListener mixedUDPLister *socks.UDPListener
tunAdapter ipstack.TunAdapter tunStackListener ipstack.Stack
// lock for recreate function // lock for recreate function
socksMux sync.Mutex socksMux sync.Mutex
@ -66,18 +65,6 @@ func SetAllowLan(al bool) {
allowLan = al allowLan = al
} }
func Tun() config.Tun {
if tunAdapter == nil {
return config.Tun{}
}
return config.Tun{
Enable: true,
Stack: tunAdapter.Stack(),
DnsHijack: tunAdapter.DnsHijack(),
AutoRoute: tunAdapter.AutoRoute(),
}
}
func SetBindAddress(host string) { func SetBindAddress(host string) {
bindAddress = host bindAddress = host
} }
@ -323,47 +310,28 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P
log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address())
} }
//func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) error { func ReCreateTun(tunConf *config.Tun, tunAddressPrefix string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
// tunMux.Lock()
// defer tunMux.Unlock()
//
// if tunAdapter != nil {
// tunAdapter.Close()
// tunAdapter = nil
// }
//
// if !conf.Enable {
// return nil
// }
//
// var err error
// tunAdapter, err = tun.New(conf, tcpIn, udpIn)
//
// return err
//}
func ReCreateTun(conf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) {
tunMux.Lock() tunMux.Lock()
defer tunMux.Unlock() defer tunMux.Unlock()
var err error var err error
defer func() { defer func() {
if err != nil { if err != nil {
log.Errorln("Start TUN interface error: %s", err.Error()) log.Errorln("Start TUN listening error: %s", err.Error())
os.Exit(2) os.Exit(2)
} }
}() }()
if tunAdapter != nil { if tunStackListener != nil {
tunAdapter.Close() tunStackListener.Close()
tunAdapter = nil tunStackListener = nil
} }
if !conf.Enable { if !tunConf.Enable {
return return
} }
tunAdapter, err = tun.New(conf, tcpIn, udpIn) tunStackListener, err = tun.New(tunConf, tunAddressPrefix, tcpIn, udpIn)
if err != nil { if err != nil {
log.Warnln("Failed to start TUN interface: %s", err.Error()) log.Warnln("Failed to start TUN interface: %s", err.Error())
} }
@ -425,11 +393,8 @@ func genAddr(host string, port int, allowLan bool) string {
return fmt.Sprintf("127.0.0.1:%d", port) return fmt.Sprintf("127.0.0.1:%d", port)
} }
// CleanUp clean up something func Cleanup() {
func CleanUp() { if tunStackListener != nil {
if runtime.GOOS == "windows" { _ = tunStackListener.Close()
if tunAdapter != nil {
tunAdapter.Close()
}
} }
} }

View File

@ -3,18 +3,17 @@ package tproxy
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/Dreamacro/clash/component/dialer"
"os/exec"
"runtime" "runtime"
"strings"
"github.com/Dreamacro/clash/common/cmd"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
) )
var ( var (
interfaceName = ""
tproxyPort = 0
dnsPort = 0 dnsPort = 0
tProxyPort = 0
interfaceName = ""
) )
const ( const (
@ -24,7 +23,7 @@ const (
func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error { func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error {
var err error var err error
if _, err = execCmd("iptables -V"); err != nil { if err = execCmd("iptables -V"); err != nil {
return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS) return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS)
} }
@ -33,7 +32,7 @@ func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error {
} }
interfaceName = ifname interfaceName = ifname
tproxyPort = tport tProxyPort = tport
dnsPort = dport dnsPort = dport
// add route // add route
@ -63,8 +62,8 @@ func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error {
addLocalnetworkToChain("clash_prerouting") addLocalnetworkToChain("clash_prerouting")
execCmd("iptables -t mangle -A clash_prerouting -p tcp -m socket -j clash_divert") execCmd("iptables -t mangle -A clash_prerouting -p tcp -m socket -j clash_divert")
execCmd("iptables -t mangle -A clash_prerouting -p udp -m socket -j clash_divert") execCmd("iptables -t mangle -A clash_prerouting -p udp -m socket -j clash_divert")
execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK)) execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s", tProxyPort, PROXY_FWMARK, PROXY_FWMARK))
execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tproxyPort, PROXY_FWMARK, PROXY_FWMARK)) execCmd(fmt.Sprintf("iptables -t mangle -A clash_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tProxyPort, PROXY_FWMARK, PROXY_FWMARK))
execCmd("iptables -t mangle -A PREROUTING -j clash_prerouting") execCmd("iptables -t mangle -A PREROUTING -j clash_prerouting")
execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort))
@ -96,20 +95,22 @@ func SetTProxyLinuxIPTables(ifname string, tport int, dport int) error {
execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j clash_dns_output") execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j clash_dns_output")
execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j clash_dns_output") execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j clash_dns_output")
log.Infoln("[TProxy] Setting iptables completed") log.Infoln("[TPROXY] Setting iptables completed")
return nil return nil
} }
func CleanUpTProxyLinuxIPTables() { func CleanUpTProxyLinuxIPTables() {
if interfaceName == "" || tproxyPort == 0 || dnsPort == 0 { if interfaceName == "" || tProxyPort == 0 || dnsPort == 0 {
return return
} }
log.Warnln("Clean up tproxy linux iptables") log.Warnln("Clean up tproxy linux iptables")
if int(dialer.DefaultRoutingMark.Load()) == 2158 {
dialer.DefaultRoutingMark.Store(0) dialer.DefaultRoutingMark.Store(0)
}
if _, err := execCmd("iptables -t mangle -L clash_divert"); err != nil { if err := execCmd("iptables -t mangle -L clash_divert"); err != nil {
return return
} }
@ -165,16 +166,13 @@ func addLocalnetworkToChain(chain string) {
execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN", chain)) execCmd(fmt.Sprintf("iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN", chain))
} }
func execCmd(cmdstr string) (string, error) { func execCmd(cmdStr string) error {
log.Debugln("[TProxy] %s", cmdstr) log.Debugln("[TPROXY] %s", cmdStr)
args := strings.Split(cmdstr, " ") _, err := cmd.ExecCmd(cmdStr)
cmd := exec.Command(args[0], args[1:]...)
out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
log.Errorln("[TProxy] error: %s, %s", err.Error(), string(out)) log.Warnln("[TPROXY] exec cmd: %v", err)
return "", err
} }
return string(out), nil return err
} }

View File

@ -1,12 +0,0 @@
package dev
// TunDevice is cross-platform tun interface
type TunDevice interface {
Name() string
URL() string
MTU() (int, error)
IsClose() bool
Close() error
Read(buff []byte) (int, error)
Write(buff []byte) (int, error)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,94 +0,0 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package wintun
import (
"fmt"
"github.com/Dreamacro/clash/listener/tun/dev/wintun/embed_dll"
"golang.zx2c4.com/wireguard/windows/driver/memmod"
"sync"
"sync/atomic"
"unsafe"
)
func newLazyDLL(name string, onLoad func(d *lazyDLL)) *lazyDLL {
return &lazyDLL{Name: name, onLoad: onLoad}
}
func (d *lazyDLL) NewProc(name string) *lazyProc {
return &lazyProc{dll: d, Name: name}
}
type lazyProc struct {
Name string
mu sync.Mutex
dll *lazyDLL
addr uintptr
}
func (p *lazyProc) Find() error {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
return nil
}
p.mu.Lock()
defer p.mu.Unlock()
if p.addr != 0 {
return nil
}
err := p.dll.Load()
if err != nil {
return fmt.Errorf("Error loading %v DLL: %w", p.dll.Name, err)
}
addr, err := p.nameToAddr()
if err != nil {
return fmt.Errorf("Error getting %v address: %w", p.Name, err)
}
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
return nil
}
func (p *lazyProc) Addr() uintptr {
err := p.Find()
if err != nil {
panic(err)
}
return p.addr
}
type lazyDLL struct {
Name string
mu sync.Mutex
module *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(embed_dll.DDlContent)
if err != nil {
return fmt.Errorf("Unable to load library: %w", err)
}
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
if d.onLoad != nil {
d.onLoad(d)
}
return nil
}
func (p *lazyProc) nameToAddr() (uintptr, error) {
return p.dll.module.ProcAddressByName(p.Name)
}

View File

@ -1,21 +0,0 @@
package embed_dll
// Copyright 2020 MeshStep Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
import (
_ "embed"
)
//go:embed x86/wintun.dll
var DDlContent []byte

View File

@ -1,21 +0,0 @@
package embed_dll
// Copyright 2020 MeshStep Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
import (
_ "embed"
)
//go:embed amd64/wintun.dll
var DDlContent []byte

View File

@ -1,21 +0,0 @@
package embed_dll
// Copyright 2020 MeshStep Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
import (
_ "embed"
)
//go:embed arm/wintun.dll
var DDlContent []byte

View File

@ -1,21 +0,0 @@
package embed_dll
// Copyright 2020 MeshStep Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
import (
_ "embed"
)
//go:embed arm64/wintun.dll
var DDlContent []byte

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,56 @@
package fdbased
import (
"fmt"
"github.com/Dreamacro/clash/listener/tun/device"
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
)
func open(fd int, mtu uint32) (device.Device, error) {
f := &FD{fd: fd, mtu: mtu}
return f, nil
}
func (f *FD) useEndpoint() 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) useIOBased() error {
return nil
}
func (f *FD) Read(packet []byte) (int, error) {
n, gvErr := rawfile.BlockingRead(f.fd, packet)
if gvErr != nil {
return 0, fmt.Errorf("read error: %s", gvErr.String())
}
return n, nil
}
func (f *FD) Write(packet []byte) (int, error) {
n := len(packet)
if n == 0 {
return 0, nil
}
gvErr := rawfile.NonBlockingWrite(f.fd, packet)
if gvErr != nil {
return 0, fmt.Errorf("write error: %s", gvErr.String())
}
return n, nil
}

View File

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

View File

@ -0,0 +1,134 @@
// Package iobased provides the implementation of io.ReadWriter
// based data-link layer endpoints.
package iobased
import (
"context"
"errors"
"io"
"sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"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
}
// 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) Close() {
e.Endpoint.Close()
}
// 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.once.Do(func() {
ctx, cancel := context.WithCancel(context.Background())
go e.dispatchLoop(cancel)
go e.outboundLoop(ctx)
})
e.Endpoint.Attach(dispatcher)
}
// 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()
for {
data := make([]byte, int(e.mtu))
n, err := e.rw.Read(data)
if err != nil {
break
}
if !e.IsAttached() {
continue /* unattached, drop packet */
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.View(data[:n]).ToVectorisedView(),
})
switch header.IPVersion(data) {
case header.IPv4Version:
e.InjectInbound(header.IPv4ProtocolNumber, pkt)
case header.IPv6Version:
e.InjectInbound(header.IPv6ProtocolNumber, pkt)
}
pkt.DecRef() /* release */
}
}
// 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 {
defer pkt.DecRef()
size := pkt.Size()
views := pkt.Views()
vView := buffer.NewVectorisedView(size, views)
if _, err := e.rw.Write(vView.ToView()); err != nil {
return &tcpip.ErrInvalidEndpointState{}
}
return nil
}

View File

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

View File

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

View File

@ -0,0 +1,17 @@
package tun
import (
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/tun"
)
func init() {
guid, _ := windows.GUIDFromString("{330EAEF8-7578-5DF2-D97B-8DADC0EA85CB}")
tun.WintunTunnelType = "Clash"
tun.WintunStaticRequestedGUID = &guid
}
func (t *TUN) LUID() uint64 {
return t.nt.LUID()
}

View File

@ -0,0 +1,117 @@
//go:build !linux
package tun
import (
"fmt"
"runtime"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/device"
"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
}
func Open(name string, mtu uint32) (_ device.Device, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("open tun: %v", r)
}
}()
var (
offset = 4 /* 4 bytes TUN_PI */
defaultMTU = 1500
)
if runtime.GOOS == "windows" {
offset = 0
defaultMTU = 0 /* auto */
}
t := &TUN{
name: name,
mtu: mtu,
offset: offset,
}
forcedMTU := defaultMTU
if t.mtu > 0 {
forcedMTU = int(t.mtu)
}
nt, err := tun.CreateTUN(t.name, forcedMTU) // forcedMTU do not work on wintun, need to be setting by other way
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)
return t, nil
}
func (t *TUN) Read(packet []byte) (int, error) {
if t.offset == 0 {
return t.nt.Read(packet, t.offset)
}
buff := pool.Get(t.offset + cap(packet))
defer pool.Put(buff)
n, err := t.nt.Read(buff, t.offset)
if err != nil {
return 0, err
}
_ = buff[:t.offset]
copy(packet, buff[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(make([]byte, t.offset), packet...)
return t.nt.Write(packet, t.offset)
}
func (t *TUN) Close() error {
return t.nt.Close()
}
func (t *TUN) Name() string {
name, _ := t.nt.Name()
return name
}
func (t *TUN) UseEndpoint() 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
}
func (t *TUN) UseIOBased() error {
return nil
}

View File

@ -1,10 +1,24 @@
package commons package commons
import ( import (
"net/netip"
"time"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
D "github.com/miekg/dns" 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) { func RelayDnsPacket(payload []byte) ([]byte, error) {
msg := &D.Msg{} msg := &D.Msg{}
if err := msg.Unpack(payload); err != nil { if err := msg.Unpack(payload); err != nil {

View File

@ -0,0 +1,17 @@
package commons
import (
"fmt"
"net"
)
var Routes = []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])
}

View File

@ -0,0 +1,61 @@
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 netstat -rnf inet | grep 'default' | awk -F ' ' 'NR==1{print $6}' | 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
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 {
routes := append(Routes, addr.String())
for _, route := range routes {
if err := execRouterCmd("add", "-inet", route, interfaceName); err != nil {
return err
}
}
// return execRouterCmd("add", "-inet6", "2000::/3", interfaceName)
return nil
}
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
}

View File

@ -0,0 +1,52 @@
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 ip route show | grep 'default via' | awk -F ' ' 'NR==1{print $5}' | xargs echo -n")
}
func ConfigInterfaceAddress(dev device.Device, addr netip.Prefix, forceMTU int, autoRoute bool) error {
var (
interfaceName = dev.Name()
ip = addr.Masked().Addr().Next()
)
_, err := cmd.ExecCmd(fmt.Sprintf("ip addr add %s dev %s", ip.String(), interfaceName))
if err != nil {
return err
}
_, err = cmd.ExecCmd(fmt.Sprintf("ip link set %s up", interfaceName))
if err != nil {
return err
}
if autoRoute {
err = configInterfaceRouting(interfaceName, addr)
}
return err
}
func configInterfaceRouting(interfaceName string, addr netip.Prefix) error {
linkIP := addr.Masked().Addr().Next()
for _, route := range Routes {
if err := execRouterCmd("add", route, interfaceName, linkIP.String()); err != nil {
return err
}
}
return nil
}
func execRouterCmd(action, route string, interfaceName string, linkIP string) error {
cmdStr := fmt.Sprintf("ip route %s %s dev %s proto kernel scope link src %s", action, route, interfaceName, linkIP)
_, err := cmd.ExecCmd(cmdStr)
return err
}

View File

@ -0,0 +1,19 @@
//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)
}

View File

@ -0,0 +1,261 @@
package commons
import (
"errors"
"fmt"
"net/netip"
"time"
"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"
)
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("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()
addresses = []netip.Prefix{netip.PrefixFrom(ip, addr.Bits())}
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/0"}
)
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: addr.Masked().Addr().Next().Next(),
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)
}
dnsAdds := []netip.Addr{netip.MustParseAddr("198.18.0.2")}
err = luid.SetDNS(family4, dnsAdds, nil)
if err == windows.ERROR_NOT_FOUND && retryOnFailure {
goto startOver
} else if err != nil {
return fmt.Errorf("unable to set DNS %s %s: %w", "198.18.0.2", "nil", err)
}
return nil
}
func cleanupAddressesOnDisconnectedInterfaces(family winipcfg.AddressFamily, addresses []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, _ := netip.AddrFromSlice(address.Address.IP()); addrHash[ip] {
prefix := netip.PrefixFrom(ip, int(address.OnLinkPrefixLength))
log.Infoln("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()
for gatewayAddress := iface.FirstGatewayAddress; gatewayAddress != nil; gatewayAddress = gatewayAddress.Next {
nextHop, _ := netip.AddrFromSlice(gatewayAddress.Address.IP())
if _, err = iface.LUID.Route(destination, nextHop); err == nil {
return ifname, nil
}
}
}
return "", errors.New("ethernet interface not found")
}

View File

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

View File

@ -0,0 +1,8 @@
package adapter
// Handler is a TCP/UDP connection handler that implements
// HandleTCPConn and HandleUDPConn methods.
type Handler interface {
HandleTCPConn(TCPConn)
HandleUDPConn(UDPConn)
}

View File

@ -0,0 +1,134 @@
package gvisor
import (
"encoding/binary"
"net"
"net/netip"
"time"
"github.com/Dreamacro/clash/adapter/inbound"
"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/log"
"github.com/Dreamacro/clash/transport/socks5"
)
var _ adapter.Handler = (*GVHandler)(nil)
type GVHandler struct {
DNSAdds []netip.AddrPort
TCPIn chan<- C.ConnContext
UDPIn chan<- *inbound.PacketAdapter
}
func (gh *GVHandler) HandleTCPConn(tunConn adapter.TCPConn) {
id := tunConn.ID()
rAddr := &net.UDPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
Zone: "",
}
addrIp, _ := netip.AddrFromSlice(rAddr.IP)
addrPort := netip.AddrPortFrom(addrIp, id.LocalPort)
if D.ShouldHijackDns(gh.DNSAdds, addrPort) {
go func() {
log.Debugln("[TUN] hijack dns tcp: %s", addrPort.String())
defer tunConn.Close()
buf := pool.Get(pool.UDPBufferSize)
defer pool.Put(buf)
for {
tunConn.SetReadDeadline(time.Now().Add(D.DefaultDnsReadTimeout))
length := uint16(0)
if err := binary.Read(tunConn, binary.BigEndian, &length); err != nil {
break
}
if int(length) > len(buf) {
break
}
n, err := tunConn.Read(buf[:length])
if err != nil {
break
}
msg, err := D.RelayDnsPacket(buf[:n])
if err != nil {
break
}
_, _ = tunConn.Write(msg)
}
}()
return
}
gh.TCPIn <- inbound.NewSocket(socks5.ParseAddrToSocksAddr(rAddr), tunConn, C.TUN)
}
func (gh *GVHandler) HandleUDPConn(tunConn adapter.UDPConn) {
id := tunConn.ID()
rAddr := &net.UDPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
Zone: "",
}
addrIp, _ := netip.AddrFromSlice(rAddr.IP)
addrPort := netip.AddrPortFrom(addrIp, id.LocalPort)
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.DNSAdds, addrPort) {
go func() {
defer pool.Put(buf)
msg, err1 := D.RelayDnsPacket(payload)
if err1 != nil {
return
}
_, _ = tunConn.WriteTo(msg, addr)
log.Debugln("[TUN] hijack dns udp: %s", rAddr.String())
}()
continue
}
gvPacket := &packet{
pc: tunConn,
rAddr: addr,
payload: payload,
}
select {
case gh.UDPIn <- inbound.NewPacket(target, gvPacket, C.TUN):
default:
}
}
}()
}

View File

@ -0,0 +1,24 @@
package gvisor
import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
func withICMPHandler() Option {
return func(s *gvStack) error {
// Add default route table for IPv4 and IPv6.
// This will handle all incoming ICMP packets.
s.SetRouteTable([]tcpip.Route{
{
Destination: header.IPv4EmptySubnet,
NIC: s.nicID,
},
{
Destination: header.IPv6EmptySubnet,
NIC: s.nicID,
},
})
return nil
}
}

View File

@ -0,0 +1,59 @@
package gvisor
import (
"fmt"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
const (
// defaultNICID is the ID of default NIC used by DefaultStack.
defaultNICID tcpip.NICID = 0x01
// 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(ep stack.LinkEndpoint) Option {
return func(s *gvStack) error {
if err := s.CreateNICWithOptions(s.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 NIC.
func withPromiscuousMode(v bool) Option {
return func(s *gvStack) error {
if err := s.SetPromiscuousMode(s.nicID, v); err != nil {
return fmt.Errorf("set promiscuous mode: %s", err)
}
return nil
}
}
// withSpoofing sets address spoofing in the given NIC, allowing
// endpoints to bind to any address in the NIC.
func withSpoofing(v bool) Option {
return func(s *gvStack) error {
if err := s.SetSpoofing(s.nicID, v); err != nil {
return fmt.Errorf("set spoofing: %s", err)
}
return nil
}
}

View File

@ -0,0 +1,223 @@
package gvisor
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/transport/tcp"
)
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 = true
// 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 // 4 KiB
// tcpMaxBufferSize is the maximum permitted size of a send/recv buffer.
tcpMaxBufferSize = tcp.MaxBufferSize // 4 MiB
// tcpDefaultBufferSize is the default size of the send/recv buffer for
// a transport endpoint.
tcpDefaultBufferSize = 212 << 10 // 212 KiB
)
type Option func(*gvStack) error
// WithDefault sets all default values for stack.
func WithDefault() Option {
return func(s *gvStack) 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
WithTCPBufferSizeRange(tcpMinBufferSize, tcpDefaultBufferSize, 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 *gvStack) 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 *gvStack) 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 *gvStack) 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 *gvStack) error {
s.SetICMPLimit(limit)
return nil
}
}
// WithTCPBufferSizeRange sets the receive and send buffer size range for TCP.
func WithTCPBufferSizeRange(a, b, c int) Option {
return func(s *gvStack) 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)
}
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
}
}
// WithTCPCongestionControl sets the current congestion control algorithm.
func WithTCPCongestionControl(cc string) Option {
return func(s *gvStack) 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 *gvStack) 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 *gvStack) 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 *gvStack) 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 *gvStack) error {
if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &v); err != nil {
return fmt.Errorf("set TCP Recovery: %s", err)
}
return nil
}
}

View File

@ -0,0 +1,96 @@
// Package gvisor provides a thin wrapper around a gVisor's stack.
package gvisor
import (
"github.com/Dreamacro/clash/listener/tun/device"
"github.com/Dreamacro/clash/listener/tun/ipstack"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"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
handler adapter.Handler
nicID tcpip.NICID
}
func (s *gvStack) Close() error {
if s.Stack != nil {
s.Stack.Close()
}
if s.device != nil {
_ = s.device.Close()
}
return nil
}
// New allocates a new *gvStack with given options.
func New(device device.Device, handler adapter.Handler, opts ...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: handler,
nicID: defaultNICID,
}
opts = append(opts,
// Important: We must initiate transport protocol handlers
// before creating NIC, otherwise NIC would dispatch packets
// to stack and cause race condition.
withICMPHandler(), withTCPHandler(), withUDPHandler(),
// Create stack NIC and then bind link endpoint.
withCreatingNIC(device.(stack.LinkEndpoint)),
// 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(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(nicSpoofingEnabled),
)
for _, opt := range opts {
if err := opt(s); err != nil {
return nil, err
}
}
return s, nil
}

View File

@ -0,0 +1,91 @@
package gvisor
import (
"fmt"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"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 = 0
// maxConnAttempts specifies the maximum number
// of in-flight tcp connection attempts.
maxConnAttempts = 2 << 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 = 9
// 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() Option {
return func(s *gvStack) error {
tcpForwarder := tcp.NewForwarder(s.Stack, defaultWndSize, maxConnAttempts, func(r *tcp.ForwarderRequest) {
var wq waiter.Queue
ep, err := r.CreateEndpoint(&wq)
if err != nil {
// RST: prevent potential half-open TCP connection leak.
r.Complete(true)
return
}
defer r.Complete(false)
setKeepalive(ep)
conn := &tcpConn{
TCPConn: gonet.NewTCPConn(&wq, ep),
id: r.ID(),
}
s.handler.HandleTCPConn(conn)
})
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
return nil
}
}
func setKeepalive(ep tcpip.Endpoint) error {
ep.SocketOptions().SetKeepAlive(true)
idle := tcpip.KeepaliveIdleOption(tcpKeepaliveIdle)
if err := ep.SetSockOpt(&idle); err != nil {
return fmt.Errorf("set keepalive idle: %s", err)
}
interval := tcpip.KeepaliveIntervalOption(tcpKeepaliveInterval)
if err := ep.SetSockOpt(&interval); err != nil {
return fmt.Errorf("set keepalive interval: %s", err)
}
if err := ep.SetSockOptInt(tcpip.KeepaliveCountOption, tcpKeepaliveCount); err != nil {
return fmt.Errorf("set keepalive count: %s", err)
}
return nil
}
type tcpConn struct {
*gonet.TCPConn
id stack.TransportEndpointID
}
func (c *tcpConn) ID() *stack.TransportEndpointID {
return &c.id
}

View File

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

View File

@ -1,433 +0,0 @@
package gvisor
import (
"fmt"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"net"
Common "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/log"
D "github.com/miekg/dns"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"gvisor.dev/gvisor/pkg/tcpip/ports"
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
)
var (
ipv4Zero = tcpip.Address(net.IPv4zero.To4())
ipv6Zero = tcpip.Address(net.IPv6zero.To16())
)
type ListenerWrap struct {
net.Listener
listener net.Listener
}
func (l *ListenerWrap) Accept() (conn net.Conn, err error) {
conn, err = l.listener.Accept()
log.Debugln("[DNS] hijack tcp:%s", l.Addr())
return
}
func (l *ListenerWrap) Close() error {
return l.listener.Close()
}
func (l *ListenerWrap) Addr() net.Addr {
return l.listener.Addr()
}
// DNSServer is DNS Server listening on tun devcice
type DNSServer struct {
dnsServers []*dns.Server
tcpListeners []net.Listener
resolver *dns.Resolver
stack *stack.Stack
udpEndpoints []*dnsEndpoint
udpEndpointIDs []*stack.TransportEndpointID
tcpip.NICID
}
// dnsEndpoint is a TransportEndpoint that will register to stack
type dnsEndpoint struct {
stack.TransportEndpoint
stack *stack.Stack
uniqueID uint64
server *dns.Server
}
// Keep track of the source of DNS request
type dnsResponseWriter struct {
s *stack.Stack
pkt *stack.PacketBuffer // The request packet
id stack.TransportEndpointID
}
func (e *dnsEndpoint) UniqueID() uint64 {
return e.uniqueID
}
func (e *dnsEndpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) {
hdr := header.UDP(pkt.TransportHeader().View())
if int(hdr.Length()) > pkt.Data().Size()+header.UDPMinimumSize {
// Malformed packet.
e.stack.Stats().UDP.MalformedPacketsReceived.Increment()
return
}
// server DNS
var msg D.Msg
msg.Unpack(pkt.Data().AsRange().ToOwnedView())
writer := dnsResponseWriter{s: e.stack, pkt: pkt, id: id}
log.Debugln("[DNS] hijack udp:%s:%d from %s:%d", id.LocalAddress.String(), id.LocalPort,
id.RemoteAddress.String(), id.RemotePort)
go e.server.ServeDNS(&writer, &msg)
}
func (e *dnsEndpoint) Close() {
}
func (e *dnsEndpoint) Wait() {
}
func (e *dnsEndpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) {
log.Warnln("DNS endpoint get a transport error: %v", transErr)
log.Debugln("DNS endpoint transport error packet : %v", pkt)
}
// Abort implements stack.TransportEndpoint.Abort.
func (e *dnsEndpoint) Abort() {
e.Close()
}
func (w *dnsResponseWriter) LocalAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(w.id.LocalAddress), Port: int(w.id.LocalPort)}
}
func (w *dnsResponseWriter) RemoteAddr() net.Addr {
return &net.UDPAddr{IP: net.IP(w.id.RemoteAddress), Port: int(w.id.RemotePort)}
}
func (w *dnsResponseWriter) WriteMsg(msg *D.Msg) error {
b, err := msg.Pack()
if err != nil {
return err
}
_, err = w.Write(b)
return err
}
func (w *dnsResponseWriter) TsigStatus() error {
// Unsupported
return nil
}
func (w *dnsResponseWriter) TsigTimersOnly(bool) {
// Unsupported
}
func (w *dnsResponseWriter) Hijack() {
// Unsupported
}
func (w *dnsResponseWriter) Write(b []byte) (int, error) {
v := buffer.NewView(len(b))
copy(v, b)
data := v.ToVectorisedView()
// w.id.LocalAddress is the source ip of DNS response
if !w.pkt.NetworkHeader().View().IsEmpty() &&
(w.pkt.NetworkProtocolNumber == ipv4.ProtocolNumber ||
w.pkt.NetworkProtocolNumber == ipv6.ProtocolNumber) {
r, _ := w.s.FindRoute(w.pkt.NICID, w.id.LocalAddress, w.id.RemoteAddress, w.pkt.NetworkProtocolNumber, false /* multicastLoop */)
return writeUDP(r, data, w.id.LocalPort, w.id.RemotePort)
} else {
log.Debugln("the network protocl[%d] is not available", w.pkt.NetworkProtocolNumber)
return 0, fmt.Errorf("the network protocl[%d] is not available", w.pkt.NetworkProtocolNumber)
}
}
func (w *dnsResponseWriter) Close() error {
return nil
}
// CreateDNSServer create a dns server on given netstack
func CreateDNSServer(s *stack.Stack, resolver *dns.Resolver, mapper *dns.ResolverEnhancer, dnsHijack []net.Addr, nicID tcpip.NICID) (*DNSServer, error) {
var err error
handler := dns.NewHandler(resolver, mapper)
serverIn := &dns.Server{}
serverIn.SetHandler(handler)
tcpDnsArr := make([]net.TCPAddr, 0, len(dnsHijack))
udpDnsArr := make([]net.UDPAddr, 0, len(dnsHijack))
for _, d := range dnsHijack {
switch d.(type) {
case *net.TCPAddr:
{
tcpDnsArr = append(tcpDnsArr, *d.(*net.TCPAddr))
break
}
case *net.UDPAddr:
{
udpDnsArr = append(udpDnsArr, *d.(*net.UDPAddr))
break
}
}
}
endpoints, ids := hijackUdpDns(udpDnsArr, s, serverIn)
tcpListeners, dnsServers := hijackTcpDns(tcpDnsArr, s, serverIn)
server := &DNSServer{
resolver: resolver,
stack: s,
udpEndpoints: endpoints,
udpEndpointIDs: ids,
NICID: nicID,
tcpListeners: tcpListeners,
}
server.dnsServers = dnsServers
return server, err
}
func hijackUdpDns(dnsArr []net.UDPAddr, s *stack.Stack, serverIn *dns.Server) ([]*dnsEndpoint, []*stack.TransportEndpointID) {
endpoints := make([]*dnsEndpoint, len(dnsArr))
ids := make([]*stack.TransportEndpointID, len(dnsArr))
for i, dns := range dnsArr {
port := dns.Port
ip := dns.IP
address := tcpip.FullAddress{NIC: nicID, Port: uint16(port)}
var protocol tcpip.NetworkProtocolNumber
if ip.To4() != nil {
address.Addr = tcpip.Address(ip.To4())
protocol = ipv4.ProtocolNumber
} else {
address.Addr = tcpip.Address(ip.To16())
protocol = ipv6.ProtocolNumber
}
protocolAddr := tcpip.ProtocolAddress{
Protocol: protocol,
AddressWithPrefix: address.Addr.WithPrefix(),
}
// netstack will only reassemble IP fragments when its' dest ip address is registered in NIC.endpoints
if err := s.AddProtocolAddress(nicID, protocolAddr, stack.AddressProperties{}); err != nil {
log.Errorln("AddProtocolAddress(%d, %+v, {}): %s", nicID, protocolAddr, err)
}
if address.Addr == ipv4Zero || address.Addr == ipv6Zero {
address.Addr = ""
}
// UDP DNS
id := &stack.TransportEndpointID{
LocalAddress: address.Addr,
LocalPort: uint16(port),
RemotePort: 0,
RemoteAddress: "",
}
// TransportEndpoint for DNS
endpoint := &dnsEndpoint{
stack: s,
uniqueID: s.UniqueID(),
server: serverIn,
}
if tcpiperr := s.RegisterTransportEndpoint(
[]tcpip.NetworkProtocolNumber{
ipv4.ProtocolNumber,
ipv6.ProtocolNumber,
},
udp.ProtocolNumber,
*id,
endpoint,
ports.Flags{LoadBalanced: true}, // it's actually the SO_REUSEPORT. Not sure it take effect.
nicID); tcpiperr != nil {
log.Errorln("Unable to start UDP DNS on tun: %v", tcpiperr.String())
}
ids[i] = id
endpoints[i] = endpoint
}
return endpoints, ids
}
func hijackTcpDns(dnsArr []net.TCPAddr, s *stack.Stack, serverIn *dns.Server) ([]net.Listener, []*dns.Server) {
tcpListeners := make([]net.Listener, len(dnsArr))
dnsServers := make([]*dns.Server, len(dnsArr))
for i, dnsAddr := range dnsArr {
var tcpListener net.Listener
var v4 bool
var err error
port := dnsAddr.Port
ip := dnsAddr.IP
address := tcpip.FullAddress{NIC: nicID, Port: uint16(port)}
if ip.To4() != nil {
address.Addr = tcpip.Address(ip.To4())
v4 = true
} else {
address.Addr = tcpip.Address(ip.To16())
v4 = false
}
if v4 {
tcpListener, err = gonet.ListenTCP(s, address, ipv4.ProtocolNumber)
} else {
tcpListener, err = gonet.ListenTCP(s, address, ipv6.ProtocolNumber)
}
if err != nil {
log.Errorln("can not listen on tun: %v, hijack tcp[%s] failed", err, dnsAddr)
} else {
tcpListeners[i] = tcpListener
server := &D.Server{Listener: &ListenerWrap{
listener: tcpListener,
}, Handler: serverIn}
dnsServer := dns.Server{}
dnsServer.Server = server
go dnsServer.ActivateAndServe()
dnsServers[i] = &dnsServer
}
}
//
//for _, listener := range tcpListeners {
// server := &D.Server{Listener: listener, Handler: serverIn}
//
// dnsServers = append(dnsServers, &dnsServer)
// go dnsServer.ActivateAndServe()
//}
return tcpListeners, dnsServers
}
// Stop stop the DNS Server on tun
func (s *DNSServer) Stop() {
if s == nil {
return
}
for i := 0; i < len(s.udpEndpointIDs); i++ {
ep := s.udpEndpoints[i]
id := s.udpEndpointIDs[i]
// remove udp endpoint from stack
s.stack.UnregisterTransportEndpoint(
[]tcpip.NetworkProtocolNumber{
ipv4.ProtocolNumber,
ipv6.ProtocolNumber,
},
udp.ProtocolNumber,
*id,
ep,
ports.Flags{LoadBalanced: true}, // should match the RegisterTransportEndpoint
s.NICID)
}
for _, server := range s.dnsServers {
server.Shutdown()
}
for _, listener := range s.tcpListeners {
listener.Close()
}
}
// DnsHijack return the listening address of DNS Server
func (t *gvisorAdapter) DnsHijack() []string {
dnsHijackArr := make([]string, len(t.dnsServer.udpEndpoints))
for _, id := range t.dnsServer.udpEndpointIDs {
dnsHijackArr = append(dnsHijackArr, fmt.Sprintf("%s:%d", id.LocalAddress.String(), id.LocalPort))
}
return dnsHijackArr
}
func (t *gvisorAdapter) StopDNSServer() {
t.dnsServer.Stop()
log.Debugln("tun DNS server stoped")
t.dnsServer = nil
}
// ReCreateDNSServer recreate the DNS Server on tun
func (t *gvisorAdapter) ReCreateDNSServer(resolver *dns.Resolver, mapper *dns.ResolverEnhancer, dnsHijackArr []string) error {
t.StopDNSServer()
if resolver == nil {
return fmt.Errorf("failed to create DNS server on tun: resolver not provided")
}
if len(dnsHijackArr) == 0 {
return fmt.Errorf("failed to create DNS server on tun: len(addrs) == 0")
}
var err error
var addrs []net.Addr
for _, addr := range dnsHijackArr {
var (
addrType string
hostPort string
)
addrType, hostPort, err = Common.SplitNetworkType(addr)
if err != nil {
return err
}
var (
host, port string
hasPort bool
)
host, port, hasPort, err = Common.SplitHostPort(hostPort)
if !hasPort {
port = "53"
}
switch addrType {
case "udp", "":
{
var udpDNS *net.UDPAddr
udpDNS, err = net.ResolveUDPAddr("udp", net.JoinHostPort(host, port))
if err != nil {
return err
}
addrs = append(addrs, udpDNS)
break
}
case "tcp":
{
var tcpDNS *net.TCPAddr
tcpDNS, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(host, port))
if err != nil {
return err
}
addrs = append(addrs, tcpDNS)
break
}
default:
err = fmt.Errorf("unspported dns scheme:%s", addrType)
}
}
server, err := CreateDNSServer(t.ipstack, resolver, mapper, addrs, nicID)
if err != nil {
return err
}
t.dnsServer = server
return nil
}

View File

@ -0,0 +1,67 @@
package gvisor
import (
"net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"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() Option {
return func(s *gvStack) error {
udpForwarder := udp.NewForwarder(s.Stack, func(r *udp.ForwarderRequest) {
var wq waiter.Queue
ep, err := r.CreateEndpoint(&wq)
if err != nil {
// TODO: handler errors in the future.
return
}
conn := &udpConn{
UDPConn: gonet.NewUDPConn(s.Stack, &wq, ep),
id: r.ID(),
}
s.handler.HandleUDPConn(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
}
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, addr net.Addr) (n int, err error) {
return c.pc.WriteTo(b, c.rAddr)
}
// LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr {
return c.rAddr
}
func (c *packet) Drop() {
pool.Put(c.payload)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,42 @@
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 netip.Addr, portal netip.Addr) (*StackListener, error) {
tcp, udp, err := nat.Start(device, gateway, portal)
if err != nil {
return nil, err
}
return &StackListener{
device: device,
tcp: tcp,
udp: udp,
}, nil
}
func (t *StackListener) Close() error {
_ = t.tcp.Close()
_ = t.udp.Close()
return t.device.Close()
}
func (t *StackListener) TCP() *nat.TCP {
return t.tcp
}
func (t *StackListener) UDP() *nat.UDP {
return t.udp
}

View File

@ -0,0 +1,193 @@
package nat
import (
"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 netip.Addr,
portal 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 tcp.Close()
defer udp.Close()
buf := make([]byte, pool.RelayBufferSize)
for {
n, err := device.Read(buf)
if err != nil {
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.Offset() != 0 {
continue
}
ip = ipv4
case tcpip.IPv6Version:
ipv6 := tcpip.IPv6Packet(raw)
if !ipv6.Valid() {
continue
}
if ipv6.HopLimit() == 0x00 {
continue
}
ip = ipv6
default:
continue
}
switch ip.Protocol() {
case tcpip.TCP:
t := tcpip.TCPPacket(ip.Payload())
if !t.Valid() {
continue
}
if ip.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(ip.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)
source := ip.SourceIP()
destination := ip.DestinationIP()
ip.SetSourceIP(destination)
ip.SetDestinationIP(source)
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)
source := ip.SourceIP()
destination := ip.DestinationIP()
ip.SetSourceIP(destination)
ip.SetDestinationIP(source)
ip.ResetChecksum()
i.ResetChecksum(ip.PseudoSum())
_, _ = device.Write(raw)
}
}
}()
return tcp, udp, nil
}

View File

@ -0,0 +1,83 @@
package nat
import (
"container/list"
"net/netip"
)
const (
portBegin = 30000
portLength = 4096
)
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
ports [portLength]*list.Element
available *list.List
}
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.(*binding).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.(*binding).offset
}
func (t *table) newConn(tuple tuple) uint16 {
elm := t.available.Back()
b := elm.Value.(*binding)
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, portLength),
ports: [portLength]*list.Element{},
available: list.New(),
}
for idx := range result.ports {
result.ports[idx] = result.available.PushFront(&binding{
tuple: tuple{},
offset: uint16(idx),
})
}
return result
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,147 @@
package nat
import (
"io"
"math/rand"
"net"
"net/netip"
"sync"
"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")
}
srcIP, _ := netip.AddrFromSlice(srcAddr.IP)
dstIp, _ := netip.AddrFromSlice(dstAddr.IP)
srcAddrPort := netip.AddrPortFrom(srcIP, uint16(srcAddr.Port))
dstAddrPort := netip.AddrPortFrom(dstIp, uint16(dstAddr.Port))
if !srcAddrPort.Addr().Is4() || !dstAddrPort.Addr().Is4() {
return 0, net.InvalidAddrError("invalid ip version")
}
tcpip.SetIPv4(u.buf[:])
ip := tcpip.IPv4Packet(u.buf[:])
ip.SetHeaderLen(tcpip.IPv4HeaderSize)
ip.SetTotalLength(tcpip.IPv4HeaderSize + tcpip.UDPHeaderSize + uint16(len(buf)))
ip.SetTypeOfService(0)
ip.SetIdentification(uint16(rand.Uint32()))
ip.SetFragmentOffset(0)
ip.SetTimeToLive(64)
ip.SetProtocol(tcpip.UDP)
ip.SetSourceIP(srcAddrPort.Addr())
ip.SetDestinationIP(dstAddrPort.Addr())
udp := tcpip.UDPPacket(ip.Payload())
udp.SetLength(tcpip.UDPHeaderSize + uint16(len(buf)))
udp.SetSourcePort(srcAddrPort.Port())
udp.SetDestinationPort(dstAddrPort.Port())
copy(udp.Payload(), buf)
ip.ResetChecksum()
udp.ResetChecksum(ip.PseudoSum())
return u.device.Write(u.buf[:ip.TotalLen()])
}
func (u *UDP) Close() error {
u.queueLock.Lock()
defer u.queueLock.Unlock()
u.closed = true
for _, c := range u.queue {
c.cond.Signal()
}
return nil
}
func (u *UDP) handleUDPPacket(ip tcpip.IP, pkt tcpip.UDPPacket) {
var c *call
u.queueLock.Lock()
if len(u.queue) > 0 {
idx := len(u.queue) - 1
c = u.queue[idx]
u.queue = u.queue[:idx]
}
u.queueLock.Unlock()
if c != nil {
c.source = &net.UDPAddr{
IP: ip.SourceIP().AsSlice(),
Port: int(pkt.SourcePort()),
}
c.destination = &net.UDPAddr{
IP: ip.DestinationIP().AsSlice(),
Port: int(pkt.DestinationPort()),
}
c.n = copy(c.buf, pkt.Payload())
c.cond.Signal()
}
}

View File

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

View File

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

View File

@ -0,0 +1,215 @@
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) Offset() uint16 {
offset := binary.BigEndian.Uint16(p[6:8])
return (offset & 0x1fff) * 8
}
func (p IPv4Packet) SourceIP() netip.Addr {
return netip.AddrFrom4([4]byte{p[12], p[13], p[14], p[15]})
}
func (p IPv4Packet) SetSourceIP(ip netip.Addr) {
if ip.Is4() {
copy(p[12:16], ip.AsSlice())
}
}
func (p IPv4Packet) DestinationIP() netip.Addr {
return netip.AddrFrom4([4]byte{p[16], p[17], p[18], p[19]})
}
func (p IPv4Packet) SetDestinationIP(ip netip.Addr) {
if ip.Is4() {
copy(p[16:20], ip.AsSlice())
}
}
func (p IPv4Packet) Checksum() uint16 {
return binary.BigEndian.Uint16(p[10:])
}
func (p IPv4Packet) SetChecksum(sum [2]byte) {
p[10] = sum[0]
p[11] = sum[1]
}
func (p IPv4Packet) TimeToLive() uint8 {
return p[8]
}
func (p IPv4Packet) SetTimeToLive(ttl uint8) {
p[8] = ttl
}
func (p IPv4Packet) DecTimeToLive() {
p[8] = p[8] - uint8(1)
}
func (p IPv4Packet) ResetChecksum() {
p.SetChecksum(zeroChecksum)
p.SetChecksum(Checksum(0, p[:p.HeaderLen()]))
}
// PseudoSum for tcp checksum
func (p IPv4Packet) PseudoSum() uint32 {
sum := Sum(p[12:20])
sum += uint32(p.Protocol())
sum += uint32(p.DataLen())
return sum
}
func (p IPv4Packet) Valid() bool {
return len(p) >= IPv4HeaderSize && uint16(len(p)) >= p.TotalLen()
}
func (p IPv4Packet) Verify() error {
if len(p) < IPv4PacketMinLength {
return ErrInvalidLength
}
checksum := []byte{p[10], p[11]}
headerLength := uint16(p[0]&0xF) * 4
packetLength := binary.BigEndian.Uint16(p[2:])
if p[0]>>4 != 4 {
return ErrInvalidIPVersion
}
if uint16(len(p)) < packetLength || packetLength < headerLength {
return ErrInvalidLength
}
p[10] = 0
p[11] = 0
defer copy(p[10:12], checksum)
answer := Checksum(0, p[:headerLength])
if answer[0] != checksum[0] || answer[1] != checksum[1] {
return ErrInvalidChecksum
}
return nil
}
var _ IP = (*IPv4Packet)(nil)

View File

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

View File

@ -0,0 +1,90 @@
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
}

Some files were not shown because too many files have changed in this diff Show More