Skip to content

Fix iptable fallback first hop - #2888

Draft
frezzzze wants to merge 1 commit into
OutlineFoundation:masterfrom
frezzzze:fix-iptable-fallback-firsthop
Draft

frezzzze wants to merge 1 commit into
OutlineFoundation:masterfrom
frezzzze:fix-iptable-fallback-firsthop

Conversation

@frezzzze

@frezzzze frezzzze commented Oct 9, 2026

Copy link
Copy Markdown

Fixes #2887

The iptable stream dialer drops the FirstHop value from its fallback dialer. When TCP uses iptable with a Shadowsocks fallback and UDP uses the same Shadowsocks endpoint, this causes the Windows client to fail with host:port separator not found.

This change preserves the fallback's FirstHop and adds a regression test.

Tested with go test ./client/go/outline/configregistry.

@frezzzze
frezzzze requested a review from a team as a code owner October 9, 2026 16:08
@greptile-apps

greptile-apps Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium impact] This PR appears safe to merge.

Summary

Preserves the fallback dialer’s FirstHop in parseIPTableStreamDialer.

  • The IP-table stream dialer keeps its fallback’s first-hop address.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Shadowsocks fallback FirstHop] --> B[iptable stream FirstHop]
  B --> C{TCP and UDP hops match?}
  D[UDP FirstHop] --> C
  C -->|Yes| E[Return shared FirstHop]
  E --> F[Windows resolves host and sets route]
Loading

Reviews (1) · Last reviewed commit: "Fix iptable fallback first hop" · Reviewed by Greptile

Dial: dialer.DialStream,
ConnectionProviderInfo: ConnectionProviderInfo{
ConnType: connType,
FirstHop: fallbackFirstHop,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that the IP routing doesn't really have a well defined first-hop. for the other IP rules, you will use a different first hop, so we can't say it's the fallback.

Could you please dig deeper and explain where the separator error is coming from? The fix most certainly belongs somewhere else.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right, my fix isn't correct for this case. An iptable can have multiple routing paths, so we can't just use the fallback's first hop for the whole thing.

I traced the error a bit further. In parse.go, FirstHop is only set when the stream and packet first hops match. With an iptable, they may not match, so FirstHop ends up empty. Then createVpnTunnel() in client/electron/index.ts calls net.splitHostPort(request.firstHop) without checking for an empty value, which causes the host:port separator not found error.

This also looks related to #2686, since that issue seems to involve the same assumption about firstHop in more complex YAML configs. I'm not sure yet if it's exactly the same root cause, but I'll look into it and try to find something.

@frezzzze
frezzzze marked this pull request as draft October 9, 2026 17:01

This branch has not been deployed

No deployments
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.

YAML Dynamic Config Not Working on Windows when iptables is used

2 participants