mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-05-08 14:13:22 +00:00
TUN inbound: Add gateway, dns, autoSystemRoutingTable, autoOutboundsInterface for Windows (#5887)
And refactor `mtu` to support setting IPv4/v6 separately Example: https://github.com/XTLS/Xray-core/pull/5887#issue-4198837696
This commit is contained in:
@@ -1,12 +1,21 @@
|
|||||||
package localdns
|
package localdns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is an implementation of dns.Client, which queries localhost for DNS.
|
// Client is an implementation of dns.Client, which queries localhost for DNS.
|
||||||
type Client struct{}
|
type Client struct {
|
||||||
|
d *net.Dialer
|
||||||
|
r *net.Resolver
|
||||||
|
}
|
||||||
|
|
||||||
// Type implements common.HasType.
|
// Type implements common.HasType.
|
||||||
func (*Client) Type() interface{} {
|
func (*Client) Type() interface{} {
|
||||||
@@ -20,8 +29,14 @@ func (*Client) Start() error { return nil }
|
|||||||
func (*Client) Close() error { return nil }
|
func (*Client) Close() error { return nil }
|
||||||
|
|
||||||
// LookupIP implements Client.
|
// LookupIP implements Client.
|
||||||
func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, error) {
|
func (c *Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, error) {
|
||||||
ips, err := net.LookupIP(host)
|
var ips []net.IP
|
||||||
|
var err error
|
||||||
|
if len(internet.Controllers) > 0 {
|
||||||
|
ips, err = c.r.LookupIP(context.Background(), "ip", host)
|
||||||
|
} else {
|
||||||
|
ips, err = net.LookupIP(host)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
@@ -62,5 +77,28 @@ func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, err
|
|||||||
|
|
||||||
// New create a new dns.Client that queries localhost for DNS.
|
// New create a new dns.Client that queries localhost for DNS.
|
||||||
func New() *Client {
|
func New() *Client {
|
||||||
return &Client{}
|
d := &net.Dialer{
|
||||||
|
Timeout: time.Second * 16,
|
||||||
|
Control: func(network, address string, c syscall.RawConn) error {
|
||||||
|
for _, ctl := range internet.Controllers {
|
||||||
|
if err := ctl(network, address, c); err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "failed to apply external controller")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
return d.DialContext(ctx, network, address)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
d: d,
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -27,6 +27,7 @@ require (
|
|||||||
golang.org/x/sys v0.43.0
|
golang.org/x/sys v0.43.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||||
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
google.golang.org/grpc v1.80.0
|
google.golang.org/grpc v1.80.0
|
||||||
google.golang.org/protobuf v1.36.11
|
google.golang.org/protobuf v1.36.11
|
||||||
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -131,6 +131,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
|||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
||||||
|
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||||
|
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
||||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
|
||||||
|
|||||||
@@ -6,25 +6,39 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TunConfig struct {
|
type TunConfig struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
MTU uint32 `json:"MTU"`
|
MTU []uint32 `json:"mtu"`
|
||||||
UserLevel uint32 `json:"userLevel"`
|
Gateway []string `json:"gateway"`
|
||||||
|
DNS []string `json:"dns"`
|
||||||
|
UserLevel uint32 `json:"userLevel"`
|
||||||
|
AutoSystemRoutingTable []string `json:"autoSystemRoutingTable"`
|
||||||
|
AutoOutboundsInterface *string `json:"autoOutboundsInterface"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TunConfig) Build() (proto.Message, error) {
|
func (v *TunConfig) Build() (proto.Message, error) {
|
||||||
config := &tun.Config{
|
config := &tun.Config{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
MTU: v.MTU,
|
MTU: v.MTU,
|
||||||
UserLevel: v.UserLevel,
|
Gateway: v.Gateway,
|
||||||
|
DNS: v.DNS,
|
||||||
|
UserLevel: v.UserLevel,
|
||||||
|
AutoSystemRoutingTable: v.AutoSystemRoutingTable,
|
||||||
|
}
|
||||||
|
if v.AutoOutboundsInterface != nil {
|
||||||
|
config.AutoOutboundsInterface = *v.AutoOutboundsInterface
|
||||||
|
}
|
||||||
|
if len(v.AutoSystemRoutingTable) > 0 && v.AutoOutboundsInterface == nil {
|
||||||
|
config.AutoOutboundsInterface = "auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Name == "" {
|
if config.Name == "" {
|
||||||
config.Name = "xray0"
|
config.Name = "xray0"
|
||||||
}
|
}
|
||||||
|
if len(config.MTU) == 0 {
|
||||||
if v.MTU == 0 {
|
config.MTU = []uint32{1500, 1280}
|
||||||
config.MTU = 1500
|
}
|
||||||
|
if len(config.MTU) == 1 {
|
||||||
|
config.MTU = append(config.MTU, config.MTU[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,78 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InterfaceUpdater struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
tunIndex int
|
||||||
|
fixedName string
|
||||||
|
iface *net.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
var updater *InterfaceUpdater
|
||||||
|
|
||||||
|
func (updater *InterfaceUpdater) Get() *net.Interface {
|
||||||
|
updater.Lock()
|
||||||
|
defer updater.Unlock()
|
||||||
|
|
||||||
|
return updater.iface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (updater *InterfaceUpdater) Update() {
|
||||||
|
updater.Lock()
|
||||||
|
defer updater.Unlock()
|
||||||
|
|
||||||
|
if updater.iface != nil {
|
||||||
|
iface, err := net.InterfaceByIndex(updater.iface.Index)
|
||||||
|
if err == nil && iface.Name == updater.iface.Name {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updater.iface = nil
|
||||||
|
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "[tun] failed to update interface")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var got *net.Interface
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.Index == updater.tunIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if updater.fixedName != "" {
|
||||||
|
if iface.Name == updater.fixedName {
|
||||||
|
got = &iface
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (iface.Flags&net.FlagUp != 0) &&
|
||||||
|
(iface.Flags&net.FlagLoopback == 0) &&
|
||||||
|
len(addrs) > 0 {
|
||||||
|
got = &iface
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if got == nil {
|
||||||
|
errors.LogInfo(context.Background(), "[tun] failed to update interface > got == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updater.iface = got
|
||||||
|
errors.LogInfo(context.Background(), "[tun] update interface ", got.Name, " ", got.Index)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,12 +22,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||||
MTU uint32 `protobuf:"varint,2,opt,name=MTU,proto3" json:"MTU,omitempty"`
|
MTU []uint32 `protobuf:"varint,2,rep,packed,name=MTU,proto3" json:"MTU,omitempty"`
|
||||||
UserLevel uint32 `protobuf:"varint,3,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
Gateway []string `protobuf:"bytes,3,rep,name=gateway,proto3" json:"gateway,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
DNS []string `protobuf:"bytes,4,rep,name=DNS,proto3" json:"DNS,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
UserLevel uint32 `protobuf:"varint,5,opt,name=user_level,json=userLevel,proto3" json:"user_level,omitempty"`
|
||||||
|
AutoSystemRoutingTable []string `protobuf:"bytes,6,rep,name=auto_system_routing_table,json=autoSystemRoutingTable,proto3" json:"auto_system_routing_table,omitempty"`
|
||||||
|
AutoOutboundsInterface string `protobuf:"bytes,7,opt,name=auto_outbounds_interface,json=autoOutboundsInterface,proto3" json:"auto_outbounds_interface,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -67,11 +71,25 @@ func (x *Config) GetName() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetMTU() uint32 {
|
func (x *Config) GetMTU() []uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.MTU
|
return x.MTU
|
||||||
}
|
}
|
||||||
return 0
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetGateway() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Gateway
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetDNS() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DNS
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetUserLevel() uint32 {
|
func (x *Config) GetUserLevel() uint32 {
|
||||||
@@ -81,16 +99,34 @@ func (x *Config) GetUserLevel() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetAutoSystemRoutingTable() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AutoSystemRoutingTable
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetAutoOutboundsInterface() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AutoOutboundsInterface
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_tun_config_proto protoreflect.FileDescriptor
|
var File_proxy_tun_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_proxy_tun_config_proto_rawDesc = "" +
|
const file_proxy_tun_config_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\x16proxy/tun/config.proto\x12\x0exray.proxy.tun\"M\n" +
|
"\x16proxy/tun/config.proto\x12\x0exray.proxy.tun\"\xee\x01\n" +
|
||||||
"\x06Config\x12\x12\n" +
|
"\x06Config\x12\x12\n" +
|
||||||
"\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" +
|
"\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" +
|
||||||
"\x03MTU\x18\x02 \x01(\rR\x03MTU\x12\x1d\n" +
|
"\x03MTU\x18\x02 \x03(\rR\x03MTU\x12\x18\n" +
|
||||||
|
"\agateway\x18\x03 \x03(\tR\agateway\x12\x10\n" +
|
||||||
|
"\x03DNS\x18\x04 \x03(\tR\x03DNS\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"user_level\x18\x03 \x01(\rR\tuserLevelBL\n" +
|
"user_level\x18\x05 \x01(\rR\tuserLevel\x129\n" +
|
||||||
|
"\x19auto_system_routing_table\x18\x06 \x03(\tR\x16autoSystemRoutingTable\x128\n" +
|
||||||
|
"\x18auto_outbounds_interface\x18\a \x01(\tR\x16autoOutboundsInterfaceBL\n" +
|
||||||
"\x12com.xray.proxy.tunP\x01Z#github.com/xtls/xray-core/proxy/tun\xaa\x02\x0eXray.Proxy.Tunb\x06proto3"
|
"\x12com.xray.proxy.tunP\x01Z#github.com/xtls/xray-core/proxy/tun\xaa\x02\x0eXray.Proxy.Tunb\x06proto3"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ option java_multiple_files = true;
|
|||||||
|
|
||||||
message Config {
|
message Config {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
uint32 MTU = 2;
|
repeated uint32 MTU = 2;
|
||||||
uint32 user_level = 3;
|
repeated string gateway = 3;
|
||||||
|
repeated string DNS = 4;
|
||||||
|
uint32 user_level = 5;
|
||||||
|
repeated string auto_system_routing_table = 6;
|
||||||
|
string auto_outbounds_interface = 7;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package tun
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/features/policy"
|
"github.com/xtls/xray-core/features/policy"
|
||||||
"github.com/xtls/xray-core/features/routing"
|
"github.com/xtls/xray-core/features/routing"
|
||||||
"github.com/xtls/xray-core/transport"
|
"github.com/xtls/xray-core/transport"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
"github.com/xtls/xray-core/transport/internet/stat"
|
"github.com/xtls/xray-core/transport/internet/stat"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,11 +40,6 @@ type ConnectionHandler interface {
|
|||||||
// Handler implements ConnectionHandler
|
// Handler implements ConnectionHandler
|
||||||
var _ ConnectionHandler = (*Handler)(nil)
|
var _ ConnectionHandler = (*Handler)(nil)
|
||||||
|
|
||||||
func (t *Handler) policy() policy.Session {
|
|
||||||
p := t.policyManager.ForLevel(t.config.UserLevel)
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the Handler instance with necessary parameters
|
// Init the Handler instance with necessary parameters
|
||||||
func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routing.Dispatcher) error {
|
func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routing.Dispatcher) error {
|
||||||
var err error
|
var err error
|
||||||
@@ -60,15 +57,37 @@ func (t *Handler) Init(ctx context.Context, pm policy.Manager, dispatcher routin
|
|||||||
t.dispatcher = dispatcher
|
t.dispatcher = dispatcher
|
||||||
|
|
||||||
tunName := t.config.Name
|
tunName := t.config.Name
|
||||||
tunOptions := TunOptions{
|
tunInterface, err := NewTun(t.config)
|
||||||
Name: tunName,
|
|
||||||
MTU: t.config.MTU,
|
|
||||||
}
|
|
||||||
tunInterface, err := NewTun(tunOptions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.config.AutoOutboundsInterface != "" {
|
||||||
|
tunIndex, err := tunInterface.Index()
|
||||||
|
if err != nil {
|
||||||
|
_ = tunInterface.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if t.config.AutoOutboundsInterface == "auto" {
|
||||||
|
t.config.AutoOutboundsInterface = ""
|
||||||
|
}
|
||||||
|
updater = &InterfaceUpdater{tunIndex: tunIndex, fixedName: t.config.AutoOutboundsInterface}
|
||||||
|
updater.Update()
|
||||||
|
internet.RegisterDialerController(func(network, address string, c syscall.RawConn) error {
|
||||||
|
iface := updater.Get()
|
||||||
|
if iface == nil {
|
||||||
|
errors.LogInfo(context.Background(), "[tun] falied to set interface > iface == nil")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.Control(func(fd uintptr) {
|
||||||
|
err := setinterface(network, address, fd, iface)
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "[tun] falied to set interface")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
errors.LogInfo(t.ctx, tunName, " created")
|
errors.LogInfo(t.ctx, tunName, " created")
|
||||||
|
|
||||||
tunStackOptions := StackOptions{
|
tunStackOptions := StackOptions{
|
||||||
|
|||||||
@@ -34,23 +34,18 @@ const (
|
|||||||
// stackGVisor is ip stack implemented by gVisor package
|
// stackGVisor is ip stack implemented by gVisor package
|
||||||
type stackGVisor struct {
|
type stackGVisor struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
tun GVisorTun
|
tun Tun
|
||||||
idleTimeout time.Duration
|
idleTimeout time.Duration
|
||||||
handler *Handler
|
handler *Handler
|
||||||
stack *stack.Stack
|
stack *stack.Stack
|
||||||
endpoint stack.LinkEndpoint
|
endpoint stack.LinkEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// GVisorTun implements a bridge to connect gVisor ip stack to tun interface
|
|
||||||
type GVisorTun interface {
|
|
||||||
newEndpoint() (stack.LinkEndpoint, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStack builds new ip stack (using gVisor)
|
// NewStack builds new ip stack (using gVisor)
|
||||||
func NewStack(ctx context.Context, options StackOptions, handler *Handler) (Stack, error) {
|
func NewStack(ctx context.Context, options StackOptions, handler *Handler) (Stack, error) {
|
||||||
gStack := &stackGVisor{
|
gStack := &stackGVisor{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
tun: options.Tun.(GVisorTun),
|
tun: options.Tun,
|
||||||
idleTimeout: options.IdleTimeout,
|
idleTimeout: options.IdleTimeout,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
|
import "gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
|
||||||
// Tun interface implements tun interface interaction
|
// Tun interface implements tun interface interaction
|
||||||
type Tun interface {
|
type Tun interface {
|
||||||
Start() error
|
Start() error
|
||||||
Close() error
|
Close() error
|
||||||
}
|
Name() (string, error)
|
||||||
|
Index() (int, error)
|
||||||
// TunOptions for tun interface implementation
|
newEndpoint() (stack.LinkEndpoint, error)
|
||||||
type TunOptions struct {
|
|
||||||
Name string
|
|
||||||
MTU uint32
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package tun
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
@@ -15,17 +16,14 @@ import (
|
|||||||
|
|
||||||
type AndroidTun struct {
|
type AndroidTun struct {
|
||||||
tunFd int
|
tunFd int
|
||||||
options TunOptions
|
options *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultTun implements Tun
|
// DefaultTun implements Tun
|
||||||
var _ Tun = (*AndroidTun)(nil)
|
var _ Tun = (*AndroidTun)(nil)
|
||||||
|
|
||||||
// DefaultTun implements GVisorTun
|
|
||||||
var _ GVisorTun = (*AndroidTun)(nil)
|
|
||||||
|
|
||||||
// NewTun builds new tun interface handler
|
// NewTun builds new tun interface handler
|
||||||
func NewTun(options TunOptions) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
fd, err := strconv.Atoi(platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "0" }))
|
fd, err := strconv.Atoi(platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "0" }))
|
||||||
errors.LogInfo(context.Background(), "read Android Tun Fd ", fd, err)
|
errors.LogInfo(context.Background(), "read Android Tun Fd ", fd, err)
|
||||||
|
|
||||||
@@ -49,10 +47,37 @@ func (t *AndroidTun) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *AndroidTun) Name() (string, error) {
|
||||||
|
ifr, err := unix.NewIfreq("")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if err = unix.IoctlIfreq(t.tunFd, unix.TUNGETIFF, ifr); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ifr.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AndroidTun) Index() (int, error) {
|
||||||
|
name, err := t.Name()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return iface.Index, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *AndroidTun) newEndpoint() (stack.LinkEndpoint, error) {
|
func (t *AndroidTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||||
return fdbased.New(&fdbased.Options{
|
return fdbased.New(&fdbased.Options{
|
||||||
FDs: []int{t.tunFd},
|
FDs: []int{t.tunFd},
|
||||||
MTU: t.options.MTU,
|
MTU: t.options.MTU[0],
|
||||||
RXChecksumOffload: true,
|
RXChecksumOffload: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setinterface(network, address string, fd uintptr, iface *net.Interface) error {
|
||||||
|
return unix.BindToDevice(int(fd), iface.Name)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
go_errors "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/buf"
|
"github.com/xtls/xray-core/common/buf"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/platform"
|
"github.com/xtls/xray-core/common/platform"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"gvisor.dev/gvisor/pkg/buffer"
|
"gvisor.dev/gvisor/pkg/buffer"
|
||||||
@@ -25,6 +25,7 @@ const (
|
|||||||
sysprotoControl = 2
|
sysprotoControl = 2
|
||||||
gateway = "169.254.10.1/30"
|
gateway = "169.254.10.1/30"
|
||||||
utunHeaderSize = 4
|
utunHeaderSize = 4
|
||||||
|
UTUN_OPT_IFNAME = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,15 +40,15 @@ func procyield(cycles uint32)
|
|||||||
|
|
||||||
type DarwinTun struct {
|
type DarwinTun struct {
|
||||||
tunFile *os.File
|
tunFile *os.File
|
||||||
options TunOptions
|
options *Config
|
||||||
|
tunFd int
|
||||||
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
|
ownsFd bool // true for macOS (we created the fd), false for iOS (fd from system)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Tun = (*DarwinTun)(nil)
|
var _ Tun = (*DarwinTun)(nil)
|
||||||
var _ GVisorTun = (*DarwinTun)(nil)
|
|
||||||
var _ GVisorDevice = (*DarwinTun)(nil)
|
var _ GVisorDevice = (*DarwinTun)(nil)
|
||||||
|
|
||||||
func NewTun(options TunOptions) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
// Check if fd is provided via environment (iOS mode)
|
// Check if fd is provided via environment (iOS mode)
|
||||||
fdStr := platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "" })
|
fdStr := platform.NewEnvFlag(platform.TunFdKey).GetValue(func() string { return "" })
|
||||||
if fdStr != "" {
|
if fdStr != "" {
|
||||||
@@ -64,6 +65,7 @@ func NewTun(options TunOptions) (Tun, error) {
|
|||||||
return &DarwinTun{
|
return &DarwinTun{
|
||||||
tunFile: os.NewFile(uintptr(fd), "utun"),
|
tunFile: os.NewFile(uintptr(fd), "utun"),
|
||||||
options: options,
|
options: options,
|
||||||
|
tunFd: fd,
|
||||||
ownsFd: false,
|
ownsFd: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -74,7 +76,7 @@ func NewTun(options TunOptions) (Tun, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = setup(options.Name, options.MTU)
|
err = setup(options.Name, options.MTU[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunFile.Close()
|
_ = tunFile.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -83,6 +85,7 @@ func NewTun(options TunOptions) (Tun, error) {
|
|||||||
return &DarwinTun{
|
return &DarwinTun{
|
||||||
tunFile: tunFile,
|
tunFile: tunFile,
|
||||||
options: options,
|
options: options,
|
||||||
|
tunFd: int(tunFile.Fd()),
|
||||||
ownsFd: true,
|
ownsFd: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -99,10 +102,26 @@ func (t *DarwinTun) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *DarwinTun) Name() (string, error) {
|
||||||
|
return unix.GetsockoptString(t.tunFd, sysprotoControl, UTUN_OPT_IFNAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DarwinTun) Index() (int, error) {
|
||||||
|
name, err := t.Name()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return iface.Index, nil
|
||||||
|
}
|
||||||
|
|
||||||
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
||||||
func (t *DarwinTun) WritePacket(packet *stack.PacketBuffer) tcpip.Error {
|
func (t *DarwinTun) WritePacket(packet *stack.PacketBuffer) tcpip.Error {
|
||||||
// request memory to write from reusable buffer pool
|
// request memory to write from reusable buffer pool
|
||||||
b := buf.NewWithSize(int32(t.options.MTU) + utunHeaderSize)
|
b := buf.NewWithSize(int32(t.options.MTU[0]) + utunHeaderSize)
|
||||||
defer b.Release()
|
defer b.Release()
|
||||||
|
|
||||||
// prepare Darwin specific packet header
|
// prepare Darwin specific packet header
|
||||||
@@ -124,7 +143,7 @@ func (t *DarwinTun) WritePacket(packet *stack.PacketBuffer) tcpip.Error {
|
|||||||
b.SetByte(3, family)
|
b.SetByte(3, family)
|
||||||
|
|
||||||
if _, err := t.tunFile.Write(b.Bytes()); err != nil {
|
if _, err := t.tunFile.Write(b.Bytes()); err != nil {
|
||||||
if errors.Is(err, unix.EAGAIN) {
|
if go_errors.Is(err, unix.EAGAIN) {
|
||||||
return &tcpip.ErrWouldBlock{}
|
return &tcpip.ErrWouldBlock{}
|
||||||
}
|
}
|
||||||
return &tcpip.ErrAborted{}
|
return &tcpip.ErrAborted{}
|
||||||
@@ -137,11 +156,11 @@ func (t *DarwinTun) WritePacket(packet *stack.PacketBuffer) tcpip.Error {
|
|||||||
// which will make the stack call Wait which should implement desired push-back
|
// which will make the stack call Wait which should implement desired push-back
|
||||||
func (t *DarwinTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
func (t *DarwinTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
||||||
// request memory to write from reusable buffer pool
|
// request memory to write from reusable buffer pool
|
||||||
b := buf.NewWithSize(int32(t.options.MTU) + utunHeaderSize)
|
b := buf.NewWithSize(int32(t.options.MTU[0]) + utunHeaderSize)
|
||||||
|
|
||||||
// read the bytes to the interface file
|
// read the bytes to the interface file
|
||||||
n, err := b.ReadFrom(t.tunFile)
|
n, err := b.ReadFrom(t.tunFile)
|
||||||
if errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EINTR) {
|
if go_errors.Is(err, unix.EAGAIN) || go_errors.Is(err, unix.EINTR) {
|
||||||
b.Release()
|
b.Release()
|
||||||
return 0, nil, ErrQueueEmpty
|
return 0, nil, ErrQueueEmpty
|
||||||
}
|
}
|
||||||
@@ -174,7 +193,7 @@ func (t *DarwinTun) Wait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *DarwinTun) newEndpoint() (stack.LinkEndpoint, error) {
|
func (t *DarwinTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||||
return &LinkEndpoint{deviceMTU: t.options.MTU, device: t}, nil
|
return &LinkEndpoint{deviceMTU: t.options.MTU[0], device: t}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// open the interface, by creating new utunN if in the system and returning its file descriptor
|
// open the interface, by creating new utunN if in the system and returning its file descriptor
|
||||||
@@ -346,9 +365,20 @@ func setIPAddress(name string, gateway netip.Prefix) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) error {
|
func ioctlPtr(fd int, req uint, arg unsafe.Pointer) error {
|
||||||
_, _, errno := unix.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
_, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg))
|
||||||
if errno != 0 {
|
if errno != 0 {
|
||||||
return errno
|
return errno
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setinterface(network, address string, fd uintptr, iface *net.Interface) error {
|
||||||
|
switch network {
|
||||||
|
case "tcp4", "udp4", "ip4":
|
||||||
|
return unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, iface.Index)
|
||||||
|
case "tcp6", "udp6", "ip6":
|
||||||
|
return unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, iface.Index)
|
||||||
|
default:
|
||||||
|
return errors.New("unknown network ", network)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
)
|
)
|
||||||
@@ -13,11 +15,8 @@ type DefaultTun struct {
|
|||||||
// DefaultTun implements Tun
|
// DefaultTun implements Tun
|
||||||
var _ Tun = (*DefaultTun)(nil)
|
var _ Tun = (*DefaultTun)(nil)
|
||||||
|
|
||||||
// DefaultTun implements GVisorTun
|
|
||||||
var _ GVisorTun = (*DefaultTun)(nil)
|
|
||||||
|
|
||||||
// NewTun builds new tun interface handler
|
// NewTun builds new tun interface handler
|
||||||
func NewTun(options TunOptions) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
return nil, errors.New("Tun is not supported on your platform")
|
return nil, errors.New("Tun is not supported on your platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,6 +28,18 @@ func (t *DefaultTun) Close() error {
|
|||||||
return errors.New("Tun is not supported on your platform")
|
return errors.New("Tun is not supported on your platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTun) Name() (string, error) {
|
||||||
|
return "", errors.New("Tun is not supported on your platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *DefaultTun) Index() (int, error) {
|
||||||
|
return 0, errors.New("Tun is not supported on your platform")
|
||||||
|
}
|
||||||
|
|
||||||
func (t *DefaultTun) newEndpoint() (stack.LinkEndpoint, error) {
|
func (t *DefaultTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||||
return nil, errors.New("Tun is not supported on your platform")
|
return nil, errors.New("Tun is not supported on your platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setinterface(string, string, uintptr, *net.Interface) error {
|
||||||
|
return errors.New("Tun is not supported on your platform")
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||||
@@ -15,23 +17,20 @@ import (
|
|||||||
type LinuxTun struct {
|
type LinuxTun struct {
|
||||||
tunFd int
|
tunFd int
|
||||||
tunLink netlink.Link
|
tunLink netlink.Link
|
||||||
options TunOptions
|
options *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxTun implements Tun
|
// LinuxTun implements Tun
|
||||||
var _ Tun = (*LinuxTun)(nil)
|
var _ Tun = (*LinuxTun)(nil)
|
||||||
|
|
||||||
// LinuxTun implements GVisorTun
|
|
||||||
var _ GVisorTun = (*LinuxTun)(nil)
|
|
||||||
|
|
||||||
// NewTun builds new tun interface handler (linux specific)
|
// NewTun builds new tun interface handler (linux specific)
|
||||||
func NewTun(options TunOptions) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
tunFd, err := open(options.Name)
|
tunFd, err := open(options.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tunLink, err := setup(options.Name, int(options.MTU))
|
tunLink, err := setup(options.Name, int(options.MTU[0]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = unix.Close(tunFd)
|
_ = unix.Close(tunFd)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -110,11 +109,23 @@ func (t *LinuxTun) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *LinuxTun) Name() (string, error) {
|
||||||
|
return t.tunLink.Attrs().Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *LinuxTun) Index() (int, error) {
|
||||||
|
return t.tunLink.Attrs().Index, nil
|
||||||
|
}
|
||||||
|
|
||||||
// newEndpoint builds new gVisor stack.LinkEndpoint from the tun interface file descriptor
|
// newEndpoint builds new gVisor stack.LinkEndpoint from the tun interface file descriptor
|
||||||
func (t *LinuxTun) newEndpoint() (stack.LinkEndpoint, error) {
|
func (t *LinuxTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||||
return fdbased.New(&fdbased.Options{
|
return fdbased.New(&fdbased.Options{
|
||||||
FDs: []int{t.tunFd},
|
FDs: []int{t.tunFd},
|
||||||
MTU: t.options.MTU,
|
MTU: t.options.MTU[0],
|
||||||
RXChecksumOffload: true,
|
RXChecksumOffload: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setinterface(network, address string, fd uintptr, iface *net.Interface) error {
|
||||||
|
return unix.BindToDevice(int(fd), iface.Name)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,11 +4,17 @@ package tun
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
"encoding/binary"
|
||||||
|
go_errors "errors"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.zx2c4.com/wintun"
|
"golang.zx2c4.com/wintun"
|
||||||
|
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||||
"gvisor.dev/gvisor/pkg/buffer"
|
"gvisor.dev/gvisor/pkg/buffer"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip"
|
"gvisor.dev/gvisor/pkg/tcpip"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
@@ -21,25 +27,26 @@ func procyield(cycles uint32)
|
|||||||
// current version is heavily stripped to do nothing more,
|
// current version is heavily stripped to do nothing more,
|
||||||
// then create a network interface, to be provided as endpoint to gVisor ip stack
|
// then create a network interface, to be provided as endpoint to gVisor ip stack
|
||||||
type WindowsTun struct {
|
type WindowsTun struct {
|
||||||
options TunOptions
|
sync.RWMutex
|
||||||
adapter *wintun.Adapter
|
|
||||||
session wintun.Session
|
options *Config
|
||||||
readWait windows.Handle
|
adapter *wintun.Adapter
|
||||||
MTU uint32
|
session wintun.Session
|
||||||
|
readWait windows.Handle
|
||||||
|
luid winipcfg.LUID
|
||||||
|
changeCallback winipcfg.ChangeCallback
|
||||||
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsTun implements Tun
|
// WindowsTun implements Tun
|
||||||
var _ Tun = (*WindowsTun)(nil)
|
var _ Tun = (*WindowsTun)(nil)
|
||||||
|
|
||||||
// WindowsTun implements GVisorTun
|
|
||||||
var _ GVisorTun = (*WindowsTun)(nil)
|
|
||||||
|
|
||||||
// WindowsTun implements GVisorDevice
|
// WindowsTun implements GVisorDevice
|
||||||
var _ GVisorDevice = (*WindowsTun)(nil)
|
var _ GVisorDevice = (*WindowsTun)(nil)
|
||||||
|
|
||||||
// NewTun creates a Wintun interface with the given name. Should a Wintun
|
// NewTun creates a Wintun interface with the given name. Should a Wintun
|
||||||
// interface with the same name exist, it tried to be reused.
|
// interface with the same name exist, it tried to be reused.
|
||||||
func NewTun(options TunOptions) (Tun, error) {
|
func NewTun(options *Config) (Tun, error) {
|
||||||
// instantiate wintun adapter
|
// instantiate wintun adapter
|
||||||
adapter, err := open(options.Name)
|
adapter, err := open(options.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,9 +65,7 @@ func NewTun(options TunOptions) (Tun, error) {
|
|||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
session: session,
|
session: session,
|
||||||
readWait: session.ReadWaitEvent(),
|
readWait: session.ReadWaitEvent(),
|
||||||
// there is currently no iphndl.dll support, which is the netlink library for windows
|
luid: winipcfg.LUID(adapter.LUID()),
|
||||||
// so there is nowhere to change MTU for the Wintun interface, and we take its default value
|
|
||||||
MTU: wintun.PacketSizeMax,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tun, nil
|
return tun, nil
|
||||||
@@ -70,13 +75,8 @@ func open(name string) (*wintun.Adapter, error) {
|
|||||||
// generate a deterministic GUID from the adapter name
|
// generate a deterministic GUID from the adapter name
|
||||||
id := md5.Sum([]byte(name))
|
id := md5.Sum([]byte(name))
|
||||||
guid := (*windows.GUID)(unsafe.Pointer(&id[0]))
|
guid := (*windows.GUID)(unsafe.Pointer(&id[0]))
|
||||||
// try to open existing adapter by name
|
|
||||||
adapter, err := wintun.OpenAdapter(name)
|
|
||||||
if err == nil {
|
|
||||||
return adapter, nil
|
|
||||||
}
|
|
||||||
// try to create adapter anew
|
// try to create adapter anew
|
||||||
adapter, err = wintun.CreateAdapter(name, "Xray", guid)
|
adapter, err := wintun.CreateAdapter(name, "Xray", guid)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return adapter, nil
|
return adapter, nil
|
||||||
}
|
}
|
||||||
@@ -84,18 +84,153 @@ func open(name string) (*wintun.Adapter, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *WindowsTun) Start() error {
|
func (t *WindowsTun) Start() error {
|
||||||
|
var has4, has6 bool
|
||||||
|
allowedIPs := make([]netip.Prefix, 0, len(t.options.AutoSystemRoutingTable))
|
||||||
|
for _, route := range t.options.AutoSystemRoutingTable {
|
||||||
|
allowedIPs = append(allowedIPs, netip.MustParsePrefix(route))
|
||||||
|
}
|
||||||
|
routesMap := make(map[winipcfg.RouteData]struct{})
|
||||||
|
for _, ip := range allowedIPs {
|
||||||
|
route := winipcfg.RouteData{
|
||||||
|
Destination: ip.Masked(),
|
||||||
|
Metric: 0,
|
||||||
|
}
|
||||||
|
if ip.Addr().Is4() {
|
||||||
|
has4 = true
|
||||||
|
route.NextHop = netip.IPv4Unspecified()
|
||||||
|
} else {
|
||||||
|
has6 = true
|
||||||
|
route.NextHop = netip.IPv6Unspecified()
|
||||||
|
}
|
||||||
|
routesMap[route] = struct{}{}
|
||||||
|
}
|
||||||
|
routesData := make([]*winipcfg.RouteData, 0, len(routesMap))
|
||||||
|
for route := range routesMap {
|
||||||
|
r := route
|
||||||
|
routesData = append(routesData, &r)
|
||||||
|
}
|
||||||
|
err := t.luid.SetRoutes(routesData)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("unable to set routes").Base(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(t.options.Gateway) > 0 {
|
||||||
|
addresses := make([]netip.Prefix, 0, len(t.options.Gateway))
|
||||||
|
for _, address := range t.options.Gateway {
|
||||||
|
addresses = append(addresses, netip.MustParsePrefix(address))
|
||||||
|
}
|
||||||
|
err := t.luid.SetIPAddresses(addresses)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("unable to set ips").Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has4 {
|
||||||
|
ipif, err := t.luid.IPInterface(windows.AF_INET)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
|
||||||
|
ipif.DadTransmits = 0
|
||||||
|
ipif.ManagedAddressConfigurationSupported = false
|
||||||
|
ipif.OtherStatefulConfigurationSupported = false
|
||||||
|
ipif.NLMTU = t.options.MTU[0]
|
||||||
|
ipif.UseAutomaticMetric = false
|
||||||
|
ipif.Metric = 0
|
||||||
|
err = ipif.Set()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has6 {
|
||||||
|
ipif, err := t.luid.IPInterface(windows.AF_INET6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
|
||||||
|
ipif.DadTransmits = 0
|
||||||
|
ipif.ManagedAddressConfigurationSupported = false
|
||||||
|
ipif.OtherStatefulConfigurationSupported = false
|
||||||
|
ipif.NLMTU = t.options.MTU[1]
|
||||||
|
ipif.UseAutomaticMetric = false
|
||||||
|
ipif.Metric = 0
|
||||||
|
err = ipif.Set()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(t.options.DNS) > 0 {
|
||||||
|
dns := make([]netip.Addr, 0, len(t.options.DNS))
|
||||||
|
for _, ip := range t.options.DNS {
|
||||||
|
dns = append(dns, netip.MustParseAddr(ip))
|
||||||
|
}
|
||||||
|
err := t.luid.SetDNS(windows.AF_INET, dns, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = t.luid.SetDNS(windows.AF_INET6, dns, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if updater != nil {
|
||||||
|
t.changeCallback, err = winipcfg.RegisterInterfaceChangeCallback(func(notificationType winipcfg.MibNotificationType, iface *winipcfg.MibIPInterfaceRow) {
|
||||||
|
if notificationType != winipcfg.MibDeleteInstance {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updater.Update()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *WindowsTun) Close() error {
|
func (t *WindowsTun) Close() error {
|
||||||
|
t.Lock()
|
||||||
|
defer t.Unlock()
|
||||||
|
if t.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t.closed = true
|
||||||
|
|
||||||
|
if t.changeCallback != nil {
|
||||||
|
t.changeCallback.Unregister()
|
||||||
|
}
|
||||||
t.session.End()
|
t.session.End()
|
||||||
_ = t.adapter.Close()
|
_ = t.adapter.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *WindowsTun) Name() (string, error) {
|
||||||
|
row, err := t.luid.Interface()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return row.Alias(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *WindowsTun) Index() (int, error) {
|
||||||
|
row, err := t.luid.Interface()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(row.InterfaceIndex), nil
|
||||||
|
}
|
||||||
|
|
||||||
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
// WritePacket implements GVisorDevice method to write one packet to the tun device
|
||||||
func (t *WindowsTun) WritePacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
|
func (t *WindowsTun) WritePacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
|
||||||
|
t.RLock()
|
||||||
|
defer t.RUnlock()
|
||||||
|
if t.closed {
|
||||||
|
return &tcpip.ErrClosedForSend{}
|
||||||
|
}
|
||||||
|
|
||||||
// request buffer from Wintun
|
// request buffer from Wintun
|
||||||
packet, err := t.session.AllocateSendPacket(packetBuffer.Size())
|
packet, err := t.session.AllocateSendPacket(packetBuffer.Size())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -119,7 +254,7 @@ func (t *WindowsTun) WritePacket(packetBuffer *stack.PacketBuffer) tcpip.Error {
|
|||||||
// which will make the stack call Wait which should implement desired push-back
|
// which will make the stack call Wait which should implement desired push-back
|
||||||
func (t *WindowsTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
func (t *WindowsTun) ReadPacket() (byte, *stack.PacketBuffer, error) {
|
||||||
packet, err := t.session.ReceivePacket()
|
packet, err := t.session.ReceivePacket()
|
||||||
if errors.Is(err, windows.ERROR_NO_MORE_ITEMS) {
|
if go_errors.Is(err, windows.ERROR_NO_MORE_ITEMS) {
|
||||||
return 0, nil, ErrQueueEmpty
|
return 0, nil, ErrQueueEmpty
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -143,5 +278,38 @@ func (t *WindowsTun) Wait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *WindowsTun) newEndpoint() (stack.LinkEndpoint, error) {
|
func (t *WindowsTun) newEndpoint() (stack.LinkEndpoint, error) {
|
||||||
return &LinkEndpoint{deviceMTU: t.options.MTU, device: t}, nil
|
return &LinkEndpoint{deviceMTU: t.options.MTU[0], device: t}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IP_UNICAST_IF = 31
|
||||||
|
IPV6_UNICAST_IF = 31
|
||||||
|
)
|
||||||
|
|
||||||
|
func setinterface(network, address string, fd uintptr, iface *net.Interface) error {
|
||||||
|
var index [4]byte
|
||||||
|
binary.BigEndian.PutUint32(index[:], uint32(iface.Index))
|
||||||
|
|
||||||
|
switch network {
|
||||||
|
case "tcp4", "udp4", "ip4":
|
||||||
|
err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_UNICAST_IF, *(*int)(unsafe.Pointer(&index[0])))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if network == "udp4" {
|
||||||
|
return windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, windows.IP_MULTICAST_IF, *(*int)(unsafe.Pointer(&index[0])))
|
||||||
|
}
|
||||||
|
case "tcp6", "udp6", "ip6":
|
||||||
|
err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_UNICAST_IF, iface.Index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if network == "udp6" {
|
||||||
|
return windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, windows.IPV6_MULTICAST_IF, iface.Index)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unknown network ", network)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package internet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,6 +12,8 @@ import (
|
|||||||
"github.com/xtls/xray-core/features/outbound"
|
"github.com/xtls/xray-core/features/outbound"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var Controllers []func(network, address string, c syscall.RawConn) error
|
||||||
|
var ControllersLock sync.Mutex
|
||||||
var effectiveSystemDialer SystemDialer = &DefaultSystemDialer{}
|
var effectiveSystemDialer SystemDialer = &DefaultSystemDialer{}
|
||||||
|
|
||||||
type SystemDialer interface {
|
type SystemDialer interface {
|
||||||
@@ -19,9 +22,8 @@ type SystemDialer interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DefaultSystemDialer struct {
|
type DefaultSystemDialer struct {
|
||||||
controllers []func(network, address string, c syscall.RawConn) error
|
dns dns.Client
|
||||||
dns dns.Client
|
obm outbound.Manager
|
||||||
obm outbound.Manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSrcAddr(network net.Network, src net.Address) net.Addr {
|
func resolveSrcAddr(network net.Network, src net.Address) net.Addr {
|
||||||
@@ -63,7 +65,7 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
lc.Control = func(network, address string, c syscall.RawConn) error {
|
lc.Control = func(network, address string, c syscall.RawConn) error {
|
||||||
for _, ctl := range d.controllers {
|
for _, ctl := range Controllers {
|
||||||
if err := ctl(network, address, c); err != nil {
|
if err := ctl(network, address, c); err != nil {
|
||||||
errors.LogInfoInner(ctx, err, "failed to apply external controller")
|
errors.LogInfoInner(ctx, err, "failed to apply external controller")
|
||||||
}
|
}
|
||||||
@@ -115,12 +117,12 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
|
|||||||
KeepAliveConfig: keepAliveConfig,
|
KeepAliveConfig: keepAliveConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
if sockopt != nil || len(d.controllers) > 0 {
|
if sockopt != nil || len(Controllers) > 0 {
|
||||||
if sockopt != nil && sockopt.TcpMptcp {
|
if sockopt != nil && sockopt.TcpMptcp {
|
||||||
dialer.SetMultipathTCP(true)
|
dialer.SetMultipathTCP(true)
|
||||||
}
|
}
|
||||||
dialer.Control = func(network, address string, c syscall.RawConn) error {
|
dialer.Control = func(network, address string, c syscall.RawConn) error {
|
||||||
for _, ctl := range d.controllers {
|
for _, ctl := range Controllers {
|
||||||
if err := ctl(network, address, c); err != nil {
|
if err := ctl(network, address, c); err != nil {
|
||||||
errors.LogInfoInner(ctx, err, "failed to apply external controller")
|
errors.LogInfoInner(ctx, err, "failed to apply external controller")
|
||||||
}
|
}
|
||||||
@@ -208,12 +210,15 @@ func RegisterDialerController(ctl func(network, address string, c syscall.RawCon
|
|||||||
return errors.New("nil listener controller")
|
return errors.New("nil listener controller")
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer, ok := effectiveSystemDialer.(*DefaultSystemDialer)
|
ControllersLock.Lock()
|
||||||
|
Controllers = append(Controllers, ctl)
|
||||||
|
ControllersLock.Unlock()
|
||||||
|
|
||||||
|
_, ok := effectiveSystemDialer.(*DefaultSystemDialer)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("RegisterListenerController not supported in custom dialer")
|
return errors.New("RegisterListenerController not supported in custom dialer")
|
||||||
}
|
}
|
||||||
|
|
||||||
dialer.controllers = append(dialer.controllers, ctl)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user