Iptables – the purpose of TPROXY, how should you use it and what happens internally

firewalliptablesPROXYrouting

After reading a bit about TPROXY (e.g at https://www.kernel.org/doc/html/latest/networking/tproxy.html ) I now have more questions then answers. I actually don't even know what TPROXY should do…

Some assumptions about what I should do and what happens inside.
Can you correct the following assumptions ?

From what i understand these are the commands that you should run (although i have no idea why):

  • iptables -t mangle -N DIVERT :
    • A chain named DIVERT is created.
    • You can choose the name. (As long as it's the same in all commands).
    • It has to be part of mangle because you will being doing things that are less trivial than redirecting, blocking and NAT.
  • iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT :
    • The PREROUTING chain makes sure that once a TCP packet is passed from a networkdevice to the kernel, the very first thing that will happen is that it is being sent to the DIVERT chain.
    • -p tcp makes sure that this it is not the case for non-TCP traffic .
    • -m socket makes sure that this it is not the case for packets that create and close the connection (e.g. SYN/ACK)
    • By removing -p tcp -m socket TPROXY will affect all IP(v4) packets. It will be a different, but working, setup.
  • iptables -t mangle -A DIVERT -j MARK --set-mark 1 :
    • The kernel will mark those packets with the number 1. You can choose another number.
    • It's also possible to add multiple marks to a packet. For example: By adding the command iptables -t mangle -A DIVERT -i eth0 -j MARK --set-mark 2 you would make sure that all TCP-packages get the mark 1 and all TCP-packages that arrive from eth0 get both the mark 1 and 2.
    • "Marking with number X" just means "categorizing as member of category X without actually changing anything in the packet".
    • Both the kernel and programs in userspace can read the mark(s) of packets
    • Only the kernel can mark packets
  • iptables -t mangle -A DIVERT -j ACCEPT : By default the kernel discards the packets so you are now making sure it doesn't.
  • ip rule add fwmark 1 lookup 100 :
    • Instead of using the default routingtable, all packets with mark 1 now use a table named 100.
    • You can use choose another number, as long as you make sure that you use the same number everywhere else
  • ip route add local 0.0.0.0/0 dev lo table 100 :
    • Creates table 100 if it didn't exist already
    • Adds a routing roule that makes sure that all packages that originate from our system (local) stay local by sending them back to lo
    • That this is the case for packages with any destination 0.0.0.0/0 (each ipv4 adddress is a member of this subnet)
    • But that it's not the case for packages that are not marked 1 (otherwise they wouldn't end up in 100)
  • iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 50080:
    • All packets sent to tcp/80 receive the mark 1 and end up at tcp/50080
    • For some, to me, unknown reason, they are marked. (It looks to me that they pass by DIVERT anyway where they are marked again)
    • For some, to me, unknown reason 0x1 is written twice

My assumption about what the purpose of TPROXY is: Re-routing packets without altering them

Best Answer

"Re-routing without altering" is basically technically correct, but to understand what it's for it's better to think "intercepting" - you're usually only "re-routing" to a program you wrote, running on the same machine (which ought to be the client's wifi access point, or something similarly positioned between them and the rest of the world). It's a way to get, within your C/C++/whatever code, what looks like a normal TCP socket you can send() and recv() on, but is actually letting you impersonate their intended destination.

A use would be transparently proxying traffic through some setup so exotic that not even e.g. a carefully configured Wireguard can handle it, and instead you need to write your own actual program to make it work.

Take a look at this: https://github.com/FarFetchd/simple_tproxy_example

It's a minimal working example of the simplest interesting thing you can do with this, which might give you a better intuition of the "why".

As for the ip rule fwmark stuff, I advise you to find an example that works for you and just treat it as a black magic incantation, unless you are trying to get capable of doing serious dev work on the Linux networking stack itself. I mean I consider myself basically competent with iptables, and have done some serious useful work with TPROXY, and I still find the ip rule fwmark stuff totally mysterious, haha.