Skip to content

Allow routing_mark when auto_redirect is enabled#4137

Open
loncharles wants to merge 1 commit into
SagerNet:testingfrom
loncharles:feat/routing-mark-auto-redirect-coexistence
Open

Allow routing_mark when auto_redirect is enabled#4137
loncharles wants to merge 1 commit into
SagerNet:testingfrom
loncharles:feat/routing-mark-auto-redirect-coexistence

Conversation

@loncharles
Copy link
Copy Markdown

@loncharles loncharles commented May 13, 2026

Summary

  • Remove the blanket rejection of routing_mark when auto_redirect
    is active — the two can now coexist
  • Validate that routing_mark does not use the low 16 bits reserved
    by auto_redirect for loop prevention
  • Use read-OR-write pattern for socket marks so auto_redirect and
    routing_mark bits are combined, not overwritten

Depends on: SagerNet/sing-tun#71

Motivation

routing_mark is needed for policy routing (e.g., WAN selection) on
outbound connections. auto_redirect is needed for transparent
proxying of forwarded traffic. There is no fundamental conflict — the
previous rejection was a safety measure because the mark field
operations would clobber each other. With sing-tun's mark field now
partitioned (low 16 for auto_redirect, high 16 for routing_mark), the
safety concern is resolved.

Changes

common/dialer/default.go:

  • setMarkWrapper: instead of rejecting, validates no bit overlap
    with AutoRedirectMarkMask, then reads current SO_MARK and ORs in
    the routing_mark

route/network.go:

  • AutoRedirectOutputMarkFunc: changed from full overwrite to
    read-OR-write pattern
  • AutoRedirectMarkMask(): new getter, returns the mask from sing-tun

adapter/network.go:

  • AutoRedirectMarkMask() uint32 added to NetworkManager interface

Known limitation

Inbound listener sockets (common/listener/) apply routing_mark
via control.RoutingMark() directly, without the read-OR-write
pattern. This is not a functional issue — inbound listeners are server
sockets that don't originate connections through the TUN redirect
chain — but it is a code path that does not participate in mark
coexistence. A future refactor could unify the mark-setting paths.

Test plan

  • routing_mark values in upper 16 bits accepted and applied
  • routing_mark values in lower 16 bits rejected with clear error
  • Combined mark (e.g., 0x32024) visible on socket
  • Routing policy applied based on upper mark bits
  • Multi-hop chains (VPN→Tor) preserve correct marks
  • Stealth consumer forwarded traffic captured via PREROUTING

Previously routing_mark was unconditionally rejected when auto_redirect
was enabled. With the sing-tun mark field partitioned into low 16 bits
(auto_redirect) and high 16 bits (routing_mark), the two can coexist.

- Change AutoRedirectOutputMarkFunc to read-OR-write pattern,
  preserving existing mark bits on the socket
- Change setMarkWrapper to OR routing_mark onto existing socket mark
  instead of rejecting when auto_redirect is active
- Validate routing_mark against the full reserved mask (low 16 bits),
  not just the output mark value
- Add AutoRedirectMarkMask to NetworkManager interface
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant