I have set up routing/nft rules to implement routing traffic from a process with different UIDs over different interfaces. This is useful, for example, to send all traffic for tor over a commercial wireguard vpn.
The three parts for this are:
Routing rule ($ ip rule add from all fwmark 0x01 lookup torroute)
Custom routing table ($ ip route add default dev XXX table torroute)
nft rules to set fwmark based on skuid
With the exception of setting up the name for a routing table (in /etc/iproute2/rt_tables), #1 and #2 can be done in LuCI/UCI.
What I don't know how to do is #3.
The nft rules I'm using now are:
table ip tor {
chain pbr {
type route hook output priority mangle; policy accept;
meta mark set ct mark counter packets 0 bytes 0
meta mark != 0x00000000 counter packets 0 bytes 0 accept
meta skuid 52 counter packets 0 bytes 0 meta mark set 52
ct mark set meta mark counter packets 0 bytes 0
}
}
or
nft create table ip tor
nft create chain ip tor pbr { type route hook output priority mangle \; policy accept \; }
nft add rule ip tor pbr mark set ct mark counter
nft add rule ip tor pbr mark ne 0 counter accept
nft add rule ip tor pbr skuid 52 counter mark set 52
nft add rule ip tor pbr ct mark set mark counter
Is there any way to get skuid rules as shown above through LuCI or UCI? I'd love to turn my experience into a howto, and I very much prefer to work with the UI as possible.
Thanks so much for your reply and the detailed guide!
I should have included more detail in my question. The uidrange setting in ip route (which is what OpenWrt employs on the back end of what you showed) can't accomplish the whole goal. In Linux, the incoming FIN and more importantly the final outgoing ACK packet closing a connection (and some RST packets) don't match to a UID any more. They are owned by the kernel, probably because the connection's link to the process has already been torn down. So if you are routing solely based on UID, the connections:
Get left hanging because the final packets get mis-routed, but more importantly they...
Leak information about who you've connected to as the the final ACK packets get sent out the wrong adapter (probably the default route in your main table which is probably a public-facing adapter).
In my case, I'm using it to route Tor over a VPN and really don't want connection information leaking out my default route.
The only way I know of to circumvent that is to use skuid in nft to link the initial SYN with a ct mark, and that ct mark will be preserved for the entire connection (even the final ACK).
If you know of a better way to accomplish this, I'd love to hear it, but the four nft rules I have in my original post is the only way I know of.
In any case, there are also other reasons one might want to use skuid (or any other of a number of nft goodies) in a rule, so I'm hoping there is a LuCI/UCI way to do it.
I have seen this done by adding an extra IP address on the interface and binding Tor to that interface then using this interface with PBR to route via the VPN.
Never tried it my self so do not know if that really can work
I don't think I can add an extra IP address since I'm constrained by my commercial VPN provider.
Similarly, I could have used netns and assigned the WireGuard adapter to a different namespace. I then executed Tor inside that namespace, but then I would have lost that WireGuard adapter to anything except Tor. Necessitating a second link to my VPN.
I do have it working now, and I like my system. I'm just hoping for a way to do the actual nft wizardry inside LuCI or UCI for ease of explaining it to someone else and replicating it. There is still a long way to go before LuCI can do arbitrary nft rules, though.
I'm actually rather happy I could do the ip rule and the route inside LuCI.