Skip to content

refactor(intra): migrate UDP path from PacketProxy to packetrelay - #630

Draft
fortuna wants to merge 1 commit into
masterfrom
refactor/packetrelay
Draft

fortuna wants to merge 1 commit into
masterfrom
refactor/packetrelay

Conversation

@fortuna

@fortuna fortuna commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Note

Stacked on #631 (Go 1.27.1 + gomobile + Outline SDK v0.1.0-rc1). Review the diff against build/upgrade-go; this PR contains no dependency changes.

Why

network.PacketProxy and network.NewPacketProxyFromPacketListener are deprecated in the Outline SDK, in favor of the flow-based network/packetrelay API. Outline did the same migration in outline-apps#2777.

This is also preparation for proxyless QUIC circumvention: retrying a blocked QUIC handshake requires a new source port (residual censorship often blackholes the flagged 4-tuple), which the relay API supports naturally by opening another association, whereas PacketProxy gives one fixed socket per session.

What changed

  • intraPacketProxyintraPacketRelay, implementing packetrelay.PacketRelay. Each association shares a dohAssociation between its sender and receiver halves (the sender needs the stack's PacketHandler to deliver locally-resolved DNS responses).
  • lwip2transport.ConfigureDeviceConfigureDeviceWithRelay.
  • packetrelay.NewPacketRelayFromPacketListener(pl, 5*time.Minute) replaces NewPacketProxyFromPacketListener(…, WithPacketListenerWriteIdleTimeout(…)), preserving the RFC 4787 REQ-5 timeout. In v0.1.0-rc1 the write-idle timeout is a constructor argument, so no TimeoutPacketRelay wrapper is needed.
  • Intra keeps its own DNS interception: the SDK's network/dnsintercept rewrites the destination to a remote resolver address, while Intra answers in-process via doh.Resolver.
  • New packet_relay_test.go covering DNS interception (including that DNS bytes are not billed to the user), error propagation, relaying with byte accounting, association-summary reporting, and sender close semantics.

Preserved semantics: DNS-only associations are still torn down right after the query is answered; DNS traffic is still excluded from UDPSocketSummary; the summary is still reported exactly once when the association concludes.

One intentional behavior change: SendPacket on a closed association now returns packetrelay.ErrClosed for all destinations. Previously a post-close packet addressed to the fake DNS server would still issue a DoH query whose response could no longer be delivered.

Out of scope: UDPSocketSummary.Duration is documented as seconds but assigned int32(time.Since(...)) (nanoseconds, overflowing after ~2.1 s). Pre-existing on both sides of this refactor; left untouched.

Testing

  • gofmt -l clean for the touched files, go vet ./...
  • go test -race ./... — all packages pass
  • ./gradlew :app:compileGoBackend — gomobile bind succeeds (the bound intra API surface is unchanged; only unexported types moved)

@fortuna
fortuna force-pushed the refactor/packetrelay branch from b0c0bca to 005ee95 Compare September 15, 2026 23:03
@fortuna
fortuna changed the base branch from master to build/upgrade-go September 15, 2026 23:03
@fortuna
fortuna added this pull request to stack #632 September 15, 2026 23:03
@fortuna
fortuna marked this pull request as draft September 15, 2026 23:29
Base automatically changed from build/upgrade-go to master September 16, 2026 17:53
network.PacketProxy and NewPacketProxyFromPacketListener are deprecated in
favor of the flow-based network/packetrelay API. This migrates Intra's UDP
handling, mirroring OutlineFoundation/outline-apps#2777.

- intraPacketProxy becomes intraPacketRelay, a packetrelay.PacketRelay whose
  associations share a dohAssociation between their sender and receiver halves.
- lwip2transport.ConfigureDevice -> ConfigureDeviceWithRelay.
- packetrelay.NewPacketRelayFromPacketListener replaces
  network.NewPacketProxyFromPacketListener, keeping the same 5 minute
  write-idle timeout (now a constructor argument).
- Add unit tests for DNS interception, packet relaying, byte accounting and
  sender close semantics.

Behavior is unchanged, except that SendPacket on a closed association now
returns ErrClosed instead of issuing a DoH query whose response can no longer
be delivered.
@fortuna
fortuna force-pushed the refactor/packetrelay branch from 005ee95 to 8be9f37 Compare September 16, 2026 17:54
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