Fix windows route

This commit is contained in:
世界 2022-09-29 18:59:33 +08:00
parent 6bad0c2380
commit a93592a9b5
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
9 changed files with 662 additions and 26 deletions

6
go.mod
View file

@ -7,9 +7,9 @@ require (
github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e
github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220
golang.org/x/net v0.0.0-20220923203811-8be639271d50
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186
golang.org/x/net v0.0.0-20220927171203-f486391704dc
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec
gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c
)

12
go.sum
View file

@ -9,17 +9,17 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220 h1:fQk/BHOeHw5murjeNTdmkXmDy9cMlbubRINRH7GDuu4=
github.com/sagernet/sing v0.0.0-20220925112014-b12b8b7fd220/go.mod h1:5/u6RMDMoGIkSNtrZb41kJvyIFg3Ysn69P3WiAu8m0c=
github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 h1:ZDlgH6dTozS3ODaYq1GxCj+H8NvYESaex90iX72gadw=
github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4=
github.com/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.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI=
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/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=

View file

@ -0,0 +1,158 @@
//go:build windows
package winsys
import (
"golang.org/x/sys/windows"
)
const (
AF_INET = 2
AF_INET6 = 23
)
const (
MAX_MODULE_NAME32 = 255
MAX_PATH = 260
)
// https://docs.microsoft.com/en-us/windows/desktop/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot
const (
TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPPROCESS = 0x00000002
TH32CS_SNAPTHREAD = 0x00000004
TH32CS_SNAPMODULE = 0x00000008
TH32CS_SNAPMODULE32 = 0x00000010
TH32CS_INHERIT = 0x80000000
TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPMODULE | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD
)
const (
MAX_ADAPTER_NAME = 128
MAX_INTERFACE_NAME_LEN = 256
MAXLEN_PHYSADDR = 8
MAXLEN_IFDESCR = 256
)
const (
ERROR_INSUFFICIENT_BUFFER = 122
)
const (
RPC_C_AUTHN_DEFAULT uint32 = 0xFFFFFFFF
FWPM_SESSION_FLAG_DYNAMIC uint32 = 0x00000001
)
const (
FWP_MATCH_EQUAL uint32 = 0
FWP_MATCH_GREATER = (FWP_MATCH_EQUAL + 1)
FWP_MATCH_LESS = (FWP_MATCH_GREATER + 1)
FWP_MATCH_GREATER_OR_EQUAL = (FWP_MATCH_LESS + 1)
FWP_MATCH_LESS_OR_EQUAL = (FWP_MATCH_GREATER_OR_EQUAL + 1)
FWP_MATCH_RANGE = (FWP_MATCH_LESS_OR_EQUAL + 1)
FWP_MATCH_FLAGS_ALL_SET = (FWP_MATCH_RANGE + 1)
FWP_MATCH_FLAGS_ANY_SET = (FWP_MATCH_FLAGS_ALL_SET + 1)
FWP_MATCH_FLAGS_NONE_SET = (FWP_MATCH_FLAGS_ANY_SET + 1)
FWP_MATCH_EQUAL_CASE_INSENSITIVE = (FWP_MATCH_FLAGS_NONE_SET + 1)
FWP_MATCH_NOT_EQUAL = (FWP_MATCH_EQUAL_CASE_INSENSITIVE + 1)
FWP_MATCH_PREFIX = (FWP_MATCH_NOT_EQUAL + 1)
FWP_MATCH_NOT_PREFIX = (FWP_MATCH_PREFIX + 1)
FWP_MATCH_TYPE_MAX = (FWP_MATCH_NOT_PREFIX + 1)
)
const (
FWP_EMPTY uint32 = 0
FWP_UINT8 = (FWP_EMPTY + 1)
FWP_UINT16 = (FWP_UINT8 + 1)
FWP_UINT32 = (FWP_UINT16 + 1)
FWP_UINT64 = (FWP_UINT32 + 1)
FWP_INT8 = (FWP_UINT64 + 1)
FWP_INT16 = (FWP_INT8 + 1)
FWP_INT32 = (FWP_INT16 + 1)
FWP_INT64 = (FWP_INT32 + 1)
FWP_FLOAT = (FWP_INT64 + 1)
FWP_DOUBLE = (FWP_FLOAT + 1)
FWP_BYTE_ARRAY16_TYPE = (FWP_DOUBLE + 1)
FWP_BYTE_BLOB_TYPE = (FWP_BYTE_ARRAY16_TYPE + 1)
FWP_SID = (FWP_BYTE_BLOB_TYPE + 1)
FWP_SECURITY_DESCRIPTOR_TYPE = (FWP_SID + 1)
FWP_TOKEN_INFORMATION_TYPE = (FWP_SECURITY_DESCRIPTOR_TYPE + 1)
FWP_TOKEN_ACCESS_INFORMATION_TYPE = (FWP_TOKEN_INFORMATION_TYPE + 1)
FWP_UNICODE_STRING_TYPE = (FWP_TOKEN_ACCESS_INFORMATION_TYPE + 1)
FWP_BYTE_ARRAY6_TYPE = (FWP_UNICODE_STRING_TYPE + 1)
FWP_BITMAP_INDEX_TYPE = (FWP_BYTE_ARRAY6_TYPE + 1)
FWP_BITMAP_ARRAY64_TYPE = (FWP_BITMAP_INDEX_TYPE + 1)
FWP_SINGLE_DATA_TYPE_MAX = 0xff
FWP_V4_ADDR_MASK = (FWP_SINGLE_DATA_TYPE_MAX + 1)
FWP_V6_ADDR_MASK = (FWP_V4_ADDR_MASK + 1)
FWP_RANGE_TYPE = (FWP_V6_ADDR_MASK + 1)
FWP_DATA_TYPE_MAX = (FWP_RANGE_TYPE + 1)
)
var FWPM_CONDITION_IP_PROTOCOL = windows.GUID{
Data1: 0x3971ef2b,
Data2: 0x623e,
Data3: 0x4f9a,
Data4: [8]byte{0x8c, 0xb1, 0x6e, 0x79, 0xb8, 0x06, 0xb9, 0xa7},
}
var FWPM_CONDITION_IP_REMOTE_PORT = windows.GUID{
Data1: 0xc35a604d,
Data2: 0xd22b,
Data3: 0x4e1a,
Data4: [8]byte{0x91, 0xb4, 0x68, 0xf6, 0x74, 0xee, 0x67, 0x4b},
}
var FWPM_LAYER_ALE_AUTH_CONNECT_V4 = windows.GUID{
Data1: 0xc38d57d1,
Data2: 0x05a7,
Data3: 0x4c33,
Data4: [8]byte{0x90, 0x4f, 0x7f, 0xbc, 0xee, 0xe6, 0x0e, 0x82},
}
var FWPM_CONDITION_LOCAL_INTERFACE_INDEX = windows.GUID{
Data1: 0x667fd755,
Data2: 0xd695,
Data3: 0x434a,
Data4: [8]byte{0x8a, 0xf5, 0xd3, 0x83, 0x5a, 0x12, 0x59, 0xbc},
}
var FWPM_LAYER_ALE_AUTH_CONNECT_V6 = windows.GUID{
Data1: 0x4a72393b,
Data2: 0x319f,
Data3: 0x44bc,
Data4: [8]byte{0x84, 0xc3, 0xba, 0x54, 0xdc, 0xb3, 0xb6, 0xb4},
}
var FWPM_CONDITION_ALE_APP_ID = windows.GUID{
Data1: 0xd78e1e87,
Data2: 0x8644,
Data3: 0x4ea5,
Data4: [8]byte{0x94, 0x37, 0xd8, 0x09, 0xec, 0xef, 0xc9, 0x71},
}
const (
IPPROTO_UDP uint32 = 17
)
const (
FWP_ACTION_FLAG_TERMINATING uint32 = 0x00001000
FWP_ACTION_BLOCK uint32 = (0x00000001 | FWP_ACTION_FLAG_TERMINATING)
FWP_ACTION_PERMIT uint32 = (0x00000002 | FWP_ACTION_FLAG_TERMINATING)
)
const (
FWPM_FILTER_FLAG_NONE = 0x00000000
FWPM_FILTER_FLAG_PERSISTENT = 0x00000001
FWPM_FILTER_FLAG_BOOTTIME = 0x00000002
FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT = 0x00000004
FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT = 0x00000008
FWPM_FILTER_FLAG_PERMIT_IF_CALLOUT_UNREGISTERED = 0x00000010
FWPM_FILTER_FLAG_DISABLED = 0x00000020
FWPM_FILTER_FLAG_INDEXED = 0x00000040
FWPM_FILTER_FLAG_HAS_SECURITY_REALM_PROVIDER_CONTEXT = 0x00000080
FWPM_FILTER_FLAG_SYSTEMOS_ONLY = 0x00000100
FWPM_FILTER_FLAG_GAMEOS_ONLY = 0x00000200
FWPM_FILTER_FLAG_SILENT_MODE = 0x00000400
FWPM_FILTER_FLAG_IPSEC_NO_ACQUIRE_INITIATE = 0x00000800
)

46
internal/winsys/helper.go Normal file
View file

@ -0,0 +1,46 @@
//go:build windows
package winsys
import (
"os"
"unsafe"
"github.com/sagernet/sing/common"
"golang.org/x/sys/windows"
)
func CreateDisplayData(name, description string) FWPM_DISPLAY_DATA0 {
namePtr, err := windows.UTF16PtrFromString(name)
common.Must(err)
descriptionPtr, err := windows.UTF16PtrFromString(description)
common.Must(err)
return FWPM_DISPLAY_DATA0{
Name: namePtr,
Description: descriptionPtr,
}
}
func GetCurrentProcessAppID() (*FWP_BYTE_BLOB, error) {
currentFile, err := os.Executable()
if err != nil {
return nil, err
}
curFilePtr, err := windows.UTF16PtrFromString(currentFile)
if err != nil {
return nil, err
}
windows.GetCurrentProcessId()
var appID *FWP_BYTE_BLOB
err = FwpmGetAppIdFromFileName0(curFilePtr, unsafe.Pointer(&appID))
if err != nil {
return nil, err
}
return appID, nil
}

View file

@ -0,0 +1,5 @@
//go:build windows
package winsys
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go

View file

@ -0,0 +1,19 @@
package winsys
// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmengineopen0
//sys FwpmEngineOpen0(serverName *uint16, authnService uint32, authIdentity *uintptr, session *FWPM_SESSION0, engineHandle unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmEngineOpen0
// https://learn.microsoft.com/en-us/windows/win32/api/fwpmu/nf-fwpmu-fwpmengineclose0
//sys FwpmEngineClose0(engineHandle uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmEngineClose0
// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmsublayeradd0
//sys FwpmSubLayerAdd0(engineHandle uintptr, subLayer *FWPM_SUBLAYER0, sd uintptr) (err error) [failretval!=0] = fwpuclnt.FwpmSubLayerAdd0
// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfilteradd0
//sys FwpmFilterAdd0(engineHandle uintptr, filter *FWPM_FILTER0, sd uintptr, id *uint64) (err error) [failretval!=0] = fwpuclnt.FwpmFilterAdd0
// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmgetappidfromfilename0
//sys FwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) [failretval!=0] = fwpuclnt.FwpmGetAppIdFromFileName0
// https://docs.microsoft.com/en-us/windows/desktop/api/fwpmu/nf-fwpmu-fwpmfreememory0
//sys FwpmFreeMemory0(p unsafe.Pointer) = fwpuclnt.FwpmFreeMemory0

View file

@ -0,0 +1,99 @@
package winsys
import (
"golang.org/x/sys/windows"
)
type (
BOOL int32
HANDLE uintptr
DWORD uint32
PDWORD uintptr
ULONG uint32
ULONG_PTR uintptr
HMODULE HANDLE
)
type MIB_IPFORWARDROW struct {
ForwardDest uint32
ForwardMask uint32
ForwardPolicy uint32
ForwardNextHop uint32
ForwardIfIndex uint32
ForwardType uint32
ForwardProto uint32
ForwardAge uint32
ForwardNextHopAS uint32
ForwardMetric1 uint32
ForwardMetric2 uint32
ForwardMetric3 uint32
ForwardMetric4 uint32
ForwardMetric5 uint32
}
type FWPM_DISPLAY_DATA0 struct {
Name *uint16
Description *uint16
}
type FWPM_SESSION0 struct {
SessionKey windows.GUID
DisplayData FWPM_DISPLAY_DATA0
Flags uint32
TxnWaitTimeoutInMSec uint32
ProcessId uint32
Sid *windows.SID
Username *uint16
KernelMode int32
}
type FWP_BYTE_BLOB struct {
size uint32
data *uint8
}
type FWPM_SUBLAYER0 struct {
SubLayerKey windows.GUID // Windows type: GUID
DisplayData FWPM_DISPLAY_DATA0
Flags uint32
ProviderKey *windows.GUID // Windows type: *GUID
ProviderData FWP_BYTE_BLOB
Weight uint16
}
type FWP_VALUE0 struct {
Type uint32
Value uintptr
}
type FWP_CONDITION_VALUE0 FWP_VALUE0
type FWPM_FILTER_CONDITION0 struct {
FieldKey windows.GUID // Windows type: GUID
MatchType uint32
ConditionValue FWP_CONDITION_VALUE0
}
type FWPM_ACTION0 struct {
Type uint32
Value windows.GUID
}
type FWPM_FILTER0 struct {
FilterKey windows.GUID
DisplayData FWPM_DISPLAY_DATA0
Flags uint32
ProviderKey *windows.GUID
ProviderData FWP_BYTE_BLOB
LayerKey windows.GUID
SubLayerKey windows.GUID
Weight FWP_VALUE0
NumFilterConditions uint32
FilterCondition *FWPM_FILTER_CONDITION0
Action FWPM_ACTION0
Offset1 [4]byte
Context windows.GUID
Reserved *windows.GUID
FilterId uint64
EffectiveWeight FWP_VALUE0
}

View file

@ -0,0 +1,94 @@
// Code generated by 'go generate'; DO NOT EDIT.
package winsys
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
// Do the interface allocations only once for common
// Errno values.
const (
errnoERROR_IO_PENDING = 997
)
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
// errnoErr returns common boxed Errno values, to prevent
// allocations at runtime.
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
// TODO: add more here, after collecting data on the common
// error values see on Windows. (perhaps when running
// all.bat?)
return e
}
var (
modfwpuclnt = windows.NewLazySystemDLL("fwpuclnt.dll")
procFwpmEngineClose0 = modfwpuclnt.NewProc("FwpmEngineClose0")
procFwpmEngineOpen0 = modfwpuclnt.NewProc("FwpmEngineOpen0")
procFwpmFilterAdd0 = modfwpuclnt.NewProc("FwpmFilterAdd0")
procFwpmFreeMemory0 = modfwpuclnt.NewProc("FwpmFreeMemory0")
procFwpmGetAppIdFromFileName0 = modfwpuclnt.NewProc("FwpmGetAppIdFromFileName0")
procFwpmSubLayerAdd0 = modfwpuclnt.NewProc("FwpmSubLayerAdd0")
)
func FwpmEngineClose0(engineHandle uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procFwpmEngineClose0.Addr(), 1, uintptr(engineHandle), 0, 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func FwpmEngineOpen0(serverName *uint16, authnService uint32, authIdentity *uintptr, session *FWPM_SESSION0, engineHandle unsafe.Pointer) (err error) {
r1, _, e1 := syscall.Syscall6(procFwpmEngineOpen0.Addr(), 5, uintptr(unsafe.Pointer(serverName)), uintptr(authnService), uintptr(unsafe.Pointer(authIdentity)), uintptr(unsafe.Pointer(session)), uintptr(engineHandle), 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func FwpmFilterAdd0(engineHandle uintptr, filter *FWPM_FILTER0, sd uintptr, id *uint64) (err error) {
r1, _, e1 := syscall.Syscall6(procFwpmFilterAdd0.Addr(), 4, uintptr(engineHandle), uintptr(unsafe.Pointer(filter)), uintptr(sd), uintptr(unsafe.Pointer(id)), 0, 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func FwpmFreeMemory0(p unsafe.Pointer) {
syscall.Syscall(procFwpmFreeMemory0.Addr(), 1, uintptr(p), 0, 0)
return
}
func FwpmGetAppIdFromFileName0(fileName *uint16, appID unsafe.Pointer) (err error) {
r1, _, e1 := syscall.Syscall(procFwpmGetAppIdFromFileName0.Addr(), 2, uintptr(unsafe.Pointer(fileName)), uintptr(appID), 0)
if r1 != 0 {
err = errnoErr(e1)
}
return
}
func FwpmSubLayerAdd0(engineHandle uintptr, subLayer *FWPM_SUBLAYER0, sd uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procFwpmSubLayerAdd0.Addr(), 3, uintptr(engineHandle), uintptr(unsafe.Pointer(subLayer)), uintptr(sd))
if r1 != 0 {
err = errnoErr(e1)
}
return
}

View file

@ -4,6 +4,8 @@ import (
"crypto/md5"
"errors"
"fmt"
"math"
"net"
"net/netip"
"os"
"sync"
@ -12,8 +14,10 @@ import (
"unsafe"
"github.com/sagernet/sing-tun/internal/winipcfg"
"github.com/sagernet/sing-tun/internal/winsys"
"github.com/sagernet/sing-tun/internal/wintun"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/windnsapi"
"golang.org/x/sys/windows"
)
@ -21,14 +25,15 @@ import (
var TunnelType = "sing-tun"
type NativeTun struct {
adapter *wintun.Adapter
options Options
session wintun.Session
readWait windows.Handle
rate rateJuggler
running sync.WaitGroup
closeOnce sync.Once
close int32
adapter *wintun.Adapter
options Options
session wintun.Session
readWait windows.Handle
rate rateJuggler
running sync.WaitGroup
closeOnce sync.Once
close int32
fwpmSession uintptr
}
func Open(options Options) (WinTun, error) {
@ -78,16 +83,41 @@ func (t *NativeTun) configure() error {
}
}
if t.options.AutoRoute {
if len(t.options.Inet4Address) > 0 {
err := luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv4 route")
if t.options.StrictRoute {
if len(t.options.Inet4Address) > 0 {
for _, prefix := range []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/1"),
netip.MustParsePrefix("128.0.0.0/1"),
} {
err := luid.AddRoute(prefix, netip.IPv4Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv4 route")
}
}
}
}
if len(t.options.Inet6Address) > 0 {
err := luid.AddRoute(netip.PrefixFrom(netip.IPv6Unspecified(), 0), netip.IPv6Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv6 route")
if len(t.options.Inet6Address) > 0 {
for _, prefix := range []netip.Prefix{
netip.MustParsePrefix("::/1"),
netip.MustParsePrefix("8000::/1"),
} {
err := luid.AddRoute(prefix, netip.IPv6Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv6 route")
}
}
}
} else {
if len(t.options.Inet4Address) > 0 {
err := luid.AddRoute(netip.PrefixFrom(netip.IPv4Unspecified(), 0), netip.IPv4Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv4 route")
}
}
if len(t.options.Inet6Address) > 0 {
err := luid.AddRoute(netip.PrefixFrom(netip.IPv6Unspecified(), 0), netip.IPv6Unspecified(), 0)
if err != nil {
return E.Cause(err, "set ipv6 route")
}
}
}
}
@ -131,6 +161,187 @@ func (t *NativeTun) configure() error {
}
}
if t.options.AutoRoute {
var engine uintptr
session := &winsys.FWPM_SESSION0{Flags: winsys.FWPM_SESSION_FLAG_DYNAMIC}
err := winsys.FwpmEngineOpen0(nil, winsys.RPC_C_AUTHN_DEFAULT, nil, session, unsafe.Pointer(&engine))
if err != nil {
return os.NewSyscallError("FwpmEngineOpen0", err)
}
t.fwpmSession = engine
subLayerKey, err := windows.GenerateGUID()
if err != nil {
return os.NewSyscallError("CoCreateGuid", err)
}
subLayer := winsys.FWPM_SUBLAYER0{}
subLayer.SubLayerKey = subLayerKey
subLayer.DisplayData = winsys.CreateDisplayData("sing-box", "auto-route rules")
subLayer.Weight = math.MaxUint16
err = winsys.FwpmSubLayerAdd0(engine, &subLayer, 0)
if err != nil {
return os.NewSyscallError("FwpmSubLayerAdd0", err)
}
processAppID, err := winsys.GetCurrentProcessAppID()
if err != nil {
return err
}
defer winsys.FwpmFreeMemory0(unsafe.Pointer(&processAppID))
var filterId uint64
permitCondition := make([]winsys.FWPM_FILTER_CONDITION0, 1)
permitCondition[0].FieldKey = winsys.FWPM_CONDITION_ALE_APP_ID
permitCondition[0].MatchType = winsys.FWP_MATCH_EQUAL
permitCondition[0].ConditionValue.Type = winsys.FWP_BYTE_BLOB_TYPE
permitCondition[0].ConditionValue.Value = uintptr(unsafe.Pointer(processAppID))
permitFilter4 := winsys.FWPM_FILTER0{}
permitFilter4.FilterCondition = &permitCondition[0]
permitFilter4.NumFilterConditions = 1
permitFilter4.DisplayData = winsys.CreateDisplayData("sing-box", "protect ipv4")
permitFilter4.SubLayerKey = subLayerKey
permitFilter4.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V4
permitFilter4.Action.Type = winsys.FWP_ACTION_PERMIT
permitFilter4.Weight.Type = winsys.FWP_UINT8
permitFilter4.Weight.Value = uintptr(12)
permitFilter4.Flags = winsys.FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT
err = winsys.FwpmFilterAdd0(engine, &permitFilter4, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
permitFilter6 := winsys.FWPM_FILTER0{}
permitFilter6.FilterCondition = &permitCondition[0]
permitFilter6.NumFilterConditions = 1
permitFilter6.DisplayData = winsys.CreateDisplayData("sing-box", "protect ipv6")
permitFilter6.SubLayerKey = subLayerKey
permitFilter6.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V6
permitFilter6.Action.Type = winsys.FWP_ACTION_PERMIT
permitFilter6.Weight.Type = winsys.FWP_UINT8
permitFilter6.Weight.Value = uintptr(12)
permitFilter6.Flags = winsys.FWPM_FILTER_FLAG_CLEAR_ACTION_RIGHT
err = winsys.FwpmFilterAdd0(engine, &permitFilter6, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
if len(t.options.Inet4Address) == 0 {
blockFilter := winsys.FWPM_FILTER0{}
blockFilter.DisplayData = winsys.CreateDisplayData("sing-box", "block ipv4")
blockFilter.SubLayerKey = subLayerKey
blockFilter.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V4
blockFilter.Action.Type = winsys.FWP_ACTION_BLOCK
blockFilter.Weight.Type = winsys.FWP_UINT8
blockFilter.Weight.Value = uintptr(13)
err = winsys.FwpmFilterAdd0(engine, &blockFilter, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
}
if len(t.options.Inet6Address) == 0 {
blockFilter := winsys.FWPM_FILTER0{}
blockFilter.DisplayData = winsys.CreateDisplayData("sing-box", "block ipv6")
blockFilter.SubLayerKey = subLayerKey
blockFilter.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V6
blockFilter.Action.Type = winsys.FWP_ACTION_BLOCK
blockFilter.Weight.Type = winsys.FWP_UINT8
blockFilter.Weight.Value = uintptr(13)
err = winsys.FwpmFilterAdd0(engine, &blockFilter, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
}
netInterface, err := net.InterfaceByName(t.options.Name)
if err != nil {
return err
}
tunCondition := make([]winsys.FWPM_FILTER_CONDITION0, 1)
tunCondition[0].FieldKey = winsys.FWPM_CONDITION_LOCAL_INTERFACE_INDEX
tunCondition[0].MatchType = winsys.FWP_MATCH_EQUAL
tunCondition[0].ConditionValue.Type = winsys.FWP_UINT32
tunCondition[0].ConditionValue.Value = uintptr(uint32(netInterface.Index))
if len(t.options.Inet4Address) > 0 {
tunFilter4 := winsys.FWPM_FILTER0{}
tunFilter4.FilterCondition = &tunCondition[0]
tunFilter4.NumFilterConditions = 1
tunFilter4.DisplayData = winsys.CreateDisplayData("sing-box", "allow ipv4")
tunFilter4.SubLayerKey = subLayerKey
tunFilter4.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V4
tunFilter4.Action.Type = winsys.FWP_ACTION_PERMIT
tunFilter4.Weight.Type = winsys.FWP_UINT8
tunFilter4.Weight.Value = uintptr(11)
err = winsys.FwpmFilterAdd0(engine, &tunFilter4, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
}
if len(t.options.Inet6Address) > 0 {
tunFilter6 := winsys.FWPM_FILTER0{}
tunFilter6.FilterCondition = &tunCondition[0]
tunFilter6.NumFilterConditions = 1
tunFilter6.DisplayData = winsys.CreateDisplayData("sing-box", "allow ipv6")
tunFilter6.SubLayerKey = subLayerKey
tunFilter6.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V6
tunFilter6.Action.Type = winsys.FWP_ACTION_PERMIT
tunFilter6.Weight.Type = winsys.FWP_UINT8
tunFilter6.Weight.Value = uintptr(11)
err = winsys.FwpmFilterAdd0(engine, &tunFilter6, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
}
blockDNSCondition := make([]winsys.FWPM_FILTER_CONDITION0, 2)
blockDNSCondition[0].FieldKey = winsys.FWPM_CONDITION_IP_PROTOCOL
blockDNSCondition[0].MatchType = winsys.FWP_MATCH_EQUAL
blockDNSCondition[0].ConditionValue.Type = winsys.FWP_UINT8
blockDNSCondition[0].ConditionValue.Value = uintptr(uint8(winsys.IPPROTO_UDP))
blockDNSCondition[1].FieldKey = winsys.FWPM_CONDITION_IP_REMOTE_PORT
blockDNSCondition[1].MatchType = winsys.FWP_MATCH_EQUAL
blockDNSCondition[1].ConditionValue.Type = winsys.FWP_UINT16
blockDNSCondition[1].ConditionValue.Value = uintptr(uint16(53))
blockDNSFilter4 := winsys.FWPM_FILTER0{}
blockDNSFilter4.FilterCondition = &blockDNSCondition[0]
blockDNSFilter4.NumFilterConditions = 2
blockDNSFilter4.DisplayData = winsys.CreateDisplayData("sing-box", "block ipv4 dns")
blockDNSFilter4.SubLayerKey = subLayerKey
blockDNSFilter4.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V4
blockDNSFilter4.Action.Type = winsys.FWP_ACTION_BLOCK
blockDNSFilter4.Weight.Type = winsys.FWP_UINT8
blockDNSFilter4.Weight.Value = uintptr(10)
err = winsys.FwpmFilterAdd0(engine, &blockDNSFilter4, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
blockDNSFilter6 := winsys.FWPM_FILTER0{}
blockDNSFilter6.FilterCondition = &blockDNSCondition[0]
blockDNSFilter6.NumFilterConditions = 2
blockDNSFilter6.DisplayData = winsys.CreateDisplayData("sing-box", "block ipv6 dns")
blockDNSFilter6.SubLayerKey = subLayerKey
blockDNSFilter6.LayerKey = winsys.FWPM_LAYER_ALE_AUTH_CONNECT_V6
blockDNSFilter6.Action.Type = winsys.FWP_ACTION_BLOCK
blockDNSFilter6.Weight.Type = winsys.FWP_UINT8
blockDNSFilter6.Weight.Value = uintptr(10)
err = winsys.FwpmFilterAdd0(engine, &blockDNSFilter6, 0, &filterId)
if err != nil {
return os.NewSyscallError("FwpmFilterAdd0", err)
}
err = windnsapi.FlushResolverCache()
if err != nil {
return err
}
}
return nil
}
@ -269,6 +480,10 @@ func (t *NativeTun) Close() error {
t.running.Wait()
t.session.End()
t.adapter.Close()
if t.fwpmSession != 0 {
winsys.FwpmEngineClose0(t.fwpmSession)
}
windnsapi.FlushResolverCache()
})
return err
}