Fix auto_redirect dropping SO_BINDTODEVICE traffic

REDIRECT in the OUTPUT chain rewrites the destination to 127.0.0.1,
then ip_route_me_harder() reroutes with the socket's bound interface
constraint (flowi4_oif). Since 127.0.0.1 is only reachable via lo,
the routing lookup fails and the packet is silently dropped.

Add a fallback routing table with `local 127.0.0.1` entries for each
non-loopback interface. When the local table lookup fails due to OIF
mismatch, the fallback table provides a matching RTN_LOCAL route.
The kernel then overrides dev_out to loopback (route.c:2857), so the
packet is delivered locally to the redirect server as intended.

This fixes NetworkManager connectivity checks and other tools that
use SO_BINDTODEVICE (e.g. curl --interface).
This commit is contained in:
世界 2026-03-11 15:06:55 +08:00
parent c81ce6d358
commit caaf8469e0
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
3 changed files with 212 additions and 23 deletions

View file

@ -19,29 +19,31 @@ import (
)
type autoRedirect struct {
tunOptions *Options
ctx context.Context
handler Handler
logger logger.Logger
tableName string
networkMonitor NetworkUpdateMonitor
networkListener *list.Element[NetworkUpdateCallback]
interfaceFinder control.InterfaceFinder
localAddresses []netip.Prefix
customRedirectPortFunc func() int
customRedirectPort int
redirectServer *redirectServer
enableIPv4 bool
enableIPv6 bool
iptablesPath string
ip6tablesPath string
useNFTables bool
androidSu bool
suPath string
routeAddressSet *[]*netipx.IPSet
routeExcludeAddressSet *[]*netipx.IPSet
nfqueueHandler *nfqueueHandler
nfqueueEnabled bool
tunOptions *Options
ctx context.Context
handler Handler
logger logger.Logger
tableName string
networkMonitor NetworkUpdateMonitor
networkListener *list.Element[NetworkUpdateCallback]
interfaceFinder control.InterfaceFinder
localAddresses []netip.Prefix
customRedirectPortFunc func() int
customRedirectPort int
redirectServer *redirectServer
enableIPv4 bool
enableIPv6 bool
iptablesPath string
ip6tablesPath string
useNFTables bool
androidSu bool
suPath string
routeAddressSet *[]*netipx.IPSet
routeExcludeAddressSet *[]*netipx.IPSet
nfqueueHandler *nfqueueHandler
nfqueueEnabled bool
redirectRouteTableIndex int
redirectInterfaces []control.Interface
}
func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) {
@ -152,6 +154,12 @@ func (r *autoRedirect) Start() error {
}
r.cleanupNFTables()
err = r.setupNFTables()
if err == nil && r.tunOptions.AutoRedirectMarkMode {
err = r.setupRedirectRoutes()
if err != nil {
r.cleanupNFTables()
}
}
} else {
r.cleanupIPTables()
err = r.setupIPTables()
@ -164,6 +172,7 @@ func (r *autoRedirect) Close() error {
r.nfqueueHandler.Close()
}
if r.useNFTables {
r.cleanupRedirectRoutes()
r.cleanupNFTables()
} else {
r.cleanupIPTables()