Compare commits

..

27 Commits

Author SHA1 Message Date
世界
323b9564f0 Update dependencies 2023-06-17 12:11:41 +08:00
世界
4bc8dc7f27 Configure default-route for systemd-resolved
Even though the documentation says this parameter doesn't matter, some people have reported that not configuring it can cause problems.
2023-06-12 19:28:29 +08:00
世界
41b2639e13 Update gVisor to 20230605.0-33-g8ec8dbe7e 2023-06-11 22:06:33 +08:00
世界
605266e65e Update gci usage 2023-06-10 08:50:20 +08:00
世界
e881f21013 Update gVisor to release-20230605.0-21-g457c1c36d 2023-06-10 08:45:55 +08:00
dyhkwong
b02f252916 Use api to create windows firewall rules 2023-05-20 12:11:00 +08:00
世界
91df97aee2 Fix macos monitor 2023-05-09 18:20:26 +08:00
世界
6999634511 Fix windows firewall for system stack 2023-05-09 12:12:00 +08:00
世界
209ec123ca Update gVisor to 20230417.0 2023-04-22 20:14:32 +08:00
世界
a6a8c23d65 Update dependencies 2023-04-21 19:37:15 +08:00
世界
8bf4113343 Ignore system bind error 2023-04-20 11:56:16 +08:00
世界
9c6e70b7cc Revert LRU cache changes 2023-04-19 23:48:14 +08:00
世界
d744d03d93 Fix default interface check for darwin 2023-04-19 14:16:14 +08:00
世界
53f50347e0 Fix system nat mapping 2023-04-19 14:16:14 +08:00
世界
bf7110b1ab Update udpnat usage 2023-04-18 10:53:39 +08:00
世界
d880656b52 Fix system stack for ios 2023-04-17 20:02:10 +08:00
世界
499c0aed67 Fix monitor for ios 2023-04-17 19:23:13 +08:00
世界
8848c0e4cb Set search domain for systemd-resolved 2023-03-26 16:09:54 +08:00
世界
980d4bf9a3 Flush DNS cache on macOS 2023-03-26 10:37:44 +08:00
世界
ca53ccf346 Fix action type 2023-03-24 16:22:20 +08:00
世界
f0c27d037c Update dependencies 2023-03-24 07:12:16 +08:00
世界
35d565af65 Fix icmp constant 2023-03-23 15:33:25 +08:00
世界
45b089e6bb Fix gVisor udp conn timeout 2023-03-23 15:13:20 +08:00
世界
56bedd2f05 Add route support 2023-03-22 01:28:18 +08:00
世界
fe89bbded2 Create gVisor stack by default in NE 2023-03-15 21:47:16 +08:00
世界
839f1792e4 Fix close platform tun 2023-03-13 19:36:43 +08:00
世界
20226b91c9 Fix system stack not passing context to handler 2023-03-12 21:53:45 +08:00
25 changed files with 801 additions and 165 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -2,7 +2,7 @@
Simple transparent proxy library.
For Linux, Windows and macOS.
For Linux, Windows, macOS and iOS.
## License

14
go.mod
View File

@@ -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
View File

@@ -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=

View File

@@ -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())
}
}

View File

@@ -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 {

View File

@@ -5,7 +5,7 @@ package tun
import (
"time"
gLog "gvisor.dev/gvisor/pkg/log"
gLog "github.com/sagernet/gvisor/pkg/log"
)
func init() {

View File

@@ -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
View 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()
}

View File

@@ -39,5 +39,6 @@ type DefaultInterfaceMonitor interface {
}
type DefaultInterfaceMonitorOptions struct {
OverrideAndroidVPN bool
OverrideAndroidVPN bool
UnderNetworkExtension bool
}

View File

@@ -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
View 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)
}

View File

@@ -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
View File

@@ -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)

View File

@@ -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
View File

@@ -0,0 +1,7 @@
//go:build !windows
package tun
func fixWindowsFirewall() error {
return nil
}

25
system_windows.go Normal file
View 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
}

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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) {

View File

@@ -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()
}
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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) {