Xray-core/app/tun/tun.go
2023-04-23 19:51:48 +08:00

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
}