blog
using fwmark for routing
After getting my LTE emergency link online last weekend, I wanted to be able to access my system remotely over the new link. The goal is to SSH into my system from anywhere in case the main link does not work.
Usually, logging in might be as simple as obtaining the public IP of one's internet connection, making SSH accessible from the outside and then pointing the SSH client at the public IP. For mobile connections, however, providers often use carrier-grade NAT, which means that the public IP of the mobile link is shared between multiple users, mostly due to IPv4 address exhaustion. This is also the case for my LTE connection, so a direct connection from outside is not possible.
In order to work around this, the only thing I could think of was to keep a tunnel permanently active. The tunnel is initiated from the LTE side of the link at home and connects to a cloud instance I'm permanently using. The problem is that there are two active routes at home – the standard default route over the fibre connection and the second one, with a larger metric value, pointing at the LTE link. That means that initiating a tunnel to the outside automatically uses the fibre connection, not LTE. While that would still allow access to the home system when the main link is down, I wanted to keep the tunnel over LTE available at all times.
It turns out that WireGuard supports FwMark settings. This
“firewall mark” is simply a value attached to the packet – as long as
the packet is handled by the system which has assigned it – and can be
used in firewall rules or ip rule settings. In order to make use of this
I set up an LTE-specific routing table.
First, I changed
/etc/iproute2/rt_tables accordingly by adding a new
routing table using ID 100:
# # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep 100 lte1
Second, the WireGuard interface gets an FwMark setting
to mark all outgoing packets with the fwmark value
10240:
[Interface] PrivateKey = AH/Tr8nGx/6UUgNzadIAeKjunVgP0a8w6AxvNSexCm0= Address = 192.168.101.2 FwMark = 10240 PostUp = systemctl restart ssh.service [Peer] PublicKey = SAnr+zy3mWU28p0Wj3q2CeLWP7vbjSKJpwVonjXpLmk= AllowedIPs = 192.168.101.1 Endpoint = 192.0.2.10:51820 PersistentKeepalive = 25
Third, ip rule is used to route packets that have the
10240 fwmark assigned:
ip rule add fwmark 10240 table lte1
And last, we need to add a default route to the still empty “lte1” routing table:
ip route add default via 192.168.0.1 dev eth1 table lte1
At this point, all outgoing WireGuard packets are routed using the new
“lte1” routing table. Since the table only contains a single default route,
all outgoing WireGuard packets for the connection defined above
always travel through the 192.168.0.1 LTE gateway. Using the
new tunnel, the home system, reachable via 192.168.101.2,
is now accessible from the cloud instance.