Files
tailscale/wgengine
Martin Minkus 827c6fe50d wgengine/router/osrouter: SNAT to the egress interface address, not (self)
The FreeBSD subnet-router NAT rule translated with "-> (self)":

  nat on ! tailscale0 inet from 100.64.0.0/10 to any -> (self)

In pf, "(self)" is a round-robin pool of every address on the machine,
including tailscale0's own address and loopback, and pf deals each new
state the next address in the pool. Only flows that happen to draw the
egress interface's address work; a flow translated to any other address
gets replies the far end cannot route, and hangs at SYN. With N usable
addresses on the box, roughly (N-1)/N of connections through the subnet
router silently fail.

Observed in a natlab vmtest against a FreeBSD 15.0 subnet router with
four addresses (WAN, LAN, QEMU debug NIC, tailscale0): exactly half of
8 HTTP requests hung, alternating, and the pf state table showed the
failed flows translated to the debug NIC's address and to tailscale0's
own address:

  10.0.0.102:51100 (100.64.0.1:35132) -> 10.0.0.103:8080  ESTABLISHED
  10.0.2.15:56553  (100.64.0.1:35148) -> 10.0.0.103:8080  SYN_SENT:CLOSED
  100.64.0.2:52655 (100.64.0.1:54106) -> 10.0.0.103:8080  SYN_SENT:CLOSED

On a production FreeBSD firewall running this branch, the equivalent
IPv6 rule shows 55 state creations totalling 117 packets (about two
packets per state): SYNs whose replies never came back.

Emit one rule per up, non-loopback, non-Tailscale interface instead,
translating to that interface's own address, per address family only
where the interface holds a usable address of that family:

  nat on vtnet0 inet from 100.64.0.0/10 to any -> (vtnet0)
  nat on vtnet1 inet from 100.64.0.0/10 to any -> (vtnet1)

which is also the rule form FreeBSD firewall operators write by hand.
With this, the same 8-request test passes 8/8, and the LAN interface's
rule shows 8 states with healthy packet counts (56 packets total).

The interface set is sampled when SNAT is enabled; interfaces added
later are not covered until SNAT is toggled or tailscaled restarts.

Updates #5573

Change-Id: Ife3367124737ce5c8785ca7f920eafca593ec705
Signed-off-by: Martin Minkus <martin.minkus@sonic.com>
2026-08-31 20:31:01 -07:00
..