mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-27 01:13:01 +00:00
297 lines
9.5 KiB
Go
297 lines
9.5 KiB
Go
|
package tun
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"net/netip"
|
||
|
"runtime"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/sagernet/sing-tun"
|
||
|
sing_common "github.com/sagernet/sing/common"
|
||
|
"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"
|
||
|
N "github.com/sagernet/sing/common/network"
|
||
|
"github.com/sagernet/sing/common/ranges"
|
||
|
"github.com/xtls/xray-core/common"
|
||
|
"github.com/xtls/xray-core/common/net"
|
||
|
"github.com/xtls/xray-core/common/session"
|
||
|
"github.com/xtls/xray-core/common/singbridge"
|
||
|
"github.com/xtls/xray-core/core"
|
||
|
"github.com/xtls/xray-core/features/routing"
|
||
|
features_tun "github.com/xtls/xray-core/features/tun"
|
||
|
"github.com/xtls/xray-core/transport"
|
||
|
"github.com/xtls/xray-core/transport/internet"
|
||
|
)
|
||
|
|
||
|
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen
|
||
|
|
||
|
func init() {
|
||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) {
|
||
|
return New(ctx, cfg.(*Config))
|
||
|
}))
|
||
|
}
|
||
|
|
||
|
var TunInitializer features_tun.Interface = (*Tun)(nil)
|
||
|
|
||
|
type Tun struct {
|
||
|
ctx context.Context
|
||
|
dispatcher routing.Dispatcher
|
||
|
logger logger.ContextLogger
|
||
|
tunOptions tun.Options
|
||
|
stack string
|
||
|
endpointIndependentNat bool
|
||
|
udpTimeout int64
|
||
|
tunIf tun.Tun
|
||
|
tunStack tun.Stack
|
||
|
networkMonitor tun.NetworkUpdateMonitor
|
||
|
interfaceMonitor tun.DefaultInterfaceMonitor
|
||
|
packageManager tun.PackageManager
|
||
|
interfaceFinder *myInterfaceFinder
|
||
|
}
|
||
|
|
||
|
func New(ctx context.Context, config *Config) (*Tun, error) {
|
||
|
instance := core.MustFromContext(ctx)
|
||
|
tunInterface := &Tun{
|
||
|
ctx: ctx,
|
||
|
dispatcher: instance.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
|
||
|
logger: singbridge.NewLogger(newError),
|
||
|
stack: config.Stack,
|
||
|
endpointIndependentNat: config.EndpointIndependentNat,
|
||
|
udpTimeout: int64(5 * time.Minute.Seconds()),
|
||
|
interfaceFinder: new(myInterfaceFinder),
|
||
|
}
|
||
|
networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(tunInterface)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defaultInterfaceMonitor, err := tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, tun.DefaultInterfaceMonitorOptions{
|
||
|
OverrideAndroidVPN: config.OverrideAndroidVpn,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defaultInterfaceMonitor.RegisterCallback(tunInterface.notifyNetworkUpdate)
|
||
|
if config.AutoDetectInterface {
|
||
|
networkUpdateMonitor.RegisterCallback(tunInterface.interfaceFinder.update)
|
||
|
const useInterfaceName = runtime.GOOS == "linux" || runtime.GOOS == "android"
|
||
|
bindFunc := control.BindToInterfaceFunc(tunInterface.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int) {
|
||
|
remoteAddr := M.ParseSocksaddr(address).Addr
|
||
|
if useInterfaceName {
|
||
|
return defaultInterfaceMonitor.DefaultInterfaceName(remoteAddr), -1
|
||
|
} else {
|
||
|
return "", defaultInterfaceMonitor.DefaultInterfaceIndex(remoteAddr)
|
||
|
}
|
||
|
})
|
||
|
internet.UseAlternativeSystemDialer(nil)
|
||
|
internet.RegisterDialerController(bindFunc)
|
||
|
internet.RegisterListenerController(bindFunc)
|
||
|
}
|
||
|
if runtime.GOOS == "android" {
|
||
|
packageManage, err := tun.NewPackageManager(tunInterface)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
tunInterface.packageManager = packageManage
|
||
|
}
|
||
|
tunInterface.networkMonitor = networkUpdateMonitor
|
||
|
tunInterface.interfaceMonitor = defaultInterfaceMonitor
|
||
|
tunName := config.InterfaceName
|
||
|
if tunName == "" {
|
||
|
tunName = tun.CalculateInterfaceName("")
|
||
|
}
|
||
|
tunMTU := config.Mtu
|
||
|
if tunMTU == 0 {
|
||
|
tunMTU = 9000
|
||
|
}
|
||
|
includeUID := uidToRange(config.IncludeUid)
|
||
|
if len(config.IncludeUidRange) > 0 {
|
||
|
var err error
|
||
|
includeUID, err = parseRange(includeUID, config.IncludeUidRange)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "parse include_uid_range")
|
||
|
}
|
||
|
}
|
||
|
excludeUID := uidToRange(config.ExcludeUid)
|
||
|
if len(config.ExcludeUidRange) > 0 {
|
||
|
var err error
|
||
|
excludeUID, err = parseRange(excludeUID, config.ExcludeUidRange)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "parse exclude_uid_range")
|
||
|
}
|
||
|
}
|
||
|
if config.UdpTimeout != 0 {
|
||
|
tunInterface.udpTimeout = config.UdpTimeout
|
||
|
}
|
||
|
tunInterface.tunOptions = tun.Options{
|
||
|
Name: tunName,
|
||
|
Inet4Address: sing_common.Map(config.Inet4Address, netip.MustParsePrefix),
|
||
|
Inet6Address: sing_common.Map(config.Inet6Address, netip.MustParsePrefix),
|
||
|
MTU: tunMTU,
|
||
|
AutoRoute: config.AutoRoute,
|
||
|
StrictRoute: config.StrictRoute,
|
||
|
Inet4RouteAddress: sing_common.Map(config.Inet4RouteAddress, netip.MustParsePrefix),
|
||
|
Inet6RouteAddress: sing_common.Map(config.Inet6RouteAddress, netip.MustParsePrefix),
|
||
|
IncludeUID: includeUID,
|
||
|
ExcludeUID: excludeUID,
|
||
|
IncludeAndroidUser: sing_common.Map(config.IncludeAndroidUser, func(it int32) int {
|
||
|
return int(it)
|
||
|
}),
|
||
|
IncludePackage: config.IncludePackage,
|
||
|
ExcludePackage: config.ExcludePackage,
|
||
|
InterfaceMonitor: defaultInterfaceMonitor,
|
||
|
TableIndex: 2022,
|
||
|
}
|
||
|
return tunInterface, nil
|
||
|
}
|
||
|
|
||
|
func (t *Tun) Type() interface{} {
|
||
|
return features_tun.InterfaceType()
|
||
|
}
|
||
|
|
||
|
func (t *Tun) Start() error {
|
||
|
err := t.interfaceMonitor.Start()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = t.networkMonitor.Start()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if runtime.GOOS == "android" {
|
||
|
err = t.packageManager.Start()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
t.tunOptions.BuildAndroidRules(t.packageManager, t)
|
||
|
}
|
||
|
tunIf, err := tun.New(t.tunOptions)
|
||
|
if err != nil {
|
||
|
return E.Cause(err, "configure tun interface")
|
||
|
}
|
||
|
t.tunIf = tunIf
|
||
|
t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
|
||
|
Context: t.ctx,
|
||
|
Tun: tunIf,
|
||
|
MTU: t.tunOptions.MTU,
|
||
|
Name: t.tunOptions.Name,
|
||
|
Inet4Address: t.tunOptions.Inet4Address,
|
||
|
Inet6Address: t.tunOptions.Inet6Address,
|
||
|
EndpointIndependentNat: t.endpointIndependentNat,
|
||
|
UDPTimeout: t.udpTimeout,
|
||
|
Handler: t,
|
||
|
Logger: t.logger,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = t.tunStack.Start()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
t.logger.Info("tun started at ", t.tunOptions.Name)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *Tun) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {
|
||
|
sid := session.NewID()
|
||
|
ctx = session.ContextWithID(ctx, sid)
|
||
|
t.logger.InfoContext(ctx, "inbound connection from ", metadata.Source)
|
||
|
t.logger.InfoContext(ctx, "inbound connection to ", metadata.Destination)
|
||
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||
|
Source: net.DestinationFromAddr(metadata.Source.TCPAddr()),
|
||
|
Conn: conn,
|
||
|
})
|
||
|
wConn := singbridge.NewConn(conn)
|
||
|
_ = t.dispatcher.DispatchLink(ctx, singbridge.ToDestination(metadata.Destination, net.Network_TCP), &transport.Link{
|
||
|
Reader: wConn,
|
||
|
Writer: wConn,
|
||
|
})
|
||
|
conn.Close()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *Tun) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata M.Metadata) error {
|
||
|
sid := session.NewID()
|
||
|
ctx = session.ContextWithID(ctx, sid)
|
||
|
t.logger.InfoContext(ctx, "inbound packet connection from ", metadata.Source)
|
||
|
t.logger.InfoContext(ctx, "inbound packet connection to ", metadata.Destination)
|
||
|
ctx = session.ContextWithInbound(ctx, &session.Inbound{
|
||
|
Source: net.DestinationFromAddr(metadata.Source.UDPAddr()),
|
||
|
})
|
||
|
pc := &PacketConn{conn}
|
||
|
_ = t.dispatcher.DispatchLink(ctx, singbridge.ToDestination(metadata.Destination, net.Network_UDP), &transport.Link{
|
||
|
Reader: pc,
|
||
|
Writer: pc,
|
||
|
})
|
||
|
conn.Close()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *Tun) Close() error {
|
||
|
return sing_common.Close(
|
||
|
t.packageManager,
|
||
|
t.interfaceMonitor,
|
||
|
t.networkMonitor,
|
||
|
t.tunStack,
|
||
|
t.tunIf,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func (t *Tun) OnPackagesUpdated(packages int, sharedUsers int) {
|
||
|
t.logger.Info("updated packages list: ", packages, " packages, ", sharedUsers, " shared users")
|
||
|
}
|
||
|
|
||
|
func (t *Tun) NewError(ctx context.Context, err error) {
|
||
|
}
|
||
|
|
||
|
func (t *Tun) notifyNetworkUpdate(int) error {
|
||
|
if runtime.GOOS == "android" {
|
||
|
var vpnStatus string
|
||
|
if t.interfaceMonitor.AndroidVPNEnabled() {
|
||
|
vpnStatus = "enabled"
|
||
|
} else {
|
||
|
vpnStatus = "disabled"
|
||
|
}
|
||
|
t.logger.Info("updated default interface ", t.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", t.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
|
||
|
} else {
|
||
|
t.logger.Info("updated default interface ", t.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", t.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func uidToRange(uidList []uint32) []ranges.Range[uint32] {
|
||
|
return sing_common.Map(uidList, func(uid uint32) ranges.Range[uint32] {
|
||
|
return ranges.NewSingle(uint32(uid))
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges.Range[uint32], error) {
|
||
|
for _, uidRange := range rangeList {
|
||
|
if !strings.Contains(uidRange, ":") {
|
||
|
return nil, E.New("missing ':' in range: ", uidRange)
|
||
|
}
|
||
|
subIndex := strings.Index(uidRange, ":")
|
||
|
if subIndex == 0 {
|
||
|
return nil, E.New("missing range start: ", uidRange)
|
||
|
} else if subIndex == len(uidRange)-1 {
|
||
|
return nil, E.New("missing range end: ", uidRange)
|
||
|
}
|
||
|
var start, end uint64
|
||
|
var err error
|
||
|
start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "parse range start")
|
||
|
}
|
||
|
end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "parse range end")
|
||
|
}
|
||
|
uidRanges = append(uidRanges, ranges.New(uint32(start), uint32(end)))
|
||
|
}
|
||
|
return uidRanges, nil
|
||
|
}
|