Add L3 forwarding support

This commit is contained in:
世界
2026-07-06 14:26:32 +08:00
parent e9b86275fa
commit eaec0cf88b
24 changed files with 763 additions and 646 deletions
+25 -59
View File
@@ -16,7 +16,6 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
@@ -99,7 +98,6 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
tunMTU := options.MTU
enableGSO := C.IsLinux && options.Stack == "gvisor" && platformInterface == nil && tunMTU > 0 && tunMTU < 49152
if tunMTU == 0 {
if platformInterface != nil && platformInterface.UnderNetworkExtension() {
// In Network Extension, when MTU exceeds 4064 (4096-UTUN_IF_HEADROOM_SIZE), the performance of tun will drop significantly, which may be a system bug.
@@ -111,6 +109,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
tunMTU = 65535
}
}
var enableGSO bool
if C.IsLinux && platformInterface == nil {
enableGSO = (options.Stack == "gvisor" && tunMTU < 49152)
}
var udpTimeout time.Duration
if options.UDPTimeout != 0 {
udpTimeout = time.Duration(options.UDPTimeout)
@@ -178,7 +180,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
excludeMACAddress = append(excludeMACAddress, mac)
}
networkManager := service.FromContext[adapter.NetworkManager](ctx)
multiPendingPackets := C.IsDarwin && ((options.Stack == "gvisor" && tunMTU < 32768) || (options.Stack != "gvisor" && options.MTU <= 9000))
multiPendingPackets := C.IsDarwin && ((options.Stack == "gvisor" && tunMTU < 32768) || (options.Stack != "gvisor" && tunMTU <= 9000))
inbound := &Inbound{
tag: tag,
ctx: ctx,
@@ -320,6 +322,22 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
t.dnsHijackAddress = append(inet4DNSAddress, inet6DNSAddress...)
}
case adapter.StartStateStart:
if t.platformInterface == nil &&
((C.IsLinux && !t.tunOptions.GSO) || (C.IsDarwin && !t.tunOptions.EXP_MultiPendingPackets)) {
endpointManager := service.FromContext[adapter.EndpointManager](t.ctx)
if endpointManager != nil {
for _, managedEndpoint := range endpointManager.Endpoints() {
if _, isFlowOutbound := managedEndpoint.(adapter.FlowOutbound); isFlowOutbound {
if C.IsLinux {
t.tunOptions.GSO = true
} else {
t.tunOptions.EXP_MultiPendingPackets = true
}
break
}
}
}
}
if C.IsAndroid && t.platformInterface == nil {
t.tunOptions.BuildAndroidRules(t.networkManager.PackageManager())
}
@@ -460,34 +478,8 @@ func (t *Inbound) Close() error {
)
}
func (t *Inbound) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
var ipVersion uint8
if !destination.IsIPv6() {
ipVersion = 4
} else {
ipVersion = 6
}
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
Inbound: t.tag,
InboundType: C.TypeTun,
IPVersion: ipVersion,
Network: network,
Source: source,
Destination: destination,
}, routeContext, timeout, false)
if err != nil {
switch {
case rule.IsBypassed(err):
err = nil
case rule.IsRejected(err):
t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
default:
if network == N.NetworkICMP {
t.logger.Warn(E.Cause(err, "link ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()))
}
}
}
return routeDestination, err
func (t *Inbound) JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort) tun.FlowVerdict {
return adapter.JudgeFlow(t.router, t.tag, C.TypeTun, network, source, destination)
}
func (t *Inbound) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
@@ -534,34 +526,8 @@ func (t *Inbound) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
type autoRedirectHandler Inbound
func (t *autoRedirectHandler) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
var ipVersion uint8
if !destination.IsIPv6() {
ipVersion = 4
} else {
ipVersion = 6
}
routeDestination, err := t.router.PreMatch(adapter.InboundContext{
Inbound: t.tag,
InboundType: C.TypeTun,
IPVersion: ipVersion,
Network: network,
Source: source,
Destination: destination,
}, routeContext, timeout, true)
if err != nil {
switch {
case rule.IsBypassed(err):
t.logger.Trace("bypass ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
case rule.IsRejected(err):
t.logger.Trace("reject ", network, " connection from ", source.AddrString(), " to ", destination.AddrString())
default:
if network == N.NetworkICMP {
t.logger.Warn(E.Cause(err, "link ", network, " connection from ", source.AddrString(), " to ", destination.AddrString()))
}
}
}
return routeDestination, err
func (t *autoRedirectHandler) JudgeFlow(network uint8, source netip.AddrPort, destination netip.AddrPort) tun.FlowVerdict {
return (*Inbound)(t).JudgeFlow(network, source, destination)
}
func (t *autoRedirectHandler) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {