Fix redirect routes

This commit is contained in:
世界 2026-07-14 10:20:18 +08:00
parent 7954dd6e20
commit 9c6760589d
No known key found for this signature in database
GPG key ID: CD109927C34A63C4

View file

@ -5,7 +5,6 @@ package tun
import ( import (
"math/rand" "math/rand"
"net" "net"
"net/netip"
"github.com/sagernet/netlink" "github.com/sagernet/netlink"
"github.com/sagernet/sing/common" "github.com/sagernet/sing/common"
@ -69,9 +68,8 @@ func (r *autoRedirect) setupRedirectRoutes() error {
} }
func (r *autoRedirect) addRedirectRoutes(iface control.Interface) error { func (r *autoRedirect) addRedirectRoutes(iface control.Interface) error {
if r.enableIPv4 && common.Any(iface.Addresses, func(it netip.Prefix) bool { hasIPv4Address, hasIPv6Address := redirectRouteAddressFamilies(iface)
return it.Addr().Is4() if r.enableIPv4 && hasIPv4Address {
}) {
err := netlink.RouteAppend(&netlink.Route{ err := netlink.RouteAppend(&netlink.Route{
LinkIndex: iface.Index, LinkIndex: iface.Index,
Dst: &net.IPNet{IP: net.IPv4(127, 0, 0, 1), Mask: net.CIDRMask(32, 32)}, Dst: &net.IPNet{IP: net.IPv4(127, 0, 0, 1), Mask: net.CIDRMask(32, 32)},
@ -83,9 +81,7 @@ func (r *autoRedirect) addRedirectRoutes(iface control.Interface) error {
return E.Cause(err, "append ipv4 loopback route") return E.Cause(err, "append ipv4 loopback route")
} }
} }
if r.enableIPv6 && common.Any(iface.Addresses, func(it netip.Prefix) bool { if r.enableIPv6 && hasIPv6Address {
return it.Addr().Is6() && !it.Addr().Is4In6()
}) {
err := netlink.RouteAppend(&netlink.Route{ err := netlink.RouteAppend(&netlink.Route{
LinkIndex: iface.Index, LinkIndex: iface.Index,
Dst: &net.IPNet{IP: net.IPv6loopback, Mask: net.CIDRMask(128, 128)}, Dst: &net.IPNet{IP: net.IPv6loopback, Mask: net.CIDRMask(128, 128)},
@ -100,6 +96,18 @@ func (r *autoRedirect) addRedirectRoutes(iface control.Interface) error {
return nil return nil
} }
func redirectRouteAddressFamilies(iface control.Interface) (hasIPv4Address bool, hasIPv6Address bool) {
for _, prefix := range iface.Addresses {
address := prefix.Addr()
if address.Is4() {
hasIPv4Address = true
} else if address.Is6() && !address.Is4In6() {
hasIPv6Address = true
}
}
return
}
func (r *autoRedirect) removeRedirectRoutes(linkIndex int) { func (r *autoRedirect) removeRedirectRoutes(linkIndex int) {
if r.enableIPv4 { if r.enableIPv4 {
_ = netlink.RouteDel(&netlink.Route{ _ = netlink.RouteDel(&netlink.Route{
@ -128,20 +136,28 @@ func (r *autoRedirect) updateRedirectRoutes() error {
newInterfaces := common.Filter(r.interfaceFinder.Interfaces(), func(it control.Interface) bool { newInterfaces := common.Filter(r.interfaceFinder.Interfaces(), func(it control.Interface) bool {
return it.Name != "lo" && it.Name != tunName && it.Flags&net.FlagUp != 0 return it.Name != "lo" && it.Name != tunName && it.Flags&net.FlagUp != 0
}) })
oldMap := make(map[int]bool, len(r.redirectInterfaces)) oldMap := make(map[int]control.Interface, len(r.redirectInterfaces))
for _, iface := range r.redirectInterfaces { for _, iface := range r.redirectInterfaces {
oldMap[iface.Index] = true oldMap[iface.Index] = iface
} }
newMap := make(map[int]bool, len(newInterfaces)) newMap := make(map[int]bool, len(newInterfaces))
for _, iface := range newInterfaces { for _, iface := range newInterfaces {
newMap[iface.Index] = true newMap[iface.Index] = true
} }
for _, iface := range newInterfaces { for _, iface := range newInterfaces {
if !oldMap[iface.Index] { oldInterface, loaded := oldMap[iface.Index]
err = r.addRedirectRoutes(iface) if loaded {
if err != nil { oldHasIPv4Address, oldHasIPv6Address := redirectRouteAddressFamilies(oldInterface)
return E.Cause(err, "add redirect routes for ", iface.Name) hasIPv4Address, hasIPv6Address := redirectRouteAddressFamilies(iface)
if (!r.enableIPv4 || oldHasIPv4Address == hasIPv4Address) &&
(!r.enableIPv6 || oldHasIPv6Address == hasIPv6Address) {
continue
} }
r.removeRedirectRoutes(iface.Index)
}
err = r.addRedirectRoutes(iface)
if err != nil {
return E.Cause(err, "add redirect routes for ", iface.Name)
} }
} }
for _, iface := range r.redirectInterfaces { for _, iface := range r.redirectInterfaces {