Ver Fonte

refactor: Platform Interfaces

世界 há 1 ano atrás
pai
commit
ee11ca4935

+ 19 - 0
adapter/network.go

@@ -9,6 +9,8 @@ type NetworkManager interface {
 	Lifecycle
 	Lifecycle
 	InterfaceFinder() control.InterfaceFinder
 	InterfaceFinder() control.InterfaceFinder
 	UpdateInterfaces() error
 	UpdateInterfaces() error
+	DefaultNetworkInterface() *NetworkInterface
+	NetworkInterfaces() []NetworkInterface
 	DefaultInterface() string
 	DefaultInterface() string
 	AutoDetectInterface() bool
 	AutoDetectInterface() bool
 	AutoDetectInterfaceFunc() control.Func
 	AutoDetectInterfaceFunc() control.Func
@@ -21,3 +23,20 @@ type NetworkManager interface {
 	WIFIState() WIFIState
 	WIFIState() WIFIState
 	ResetNetwork()
 	ResetNetwork()
 }
 }
+
+type InterfaceUpdateListener interface {
+	InterfaceUpdated()
+}
+
+type WIFIState struct {
+	SSID  string
+	BSSID string
+}
+
+type NetworkInterface struct {
+	control.Interface
+	Type        string
+	DNSServers  []string
+	Expensive   bool
+	Constrained bool
+}

+ 0 - 9
adapter/router.go

@@ -119,12 +119,3 @@ func (c *HTTPStartContext) Close() {
 		client.CloseIdleConnections()
 		client.CloseIdleConnections()
 	}
 	}
 }
 }
-
-type InterfaceUpdateListener interface {
-	InterfaceUpdated()
-}
-
-type WIFIState struct {
-	SSID  string
-	BSSID string
-}

+ 3 - 4
common/settings/proxy_darwin.go

@@ -2,7 +2,6 @@ package settings
 
 
 import (
 import (
 	"context"
 	"context"
-	"net/netip"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
@@ -77,14 +76,14 @@ func (p *DarwinSystemProxy) update(event int) {
 }
 }
 
 
 func (p *DarwinSystemProxy) update0() error {
 func (p *DarwinSystemProxy) update0() error {
-	newInterfaceName := p.monitor.DefaultInterfaceName(netip.IPv4Unspecified())
-	if p.interfaceName == newInterfaceName {
+	newInterface := p.monitor.DefaultInterface()
+	if p.interfaceName == newInterface.Name {
 		return nil
 		return nil
 	}
 	}
 	if p.interfaceName != "" {
 	if p.interfaceName != "" {
 		_ = p.Disable()
 		_ = p.Disable()
 	}
 	}
-	p.interfaceName = newInterfaceName
+	p.interfaceName = newInterface.Name
 	interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
 	interfaceDisplayName, err := getInterfaceDisplayName(p.interfaceName)
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 8 - 0
constant/network.go

@@ -0,0 +1,8 @@
+package constant
+
+const (
+	InterfaceTypeWIFI     = "wifi"
+	InterfaceTypeCellular = "cellular"
+	InterfaceTypeEthernet = "ethernet"
+	InterfaceTypeOther    = "other"
+)

+ 1 - 1
constant/os.go

@@ -6,7 +6,7 @@ import (
 
 
 const IsAndroid = goos.IsAndroid == 1
 const IsAndroid = goos.IsAndroid == 1
 
 
-const IsDarwin = goos.IsDarwin == 1
+const IsDarwin = goos.IsDarwin == 1 || goos.IsIos == 1
 
 
 const IsDragonfly = goos.IsDragonfly == 1
 const IsDragonfly = goos.IsDragonfly == 1
 
 

+ 3 - 15
experimental/libbox/config.go

@@ -74,11 +74,7 @@ func (s *platformInterfaceStub) CreateDefaultInterfaceMonitor(logger logger.Logg
 	return (*interfaceMonitorStub)(nil)
 	return (*interfaceMonitorStub)(nil)
 }
 }
 
 
-func (s *platformInterfaceStub) UsePlatformInterfaceGetter() bool {
-	return true
-}
-
-func (s *platformInterfaceStub) Interfaces() ([]control.Interface, error) {
+func (s *platformInterfaceStub) Interfaces() ([]adapter.NetworkInterface, error) {
 	return nil, os.ErrInvalid
 	return nil, os.ErrInvalid
 }
 }
 
 
@@ -111,16 +107,8 @@ func (s *interfaceMonitorStub) Close() error {
 	return os.ErrInvalid
 	return os.ErrInvalid
 }
 }
 
 
-func (s *interfaceMonitorStub) DefaultInterfaceName(destination netip.Addr) string {
-	return ""
-}
-
-func (s *interfaceMonitorStub) DefaultInterfaceIndex(destination netip.Addr) int {
-	return -1
-}
-
-func (s *interfaceMonitorStub) DefaultInterface(destination netip.Addr) (string, int) {
-	return "", -1
+func (s *interfaceMonitorStub) DefaultInterface() *control.Interface {
+	return nil
 }
 }
 
 
 func (s *interfaceMonitorStub) OverrideAndroidVPN() bool {
 func (s *interfaceMonitorStub) OverrideAndroidVPN() bool {

+ 1 - 1
experimental/libbox/link_flags_stub.go

@@ -1,4 +1,4 @@
-//go:build !linux
+//go:build !unix
 
 
 package libbox
 package libbox
 
 

+ 2 - 0
experimental/libbox/link_flags_linux.go → experimental/libbox/link_flags_unix.go

@@ -1,3 +1,5 @@
+//go:build unix
+
 package libbox
 package libbox
 
 
 import (
 import (

+ 36 - 125
experimental/libbox/monitor.go

@@ -1,16 +1,11 @@
 package libbox
 package libbox
 
 
 import (
 import (
-	"net"
-	"net/netip"
-	"sync"
-
 	C "github.com/sagernet/sing-box/constant"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing-tun"
-	"github.com/sagernet/sing/common"
+	"github.com/sagernet/sing/common/control"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	"github.com/sagernet/sing/common/logger"
 	"github.com/sagernet/sing/common/logger"
-	M "github.com/sagernet/sing/common/metadata"
 	"github.com/sagernet/sing/common/x/list"
 	"github.com/sagernet/sing/common/x/list"
 )
 )
 
 
@@ -21,19 +16,9 @@ var (
 
 
 type platformDefaultInterfaceMonitor struct {
 type platformDefaultInterfaceMonitor struct {
 	*platformInterfaceWrapper
 	*platformInterfaceWrapper
-	networkAddresses      []networkAddress
-	defaultInterfaceName  string
-	defaultInterfaceIndex int
-	element               *list.Element[tun.NetworkUpdateCallback]
-	access                sync.Mutex
-	callbacks             list.List[tun.DefaultInterfaceUpdateCallback]
-	logger                logger.Logger
-}
-
-type networkAddress struct {
-	interfaceName  string
-	interfaceIndex int
-	addresses      []netip.Prefix
+	element   *list.Element[tun.NetworkUpdateCallback]
+	callbacks list.List[tun.DefaultInterfaceUpdateCallback]
+	logger    logger.Logger
 }
 }
 
 
 func (m *platformDefaultInterfaceMonitor) Start() error {
 func (m *platformDefaultInterfaceMonitor) Start() error {
@@ -44,37 +29,10 @@ func (m *platformDefaultInterfaceMonitor) Close() error {
 	return m.iif.CloseDefaultInterfaceMonitor(m)
 	return m.iif.CloseDefaultInterfaceMonitor(m)
 }
 }
 
 
-func (m *platformDefaultInterfaceMonitor) DefaultInterfaceName(destination netip.Addr) string {
-	for _, address := range m.networkAddresses {
-		for _, prefix := range address.addresses {
-			if prefix.Contains(destination) {
-				return address.interfaceName
-			}
-		}
-	}
-	return m.defaultInterfaceName
-}
-
-func (m *platformDefaultInterfaceMonitor) DefaultInterfaceIndex(destination netip.Addr) int {
-	for _, address := range m.networkAddresses {
-		for _, prefix := range address.addresses {
-			if prefix.Contains(destination) {
-				return address.interfaceIndex
-			}
-		}
-	}
-	return m.defaultInterfaceIndex
-}
-
-func (m *platformDefaultInterfaceMonitor) DefaultInterface(destination netip.Addr) (string, int) {
-	for _, address := range m.networkAddresses {
-		for _, prefix := range address.addresses {
-			if prefix.Contains(destination) {
-				return address.interfaceName, address.interfaceIndex
-			}
-		}
-	}
-	return m.defaultInterfaceName, m.defaultInterfaceIndex
+func (m *platformDefaultInterfaceMonitor) DefaultInterface() *control.Interface {
+	m.defaultInterfaceAccess.Lock()
+	defer m.defaultInterfaceAccess.Unlock()
+	return m.defaultInterface
 }
 }
 
 
 func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
 func (m *platformDefaultInterfaceMonitor) OverrideAndroidVPN() bool {
@@ -86,104 +44,57 @@ func (m *platformDefaultInterfaceMonitor) AndroidVPNEnabled() bool {
 }
 }
 
 
 func (m *platformDefaultInterfaceMonitor) RegisterCallback(callback tun.DefaultInterfaceUpdateCallback) *list.Element[tun.DefaultInterfaceUpdateCallback] {
 func (m *platformDefaultInterfaceMonitor) RegisterCallback(callback tun.DefaultInterfaceUpdateCallback) *list.Element[tun.DefaultInterfaceUpdateCallback] {
-	m.access.Lock()
-	defer m.access.Unlock()
+	m.defaultInterfaceAccess.Lock()
+	defer m.defaultInterfaceAccess.Unlock()
 	return m.callbacks.PushBack(callback)
 	return m.callbacks.PushBack(callback)
 }
 }
 
 
 func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
 func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Element[tun.DefaultInterfaceUpdateCallback]) {
-	m.access.Lock()
-	defer m.access.Unlock()
+	m.defaultInterfaceAccess.Lock()
+	defer m.defaultInterfaceAccess.Unlock()
 	m.callbacks.Remove(element)
 	m.callbacks.Remove(element)
 }
 }
 
 
-func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
+func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
 	if C.FixAndroidStack {
 	if C.FixAndroidStack {
-		go m.updateDefaultInterface(interfaceName, interfaceIndex32)
+		go m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
 	} else {
 	} else {
-		m.updateDefaultInterface(interfaceName, interfaceIndex32)
+		m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
 	}
 	}
 }
 }
 
 
-func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName string, interfaceIndex32 int32) {
-	if interfaceName == "" || interfaceIndex32 == -1 {
-		m.defaultInterfaceName = ""
-		m.defaultInterfaceIndex = -1
-		m.access.Lock()
+func (m *platformDefaultInterfaceMonitor) updateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
+	m.isExpensive = isExpensive
+	m.isConstrained = isConstrained
+	err := m.networkManager.UpdateInterfaces()
+	if err != nil {
+		m.logger.Error(E.Cause(err, "update interfaces"))
+	}
+	m.defaultInterfaceAccess.Lock()
+	if interfaceIndex32 == -1 {
+		m.defaultInterface = nil
 		callbacks := m.callbacks.Array()
 		callbacks := m.callbacks.Array()
-		m.access.Unlock()
+		m.defaultInterfaceAccess.Unlock()
 		for _, callback := range callbacks {
 		for _, callback := range callbacks {
-			callback(tun.EventNoRoute)
+			callback(tun.EventInterfaceUpdate)
 		}
 		}
 		return
 		return
 	}
 	}
-	var err error
-	if m.iif.UsePlatformInterfaceGetter() {
-		err = m.updateInterfacesPlatform()
-	} else {
-		err = m.updateInterfaces()
-	}
-	if err == nil {
-		err = m.networkManager.UpdateInterfaces()
-	}
+	oldInterface := m.defaultInterface
+	newInterface, err := m.networkManager.InterfaceFinder().ByIndex(int(interfaceIndex32))
 	if err != nil {
 	if err != nil {
-		m.logger.Error(E.Cause(err, "update interfaces"))
+		m.defaultInterfaceAccess.Unlock()
+		m.logger.Error(E.Cause(err, "find updated interface: ", interfaceName))
+		return
 	}
 	}
-	interfaceIndex := int(interfaceIndex32)
-	if m.defaultInterfaceName == interfaceName && m.defaultInterfaceIndex == interfaceIndex {
+	m.defaultInterface = newInterface
+	if oldInterface != nil && oldInterface.Name == m.defaultInterface.Name && oldInterface.Index == m.defaultInterface.Index {
+		m.defaultInterfaceAccess.Unlock()
 		return
 		return
 	}
 	}
-	m.defaultInterfaceName = interfaceName
-	m.defaultInterfaceIndex = interfaceIndex
-	m.access.Lock()
 	callbacks := m.callbacks.Array()
 	callbacks := m.callbacks.Array()
-	m.access.Unlock()
+	m.defaultInterfaceAccess.Unlock()
 	for _, callback := range callbacks {
 	for _, callback := range callbacks {
 		callback(tun.EventInterfaceUpdate)
 		callback(tun.EventInterfaceUpdate)
 	}
 	}
 }
 }
-
-func (m *platformDefaultInterfaceMonitor) updateInterfaces() error {
-	interfaces, err := net.Interfaces()
-	if err != nil {
-		return err
-	}
-	var addresses []networkAddress
-	for _, iif := range interfaces {
-		var netAddresses []net.Addr
-		netAddresses, err = iif.Addrs()
-		if err != nil {
-			return err
-		}
-		var address networkAddress
-		address.interfaceName = iif.Name
-		address.interfaceIndex = iif.Index
-		address.addresses = common.Map(common.FilterIsInstance(netAddresses, func(it net.Addr) (*net.IPNet, bool) {
-			value, loaded := it.(*net.IPNet)
-			return value, loaded
-		}), func(it *net.IPNet) netip.Prefix {
-			bits, _ := it.Mask.Size()
-			return netip.PrefixFrom(M.AddrFromIP(it.IP), bits)
-		})
-		addresses = append(addresses, address)
-	}
-	m.networkAddresses = addresses
-	return nil
-}
-
-func (m *platformDefaultInterfaceMonitor) updateInterfacesPlatform() error {
-	interfaces, err := m.Interfaces()
-	if err != nil {
-		return err
-	}
-	var addresses []networkAddress
-	for _, iif := range interfaces {
-		var address networkAddress
-		address.interfaceName = iif.Name
-		address.interfaceIndex = iif.Index
-		// address.addresses = common.Map(iif.Addresses, netip.MustParsePrefix)
-		addresses = append(addresses, address)
-	}
-	m.networkAddresses = addresses
-	return nil
-}

+ 13 - 3
experimental/libbox/platform.go

@@ -1,6 +1,7 @@
 package libbox
 package libbox
 
 
 import (
 import (
+	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 )
 )
 
 
@@ -13,10 +14,8 @@ type PlatformInterface interface {
 	FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
 	FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int32, destinationAddress string, destinationPort int32) (int32, error)
 	PackageNameByUid(uid int32) (string, error)
 	PackageNameByUid(uid int32) (string, error)
 	UIDByPackageName(packageName string) (int32, error)
 	UIDByPackageName(packageName string) (int32, error)
-	UsePlatformDefaultInterfaceMonitor() bool
 	StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
 	StartDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
 	CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
 	CloseDefaultInterfaceMonitor(listener InterfaceUpdateListener) error
-	UsePlatformInterfaceGetter() bool
 	GetInterfaces() (NetworkInterfaceIterator, error)
 	GetInterfaces() (NetworkInterfaceIterator, error)
 	UnderNetworkExtension() bool
 	UnderNetworkExtension() bool
 	IncludeAllNetworks() bool
 	IncludeAllNetworks() bool
@@ -31,15 +30,26 @@ type TunInterface interface {
 }
 }
 
 
 type InterfaceUpdateListener interface {
 type InterfaceUpdateListener interface {
-	UpdateDefaultInterface(interfaceName string, interfaceIndex int32)
+	UpdateDefaultInterface(interfaceName string, interfaceIndex int32, isExpensive bool, isConstrained bool)
 }
 }
 
 
+const (
+	InterfaceTypeWIFI     = C.InterfaceTypeWIFI
+	InterfaceTypeCellular = C.InterfaceTypeCellular
+	InterfaceTypeEthernet = C.InterfaceTypeEthernet
+	InterfaceTypeOther    = C.InterfaceTypeOther
+)
+
 type NetworkInterface struct {
 type NetworkInterface struct {
 	Index     int32
 	Index     int32
 	MTU       int32
 	MTU       int32
 	Name      string
 	Name      string
 	Addresses StringIterator
 	Addresses StringIterator
 	Flags     int32
 	Flags     int32
+
+	Type      string
+	DNSServer StringIterator
+	Metered   bool
 }
 }
 
 
 type WIFIState struct {
 type WIFIState struct {

+ 1 - 4
experimental/libbox/platform/interface.go

@@ -5,7 +5,6 @@ import (
 	"github.com/sagernet/sing-box/common/process"
 	"github.com/sagernet/sing-box/common/process"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing-tun"
-	"github.com/sagernet/sing/common/control"
 	"github.com/sagernet/sing/common/logger"
 	"github.com/sagernet/sing/common/logger"
 )
 )
 
 
@@ -14,10 +13,8 @@ type Interface interface {
 	UsePlatformAutoDetectInterfaceControl() bool
 	UsePlatformAutoDetectInterfaceControl() bool
 	AutoDetectInterfaceControl(fd int) error
 	AutoDetectInterfaceControl(fd int) error
 	OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
 	OpenTun(options *tun.Options, platformOptions option.TunPlatformOptions) (tun.Tun, error)
-	UsePlatformDefaultInterfaceMonitor() bool
 	CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
 	CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor
-	UsePlatformInterfaceGetter() bool
-	Interfaces() ([]control.Interface, error)
+	Interfaces() ([]adapter.NetworkInterface, error)
 	UnderNetworkExtension() bool
 	UnderNetworkExtension() bool
 	IncludeAllNetworks() bool
 	IncludeAllNetworks() bool
 	ClearDNSCache()
 	ClearDNSCache()

+ 34 - 21
experimental/libbox/service.go

@@ -6,6 +6,7 @@ import (
 	"os"
 	"os"
 	"runtime"
 	"runtime"
 	runtimeDebug "runtime/debug"
 	runtimeDebug "runtime/debug"
+	"sync"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
 
 
@@ -54,7 +55,10 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
 	ctx, cancel := context.WithCancel(ctx)
 	ctx, cancel := context.WithCancel(ctx)
 	urlTestHistoryStorage := urltest.NewHistoryStorage()
 	urlTestHistoryStorage := urltest.NewHistoryStorage()
 	ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
 	ctx = service.ContextWithPtr(ctx, urlTestHistoryStorage)
-	platformWrapper := &platformInterfaceWrapper{iif: platformInterface, useProcFS: platformInterface.UseProcFS()}
+	platformWrapper := &platformInterfaceWrapper{
+		iif:       platformInterface,
+		useProcFS: platformInterface.UseProcFS(),
+	}
 	service.MustRegister[platform.Interface](ctx, platformWrapper)
 	service.MustRegister[platform.Interface](ctx, platformWrapper)
 	instance, err := box.New(box.Options{
 	instance, err := box.New(box.Options{
 		Context:           ctx,
 		Context:           ctx,
@@ -119,9 +123,14 @@ var (
 )
 )
 
 
 type platformInterfaceWrapper struct {
 type platformInterfaceWrapper struct {
-	iif            PlatformInterface
-	useProcFS      bool
-	networkManager adapter.NetworkManager
+	iif                    PlatformInterface
+	useProcFS              bool
+	networkManager         adapter.NetworkManager
+	myTunName              string
+	defaultInterfaceAccess sync.Mutex
+	defaultInterface       *control.Interface
+	isExpensive            bool
+	isConstrained          bool
 }
 }
 
 
 func (w *platformInterfaceWrapper) Initialize(networkManager adapter.NetworkManager) error {
 func (w *platformInterfaceWrapper) Initialize(networkManager adapter.NetworkManager) error {
@@ -161,38 +170,42 @@ func (w *platformInterfaceWrapper) OpenTun(options *tun.Options, platformOptions
 		return nil, E.Cause(err, "dup tun file descriptor")
 		return nil, E.Cause(err, "dup tun file descriptor")
 	}
 	}
 	options.FileDescriptor = dupFd
 	options.FileDescriptor = dupFd
+	w.myTunName = options.Name
 	return tun.New(*options)
 	return tun.New(*options)
 }
 }
 
 
-func (w *platformInterfaceWrapper) UsePlatformDefaultInterfaceMonitor() bool {
-	return w.iif.UsePlatformDefaultInterfaceMonitor()
-}
-
 func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
 func (w *platformInterfaceWrapper) CreateDefaultInterfaceMonitor(logger logger.Logger) tun.DefaultInterfaceMonitor {
 	return &platformDefaultInterfaceMonitor{
 	return &platformDefaultInterfaceMonitor{
 		platformInterfaceWrapper: w,
 		platformInterfaceWrapper: w,
-		defaultInterfaceIndex:    -1,
 		logger:                   logger,
 		logger:                   logger,
 	}
 	}
 }
 }
 
 
-func (w *platformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
-	return w.iif.UsePlatformInterfaceGetter()
-}
-
-func (w *platformInterfaceWrapper) Interfaces() ([]control.Interface, error) {
+func (w *platformInterfaceWrapper) Interfaces() ([]adapter.NetworkInterface, error) {
 	interfaceIterator, err := w.iif.GetInterfaces()
 	interfaceIterator, err := w.iif.GetInterfaces()
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	var interfaces []control.Interface
+	var interfaces []adapter.NetworkInterface
 	for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
 	for _, netInterface := range iteratorToArray[*NetworkInterface](interfaceIterator) {
-		interfaces = append(interfaces, control.Interface{
-			Index:     int(netInterface.Index),
-			MTU:       int(netInterface.MTU),
-			Name:      netInterface.Name,
-			Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
-			Flags:     linkFlags(uint32(netInterface.Flags)),
+		if netInterface.Name == w.myTunName {
+			continue
+		}
+		w.defaultInterfaceAccess.Lock()
+		isDefault := w.defaultInterface != nil && int(netInterface.Index) == w.defaultInterface.Index
+		w.defaultInterfaceAccess.Unlock()
+		interfaces = append(interfaces, adapter.NetworkInterface{
+			Interface: control.Interface{
+				Index:     int(netInterface.Index),
+				MTU:       int(netInterface.MTU),
+				Name:      netInterface.Name,
+				Addresses: common.Map(iteratorToArray[string](netInterface.Addresses), netip.MustParsePrefix),
+				Flags:     linkFlags(uint32(netInterface.Flags)),
+			},
+			Type:        netInterface.Type,
+			DNSServers:  iteratorToArray[string](netInterface.DNSServer),
+			Expensive:   netInterface.Metered || isDefault && w.isExpensive,
+			Constrained: isDefault && w.isConstrained,
 		})
 		})
 	}
 	}
 	return interfaces, nil
 	return interfaces, nil

+ 3 - 3
protocol/direct/loopback_detect.go

@@ -33,7 +33,7 @@ func (l *loopBackDetector) NewConn(conn net.Conn) net.Conn {
 	}
 	}
 	if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
 	if udpConn, isUDPConn := conn.(abstractUDPConn); isUDPConn {
 		if !source.Addr().IsLoopback() {
 		if !source.Addr().IsLoopback() {
-			_, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
+			_, err := l.networkManager.InterfaceFinder().ByAddr(source.Addr())
 			if err != nil {
 			if err != nil {
 				return conn
 				return conn
 			}
 			}
@@ -59,7 +59,7 @@ func (l *loopBackDetector) NewPacketConn(conn N.NetPacketConn, destination M.Soc
 		return conn
 		return conn
 	}
 	}
 	if !source.Addr().IsLoopback() {
 	if !source.Addr().IsLoopback() {
-		_, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
+		_, err := l.networkManager.InterfaceFinder().ByAddr(source.Addr())
 		if err != nil {
 		if err != nil {
 			return conn
 			return conn
 		}
 		}
@@ -82,7 +82,7 @@ func (l *loopBackDetector) CheckPacketConn(source netip.AddrPort, local netip.Ad
 		return false
 		return false
 	}
 	}
 	if !source.Addr().IsLoopback() {
 	if !source.Addr().IsLoopback() {
-		_, err := l.networkManager.InterfaceFinder().InterfaceByAddr(source.Addr())
+		_, err := l.networkManager.InterfaceFinder().ByAddr(source.Addr())
 		if err != nil {
 		if err != nil {
 			return false
 			return false
 		}
 		}

+ 128 - 51
route/network.go

@@ -3,9 +3,10 @@ package route
 import (
 import (
 	"context"
 	"context"
 	"errors"
 	"errors"
-	"net/netip"
+	"net"
 	"os"
 	"os"
 	"runtime"
 	"runtime"
+	"strings"
 	"syscall"
 	"syscall"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
@@ -15,33 +16,41 @@ import (
 	"github.com/sagernet/sing-box/experimental/libbox/platform"
 	"github.com/sagernet/sing-box/experimental/libbox/platform"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing-tun"
+	"github.com/sagernet/sing/common"
+	"github.com/sagernet/sing/common/atomic"
 	"github.com/sagernet/sing/common/control"
 	"github.com/sagernet/sing/common/control"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
+	F "github.com/sagernet/sing/common/format"
 	"github.com/sagernet/sing/common/logger"
 	"github.com/sagernet/sing/common/logger"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	"github.com/sagernet/sing/common/winpowrprof"
 	"github.com/sagernet/sing/common/winpowrprof"
 	"github.com/sagernet/sing/service"
 	"github.com/sagernet/sing/service"
 	"github.com/sagernet/sing/service/pause"
 	"github.com/sagernet/sing/service/pause"
+
+	"golang.org/x/exp/slices"
 )
 )
 
 
 var _ adapter.NetworkManager = (*NetworkManager)(nil)
 var _ adapter.NetworkManager = (*NetworkManager)(nil)
 
 
 type NetworkManager struct {
 type NetworkManager struct {
-	logger                 logger.ContextLogger
-	interfaceFinder        *control.DefaultInterfaceFinder
+	logger            logger.ContextLogger
+	interfaceFinder   *control.DefaultInterfaceFinder
+	networkInterfaces atomic.TypedValue[[]adapter.NetworkInterface]
+
 	autoDetectInterface    bool
 	autoDetectInterface    bool
 	defaultInterface       string
 	defaultInterface       string
 	defaultMark            uint32
 	defaultMark            uint32
 	autoRedirectOutputMark uint32
 	autoRedirectOutputMark uint32
-	networkMonitor         tun.NetworkUpdateMonitor
-	interfaceMonitor       tun.DefaultInterfaceMonitor
-	packageManager         tun.PackageManager
-	powerListener          winpowrprof.EventListener
-	pauseManager           pause.Manager
-	platformInterface      platform.Interface
-	outboundManager        adapter.OutboundManager
-	wifiState              adapter.WIFIState
-	started                bool
+
+	networkMonitor    tun.NetworkUpdateMonitor
+	interfaceMonitor  tun.DefaultInterfaceMonitor
+	packageManager    tun.PackageManager
+	powerListener     winpowrprof.EventListener
+	pauseManager      pause.Manager
+	platformInterface platform.Interface
+	outboundManager   adapter.OutboundManager
+	wifiState         adapter.WIFIState
+	started           bool
 }
 }
 
 
 func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) {
 func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) {
@@ -55,7 +64,7 @@ func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOp
 		platformInterface:   service.FromContext[platform.Interface](ctx),
 		platformInterface:   service.FromContext[platform.Interface](ctx),
 		outboundManager:     service.FromContext[adapter.OutboundManager](ctx),
 		outboundManager:     service.FromContext[adapter.OutboundManager](ctx),
 	}
 	}
-	usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil && nm.platformInterface.UsePlatformDefaultInterfaceMonitor()
+	usePlatformDefaultInterfaceMonitor := nm.platformInterface != nil
 	enforceInterfaceMonitor := routeOptions.AutoDetectInterface
 	enforceInterfaceMonitor := routeOptions.AutoDetectInterface
 	if !usePlatformDefaultInterfaceMonitor {
 	if !usePlatformDefaultInterfaceMonitor {
 		networkMonitor, err := tun.NewNetworkUpdateMonitor(logger)
 		networkMonitor, err := tun.NewNetworkUpdateMonitor(logger)
@@ -90,17 +99,17 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
 	monitor := taskmonitor.New(r.logger, C.StartTimeout)
 	monitor := taskmonitor.New(r.logger, C.StartTimeout)
 	switch stage {
 	switch stage {
 	case adapter.StartStateInitialize:
 	case adapter.StartStateInitialize:
-		if r.interfaceMonitor != nil {
-			monitor.Start("initialize interface monitor")
-			err := r.interfaceMonitor.Start()
+		if r.networkMonitor != nil {
+			monitor.Start("initialize network monitor")
+			err := r.networkMonitor.Start()
 			monitor.Finish()
 			monitor.Finish()
 			if err != nil {
 			if err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
-		if r.networkMonitor != nil {
-			monitor.Start("initialize network monitor")
-			err := r.networkMonitor.Start()
+		if r.interfaceMonitor != nil {
+			monitor.Start("initialize interface monitor")
+			err := r.interfaceMonitor.Start()
 			monitor.Finish()
 			monitor.Finish()
 			if err != nil {
 			if err != nil {
 				return err
 				return err
@@ -151,20 +160,6 @@ func (r *NetworkManager) Start(stage adapter.StartStage) error {
 func (r *NetworkManager) Close() error {
 func (r *NetworkManager) Close() error {
 	monitor := taskmonitor.New(r.logger, C.StopTimeout)
 	monitor := taskmonitor.New(r.logger, C.StopTimeout)
 	var err error
 	var err error
-	if r.interfaceMonitor != nil {
-		monitor.Start("close interface monitor")
-		err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
-			return E.Cause(err, "close interface monitor")
-		})
-		monitor.Finish()
-	}
-	if r.networkMonitor != nil {
-		monitor.Start("close network monitor")
-		err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
-			return E.Cause(err, "close network monitor")
-		})
-		monitor.Finish()
-	}
 	if r.packageManager != nil {
 	if r.packageManager != nil {
 		monitor.Start("close package manager")
 		monitor.Start("close package manager")
 		err = E.Append(err, r.packageManager.Close(), func(err error) error {
 		err = E.Append(err, r.packageManager.Close(), func(err error) error {
@@ -179,6 +174,20 @@ func (r *NetworkManager) Close() error {
 		})
 		})
 		monitor.Finish()
 		monitor.Finish()
 	}
 	}
+	if r.interfaceMonitor != nil {
+		monitor.Start("close interface monitor")
+		err = E.Append(err, r.interfaceMonitor.Close(), func(err error) error {
+			return E.Cause(err, "close interface monitor")
+		})
+		monitor.Finish()
+	}
+	if r.networkMonitor != nil {
+		monitor.Start("close network monitor")
+		err = E.Append(err, r.networkMonitor.Close(), func(err error) error {
+			return E.Cause(err, "close network monitor")
+		})
+		monitor.Finish()
+	}
 	return nil
 	return nil
 }
 }
 
 
@@ -187,18 +196,75 @@ func (r *NetworkManager) InterfaceFinder() control.InterfaceFinder {
 }
 }
 
 
 func (r *NetworkManager) UpdateInterfaces() error {
 func (r *NetworkManager) UpdateInterfaces() error {
-	if r.platformInterface == nil || !r.platformInterface.UsePlatformInterfaceGetter() {
+	if r.platformInterface == nil {
 		return r.interfaceFinder.Update()
 		return r.interfaceFinder.Update()
 	} else {
 	} else {
 		interfaces, err := r.platformInterface.Interfaces()
 		interfaces, err := r.platformInterface.Interfaces()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		r.interfaceFinder.UpdateInterfaces(interfaces)
+		if C.IsDarwin {
+			err = r.interfaceFinder.Update()
+			if err != nil {
+				return err
+			}
+			// NEInterface only provides name,index and type
+			interfaces = common.Map(interfaces, func(it adapter.NetworkInterface) adapter.NetworkInterface {
+				iif, _ := r.interfaceFinder.ByIndex(it.Index)
+				if iif != nil {
+					it.Interface = *iif
+				}
+				return it
+			})
+		} else {
+			r.interfaceFinder.UpdateInterfaces(common.Map(interfaces, func(it adapter.NetworkInterface) control.Interface { return it.Interface }))
+		}
+		oldInterfaces := r.networkInterfaces.Load()
+		newInterfaces := common.Filter(interfaces, func(it adapter.NetworkInterface) bool {
+			return it.Flags&net.FlagUp != 0
+		})
+		r.networkInterfaces.Store(newInterfaces)
+		if len(newInterfaces) > 0 && !slices.EqualFunc(oldInterfaces, newInterfaces, func(oldInterface adapter.NetworkInterface, newInterface adapter.NetworkInterface) bool {
+			return oldInterface.Interface.Index == newInterface.Interface.Index &&
+				oldInterface.Interface.Name == newInterface.Interface.Name &&
+				oldInterface.Interface.Flags == newInterface.Interface.Flags &&
+				oldInterface.Type == newInterface.Type &&
+				oldInterface.Expensive == newInterface.Expensive &&
+				oldInterface.Constrained == newInterface.Constrained
+		}) {
+			r.logger.Info("updated available networks: ", strings.Join(common.Map(newInterfaces, func(it adapter.NetworkInterface) string {
+				var options []string
+				options = append(options, F.ToString(it.Type))
+				if it.Expensive {
+					options = append(options, "expensive")
+				}
+				if it.Constrained {
+					options = append(options, "constrained")
+				}
+				return F.ToString(it.Name, " (", strings.Join(options, ", "), ")")
+			}), ", "))
+		}
 		return nil
 		return nil
 	}
 	}
 }
 }
 
 
+func (r *NetworkManager) DefaultNetworkInterface() *adapter.NetworkInterface {
+	iif := r.interfaceMonitor.DefaultInterface()
+	if iif == nil {
+		return nil
+	}
+	for _, it := range r.networkInterfaces.Load() {
+		if it.Interface.Index == iif.Index {
+			return &it
+		}
+	}
+	return &adapter.NetworkInterface{Interface: *iif}
+}
+
+func (r *NetworkManager) NetworkInterfaces() []adapter.NetworkInterface {
+	return r.networkInterfaces.Load()
+}
+
 func (r *NetworkManager) DefaultInterface() string {
 func (r *NetworkManager) DefaultInterface() string {
 	return r.defaultInterface
 	return r.defaultInterface
 }
 }
@@ -220,18 +286,17 @@ func (r *NetworkManager) AutoDetectInterfaceFunc() control.Func {
 		}
 		}
 		return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
 		return control.BindToInterfaceFunc(r.interfaceFinder, func(network string, address string) (interfaceName string, interfaceIndex int, err error) {
 			remoteAddr := M.ParseSocksaddr(address).Addr
 			remoteAddr := M.ParseSocksaddr(address).Addr
-			if C.IsLinux {
-				interfaceName, interfaceIndex = r.interfaceMonitor.DefaultInterface(remoteAddr)
-				if interfaceIndex == -1 {
-					err = tun.ErrNoRoute
-				}
-			} else {
-				interfaceIndex = r.interfaceMonitor.DefaultInterfaceIndex(remoteAddr)
-				if interfaceIndex == -1 {
-					err = tun.ErrNoRoute
+			if remoteAddr.IsValid() {
+				iif, err := r.interfaceFinder.ByAddr(remoteAddr)
+				if err == nil {
+					return iif.Name, iif.Index, nil
 				}
 				}
 			}
 			}
-			return
+			defaultInterface := r.interfaceMonitor.DefaultInterface()
+			if defaultInterface == nil {
+				return "", -1, tun.ErrNoRoute
+			}
+			return defaultInterface.Name, defaultInterface.Index, nil
 		})
 		})
 	}
 	}
 }
 }
@@ -285,6 +350,12 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
 		r.logger.Error("missing default interface")
 		r.logger.Error("missing default interface")
 	} else {
 	} else {
 		r.pauseManager.NetworkWake()
 		r.pauseManager.NetworkWake()
+		defaultInterface := r.DefaultNetworkInterface()
+		if defaultInterface == nil {
+			panic("invalid interface context")
+		}
+		var options []string
+		options = append(options, F.ToString("index ", defaultInterface.Index))
 		if C.IsAndroid && r.platformInterface == nil {
 		if C.IsAndroid && r.platformInterface == nil {
 			var vpnStatus string
 			var vpnStatus string
 			if r.interfaceMonitor.AndroidVPNEnabled() {
 			if r.interfaceMonitor.AndroidVPNEnabled() {
@@ -292,17 +363,24 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
 			} else {
 			} else {
 				vpnStatus = "disabled"
 				vpnStatus = "disabled"
 			}
 			}
-			r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()), ", vpn ", vpnStatus)
+			options = append(options, "vpn "+vpnStatus)
 		} else {
 		} else {
-			r.logger.Info("updated default interface ", r.interfaceMonitor.DefaultInterfaceName(netip.IPv4Unspecified()), ", index ", r.interfaceMonitor.DefaultInterfaceIndex(netip.IPv4Unspecified()))
+			if defaultInterface.Type != "" {
+				options = append(options, F.ToString("type ", defaultInterface.Type))
+			}
+			if defaultInterface.Expensive {
+				options = append(options, "expensive")
+			}
+			if defaultInterface.Constrained {
+				options = append(options, "constrained")
+			}
 		}
 		}
+		r.logger.Info("updated default interface ", defaultInterface.Name, ", ", strings.Join(options, ", "))
 		if r.platformInterface != nil {
 		if r.platformInterface != nil {
 			state := r.platformInterface.ReadWIFIState()
 			state := r.platformInterface.ReadWIFIState()
 			if state != r.wifiState {
 			if state != r.wifiState {
 				r.wifiState = state
 				r.wifiState = state
-				if state.SSID == "" && state.BSSID == "" {
-					r.logger.Info("updated WIFI state: disconnected")
-				} else {
+				if state.SSID != "" {
 					r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
 					r.logger.Info("updated WIFI state: SSID=", state.SSID, ", BSSID=", state.BSSID)
 				}
 				}
 			}
 			}
@@ -312,7 +390,6 @@ func (r *NetworkManager) notifyNetworkUpdate(event int) {
 	if !r.started {
 	if !r.started {
 		return
 		return
 	}
 	}
-
 	r.ResetNetwork()
 	r.ResetNetwork()
 }
 }
 
 

+ 11 - 10
transport/dhcp/server.go

@@ -119,18 +119,19 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
 	return nil, err
 	return nil, err
 }
 }
 
 
-func (t *Transport) fetchInterface() (*net.Interface, error) {
-	interfaceName := t.interfaceName
+func (t *Transport) fetchInterface() (*control.Interface, error) {
 	if t.autoInterface {
 	if t.autoInterface {
 		if t.networkManager.InterfaceMonitor() == nil {
 		if t.networkManager.InterfaceMonitor() == nil {
 			return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
 			return nil, E.New("missing monitor for auto DHCP, set route.auto_detect_interface")
 		}
 		}
-		interfaceName = t.networkManager.InterfaceMonitor().DefaultInterfaceName(netip.Addr{})
-	}
-	if interfaceName == "" {
-		return nil, E.New("missing default interface")
+		defaultInterface := t.networkManager.InterfaceMonitor().DefaultInterface()
+		if defaultInterface == nil {
+			return nil, E.New("missing default interface")
+		}
+		return defaultInterface, nil
+	} else {
+		return t.networkManager.InterfaceFinder().ByName(t.interfaceName)
 	}
 	}
-	return net.InterfaceByName(interfaceName)
 }
 }
 
 
 func (t *Transport) fetchServers() error {
 func (t *Transport) fetchServers() error {
@@ -172,7 +173,7 @@ func (t *Transport) interfaceUpdated(int) {
 	}
 	}
 }
 }
 
 
-func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) error {
+func (t *Transport) fetchServers0(ctx context.Context, iface *control.Interface) error {
 	var listener net.ListenConfig
 	var listener net.ListenConfig
 	listener.Control = control.Append(listener.Control, control.BindToInterface(t.networkManager.InterfaceFinder(), iface.Name, iface.Index))
 	listener.Control = control.Append(listener.Control, control.BindToInterface(t.networkManager.InterfaceFinder(), iface.Name, iface.Index))
 	listener.Control = control.Append(listener.Control, control.ReuseAddr())
 	listener.Control = control.Append(listener.Control, control.ReuseAddr())
@@ -206,7 +207,7 @@ func (t *Transport) fetchServers0(ctx context.Context, iface *net.Interface) err
 	return group.Run(ctx)
 	return group.Run(ctx)
 }
 }
 
 
-func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.PacketConn, transactionID dhcpv4.TransactionID) error {
+func (t *Transport) fetchServersResponse(iface *control.Interface, packetConn net.PacketConn, transactionID dhcpv4.TransactionID) error {
 	buffer := buf.NewSize(dhcpv4.MaxMessageSize)
 	buffer := buf.NewSize(dhcpv4.MaxMessageSize)
 	defer buffer.Release()
 	defer buffer.Release()
 
 
@@ -246,7 +247,7 @@ func (t *Transport) fetchServersResponse(iface *net.Interface, packetConn net.Pa
 	}
 	}
 }
 }
 
 
-func (t *Transport) recreateServers(iface *net.Interface, serverAddrs []netip.Addr) error {
+func (t *Transport) recreateServers(iface *control.Interface, serverAddrs []netip.Addr) error {
 	if len(serverAddrs) > 0 {
 	if len(serverAddrs) > 0 {
 		t.options.Logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
 		t.options.Logger.Info("dhcp: updated DNS servers from ", iface.Name, ": [", strings.Join(common.Map(serverAddrs, func(it netip.Addr) string {
 			return it.String()
 			return it.String()