How to Build Asymmetric Routing with WireGuard on Linux
Learn how to configure WireGuard to create asymmetric routes across multiple machines, using link‑local addresses and static routing, with step‑by‑step examples for three‑node and two‑node setups, and see performance improvements by avoiding costly outbound paths.
Solution two is considered a better configuration; it uses the same WireGuard setup as solution one but with clearer steps, so readers in a hurry can jump directly to it.
In a peculiar environment where the outbound path can only connect directly via a mobile network while the return path goes through other ISPs, severe jitter and packet loss occur at night. To optimize only the outbound route without changing the return path, we create an asymmetric routing setup using WireGuard as a virtual network.
Asymmetric Routing Across Three Machines
Environment Preparation
The experiment uses three machines:
Local machine A with WireGuard internal IPs 192.168.51.5 and 169.254.1.5
Outbound‑limited machine B with IPs 192.168.51.1 and 169.254.1.1
Outbound‑bypass, higher‑bandwidth machine C with IPs 192.168.51.2 and 169.254.1.2
The desired traffic flow is A → B → C → A.
After installing WireGuard, generate keys and enable packet forwarding:
apt install wireguard wireguard-tools
wg genkey | tee privatekey | wg pubkey > publickey
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
echo "net.ipv6.conf.default.forwarding=1" >> /etc/sysctl.conf
echo "net.ipv6.conf.all.forwarding=1" >> /etc/sysctl.conf
sysctl -pPairwise Connections
Each pair of machines needs its own WireGuard configuration file (six files total) with Table=off to prevent automatic route changes.
Node A ↔ Node B:
[Interface]
PrivateKey =
ListenPort = 27000
PostUp = ip addr add 169.254.1.5/32 peer 169.254.1.1/32 dev %i
PostDown = ip addr del 169.254.1.5/32 peer 169.254.1.1/32 dev %i
Table = off
# B
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =
PersistentKeepalive = 10Node A ↔ Node C:
[Interface]
PrivateKey =
ListenPort = 27001
PostUp = ip addr add 169.254.1.5/32 peer 169.254.1.2/32 dev %i
PostDown = ip addr del 169.254.1.5/32 peer 169.254.1.2/32 dev %i
Table = off
# C
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =
PersistentKeepalive = 10Node B ↔ Node A:
[Interface]
PrivateKey =
ListenPort = 27000
PostUp = ip addr add 169.254.1.1/32 peer 169.254.1.5/32 dev %i
PostDown = ip addr del 169.254.1.1/32 peer 169.254.1.5/32 dev %i
Table = off
# A
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0Node B ↔ Node C:
[Interface]
PrivateKey =
ListenPort = 26002
PostUp = ip addr add 169.254.1.1/32 peer 169.254.1.2/32 dev %i
PostDown = ip addr del 169.254.1.1/32 peer 169.254.1.2/32 dev %i
Table = off
# C
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =Node C ↔ Node A:
[Interface]
PrivateKey =
ListenPort = 27001
PostUp = ip addr add 169.254.1.2/32 peer 169.254.1.5/32 dev %i
PostDown = ip addr del 169.254.1.2/32 peer 169.254.1.5/32 dev %i
Table = off
# A
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0Node C ↔ Node B:
[Interface]
PrivateKey =
ListenPort = 26002
PostUp = ip addr add 169.254.1.2/32 peer 169.254.1.1/32 dev %i
PostDown = ip addr del 169.254.1.2/32 peer 169.254.1.1/32 dev %i
Table = off
# thk
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint =IP Allocation and Static Routing
After establishing pairwise links, create a dummy interface on each host (or add the address directly in PostUp) to receive packets destined for the host itself.
# ip link del dummy
ip link add dummy type dummy
ip addr add 192.168.50.5/32 dev dummy
ip link set dummy upSet static routes so that traffic from A to C goes through B:
ip route add 192.168.51.2/32 dev <em>wireguard‑AB‑interface</em>
# or ip route add 192.168.51.2/32 via 169.254.1.1On B, add a route to C:
ip route add 192.168.51.2/32 dev <em>wireguard‑BC‑interface</em>
# or ip route add 192.168.51.2/32 via 169.254.1.2Testing with ping shows one‑way traffic on B while A receives replies, confirming the asymmetric path.
Further captures on C reveal that responses travel directly from C to A using the link‑local address, completing the asymmetric routing.
Why Use Link‑Local Addresses
Using 169.x.x.x addresses avoids automatic route entries that would conflict with the manually added static routes; the dummy interface provides a stable endpoint for the point‑to‑point WireGuard links.
Asymmetric Routing with Only Two Machines
The three‑node setup can be reduced to two machines by leveraging a traffic‑forwarding service. Each host runs two WireGuard interfaces: one through the forwarding service and one direct.
Interface naming:
a1 – local WireGuard over forwarding service (169.254.2.1, 192.168.51.3)
a2 – local direct WireGuard (169.254.2.2, 192.168.51.3)
c1 – remote WireGuard over forwarding service (169.254.2.3, 192.168.51.2)
c2 – remote direct WireGuard (169.254.2.4, 192.168.51.2)
Example configuration for a1:
[Interface]
PrivateKey =
Address = 192.168.51.3/32
PostUp = ip route add 192.168.51.2/32 dev %i
Table = off
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint = <em>forwarding‑service:14967</em>
PersistentKeepalive = 10a2 differs only in the endpoint IP/port.
[Interface]
PrivateKey =
Address = 192.168.51.3/32
Table = off
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0
Endpoint = <em>remote‑C:14967</em>
PersistentKeepalive = 10c1 and c2 are analogous, with c2 handling the direct path.
[Interface]
PrivateKey =
ListenPort = 27002
Address = 192.168.51.2/32
PostUp = ip route add 192.168.51.3/32 dev %i # c2 config sets direct link
Table = off
[Peer]
PublicKey =
AllowedIPs = 0.0.0.0/0Static routes are then added:
# On local machine (outbound via a1)
ip route add 192.168.51.2/32 dev a1
# On remote machine (return direct via c2)
ip route add 192.168.51.3/32 dev c2Ping test shows latency reduced from ~70 ms (via NTT) to ~30 ms, confirming the benefit.
Conclusion
While the three‑node solution works, it is somewhat cumbersome due to dummy interfaces and link‑local addressing. The two‑machine approach using a forwarding service achieves the same asymmetric routing with fewer hosts and better performance, making it a practical choice for scenarios where only the outbound path needs to be redirected.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
