Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
323b9564f0 | ||
|
|
4bc8dc7f27 | ||
|
|
41b2639e13 | ||
|
|
605266e65e | ||
|
|
e881f21013 | ||
|
|
b02f252916 | ||
|
|
91df97aee2 | ||
|
|
6999634511 | ||
|
|
209ec123ca | ||
|
|
a6a8c23d65 | ||
|
|
8bf4113343 | ||
|
|
9c6e70b7cc | ||
|
|
d744d03d93 | ||
|
|
53f50347e0 | ||
|
|
bf7110b1ab | ||
|
|
d880656b52 | ||
|
|
499c0aed67 | ||
|
|
8848c0e4cb | ||
|
|
980d4bf9a3 | ||
|
|
ca53ccf346 | ||
|
|
f0c27d037c | ||
|
|
35d565af65 | ||
|
|
45b089e6bb | ||
|
|
56bedd2f05 | ||
|
|
fe89bbded2 | ||
|
|
839f1792e4 | ||
|
|
20226b91c9 |
3
.github/update_dependencies.sh
vendored
3
.github/update_dependencies.sh
vendored
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PROJECTS=$(dirname "$0")/../..
|
||||
|
||||
go get -x github.com/sagernet/sing@$(git -C $PROJECTS/sing rev-parse HEAD)
|
||||
go get -x github.com/sagernet/$1@$(git -C $PROJECTS/$1 rev-parse HEAD)
|
||||
go mod tidy
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,7 +1,16 @@
|
||||
build:
|
||||
GOOS=darwin GOARCH=arm64 go build -v -tags with_gvisor .
|
||||
GOOS=ios GOARCH=arm64 go build -v -tags with_gvisor .
|
||||
GOOS=linux GOARCH=amd64 go build -v -tags with_gvisor .
|
||||
GOOS=linux GOARCH=arm64 go build -v -tags with_gvisor .
|
||||
GOOS=linux GOARCH=386 go build -v -tags with_gvisor .
|
||||
GOOS=linux GOARCH=arm go build -v -tags with_gvisor .
|
||||
GOOS=windows GOARCH=amd64 go build -v -tags with_gvisor .
|
||||
|
||||
fmt:
|
||||
@gofumpt -l -w .
|
||||
@gofmt -s -w .
|
||||
@gci write --custom-order -s "standard,prefix(github.com/sagernet/),default" .
|
||||
@gci write --custom-order -s standard -s "prefix(github.com/sagernet/)" -s "default" .
|
||||
|
||||
fmt_install:
|
||||
go install -v mvdan.cc/gofumpt@latest
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Simple transparent proxy library.
|
||||
|
||||
For Linux, Windows and macOS.
|
||||
For Linux, Windows, macOS and iOS.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
14
go.mod
14
go.mod
@@ -4,16 +4,18 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-ole/go-ole v1.2.6
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61
|
||||
github.com/sagernet/gvisor v0.0.0-20230611140528-4411f7659a08
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
|
||||
github.com/sagernet/sing v0.1.7
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/sys v0.5.0
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
|
||||
github.com/sagernet/sing v0.2.5
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/sys v0.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
)
|
||||
|
||||
29
go.sum
29
go.sum
@@ -1,24 +1,29 @@
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=
|
||||
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=
|
||||
github.com/sagernet/gvisor v0.0.0-20230611140528-4411f7659a08 h1:p1z8y0tXLCKSiJ7GbUlaYPhyEbWL8LKLMYFpVxRVsBg=
|
||||
github.com/sagernet/gvisor v0.0.0-20230611140528-4411f7659a08/go.mod h1:FgbjODax/nj7J2lr7+rqe88vHs0Ts93pC9na5ZiG9wg=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
|
||||
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
|
||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||
github.com/sagernet/sing v0.1.7 h1:g4vjr3q8SUlBZSx97Emz5OBfSMBxxW5Q8C2PfdoSo08=
|
||||
github.com/sagernet/sing v0.1.7/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||
github.com/sagernet/sing v0.2.5 h1:N8sUluR8GZvR9DqUiH3FA3vBb4m/EDdOVTYUrDzJvmY=
|
||||
github.com/sagernet/sing v0.2.5/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
|
||||
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
|
||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4=
|
||||
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
61
gvisor.go
61
gvisor.go
@@ -4,22 +4,24 @@ package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
|
||||
"github.com/sagernet/gvisor/pkg/waiter"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
"github.com/sagernet/sing/common/canceler"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"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/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const WithGVisor = true
|
||||
@@ -33,6 +35,7 @@ type GVisor struct {
|
||||
endpointIndependentNat bool
|
||||
udpTimeout int64
|
||||
handler Handler
|
||||
logger logger.Logger
|
||||
stack *stack.Stack
|
||||
endpoint stack.LinkEndpoint
|
||||
}
|
||||
@@ -50,14 +53,16 @@ func NewGVisor(
|
||||
return nil, E.New("gVisor stack is unsupported on current platform")
|
||||
}
|
||||
|
||||
return &GVisor{
|
||||
gStack := &GVisor{
|
||||
ctx: options.Context,
|
||||
tun: gTun,
|
||||
tunMtu: options.MTU,
|
||||
endpointIndependentNat: options.EndpointIndependentNat,
|
||||
udpTimeout: options.UDPTimeout,
|
||||
handler: options.Handler,
|
||||
}, nil
|
||||
logger: options.Logger,
|
||||
}
|
||||
return gStack, nil
|
||||
}
|
||||
|
||||
func (t *GVisor) Start() error {
|
||||
@@ -103,7 +108,7 @@ func (t *GVisor) Start() error {
|
||||
mOpt := tcpip.TCPModerateReceiveBufferOption(true)
|
||||
ipStack.SetTransportProtocolOption(tcp.ProtocolNumber, &mOpt)
|
||||
|
||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
|
||||
tcpForwarder := tcp.NewForwarder(ipStack, 0, 1024, func(r *tcp.ForwarderRequest) {
|
||||
var wq waiter.Queue
|
||||
handshakeCtx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
@@ -141,10 +146,10 @@ func (t *GVisor) Start() error {
|
||||
endpoint.Abort()
|
||||
}
|
||||
}()
|
||||
}).HandlePacket)
|
||||
|
||||
})
|
||||
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||
if !t.endpointIndependentNat {
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) {
|
||||
udpForwarder := udp.NewForwarder(ipStack, func(request *udp.ForwarderRequest) {
|
||||
var wq waiter.Queue
|
||||
endpoint, err := request.CreateEndpoint(&wq)
|
||||
if err != nil {
|
||||
@@ -161,12 +166,14 @@ func (t *GVisor) Start() error {
|
||||
var metadata M.Metadata
|
||||
metadata.Source = M.SocksaddrFromNet(lAddr)
|
||||
metadata.Destination = M.SocksaddrFromNet(rAddr)
|
||||
hErr := t.handler.NewPacketConnection(ContextWithNeedTimeout(t.ctx, true), bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(&gUDPConn{udpConn}), Addr: M.SocksaddrFromNet(rAddr)}), metadata)
|
||||
ctx, conn := canceler.NewPacketConn(t.ctx, bufio.NewPacketConn(&bufio.UnbindPacketConn{ExtendedConn: bufio.NewExtendedConn(&gUDPConn{udpConn}), Addr: M.SocksaddrFromNet(rAddr)}), time.Duration(t.udpTimeout)*time.Second)
|
||||
hErr := t.handler.NewPacketConnection(ctx, conn, metadata)
|
||||
if hErr != nil {
|
||||
endpoint.Abort()
|
||||
}
|
||||
}()
|
||||
}).HandlePacket)
|
||||
})
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
} else {
|
||||
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, NewUDPForwarder(t.ctx, ipStack, t.handler, t.udpTimeout).HandlePacket)
|
||||
}
|
||||
@@ -184,3 +191,19 @@ func (t *GVisor) Close() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddressFromAddr(destination netip.Addr) tcpip.Address {
|
||||
if destination.Is6() {
|
||||
return tcpip.AddrFrom16(destination.As16())
|
||||
} else {
|
||||
return tcpip.AddrFrom4(destination.As4())
|
||||
}
|
||||
}
|
||||
|
||||
func AddrFromAddress(address tcpip.Address) netip.Addr {
|
||||
if address.Len() == 16 {
|
||||
return netip.AddrFrom16(address.As16())
|
||||
} else {
|
||||
return netip.AddrFrom4(address.As4())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,9 @@ package tun
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
)
|
||||
|
||||
type gTCPConn struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ package tun
|
||||
import (
|
||||
"time"
|
||||
|
||||
gLog "gvisor.dev/gvisor/pkg/log"
|
||||
gLog "github.com/sagernet/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -5,18 +5,17 @@ package tun
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/checksum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/udpnat"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type UDPForwarder struct {
|
||||
@@ -33,10 +32,10 @@ func NewUDPForwarder(ctx context.Context, stack *stack.Stack, handler Handler, u
|
||||
}
|
||||
}
|
||||
|
||||
func (f *UDPForwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
func (f *UDPForwarder) HandlePacket(id stack.TransportEndpointID, pkt stack.PacketBufferPtr) bool {
|
||||
var upstreamMetadata M.Metadata
|
||||
upstreamMetadata.Source = M.SocksaddrFrom(M.AddrFromIP(net.IP(id.RemoteAddress)), id.RemotePort)
|
||||
upstreamMetadata.Destination = M.SocksaddrFrom(M.AddrFromIP(net.IP(id.LocalAddress)), id.LocalPort)
|
||||
upstreamMetadata.Source = M.SocksaddrFrom(AddrFromAddress(id.RemoteAddress), id.RemotePort)
|
||||
upstreamMetadata.Destination = M.SocksaddrFrom(AddrFromAddress(id.LocalAddress), id.LocalPort)
|
||||
var netProto tcpip.NetworkProtocolNumber
|
||||
if upstreamMetadata.Source.IsIPv4() {
|
||||
netProto = header.IPv4ProtocolNumber
|
||||
@@ -62,12 +61,12 @@ type UDPBackWriter struct {
|
||||
sourceNetwork tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
func (w *UDPBackWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer packetBuffer.Release()
|
||||
|
||||
route, err := w.stack.FindRoute(
|
||||
defaultNIC,
|
||||
tcpip.Address(destination.Addr.AsSlice()),
|
||||
AddressFromAddr(destination.Addr),
|
||||
w.source,
|
||||
w.sourceNetwork,
|
||||
false,
|
||||
@@ -79,7 +78,7 @@ func (w *UDPBackWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
|
||||
|
||||
packet := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: header.UDPMinimumSize + int(route.MaxHeaderLength()),
|
||||
Payload: bufferv2.MakeWithData(buffer.Bytes()),
|
||||
Payload: buffer.MakeWithData(packetBuffer.Bytes()),
|
||||
})
|
||||
defer packet.DecRef()
|
||||
|
||||
@@ -93,9 +92,9 @@ func (w *UDPBackWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr)
|
||||
})
|
||||
|
||||
if route.RequiresTXTransportChecksum() && w.sourceNetwork == header.IPv6ProtocolNumber {
|
||||
xsum := udpHdr.CalculateChecksum(header.ChecksumCombine(
|
||||
xsum := udpHdr.CalculateChecksum(checksum.Combine(
|
||||
route.PseudoHeaderChecksum(header.UDPProtocolNumber, pLen),
|
||||
packet.Data().AsRange().Checksum(),
|
||||
packet.Data().Checksum(),
|
||||
))
|
||||
if xsum != math.MaxUint16 {
|
||||
xsum = ^xsum
|
||||
|
||||
274
internal/winfw/winfw.go
Normal file
274
internal/winfw/winfw.go
Normal file
@@ -0,0 +1,274 @@
|
||||
// Copyright (c) 2018 Samuel Melrose
|
||||
// SPDX-License-Identifier: MIT
|
||||
// https://github.com/iamacarpet/go-win64api/blob/ef6dbdd6db97301ae08a55eedea773476985a602/firewall.go
|
||||
|
||||
//go:build windows
|
||||
|
||||
package winfw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/go-ole/go-ole/oleutil"
|
||||
"github.com/scjalliance/comshim"
|
||||
)
|
||||
|
||||
// Firewall related API constants.
|
||||
const (
|
||||
NET_FW_IP_PROTOCOL_TCP = 6
|
||||
NET_FW_IP_PROTOCOL_UDP = 17
|
||||
NET_FW_IP_PROTOCOL_ICMPv4 = 1
|
||||
NET_FW_IP_PROTOCOL_ICMPv6 = 58
|
||||
NET_FW_IP_PROTOCOL_ANY = 256
|
||||
|
||||
NET_FW_RULE_DIR_IN = 1
|
||||
NET_FW_RULE_DIR_OUT = 2
|
||||
|
||||
NET_FW_ACTION_BLOCK = 0
|
||||
NET_FW_ACTION_ALLOW = 1
|
||||
|
||||
// NET_FW_PROFILE2_CURRENT is not real API constant, just helper used in FW functions.
|
||||
// It can mean one profile or multiple (even all) profiles. It depends on which profiles
|
||||
// are currently in use. Every active interface can have it's own profile. F.e.: Public for Wifi,
|
||||
// Domain for VPN, and Private for LAN. All at the same time.
|
||||
NET_FW_PROFILE2_CURRENT = 0
|
||||
NET_FW_PROFILE2_DOMAIN = 1
|
||||
NET_FW_PROFILE2_PRIVATE = 2
|
||||
NET_FW_PROFILE2_PUBLIC = 4
|
||||
NET_FW_PROFILE2_ALL = 2147483647
|
||||
)
|
||||
|
||||
// Firewall Rule Groups
|
||||
// Use this magical strings instead of group names. It will work on all language Windows versions.
|
||||
// You can find more string locations here:
|
||||
// https://windows10dll.nirsoft.net/firewallapi_dll.html
|
||||
const (
|
||||
NET_FW_FILE_AND_PRINTER_SHARING = "@FirewallAPI.dll,-28502"
|
||||
NET_FW_REMOTE_DESKTOP = "@FirewallAPI.dll,-28752"
|
||||
)
|
||||
|
||||
// FWRule represents Firewall Rule.
|
||||
type FWRule struct {
|
||||
Name, Description, ApplicationName, ServiceName string
|
||||
LocalPorts, RemotePorts string
|
||||
// LocalAddresses, RemoteAddresses are always returned with netmask, f.e.:
|
||||
// `10.10.1.1/255.255.255.0`
|
||||
LocalAddresses, RemoteAddresses string
|
||||
// ICMPTypesAndCodes is string. You can find define multiple codes separated by ":" (colon).
|
||||
// Types are listed here:
|
||||
// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
|
||||
// So to allow ping set it to:
|
||||
// "0"
|
||||
ICMPTypesAndCodes string
|
||||
Grouping string
|
||||
// InterfaceTypes can be:
|
||||
// "LAN", "Wireless", "RemoteAccess", "All"
|
||||
// You can add multiple deviding with comma:
|
||||
// "LAN, Wireless"
|
||||
InterfaceTypes string
|
||||
Protocol, Direction, Action, Profiles int32
|
||||
Enabled, EdgeTraversal bool
|
||||
}
|
||||
|
||||
// FirewallRuleAddAdvanced allows to modify almost all available FW Rule parameters.
|
||||
// You probably do not want to use this, as function allows to create any rule, even opening all ports
|
||||
// in given profile. So use with caution.
|
||||
func FirewallRuleAddAdvanced(rule FWRule) (bool, error) {
|
||||
return firewallRuleAdd(rule.Name, rule.Description, rule.Grouping, rule.ApplicationName, rule.ServiceName,
|
||||
rule.LocalPorts, rule.RemotePorts, rule.LocalAddresses, rule.RemoteAddresses, rule.ICMPTypesAndCodes,
|
||||
rule.Protocol, rule.Direction, rule.Action, rule.Profiles, rule.Enabled, rule.EdgeTraversal)
|
||||
}
|
||||
|
||||
// firewallRuleAdd is universal function to add all kinds of rules.
|
||||
func firewallRuleAdd(name, description, group, appPath, serviceName, ports, remotePorts, localAddresses, remoteAddresses, icmpTypes string, protocol, direction, action, profile int32, enabled, edgeTraversal bool) (bool, error) {
|
||||
if name == "" {
|
||||
return false, fmt.Errorf("empty FW Rule name, name is mandatory")
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
u, fwPolicy, err := firewallAPIInit()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer firewallAPIRelease(u, fwPolicy)
|
||||
|
||||
if profile == NET_FW_PROFILE2_CURRENT {
|
||||
currentProfiles, err := oleutil.GetProperty(fwPolicy, "CurrentProfileTypes")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get CurrentProfiles: %s", err)
|
||||
}
|
||||
profile = currentProfiles.Value().(int32)
|
||||
}
|
||||
unknownRules, err := oleutil.GetProperty(fwPolicy, "Rules")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get Rules: %s", err)
|
||||
}
|
||||
rules := unknownRules.ToIDispatch()
|
||||
|
||||
if ok, err := FirewallRuleExistsByName(rules, name); err != nil {
|
||||
return false, fmt.Errorf("Error while checking rules for duplicate: %s", err)
|
||||
} else if ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
unknown2, err := oleutil.CreateObject("HNetCfg.FWRule")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error creating Rule object: %s", err)
|
||||
}
|
||||
defer unknown2.Release()
|
||||
|
||||
fwRule, err := unknown2.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error creating Rule object (2): %s", err)
|
||||
}
|
||||
defer fwRule.Release()
|
||||
|
||||
if _, err := oleutil.PutProperty(fwRule, "Name", name); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Name) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Description", description); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Description) of Rule: %s", err)
|
||||
}
|
||||
if appPath != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "Applicationname", appPath); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Applicationname) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if serviceName != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "ServiceName", serviceName); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (ServiceName) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if protocol != 0 {
|
||||
if _, err := oleutil.PutProperty(fwRule, "Protocol", protocol); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Protocol) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if icmpTypes != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "IcmpTypesAndCodes", icmpTypes); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (IcmpTypesAndCodes) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if ports != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "LocalPorts", ports); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (LocalPorts) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if remotePorts != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "RemotePorts", remotePorts); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (RemotePorts) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if localAddresses != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "LocalAddresses", localAddresses); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (LocalAddresses) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if remoteAddresses != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "RemoteAddresses", remoteAddresses); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (RemoteAddresses) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if direction != 0 {
|
||||
if _, err := oleutil.PutProperty(fwRule, "Direction", direction); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Direction) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Enabled", enabled); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Enabled) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Grouping", group); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Grouping) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Profiles", profile); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Profiles) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Action", action); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Action) of Rule: %s", err)
|
||||
}
|
||||
if edgeTraversal {
|
||||
if _, err := oleutil.PutProperty(fwRule, "EdgeTraversal", edgeTraversal); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (EdgeTraversal) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := oleutil.CallMethod(rules, "Add", fwRule); err != nil {
|
||||
return false, fmt.Errorf("Error adding Rule: %s", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func FirewallRuleExistsByName(rules *ole.IDispatch, name string) (bool, error) {
|
||||
enumProperty, err := rules.GetProperty("_NewEnum")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get enumeration property on Rules: %s", err)
|
||||
}
|
||||
defer enumProperty.Clear()
|
||||
|
||||
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to cast enum to correct type: %s", err)
|
||||
}
|
||||
if enum == nil {
|
||||
return false, fmt.Errorf("can't get IEnumVARIANT, enum is nil")
|
||||
}
|
||||
|
||||
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to seek next Rule item: %s", err)
|
||||
}
|
||||
|
||||
t, err := func() (bool, error) {
|
||||
item := itemRaw.ToIDispatch()
|
||||
defer item.Release()
|
||||
|
||||
if item, err := oleutil.GetProperty(item, "Name"); err != nil {
|
||||
return false, fmt.Errorf("Failed to get Property (Name) of Rule")
|
||||
} else if item.ToString() == name {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if t {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// firewallAPIInit initialize common fw api.
|
||||
// then:
|
||||
// dispatch firewallAPIRelease(u, fwp)
|
||||
func firewallAPIInit() (*ole.IUnknown, *ole.IDispatch, error) {
|
||||
comshim.Add(1)
|
||||
|
||||
unknown, err := oleutil.CreateObject("HNetCfg.FwPolicy2")
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to create FwPolicy Object: %s", err)
|
||||
}
|
||||
|
||||
fwPolicy, err := unknown.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
unknown.Release()
|
||||
return nil, nil, fmt.Errorf("Failed to create FwPolicy Object (2): %s", err)
|
||||
}
|
||||
|
||||
return unknown, fwPolicy, nil
|
||||
}
|
||||
|
||||
// firewallAPIRelease cleans memory.
|
||||
func firewallAPIRelease(u *ole.IUnknown, fwp *ole.IDispatch) {
|
||||
fwp.Release()
|
||||
u.Release()
|
||||
comshim.Done()
|
||||
}
|
||||
@@ -39,5 +39,6 @@ type DefaultInterfaceMonitor interface {
|
||||
}
|
||||
|
||||
type DefaultInterfaceMonitorOptions struct {
|
||||
OverrideAndroidVPN bool
|
||||
OverrideAndroidVPN bool
|
||||
UnderNetworkExtension bool
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
@@ -83,29 +84,119 @@ func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var defaultInterface *net.Interface
|
||||
for _, rawRouteMessage := range routeMessages {
|
||||
routeMessage := rawRouteMessage.(*route.RouteMessage)
|
||||
if common.Any(common.FilterIsInstance(routeMessage.Addrs, func(it route.Addr) (*route.Inet4Addr, bool) {
|
||||
addr, loaded := it.(*route.Inet4Addr)
|
||||
return addr, loaded
|
||||
}), func(addr *route.Inet4Addr) bool {
|
||||
return addr.IP == netip.IPv4Unspecified().As4()
|
||||
}) {
|
||||
oldInterface := m.defaultInterfaceName
|
||||
oldIndex := m.defaultInterfaceIndex
|
||||
|
||||
m.defaultInterfaceIndex = routeMessage.Index
|
||||
defaultInterface, err := net.InterfaceByIndex(routeMessage.Index)
|
||||
if len(routeMessage.Addrs) <= unix.RTAX_NETMASK {
|
||||
continue
|
||||
}
|
||||
destination, isIPv4Destination := routeMessage.Addrs[unix.RTAX_DST].(*route.Inet4Addr)
|
||||
if !isIPv4Destination {
|
||||
continue
|
||||
}
|
||||
if destination.IP != netip.IPv4Unspecified().As4() {
|
||||
continue
|
||||
}
|
||||
mask, isIPv4Mask := routeMessage.Addrs[unix.RTAX_NETMASK].(*route.Inet4Addr)
|
||||
if !isIPv4Mask {
|
||||
continue
|
||||
}
|
||||
ones, _ := net.IPMask(mask.IP[:]).Size()
|
||||
if ones != 0 {
|
||||
continue
|
||||
}
|
||||
routeInterface, err := net.InterfaceByIndex(routeMessage.Index)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routeMessage.Flags&unix.RTF_UP == 0 {
|
||||
continue
|
||||
}
|
||||
if routeMessage.Flags&unix.RTF_GATEWAY == 0 {
|
||||
continue
|
||||
}
|
||||
if routeMessage.Flags&unix.RTF_IFSCOPE != 0 {
|
||||
continue
|
||||
}
|
||||
defaultInterface = routeInterface
|
||||
break
|
||||
}
|
||||
if defaultInterface == nil {
|
||||
if m.options.UnderNetworkExtension {
|
||||
defaultInterface, err = getDefaultInterfaceBySocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.defaultInterfaceName = defaultInterface.Name
|
||||
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||
return nil
|
||||
}
|
||||
m.emit(EventInterfaceUpdate)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrNoRoute
|
||||
if defaultInterface == nil {
|
||||
return ErrNoRoute
|
||||
}
|
||||
oldInterface := m.defaultInterfaceName
|
||||
oldIndex := m.defaultInterfaceIndex
|
||||
m.defaultInterfaceIndex = defaultInterface.Index
|
||||
m.defaultInterfaceName = defaultInterface.Name
|
||||
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||
return nil
|
||||
}
|
||||
m.emit(EventInterfaceUpdate)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDefaultInterfaceBySocket() (*net.Interface, error) {
|
||||
socketFd, err := unix.Socket(unix.AF_INET, unix.SOCK_STREAM, 0)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "create file descriptor")
|
||||
}
|
||||
defer unix.Close(socketFd)
|
||||
go unix.Connect(socketFd, &unix.SockaddrInet4{
|
||||
Addr: [4]byte{10, 255, 255, 255},
|
||||
Port: 80,
|
||||
})
|
||||
result := make(chan netip.Addr, 1)
|
||||
go func() {
|
||||
for {
|
||||
sockname, sockErr := unix.Getsockname(socketFd)
|
||||
if sockErr != nil {
|
||||
break
|
||||
}
|
||||
sockaddr, isInet4Sockaddr := sockname.(*unix.SockaddrInet4)
|
||||
if !isInet4Sockaddr {
|
||||
break
|
||||
}
|
||||
addr := netip.AddrFrom4(sockaddr.Addr)
|
||||
if addr.IsUnspecified() {
|
||||
time.Sleep(time.Millisecond)
|
||||
continue
|
||||
}
|
||||
result <- addr
|
||||
break
|
||||
}
|
||||
}()
|
||||
var selectedAddr netip.Addr
|
||||
select {
|
||||
case selectedAddr = <-result:
|
||||
case <-time.After(time.Second):
|
||||
return nil, os.ErrDeadlineExceeded
|
||||
}
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "net.Interfaces")
|
||||
}
|
||||
for _, netInterface := range interfaces {
|
||||
interfaceAddrs, err := netInterface.Addrs()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "net.Interfaces.Addrs")
|
||||
}
|
||||
for _, interfaceAddr := range interfaceAddrs {
|
||||
ipNet, isIPNet := interfaceAddr.(*net.IPNet)
|
||||
if !isIPNet {
|
||||
continue
|
||||
}
|
||||
if ipNet.Contains(selectedAddr.AsSlice()) {
|
||||
return &netInterface, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, E.New("no interface found for address ", selectedAddr)
|
||||
}
|
||||
|
||||
41
network_name.go
Normal file
41
network_name.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func NetworkName(network uint8) string {
|
||||
switch network {
|
||||
case clashtcpip.TCP:
|
||||
return N.NetworkTCP
|
||||
case clashtcpip.UDP:
|
||||
return N.NetworkUDP
|
||||
case clashtcpip.ICMP:
|
||||
return N.NetworkICMPv4
|
||||
case clashtcpip.ICMPv6:
|
||||
return N.NetworkICMPv6
|
||||
}
|
||||
return F.ToString(network)
|
||||
}
|
||||
|
||||
func NetworkFromName(name string) uint8 {
|
||||
switch name {
|
||||
case N.NetworkTCP:
|
||||
return clashtcpip.TCP
|
||||
case N.NetworkUDP:
|
||||
return clashtcpip.UDP
|
||||
case N.NetworkICMPv4:
|
||||
return clashtcpip.ICMP
|
||||
case N.NetworkICMPv6:
|
||||
return clashtcpip.ICMPv6
|
||||
}
|
||||
parseNetwork, err := strconv.ParseUint(name, 10, 8)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return uint8(parseNetwork)
|
||||
}
|
||||
3
stack.go
3
stack.go
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"net/netip"
|
||||
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
@@ -24,6 +25,8 @@ type StackOptions struct {
|
||||
UDPTimeout int64
|
||||
Handler Handler
|
||||
Logger logger.Logger
|
||||
ForwarderBindInterface bool
|
||||
InterfaceFinder control.InterfaceFinder
|
||||
}
|
||||
|
||||
func NewStack(
|
||||
|
||||
149
system.go
149
system.go
@@ -4,11 +4,13 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/clashtcpip"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/control"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
@@ -19,6 +21,7 @@ import (
|
||||
type System struct {
|
||||
ctx context.Context
|
||||
tun Tun
|
||||
tunName string
|
||||
mtu uint32
|
||||
handler Handler
|
||||
logger logger.Logger
|
||||
@@ -35,6 +38,8 @@ type System struct {
|
||||
tcpPort6 uint16
|
||||
tcpNat *TCPNat
|
||||
udpNat *udpnat.Service[netip.AddrPort]
|
||||
bindInterface bool
|
||||
interfaceFinder control.InterfaceFinder
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
@@ -46,14 +51,17 @@ type Session struct {
|
||||
|
||||
func NewSystem(options StackOptions) (Stack, error) {
|
||||
stack := &System{
|
||||
ctx: options.Context,
|
||||
tun: options.Tun,
|
||||
mtu: options.MTU,
|
||||
udpTimeout: options.UDPTimeout,
|
||||
handler: options.Handler,
|
||||
logger: options.Logger,
|
||||
inet4Prefixes: options.Inet4Address,
|
||||
inet6Prefixes: options.Inet6Address,
|
||||
ctx: options.Context,
|
||||
tun: options.Tun,
|
||||
tunName: options.Name,
|
||||
mtu: options.MTU,
|
||||
udpTimeout: options.UDPTimeout,
|
||||
handler: options.Handler,
|
||||
logger: options.Logger,
|
||||
inet4Prefixes: options.Inet4Address,
|
||||
inet6Prefixes: options.Inet6Address,
|
||||
bindInterface: options.ForwarderBindInterface,
|
||||
interfaceFinder: options.InterfaceFinder,
|
||||
}
|
||||
if len(options.Inet4Address) > 0 {
|
||||
if options.Inet4Address[0].Bits() == 32 {
|
||||
@@ -83,8 +91,22 @@ func (s *System) Close() error {
|
||||
}
|
||||
|
||||
func (s *System) Start() error {
|
||||
err := fixWindowsFirewall()
|
||||
if err != nil {
|
||||
return E.Cause(err, "fix windows firewall for system stack")
|
||||
}
|
||||
var listener net.ListenConfig
|
||||
if s.bindInterface {
|
||||
listener.Control = control.Append(listener.Control, func(network, address string, conn syscall.RawConn) error {
|
||||
err := control.BindToInterface(s.interfaceFinder, s.tunName, -1)(network, address, conn)
|
||||
if err != nil {
|
||||
s.logger.Warn("bind forwarder to interface: ", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if s.inet4Address.IsValid() {
|
||||
tcpListener, err := net.Listen("tcp4", net.JoinHostPort(s.inet4ServerAddress.String(), "0"))
|
||||
tcpListener, err := listener.Listen(s.ctx, "tcp4", net.JoinHostPort(s.inet4ServerAddress.String(), "0"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,7 +115,7 @@ func (s *System) Start() error {
|
||||
go s.acceptLoop(tcpListener)
|
||||
}
|
||||
if s.inet6Address.IsValid() {
|
||||
tcpListener, err := net.Listen("tcp6", net.JoinHostPort(s.inet6ServerAddress.String(), "0"))
|
||||
tcpListener, err := listener.Listen(s.ctx, "tcp6", net.JoinHostPort(s.inet6ServerAddress.String(), "0"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,7 +123,7 @@ func (s *System) Start() error {
|
||||
s.tcpPort6 = M.SocksaddrFromNet(tcpListener.Addr()).Port
|
||||
go s.acceptLoop(tcpListener)
|
||||
}
|
||||
s.tcpNat = NewNat()
|
||||
s.tcpNat = NewNat(s.ctx, time.Second*time.Duration(s.udpTimeout))
|
||||
s.udpNat = udpnat.New[netip.AddrPort](s.udpTimeout, s.handler)
|
||||
go s.tunLoop()
|
||||
return nil
|
||||
@@ -194,13 +216,14 @@ func (s *System) acceptLoop(listener net.Listener) {
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
s.handler.NewConnection(context.Background(), conn, M.Metadata{
|
||||
_ = s.handler.NewConnection(s.ctx, conn, M.Metadata{
|
||||
Source: M.SocksaddrFromNetIP(session.Source),
|
||||
Destination: destination,
|
||||
})
|
||||
conn.Close()
|
||||
time.Sleep(time.Second)
|
||||
s.tcpNat.Revoke(connPort, session)
|
||||
if tcpConn, isTCPConn := conn.(*net.TCPConn); isTCPConn {
|
||||
_ = tcpConn.SetLinger(0)
|
||||
}
|
||||
_ = conn.Close()
|
||||
}()
|
||||
}
|
||||
}
|
||||
@@ -307,7 +330,7 @@ func (s *System) processIPv4UDP(packet clashtcpip.IPv4Packet, header clashtcpip.
|
||||
headerLen := packet.HeaderLen() + clashtcpip.UDPHeaderSize
|
||||
headerCopy := make([]byte, headerLen)
|
||||
copy(headerCopy, packet[:headerLen])
|
||||
return &systemPacketWriter4{s.tun, headerCopy, source}
|
||||
return &systemUDPPacketWriter4{s.tun, headerCopy, source}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -330,7 +353,7 @@ func (s *System) processIPv6UDP(packet clashtcpip.IPv6Packet, header clashtcpip.
|
||||
headerLen := len(packet) - int(header.Length()) + clashtcpip.UDPHeaderSize
|
||||
headerCopy := make([]byte, headerLen)
|
||||
copy(headerCopy, packet[:headerLen])
|
||||
return &systemPacketWriter6{s.tun, headerCopy, source}
|
||||
return &systemUDPPacketWriter6{s.tun, headerCopy, source}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
@@ -361,13 +384,97 @@ func (s *System) processIPv6ICMP(packet clashtcpip.IPv6Packet, header clashtcpip
|
||||
return common.Error(s.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemPacketWriter4 struct {
|
||||
type systemTCPDirectPacketWriter4 struct {
|
||||
tun Tun
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemTCPDirectPacketWriter4) WritePacket(p []byte) error {
|
||||
packet := clashtcpip.IPv4Packet(p)
|
||||
header := clashtcpip.TCPPacket(packet.Payload())
|
||||
packet.SetDestinationIP(w.source.Addr())
|
||||
header.SetDestinationPort(w.source.Port())
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
return common.Error(w.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemTCPDirectPacketWriter6 struct {
|
||||
tun Tun
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemTCPDirectPacketWriter6) WritePacket(p []byte) error {
|
||||
packet := clashtcpip.IPv6Packet(p)
|
||||
header := clashtcpip.TCPPacket(packet.Payload())
|
||||
packet.SetDestinationIP(w.source.Addr())
|
||||
header.SetDestinationPort(w.source.Port())
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
return common.Error(w.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemUDPDirectPacketWriter4 struct {
|
||||
tun Tun
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemUDPDirectPacketWriter4) WritePacket(p []byte) error {
|
||||
packet := clashtcpip.IPv4Packet(p)
|
||||
header := clashtcpip.UDPPacket(packet.Payload())
|
||||
packet.SetDestinationIP(w.source.Addr())
|
||||
header.SetDestinationPort(w.source.Port())
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
return common.Error(w.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemUDPDirectPacketWriter6 struct {
|
||||
tun Tun
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemUDPDirectPacketWriter6) WritePacket(p []byte) error {
|
||||
packet := clashtcpip.IPv6Packet(p)
|
||||
header := clashtcpip.UDPPacket(packet.Payload())
|
||||
packet.SetDestinationIP(w.source.Addr())
|
||||
header.SetDestinationPort(w.source.Port())
|
||||
header.ResetChecksum(packet.PseudoSum())
|
||||
packet.ResetChecksum()
|
||||
return common.Error(w.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemICMPDirectPacketWriter4 struct {
|
||||
tun Tun
|
||||
source netip.Addr
|
||||
}
|
||||
|
||||
func (w *systemICMPDirectPacketWriter4) WritePacket(p []byte) error {
|
||||
packet := clashtcpip.IPv4Packet(p)
|
||||
packet.SetDestinationIP(w.source)
|
||||
packet.ResetChecksum()
|
||||
return common.Error(w.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemICMPDirectPacketWriter6 struct {
|
||||
tun Tun
|
||||
source netip.Addr
|
||||
}
|
||||
|
||||
func (w *systemICMPDirectPacketWriter6) WritePacket(p []byte) error {
|
||||
packet := clashtcpip.IPv6Packet(p)
|
||||
packet.SetDestinationIP(w.source)
|
||||
packet.ResetChecksum()
|
||||
return common.Error(w.tun.Write(packet))
|
||||
}
|
||||
|
||||
type systemUDPPacketWriter4 struct {
|
||||
tun Tun
|
||||
header []byte
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
func (w *systemUDPPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
newPacket := buf.StackNewSize(len(w.header) + buffer.Len())
|
||||
defer newPacket.Release()
|
||||
newPacket.Write(w.header)
|
||||
@@ -385,13 +492,13 @@ func (w *systemPacketWriter4) WritePacket(buffer *buf.Buffer, destination M.Sock
|
||||
return common.Error(w.tun.Write(newPacket.Bytes()))
|
||||
}
|
||||
|
||||
type systemPacketWriter6 struct {
|
||||
type systemUDPPacketWriter6 struct {
|
||||
tun Tun
|
||||
header []byte
|
||||
source netip.AddrPort
|
||||
}
|
||||
|
||||
func (w *systemPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
func (w *systemUDPPacketWriter6) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
newPacket := buf.StackNewSize(len(w.header) + buffer.Len())
|
||||
defer newPacket.Release()
|
||||
newPacket.Write(w.header)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TCPNat struct {
|
||||
@@ -16,20 +18,54 @@ type TCPNat struct {
|
||||
type TCPSession struct {
|
||||
Source netip.AddrPort
|
||||
Destination netip.AddrPort
|
||||
LastActive time.Time
|
||||
}
|
||||
|
||||
func NewNat() *TCPNat {
|
||||
return &TCPNat{
|
||||
func NewNat(ctx context.Context, timeout time.Duration) *TCPNat {
|
||||
natMap := &TCPNat{
|
||||
portIndex: 10000,
|
||||
addrMap: make(map[netip.AddrPort]uint16),
|
||||
portMap: make(map[uint16]*TCPSession),
|
||||
}
|
||||
go natMap.loopCheckTimeout(ctx, timeout)
|
||||
return natMap
|
||||
}
|
||||
|
||||
func (n *TCPNat) loopCheckTimeout(ctx context.Context, timeout time.Duration) {
|
||||
ticker := time.NewTicker(timeout)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
n.checkTimeout(timeout)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *TCPNat) checkTimeout(timeout time.Duration) {
|
||||
now := time.Now()
|
||||
n.portAccess.Lock()
|
||||
defer n.portAccess.Unlock()
|
||||
n.addrAccess.Lock()
|
||||
defer n.addrAccess.Unlock()
|
||||
for natPort, session := range n.portMap {
|
||||
if now.Sub(session.LastActive) > timeout {
|
||||
delete(n.addrMap, session.Source)
|
||||
delete(n.portMap, natPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *TCPNat) LookupBack(port uint16) *TCPSession {
|
||||
n.portAccess.RLock()
|
||||
defer n.portAccess.RUnlock()
|
||||
return n.portMap[port]
|
||||
session := n.portMap[port]
|
||||
n.portAccess.RUnlock()
|
||||
if session != nil {
|
||||
session.LastActive = time.Now()
|
||||
}
|
||||
return session
|
||||
}
|
||||
|
||||
func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort) uint16 {
|
||||
@@ -53,16 +89,8 @@ func (n *TCPNat) Lookup(source netip.AddrPort, destination netip.AddrPort) uint1
|
||||
n.portMap[nextPort] = &TCPSession{
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
LastActive: time.Now(),
|
||||
}
|
||||
n.portAccess.Unlock()
|
||||
return nextPort
|
||||
}
|
||||
|
||||
func (n *TCPNat) Revoke(natPort uint16, session *TCPSession) {
|
||||
n.addrAccess.Lock()
|
||||
delete(n.addrMap, session.Source)
|
||||
n.addrAccess.Unlock()
|
||||
n.portAccess.Lock()
|
||||
delete(n.portMap, natPort)
|
||||
n.portAccess.Unlock()
|
||||
}
|
||||
|
||||
7
system_nonwindows.go
Normal file
7
system_nonwindows.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build !windows
|
||||
|
||||
package tun
|
||||
|
||||
func fixWindowsFirewall() error {
|
||||
return nil
|
||||
}
|
||||
25
system_windows.go
Normal file
25
system_windows.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/sagernet/sing-tun/internal/winfw"
|
||||
)
|
||||
|
||||
func fixWindowsFirewall() error {
|
||||
absPath, err := filepath.Abs(os.Args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rule := winfw.FWRule{
|
||||
Name: "sing-tun (" + absPath + ")",
|
||||
ApplicationName: absPath,
|
||||
Enabled: true,
|
||||
Protocol: winfw.NET_FW_IP_PROTOCOL_TCP,
|
||||
Direction: winfw.NET_FW_RULE_DIR_IN,
|
||||
Action: winfw.NET_FW_ACTION_ALLOW,
|
||||
}
|
||||
_, err = winfw.FirewallRuleAddAdvanced(rule)
|
||||
return err
|
||||
}
|
||||
14
timeout.go
14
timeout.go
@@ -1,14 +0,0 @@
|
||||
package tun
|
||||
|
||||
import "context"
|
||||
|
||||
type needTimeoutKey struct{}
|
||||
|
||||
func ContextWithNeedTimeout(ctx context.Context, need bool) context.Context {
|
||||
return context.WithValue(ctx, (*needTimeoutKey)(nil), need)
|
||||
}
|
||||
|
||||
func NeedTimeoutFromContext(ctx context.Context) bool {
|
||||
need, _ := ctx.Value((*needTimeoutKey)(nil)).(bool)
|
||||
return need
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
|
||||
"golang.org/x/net/route"
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -24,8 +25,8 @@ type NativeTun struct {
|
||||
tunFile *os.File
|
||||
tunWriter N.VectorisedWriter
|
||||
mtu uint32
|
||||
inet4Address string
|
||||
inet6Address string
|
||||
inet4Address [4]byte
|
||||
inet6Address [16]byte
|
||||
}
|
||||
|
||||
func New(options Options) (Tun, error) {
|
||||
@@ -56,10 +57,10 @@ func New(options Options) (Tun, error) {
|
||||
mtu: options.MTU,
|
||||
}
|
||||
if len(options.Inet4Address) > 0 {
|
||||
nativeTun.inet4Address = string(options.Inet4Address[0].Addr().AsSlice())
|
||||
nativeTun.inet4Address = options.Inet4Address[0].Addr().As4()
|
||||
}
|
||||
if len(options.Inet6Address) > 0 {
|
||||
nativeTun.inet6Address = string(options.Inet6Address[0].Addr().AsSlice())
|
||||
nativeTun.inet6Address = options.Inet6Address[0].Addr().As16()
|
||||
}
|
||||
var ok bool
|
||||
nativeTun.tunWriter, ok = bufio.CreateVectorisedWriter(nativeTun.tunFile)
|
||||
@@ -101,6 +102,7 @@ func (t *NativeTun) Write(p []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (t *NativeTun) Close() error {
|
||||
flushDNSCache()
|
||||
return t.tunFile.Close()
|
||||
}
|
||||
|
||||
@@ -285,6 +287,7 @@ func configure(tunFd int, ifIndex int, name string, options Options) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
flushDNSCache()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -326,3 +329,7 @@ func addRoute(destination netip.Prefix, gateway netip.Addr) error {
|
||||
return common.Error(unix.Write(socketFd, request))
|
||||
})
|
||||
}
|
||||
|
||||
func flushDNSCache() {
|
||||
shell.Exec("dscacheutil", "-flushcache").Start()
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
"github.com/sagernet/sing/common/bufio"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ GVisorTun = (*NativeTun)(nil)
|
||||
@@ -56,9 +55,9 @@ func (e *DarwinEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
func (e *DarwinEndpoint) dispatchLoop() {
|
||||
_buffer := buf.StackNewSize(int(e.tun.mtu) + 4)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
data := buffer.FreeBytes()
|
||||
packetBuffer := common.Dup(_buffer)
|
||||
defer packetBuffer.Release()
|
||||
data := packetBuffer.FreeBytes()
|
||||
for {
|
||||
n, err := e.tun.tunFile.Read(data)
|
||||
if err != nil {
|
||||
@@ -69,13 +68,13 @@ func (e *DarwinEndpoint) dispatchLoop() {
|
||||
switch header.IPVersion(packet) {
|
||||
case header.IPv4Version:
|
||||
networkProtocol = header.IPv4ProtocolNumber
|
||||
if header.IPv4(packet).DestinationAddress() == tcpip.Address(e.tun.inet4Address) {
|
||||
if header.IPv4(packet).DestinationAddress().As4() == e.tun.inet4Address {
|
||||
e.tun.tunFile.Write(data[:n])
|
||||
continue
|
||||
}
|
||||
case header.IPv6Version:
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
if header.IPv6(packet).DestinationAddress() == tcpip.Address(e.tun.inet6Address) {
|
||||
if header.IPv6(packet).DestinationAddress().As16() == e.tun.inet6Address {
|
||||
e.tun.tunFile.Write(data[:n])
|
||||
continue
|
||||
}
|
||||
@@ -84,7 +83,7 @@ func (e *DarwinEndpoint) dispatchLoop() {
|
||||
continue
|
||||
}
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: bufferv2.MakeWithData(data[4:n]),
|
||||
Payload: buffer.MakeWithData(data[4:n]),
|
||||
IsForwardedPacket: true,
|
||||
})
|
||||
pkt.NetworkProtocolNumber = networkProtocol
|
||||
@@ -109,7 +108,7 @@ func (e *DarwinEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||
func (e *DarwinEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
|
||||
}
|
||||
|
||||
func (e *DarwinEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
|
||||
34
tun_linux.go
34
tun_linux.go
@@ -5,13 +5,16 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/sagernet/netlink"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
"github.com/sagernet/sing/common/shell"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -164,6 +167,8 @@ func (t *NativeTun) configure(tunLink netlink.Link) error {
|
||||
return err
|
||||
}
|
||||
|
||||
t.setSearchDomainForSystemdResolved()
|
||||
|
||||
if t.options.AutoRoute && runtime.GOOS == "android" {
|
||||
t.interfaceCallback = t.options.InterfaceMonitor.RegisterCallback(t.routeUpdate)
|
||||
}
|
||||
@@ -383,7 +388,7 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
if p4 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority
|
||||
it.IPProto = unix.IPPROTO_ICMP
|
||||
it.IPProto = syscall.IPPROTO_ICMP
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET
|
||||
rules = append(rules, it)
|
||||
@@ -392,7 +397,7 @@ func (t *NativeTun) rules() []*netlink.Rule {
|
||||
if p6 {
|
||||
it = netlink.NewRule()
|
||||
it.Priority = priority6
|
||||
it.IPProto = unix.IPPROTO_ICMPV6
|
||||
it.IPProto = syscall.IPPROTO_ICMPV6
|
||||
it.Goto = nopPriority
|
||||
it.Family = unix.AF_INET6
|
||||
rules = append(rules, it)
|
||||
@@ -525,6 +530,9 @@ func (t *NativeTun) setRules() error {
|
||||
}
|
||||
|
||||
func (t *NativeTun) unsetRoute() error {
|
||||
if t.options.FileDescriptor > 0 {
|
||||
return nil
|
||||
}
|
||||
tunLink, err := netlink.LinkByName(t.options.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -540,6 +548,9 @@ func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
|
||||
}
|
||||
|
||||
func (t *NativeTun) unsetRules() error {
|
||||
if t.options.FileDescriptor > 0 {
|
||||
return nil
|
||||
}
|
||||
if len(t.ruleIndex6) > 0 {
|
||||
for _, index := range t.ruleIndex6 {
|
||||
ruleToDel := netlink.NewRule()
|
||||
@@ -587,3 +598,22 @@ func (t *NativeTun) routeUpdate(event int) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *NativeTun) setSearchDomainForSystemdResolved() {
|
||||
ctlPath, err := exec.LookPath("resolvectl")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var dnsServer []netip.Addr
|
||||
if len(t.options.Inet4Address) > 0 {
|
||||
dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
|
||||
}
|
||||
if len(t.options.Inet6Address) > 0 {
|
||||
dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
|
||||
}
|
||||
shell.Exec(ctlPath, "domain", t.options.Name, "~.").Start()
|
||||
if t.options.AutoRoute {
|
||||
shell.Exec(ctlPath, "default-route", t.options.Name, "true").Start()
|
||||
shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Start()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/link/fdbased"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ GVisorTun = (*NativeTun)(nil)
|
||||
|
||||
@@ -116,6 +116,10 @@ func (t *NativeTun) configure() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
err := windnsapi.FlushResolverCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(t.options.Inet4Address) > 0 {
|
||||
inetIf, err := luid.IPInterface(winipcfg.AddressFamily(windows.AF_INET))
|
||||
@@ -331,11 +335,6 @@ func (t *NativeTun) configure() error {
|
||||
if err != nil {
|
||||
return os.NewSyscallError("FwpmFilterAdd0", err)
|
||||
}
|
||||
|
||||
err = windnsapi.FlushResolverCache()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -479,7 +478,9 @@ func (t *NativeTun) Close() error {
|
||||
if t.fwpmSession != 0 {
|
||||
winsys.FwpmEngineClose0(t.fwpmSession)
|
||||
}
|
||||
windnsapi.FlushResolverCache()
|
||||
if t.options.AutoRoute {
|
||||
windnsapi.FlushResolverCache()
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
package tun
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ GVisorTun = (*NativeTun)(nil)
|
||||
@@ -51,16 +51,16 @@ func (e *WintunEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
|
||||
func (e *WintunEndpoint) dispatchLoop() {
|
||||
for {
|
||||
var buffer bufferv2.Buffer
|
||||
var packetBuffer buffer.Buffer
|
||||
err := e.tun.ReadFunc(func(b []byte) {
|
||||
buffer = bufferv2.MakeWithData(b)
|
||||
packetBuffer = buffer.MakeWithData(b)
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ihl, ok := buffer.PullUp(0, 1)
|
||||
ihl, ok := packetBuffer.PullUp(0, 1)
|
||||
if !ok {
|
||||
buffer.Release()
|
||||
packetBuffer.Release()
|
||||
continue
|
||||
}
|
||||
var networkProtocol tcpip.NetworkProtocolNumber
|
||||
@@ -70,12 +70,12 @@ func (e *WintunEndpoint) dispatchLoop() {
|
||||
case header.IPv6Version:
|
||||
networkProtocol = header.IPv6ProtocolNumber
|
||||
default:
|
||||
e.tun.Write(buffer.Flatten())
|
||||
buffer.Release()
|
||||
e.tun.Write(packetBuffer.Flatten())
|
||||
packetBuffer.Release()
|
||||
continue
|
||||
}
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer,
|
||||
Payload: packetBuffer,
|
||||
IsForwardedPacket: true,
|
||||
})
|
||||
dispatcher := e.dispatcher
|
||||
@@ -99,7 +99,7 @@ func (e *WintunEndpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
return header.ARPHardwareNone
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) AddHeader(buffer *stack.PacketBuffer) {
|
||||
func (e *WintunEndpoint) AddHeader(buffer stack.PacketBufferPtr) {
|
||||
}
|
||||
|
||||
func (e *WintunEndpoint) WritePackets(packetBufferList stack.PacketBufferList) (int, tcpip.Error) {
|
||||
|
||||
Reference in New Issue
Block a user