Skip to content

Configuring Gateway Routes

A gateway is a TVPN node that bridges its overlay tunnel to a physical network, so remote peers can reach devices that don’t run TVPN — cameras, sensors, printers, or an entire LAN behind the gateway host.

Gateway routes let you:

  • Expose a physical subnet to every peer in your Workspace
  • Reach those devices by address, with no software installed on them
  • Choose between translated addressing and advertising your real subnet as-is
  • Have peers pick up the routes automatically, with no per-peer configuration

Example

A vehicle carries a TVPN node with a camera subnet behind it. The operator wants remote peers to reach those cameras.

config.toml:

[gateway-routes]
# Map the camera subnet into TVPN's reserved overlay space.
[[gateway-routes.virtual]]
physical = "192.168.10.0/24"
virtual = "100.127.10.0/24"

Restart the daemon. Every peer in the Workspace now has a route for 100.127.10.0/24 pointing at this node, and reaches camera 192.168.10.42 as 100.127.10.42.

Route types

There are two kinds of gateway route, and the difference is whether addresses get translated.

Virtual routes

A virtual route maps a physical subnet onto a slice of TVPN’s reserved overlay range. Host bits are preserved; only the network bits change.

[[gateway-routes.virtual]]
physical = "192.168.10.0/24"
virtual = "100.127.10.0/24"
Peers send to Gateway forwards to
───────────── ───────────────────
100.127.10.42 ⇄ 192.168.10.42
100.127.10.99 ⇄ 192.168.10.99
100.127.10.X ⇄ 192.168.10.X

The reverse translation runs automatically when the device replies.

Use virtual routes by default. They need no opt-in from peers, and because the overlay range is reserved they can’t collide with a peer’s own network.

Exposing a single device

Use a /32 on both sides to map one address to one address. With no host bits left, the two addresses need no relationship to each other:

[[gateway-routes.virtual]]
physical = "192.168.1.227/32"
virtual = "100.126.100.43/32"

Peers reach 192.168.1.227 at 100.126.100.43, and nothing else on 192.168.1.0/24 is exposed. A /24 route advertises 254 addresses whether or not anything is at them; a /32 advertises exactly one device.

Literal routes

A literal route advertises your real physical subnet onto the overlay with no translation. Peers send to the actual address.

[gateway-routes]
allow-literal = true
literal = ["192.168.10.0/24"]

A peer reaches the camera at 192.168.10.42, its real address.

Use literal routes when you manage your private address space carefully and would rather not have a second range to reason about.

Configuration options

Gateway routes live in the daemon’s main config file, config.toml, under a [gateway-routes] table.

Global options

FieldTypeRequiredDefaultNotes
allow-literalbooleannofalseEnables literal routes. Required on the gateway advertising them and on every peer that wants to use them. See “Enabling literal routes” below.
literallist of CIDR stringsnoemptyPhysical subnets to advertise onto the overlay with no translation. Ignored entirely unless allow-literal = true.
virtuallist of tablesnoemptyDeclared as repeated [[gateway-routes.virtual]] blocks, one per route.

Virtual route fields

Each [[gateway-routes.virtual]] block takes two fields, both required.

FieldTypeRequiredNotes
physicalCIDR stringyesThe real subnet on your network, e.g. "192.168.10.0/24".
virtualCIDR stringyesThe overlay subnet peers send to, e.g. "100.127.10.0/24". Must be inside 100.124.0.0/14.

Constraints

RuleDetail
Virtual rangeMust fall inside 100.124.0.0/14 (100.124.0.0–100.127.255.255). This block is reserved for gateway translation.
Matching prefix lengthphysical and virtual must use the same prefix length. A /24 maps to a /24, never a /25.
Minimum virtual prefix/16 or longer.
Minimum literal prefix/8 or longer.
Literal rangeMust not overlap 100.64.0.0/10, TVPN’s overlay address space.
No overlapsVirtual ranges may not overlap each other, and physical ranges may not overlap each other, across the whole route set.
ReloadRead once at startup. Restart the daemon after editing.

A complete example

[gateway-routes]
allow-literal = true
# Advertised as-is; peers reach 10.20.0.5 at 10.20.0.5.
literal = [
"10.20.0.0/24", # sensors
"10.30.0.0/24", # management
]
# Translated; peers reach 192.168.10.42 at 100.127.10.42.
[[gateway-routes.virtual]]
physical = "192.168.10.0/24"
virtual = "100.127.10.0/24"
# A second translated subnet.
[[gateway-routes.virtual]]
physical = "192.168.20.0/24"
virtual = "100.127.20.0/24"

Note the two different shapes: literal routes are one list, so additional subnets are extra entries in the literal = [...] array. Virtual routes are repeated blocks, so each one needs its own [[gateway-routes.virtual]] header with a physical and a virtual field.

Enabling literal routes

allow-literal is a two-sided switch, off by default, and it must be true at both ends:

  • On the gateway, it decides whether your own literal entries are advertised at all. Left at false, they’re dropped at startup with a warning in the log.
  • On a peer, it decides whether that peer installs a route for a literal subnet some gateway advertised. Left at false, the advertisement is silently ignored and no route is created.

Virtual routes need no opt-in at either end.

Operator setup

Beyond [gateway-routes] itself, three things must be true.

1. IP forwarding on the gateway host

TVPN writes the translated packet to its tunnel interface and lets the operating system route it onto your physical network — that hop only happens with IP forwarding enabled.

OSHow
LinuxAutomatic — TVPN enables it at startup when gateway routes are configured.
macOSsysctl -w net.inet.ip.forwarding=1
FreeBSDsysctl net.inet.ip.forwarding=1, persisted via /etc/sysctl.conf
WindowsEnable Routing and Remote Access, or set HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\IPEnableRouter to 1

2. The gateway can reach the physical network

It needs an interface and a route into the subnet you’re exposing.

3. The physical network can route replies back

This is the step most often missed. TVPN doesn’t rewrite source addresses on the inbound path — a camera receives a packet from a source like 100.64.0.10 and tries to reply to it. For that reply to reach the gateway, the physical device or its upstream router needs a route:

100.64.0.0/10 via <gateway host's physical IP>

Hosts routed through the gateway need an MTU of 1320, matching the VPN overlay network — a default 1500 can drop larger packets at the gateway. On Linux, use a route MTU:

ip route replace 100.64.0.0/10 via 192.168.10.2 mtu 1320

Troubleshooting

TVPN starts normally even if your gateway routes are invalid — only gateway forwarding is affected, the rest of the daemon comes up.

Validate before deploying:

tvpn config validate

Check what loaded — on a peer, confirm the gateway is actually advertising what you expect:

tvpn peer get

Advertised routes appear under Gateway Routes: for each peer. tvpn peer list is the summary view and doesn’t include them.

Log locations:

OSPath
Linux, macOS/var/lib/somewear/tvpn/log/tvpn.log
FreeBSD/var/log/somewear/tvpn/tvpn.log
Windows%ProgramData%\Somewear\tvpn\log\tvpn.log
MikroTikRun /container/log/print

Interaction with QoS traffic policies

If you also use a traffic policy, packets from gateway-served devices are classified like any other outbound traffic. For virtual routes, classification runs before the source address is translated, so a rule matching src-ip sees the device’s real physical address, not the overlay address peers use:

# Matches the camera's actual address, not 100.127.10.42.
[[rule]]
priority = 15
match = { src-ip = "192.168.10.42", proto = "udp" }
class = "important"
  • Quality of Service — prioritize traffic across multiple network links, including gateway-served traffic
  • Install TVPN — set up TVPN before configuring gateway routes