Skip to content

feat: TCP NAT traversal — Via alias, outbound proxy, connection reuse#109

Merged
shenjinti merged 2 commits intorestsend:mainfrom
bevenky:fix/tcp-nat-traversal
Apr 1, 2026
Merged

feat: TCP NAT traversal — Via alias, outbound proxy, connection reuse#109
shenjinti merged 2 commits intorestsend:mainfrom
bevenky:fix/tcp-nat-traversal

Conversation

@bevenky
Copy link
Copy Markdown
Contributor

@bevenky bevenky commented Apr 1, 2026

Summary

Three changes for reliable SIP over TCP behind NAT with load-balanced proxy clusters:

1. Via alias parameter (RFC 5923)

Add ;alias to Via header for TCP/TLS connections, telling the proxy to reuse this connection for sending requests back. Essential for receiving INVITEs behind NAT where the proxy's B2BUA leg may come from a different cluster node.

2. Outbound proxy support on Registration

New outbound_proxy: Option<SocketAddr> field on Registration that overrides transport destination while keeping the domain in SIP headers (Request-URI, From, To). Allows pinning to a DNS-resolved proxy IP for NAT consistency — prevents re-registration from resolving to a different proxy node and breaking the NAT mapping.

3. TCP connection reuse in transport layer

Include local addresses from TCP/TLS client connections in the address list used for Via/Contact headers. Without this, only listener addresses are returned, and TCP client connections are invisible for header construction.

Additional fixes

  • Message inspector ordering: Call before_send before computing destination so the inspector can modify headers that affect routing
  • Contact preservation: Preserve URI params (e.g., transport=tcp) across re-registrations instead of clearing contact on 200 OK
  • Supported header: Add Supported: path, outbound to REGISTER (RFC 3327 / RFC 5626) so proxies that support Path-based routing record the correct edge node

Context

These changes are needed for production use with SIP providers that use load-balanced proxy clusters (e.g., phone.plivo.com resolves to multiple IPs). Without them:

  • UDP: NAT mapping is per-destination-IP, so INVITE from proxy B fails if REGISTER went to proxy A
  • TCP without alias: proxy doesn't know to reuse the TCP connection for inbound requests
  • Without outbound proxy: re-registration DNS resolves to different IP, opening new TCP connection and breaking Via alias mapping

Test plan

  • All 65 existing tests pass
  • Tested in production against SIP proxy clusters with TCP signaling

Three changes for reliable SIP over TCP behind NAT with load-balanced
proxy clusters:

1. Via alias parameter (RFC 5923): Add ;alias to Via header for TCP/TLS
   connections, telling the proxy to reuse this connection for sending
   requests back. Essential for receiving INVITEs behind NAT.

2. Outbound proxy support on Registration: New outbound_proxy field
   (Option<SocketAddr>) that overrides transport destination while
   keeping the domain in SIP headers (Request-URI, From, To). Allows
   pinning to a DNS-resolved proxy IP for NAT consistency — prevents
   re-registration from resolving to a different proxy node.

3. TCP connection reuse in transport layer: Include local addresses from
   TCP/TLS client connections in the address list used for Via/Contact
   headers. Without this, only listener addresses are returned, and
   TCP client connections are invisible for header construction.

Also fixes message inspector ordering: call before_send before
computing destination so the inspector can modify headers that
affect routing.

Registration changes:
- Outbound proxy inherits transport type from request URI (e.g., TCP)
- Contact preservation across re-registrations: preserve URI params
  (transport=tcp) instead of clearing contact on 200 OK, preventing
  loss of transport parameter on re-register
- Supported: path, outbound header in REGISTER (RFC 3327 / RFC 5626)
  so proxies that support Path-based routing record the correct edge
  node for the TCP connection
@bevenky bevenky force-pushed the fix/tcp-nat-traversal branch from 09d5155 to 228fe48 Compare April 1, 2026 03:55
@shenjinti shenjinti merged commit 04f160c into restsend:main Apr 1, 2026
1 of 3 checks passed
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.

2 participants