feat: configurable traceroute hop limit and TTL decrement

Add max_traceroute_hop_limit configuration for TUN inbound and enable
ICMP TTL decrement on WireGuard and Tailscale endpoints for VPN server mode.

- Add MaxTracerouteHopLimit option to TUN inbound configuration
- Pass local addresses to DirectRoute wrapper for TTL decrement
- Use DirectRoute wrapper for WireGuard and Tailscale stacks
- Enable ICMP TTL decrement on WG and Tailscale servers
This commit is contained in:
Sheldon Qi 2026-05-04 12:29:32 +08:00
parent 793b81c190
commit d914eeac83
No known key found for this signature in database
4 changed files with 33 additions and 9 deletions

View file

@ -44,6 +44,7 @@ type TunInboundOptions struct {
IncludeMACAddress badoption.Listable[string] `json:"include_mac_address,omitempty"`
ExcludeMACAddress badoption.Listable[string] `json:"exclude_mac_address,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
MaxTracerouteHopLimit uint8 `json:"max_traceroute_hop_limit,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InboundOptions

View file

@ -23,6 +23,8 @@ import (
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/common/dialer"
@ -31,7 +33,7 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-tun"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing-tun/ping"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
@ -398,6 +400,20 @@ func (t *Endpoint) postStart() error {
icmpForwarder := tun.NewICMPForwarder(t.ctx, ipStack, t, t.udpTimeout)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
// Wrap Tailscale's existing TCP/UDP handlers with DirectRoute for traceroute support.
// Low-TTL packets are handled via DirectRoute (ICMP Time Exceeded), while
// normal packets are passed to Tailscale's original netstack handlers.
tsAddr4, tsAddr6 := t.server.TailscaleIPs()
if prevTCP := ipStack.GetTransportProtocolHandler(tcp.ProtocolNumber); prevTCP != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber,
tun.WrapTCPHandlerWithDirectRoute(ipStack, t, icmpForwarder, t.udpTimeout, 0, tsAddr4, tsAddr6, prevTCP))
}
if prevUDP := ipStack.GetTransportProtocolHandler(udp.ProtocolNumber); prevUDP != nil {
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber,
tun.WrapUDPHandlerWithDirectRoute(ipStack, t, icmpForwarder, t.udpTimeout, 0, tsAddr4, tsAddr6, prevUDP))
}
t.stack = ipStack
t.icmpForwarder = icmpForwarder
t.registerNetstackHandlers()
@ -798,6 +814,7 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf
}
}
t.icmpForwarder.SetLocalAddresses(inet4Address, inet6Address)
t.icmpForwarder.SetTTLDecrement(inet4Address, inet6Address, 0)
t.cfg = cfg
t.dnsCfg = dnsCfg

View file

@ -17,7 +17,7 @@ import (
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-tun"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json/badoption"
@ -43,6 +43,7 @@ type Inbound struct {
tunOptions tun.Options
udpTimeout time.Duration
dnsHijackAddress []netip.Addr
maxTracerouteHopLimit uint8
stack string
tunIf tun.Tun
tunStack tun.Stack
@ -221,10 +222,11 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
InterfaceMonitor: networkManager.InterfaceMonitor(),
EXP_MultiPendingPackets: multiPendingPackets,
},
udpTimeout: udpTimeout,
stack: options.Stack,
platformInterface: platformInterface,
platformOptions: common.PtrValueOrDefault(options.Platform),
udpTimeout: udpTimeout,
maxTracerouteHopLimit: options.MaxTracerouteHopLimit,
stack: options.Stack,
platformInterface: platformInterface,
platformOptions: common.PtrValueOrDefault(options.Platform),
}
for _, routeAddressSet := range options.RouteAddressSet {
ruleSet, loaded := router.RuleSet(routeAddressSet)
@ -409,6 +411,7 @@ func (t *Inbound) Start(stage adapter.StartStage) error {
ForwarderBindInterface: forwarderBindInterface,
InterfaceFinder: t.networkManager.InterfaceFinder(),
IncludeAllNetworks: includeAllNetworks,
MaxTracerouteHopLimit: t.maxTracerouteHopLimit,
})
if err != nil {
return err

View file

@ -18,7 +18,7 @@ import (
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-tun"
tun "github.com/sagernet/sing-tun"
"github.com/sagernet/sing-tun/ping"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
@ -74,10 +74,13 @@ func newSystemStackDevice(options DeviceOptions) (*systemStackDevice, error) {
}
}
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.NewUDPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout).HandlePacket)
icmpForwarder := tun.NewICMPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout)
icmpForwarder.SetLocalAddresses(inet4Address, inet6Address)
icmpForwarder.SetTTLDecrement(inet4Address, inet6Address, 0)
tcpHandler := tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket
udpHandler := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, options.UDPTimeout).HandlePacket
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.WrapTCPHandlerWithDirectRoute(ipStack, options.Handler, icmpForwarder, options.UDPTimeout, 0, inet4Address, inet6Address, tcpHandler))
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, tun.WrapUDPHandlerWithDirectRoute(ipStack, options.Handler, icmpForwarder, options.UDPTimeout, 0, inet4Address, inet6Address, udpHandler))
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
}