Skip to content

group: Fix interrupt_exist_connections for routed connections#4285

Open
xxspa wants to merge 2 commits into
SagerNet:testingfrom
xxspa:fix-selector-interrupt-routed
Open

group: Fix interrupt_exist_connections for routed connections#4285
xxspa wants to merge 2 commits into
SagerNet:testingfrom
xxspa:fix-selector-interrupt-routed

Conversation

@xxspa

@xxspa xxspa commented Jul 12, 2026

Copy link
Copy Markdown

Fixes #4281.

Root cause

Connections routed to a selector (as a route target or route.final) enter through the ConnectionHandler path (Selector.NewConnection / NewPacketConnection). Neither branch of that path goes through Selector.DialContext / ListenPacket, so these connections were never registered in the selector's interrupt group. SelectOutbound then interrupted an empty set, and existing connections kept flowing over the previously selected outbound — making interrupt_exist_connections a near no-op for user traffic.

URLTest is not affected on this path because its NewConnection passes itself as the dialer, so dialing goes through URLTest.DialContext, which registers the connection.

Fix

  • Register the inbound connection in the selector's interrupt group (as an external connection) at the top of NewConnection / NewPacketConnection, covering both the nested-handler branch and the connection-manager branch. Simply passing the selector as the dialer (URLTest-style) would not be enough: members that only implement ConnectionHandler (e.g. the dns outbound) need the handler branch, and connections handled by nested group members are only registered in the inner group.
  • Add SingPacketConn / Group.NewSingPacketConn to common/interrupt so an N.PacketConn can be registered, mirroring the existing wrappers (with Upstream / ReaderReplaceable / WriterReplaceable pass-through).

Tests

New end-to-end tests in test/group_test.go (full box instance, SOCKS inbound, selector as route.final, direct members, local echo servers):

  • TestSelectorInterruptRoutedConnection: an established TCP connection must be closed after SelectOutbound when interrupt_exist_connections is enabled — failed before this fix (read timed out), passes now.
  • TestSelectorInterruptRoutedPacketConnection: a UDP session must be re-established over a new outbound socket after switching — failed before this fix (same source observed by the echo server), passes now.
  • TestSelectorKeepRoutedConnection: with interrupt_exist_connections disabled, existing connections must survive switching — passes before and after, guarding the default semantics.

Note: the test module's go.mod / go.sum are currently behind the main module (missing entries for sing-tun/gtcpip/checksum imported by common/tlsspoof), so running the tests requires a go mod tidy in test/ first; that sync is intentionally not included in this PR.

Add SingPacketConn and Group.NewSingPacketConn to allow registering
N.PacketConn connections in an interrupt group, mirroring the existing
net.Conn and net.PacketConn wrappers.
@xxspa xxspa force-pushed the fix-selector-interrupt-routed branch from 11776c6 to 09e819f Compare July 12, 2026 08:15
Connections routed to a selector (route target or final) enter through
the ConnectionHandler path (NewConnection/NewPacketConnection), which
never goes through Selector.DialContext/ListenPacket, so they were
never registered in the interrupt group. As a result, SelectOutbound
interrupted nothing and existing connections kept flowing over the
previously selected outbound.

Register the inbound connection in the interrupt group as an external
connection on both handler branches, so switching the selected outbound
interrupts routed connections when interrupt_exist_connections is
enabled, while leaving them untouched by default.

Fixes SagerNet#4281.
@xxspa xxspa force-pushed the fix-selector-interrupt-routed branch from 09e819f to 07460d1 Compare July 12, 2026 08:17
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.

selector 的 interrupt_exist_connections 对经路由的连接不生效(切换所选成员后存量连接不被中断)

2 participants