TailscaleGuide
Isometric subnet routing setup with servers, a central router, network adapter, switch and monitor linked by cyan cables beside a padlock.
Networking

Tailscale Subnet Router Configuration: Routes, ACLs, Tests

Advertise LAN routes, approve them with autoApprovers, scope access with grants, and handle the SNAT, MTU and overlapping-subnet traps before they bite.

By TailscaleGuide Editorial · · 5 min read

A subnet router is how a tailnet reaches the hardware that will never run a Tailscale client: the IPMI board, the PoE switch management interface, the NAS that only speaks SMB on the LAN. Tailscale subnet router configuration fails in four predictable places, and almost always in this order: IP forwarding is off on the host, the routes are advertised but never approved, the Linux clients never got --accept-routes, and the policy file is far more permissive than anyone intended. Fix them in that order and the rest is detail.

Topology

One site, one gateway, one subnet router. The router host sits on a single VLAN and lets the gateway do inter-VLAN routing:

UDM Pro  10.0.0.1
  └─ USW-Pro-24-PoE
       ├─ port 8   access, PVID 20   subnet-router host  10.0.20.5/24
       ├─ port 12  access, PVID 40   NAS                 10.0.40.10/24
       └─ port 24  trunk,  native 1, tagged 20/40/50     U6-Enterprise

The alternative is trunking 802.1Q subinterfaces into the host and letting it route. That gives per-VLAN control on the host, at the cost of putting a Linux box in the forwarding path for east-west traffic that never needed to leave the switch. On a 1 to 3 AP home network, let the gateway route.

VLANNameCIDRAdvertised to tailnetInter-VLAN policy
20services10.0.20.0/24yesdeny-all inbound, exceptions below
40storage10.0.40.0/24yes20 → 40 on 445/2049 only
50iot10.0.50.0/24nodeny-all, no tailnet exposure

Advertise the smallest prefixes you can live with. 10.0.0.0/8 is legal RFC 1918 space and a terrible advertisement: it hands every tailnet node a route to everything you will ever number.

Three separate steps, and skipping any one produces the same symptom (traffic goes nowhere).

Enable forwarding on the router host. Tailscale’s subnet router documentation gives the sysctl drop-in:

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

On a firewalld host (Fedora, RHEL, Rocky) also run firewall-cmd --permanent --add-masquerade. Then advertise:

sudo tailscale set --advertise-routes=10.0.20.0/24,10.0.40.0/24

tailscale set changes one preference without re-running the whole up flow, which matters when you are editing routes on a box you are reaching over the tunnel. Approve the routes in the admin console under Machines, filtering with property:subnet to find every advertising device.

Finally, teach the clients. Per the tailscale up reference, --accept-routes defaults to on for Windows, iOS, Android and both macOS variants, and off everywhere else. Linux clients need it explicitly:

sudo tailscale set --accept-routes

Approve routes from the policy file

Clicking approve in a browser does not survive a node rebuild. Tag the router and let the policy file approve it, using the autoApprovers syntax:

{
  "tagOwners": {
    "tag:subnet-router": ["autogroup:admin"]
  },
  "autoApprovers": {
    "routes": {
      "10.0.20.0/24": ["tag:subnet-router"],
      "10.0.40.0/24": ["tag:subnet-router"]
    }
  },
  "grants": [
    { "src": ["tag:laptop"], "dst": ["10.0.40.0/24"], "ip": ["tcp:445", "tcp:2049"] },
    { "src": ["autogroup:admin"], "dst": ["10.0.20.0/24"], "ip": ["*:*"] }
  ]
}

Bring the node up with sudo tailscale up --advertise-tags=tag:subnet-router --advertise-routes=10.0.20.0/24,10.0.40.0/24 and the routes approve themselves.

Note the split of responsibility, because it is where people get burned. Route approval decides whether the route exists in the tailnet. Grants decide who may use it and on which ports. An approved /24 under a permissive default policy means every node, including a phone with a stale key, reaches every host on that subnet. Anything exposed this way widens what an attacker who lands on one tailnet device can pivot to, the same lateral-movement problem covered at techsentinel.news. If the advertised subnet holds an internal inference host, read the agent-exploitation work at aisec.blog before opening port 8000 to the whole tailnet.

SNAT, stateful filtering, and the flags that bite

By default the subnet router source-NATs traffic onto the LAN, so the NAS sees connections from 10.0.20.5 rather than from the tailnet address. That hides the real client from your logs and from any per-host ACL on the target. Disable it with --snat-subnet-routes=false (Linux only), then give the LAN hosts a return route for the CGNAT range: 100.64.0.0/10 via the subnet router’s LAN IP. Miss that route and you get one-way traffic that looks exactly like a firewall drop.

--stateful-filtering is the other Linux-only switch. The CLI reference describes it as dropping inbound packets addressed to another node unless they belong to a tracked outbound connection, and states it defaults to disabled. It has not always: enabling it by default in the 1.66 series broke MagicDNS and other traffic for a lot of people, as documented in tailscale/tailscale#12087. If a subnet router works on one version and drops traffic after an upgrade, check this flag before rewriting your ACLs.

Overlapping subnets and a second router

Two sites both numbered 192.168.1.0/24 cannot be advertised as-is. That is what 4via6 is for: it maps each overlapping IPv4 subnet into a unique IPv6 prefix keyed by a site ID from 0 to 65535.

tailscale debug via 7 10.1.1.0/24
# fd7a:115c:a1e0:b1a:0:7:a01:100/120
tailscale set --advertise-routes=fd7a:115c:a1e0:b1a:0:7:a01:100/120

With MagicDNS on, hosts answer to names of the form 10-1-1-16-via-7.

For redundancy, two routers can advertise the same prefix, and Tailscale picks the most specific route by longest prefix match. Two documented caveats: it does not fall back to a less specific route when the more specific one goes offline, and turning on --accept-routes for HA pairs sharing routes in the same region makes the standby accept its own advertised routes back from the primary.

MTU, MSS clamping, throughput

Tailscale runs a 1280-byte MTU, which the TCP troubleshooting page states outright. A LAN host at 1500 sends packets the tunnel cannot carry, and if ICMP is filtered anywhere in the path you get a PMTU black hole: the TCP handshake completes, then the first full-size segment vanishes. The site-to-site guide clamps it on each subnet router:

iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp \
  --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

On Tailscale 1.54+ with kernel 6.2+, the performance guide recommends UDP GRO forwarding on the physical NIC of any subnet router or exit node:

NETDEV=$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")
sudo ethtool -K $NETDEV rx-udp-gro-forwarding on rx-gro-list off

If throughput is still poor after that, the connection is probably relayed rather than direct. That is a different problem, covered in Tailscale throughput fixes.

Things to test before you call it done

Run these from a client, not from the router host, which can reach the LAN regardless.

tailscale status | grep -i 'offers\|relay'
ip route get 10.0.40.10
tailscale ping --until-direct 10.0.20.5
ping -c 4 -M do -s 1252 10.0.40.10
mtr -rwzbc 50 10.0.40.10
iperf3 -c 10.0.40.10 -t 30 -P 4
dig +short nas.lan @10.0.20.53

ip route get proves the client installed the route at all. The -M do -s 1252 ping is the MTU check: 1252 bytes of payload plus 28 of headers is exactly 1280, so it passes while -s 1253 fails cleanly rather than hanging. tailscale ping --until-direct says whether the path is direct or a DERP relay, before you blame the subnet router for latency. dig against the internal resolver confirms split DNS, which is set on the admin console DNS page and is a separate step from route approval.

Nothing here stays correct on its own. Re-run the checks after a Tailscale upgrade, after a VLAN renumber, and after anyone touches the policy file. If the subnet router is also carrying general internet traffic, that is an exit node and a different set of flags, covered in Tailscale exit node setup.

Sources

  1. Subnet routers
  2. tailscale up command
  3. Syntax reference for the tailnet policy file
  4. Site-to-site networking
  5. 4via6 subnet routers
  6. Performance best practices
  7. Troubleshoot TCP connection issues between two devices
  8. tailscale/tailscale issue 12087: v1.66.x breaks MagicDNS without --stateful-filtering=false
  9. RFC 1918: Address Allocation for Private Internets
#tailscale #subnet-router #wireguard #networking#vlan

Related