From 3263ae925506afbe905c13467e57f37b17f5a895 Mon Sep 17 00:00:00 2001 From: Jasper344612 <282825138+Jasper344612@users.noreply.github.com> Date: Sat, 4 Jul 2026 09:38:25 +0800 Subject: [PATCH] TUN inbound: Fix `autoOutboundsInterface` on Linux (#6413) Fixes https://github.com/XTLS/Xray-core/issues/6412 --- proxy/tun/tun_linux.go | 70 +++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/proxy/tun/tun_linux.go b/proxy/tun/tun_linux.go index b8eff0174..110809549 100644 --- a/proxy/tun/tun_linux.go +++ b/proxy/tun/tun_linux.go @@ -308,36 +308,37 @@ func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, erro return iface, nil } - probeIPs := []net.IP{ - net.ParseIP("8.8.8.8"), - net.ParseIP("2001:4860:4860::8888"), + for _, family := range []int{ + netlink.FAMILY_V4, + netlink.FAMILY_V6, + } { + iface, err := findDefaultInterface(family, tunIndex) + if err == nil { + return iface, nil + } } - for _, ip := range probeIPs { - routes, err := netlink.RouteGet(ip) - if err != nil || len(routes) == 0 { - continue - } - route := routes[0] - if route.LinkIndex == tunIndex { - continue + return nil, errors.New("no usable outbound interface found") +} + +func findDefaultInterface(family int, tunIndex int) (*net.Interface, error) { + routes, err := netlink.RouteList(nil, family) + if err != nil { + return nil, err + } + + var selected *net.Interface + selectedMetric := -1 + + for _, route := range routes { + if route.Dst != nil { + ones, _ := route.Dst.Mask.Size() + if ones != 0 { + continue + } } - link, err := netlink.LinkByIndex(route.LinkIndex) - if err != nil { - continue - } - attrs := link.Attrs() - - if attrs.Flags&net.FlagUp == 0 { - continue - } - operState := attrs.OperState - if operState != netlink.OperUp && operState != netlink.OperUnknown { - continue - } - - if route.Src == nil || route.Src.IsLoopback() || route.Src.IsLinkLocalUnicast() { + if route.LinkIndex == 0 || route.LinkIndex == tunIndex { continue } @@ -345,8 +346,21 @@ func findOutboundInterface(tunIndex int, fixedName string) (*net.Interface, erro if err != nil { continue } - return iface, nil + + if iface.Flags&net.FlagUp == 0 || + iface.Flags&net.FlagLoopback != 0 { + continue + } + + if selected == nil || route.Priority < selectedMetric { + selected = iface + selectedMetric = route.Priority + } } - return nil, errors.New("no usable outbound interface found") + if selected == nil { + return nil, errors.New("physical default route not found") + } + + return selected, nil }