Skip to content

Commit 4bd3ae5

Browse files
committed
chore: dialer will consider the routing of the local interface when auto-detect-interface in tun is enabled
for MetaCubeX#1881 MetaCubeX#1819
1 parent 00e6466 commit 4bd3ae5

File tree

5 files changed

+65
-31
lines changed

5 files changed

+65
-31
lines changed

component/dialer/dialer.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.
8888
if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CMFA)
8989
socketHookToListenConfig(lc)
9090
} else {
91+
if cfg.interfaceName == "" {
92+
if finder := DefaultInterfaceFinder.Load(); finder != nil {
93+
cfg.interfaceName = finder.FindInterfaceName(rAddrPort.Addr())
94+
}
95+
}
96+
if rAddrPort.Addr().Unmap().IsLoopback() {
97+
// avoid "The requested address is not valid in its context."
98+
cfg.interfaceName = ""
99+
}
91100
if cfg.interfaceName != "" {
92101
bind := bindIfaceToListenConfig
93102
if cfg.fallbackBind {
@@ -153,6 +162,11 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
153162
if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CMFA)
154163
socketHookToToDialer(dialer)
155164
} else {
165+
if opt.interfaceName == "" {
166+
if finder := DefaultInterfaceFinder.Load(); finder != nil {
167+
opt.interfaceName = finder.FindInterfaceName(destination)
168+
}
169+
}
156170
if opt.interfaceName != "" {
157171
bind := bindIfaceToDialer
158172
if opt.fallbackBind {
@@ -373,12 +387,7 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (net.C
373387
}
374388

375389
func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
376-
opt := d.Opt // make a copy
377-
if rAddrPort.Addr().Unmap().IsLoopback() {
378-
// avoid "The requested address is not valid in its context."
379-
WithInterface("")(&opt)
380-
}
381-
return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(opt))
390+
return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(d.Opt))
382391
}
383392

384393
func NewDialer(options ...Option) Dialer {

component/dialer/options.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dialer
33
import (
44
"context"
55
"net"
6+
"net/netip"
67

78
"github.com/metacubex/mihomo/common/atomic"
89
"github.com/metacubex/mihomo/component/resolver"
@@ -12,8 +13,14 @@ var (
1213
DefaultOptions []Option
1314
DefaultInterface = atomic.NewTypedValue[string]("")
1415
DefaultRoutingMark = atomic.NewInt32(0)
16+
17+
DefaultInterfaceFinder = atomic.NewTypedValue[InterfaceFinder](nil)
1518
)
1619

20+
type InterfaceFinder interface {
21+
FindInterfaceName(destination netip.Addr) string
22+
}
23+
1724
type NetDialer interface {
1825
DialContext(ctx context.Context, network, address string) (net.Conn, error)
1926
}

listener/listener.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,6 @@ func ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) {
512512
}()
513513

514514
if tunConf.Equal(LastTunConf) {
515-
if tunLister != nil {
516-
tunLister.FlushDefaultInterface()
517-
}
518515
return
519516
}
520517

listener/sing_tun/server.go

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ type Listener struct {
5252
autoRedirect tun.AutoRedirect
5353
autoRedirectOutputMark int32
5454

55+
cDialerInterfaceFinder dialer.InterfaceFinder
56+
5557
ruleUpdateCallbackCloser io.Closer
5658
ruleUpdateMutex sync.Mutex
5759
routeAddressMap map[string]*netipx.IPSet
@@ -290,13 +292,25 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
290292
}
291293
l.defaultInterfaceMonitor = defaultInterfaceMonitor
292294
defaultInterfaceMonitor.RegisterCallback(func(event int) {
293-
l.FlushDefaultInterface()
295+
iface.FlushCache()
296+
resolver.ResetConnection() // reset resolver's connection after default interface changed
294297
})
295298
err = defaultInterfaceMonitor.Start()
296299
if err != nil {
297300
err = E.Cause(err, "start DefaultInterfaceMonitor")
298301
return
299302
}
303+
304+
if options.AutoDetectInterface {
305+
l.cDialerInterfaceFinder = &cDialerInterfaceFinder{
306+
tunName: tunName,
307+
defaultInterfaceMonitor: defaultInterfaceMonitor,
308+
}
309+
if !dialer.DefaultInterfaceFinder.CompareAndSwap(nil, l.cDialerInterfaceFinder) {
310+
err = E.New("don't allowed two tun listener using auto-detect-interface")
311+
return
312+
}
313+
}
300314
}
301315

302316
tunOptions := tun.Options{
@@ -503,27 +517,25 @@ func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool,
503517
}
504518
}
505519

506-
func (l *Listener) FlushDefaultInterface() {
507-
if l.options.AutoDetectInterface && l.defaultInterfaceMonitor != nil {
508-
for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} {
509-
autoDetectInterfaceName := l.defaultInterfaceMonitor.DefaultInterfaceName(destination)
510-
if autoDetectInterfaceName == l.tunName {
511-
log.Warnln("[TUN] Auto detect interface by %s get same name with tun", destination.String())
512-
} else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "<nil>" {
513-
log.Warnln("[TUN] Auto detect interface by %s get empty name.", destination.String())
514-
} else {
515-
if old := dialer.DefaultInterface.Swap(autoDetectInterfaceName); old != autoDetectInterfaceName {
516-
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, autoDetectInterfaceName)
517-
iface.FlushCache()
518-
resolver.ResetConnection() // reset resolver's connection after default interface changed
519-
}
520-
return
521-
}
522-
}
523-
if dialer.DefaultInterface.CompareAndSwap("", "<invalid>") {
524-
log.Warnln("[TUN] Auto detect interface failed, set '<invalid>' to DefaultInterface to avoid lookback")
520+
type cDialerInterfaceFinder struct {
521+
tunName string
522+
defaultInterfaceMonitor tun.DefaultInterfaceMonitor
523+
}
524+
525+
func (d *cDialerInterfaceFinder) FindInterfaceName(destination netip.Addr) string {
526+
for _, dest := range []netip.Addr{destination, netip.IPv4Unspecified(), netip.IPv6Unspecified()} {
527+
autoDetectInterfaceName := d.defaultInterfaceMonitor.DefaultInterfaceName(dest)
528+
if autoDetectInterfaceName == d.tunName {
529+
log.Warnln("[TUN] Auto detect interface for %s get same name with tun", destination.String())
530+
} else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "<nil>" {
531+
log.Warnln("[TUN] Auto detect interface for %s get empty name.", destination.String())
532+
} else {
533+
log.Debugln("[TUN] Auto detect interface for %s --> %s", destination, autoDetectInterfaceName)
534+
return autoDetectInterfaceName
525535
}
526536
}
537+
log.Warnln("[TUN] Auto detect interface for %s failed, return '<invalid>' to avoid lookback", destination)
538+
return "<invalid>"
527539
}
528540

529541
func uidToRange(uidList []uint32) []ranges.Range[uint32] {
@@ -564,6 +576,9 @@ func (l *Listener) Close() error {
564576
if l.autoRedirectOutputMark != 0 {
565577
dialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0)
566578
}
579+
if l.cDialerInterfaceFinder != nil {
580+
dialer.DefaultInterfaceFinder.CompareAndSwap(l.cDialerInterfaceFinder, nil)
581+
}
567582
return common.Close(
568583
l.ruleUpdateCallbackCloser,
569584
l.tunStack,

transport/hysteria/conns/faketcp/tcp_linux.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,14 @@ func Dial(network, address string) (*TCPConn, error) {
404404

405405
var lTcpAddr *net.TCPAddr
406406
var lIpAddr *net.IPAddr
407-
if ifaceName := dialer.DefaultInterface.Load(); len(ifaceName) > 0 {
408-
rAddrPort := raddr.AddrPort()
407+
rAddrPort := raddr.AddrPort()
408+
ifaceName := dialer.DefaultInterface.Load()
409+
if ifaceName == "" {
410+
if finder := dialer.DefaultInterfaceFinder.Load(); finder != nil {
411+
ifaceName = finder.FindInterfaceName(rAddrPort.Addr())
412+
}
413+
}
414+
if len(ifaceName) > 0 {
409415
addr, err := dialer.LookupLocalAddrFromIfaceName(ifaceName, network, rAddrPort.Addr(), int(rAddrPort.Port()))
410416
if err != nil {
411417
return nil, err

0 commit comments

Comments
 (0)