Linux Transparent Proxy Internals

ldelossa1 pts1 comments

Linux transparent proxy internals - by AllHailTheTopology

SubscribeSign in

Linux transparent proxy internals<br>Learn how Linux's transparent proxying works by building a toy proxy server in C.

AllHailTheTopology<br>Jul 13, 2026

Share

I've always thought the transparent proxying functionality in the Linux kernel is one of the coolest but hardest to understand.<br>This may have to do with the sheer lack of documentation on the subject or maybe the fact that configuring it requires quite a few moving pieces like IP rules, socket options, and routing.<br>Let's explore how transparent proxying works and build a toy transparent proxy server to drive the concepts home.<br>What is transparent proxying?

Most people will have colloquial familiarity with the term "proxy" as a networking infrastructure.<br>A proxy sits between a client, the source, and a server, the destination, and manages the connections and data between them.<br>If this is a TCP proxy, it will terminate the client's connection, holding a socket to it, and will connect to the server, holding a socket for this "leg" as well.<br>With both sockets in-hand the proxy will pipe the data between them, performing whatever actions on the data is required.<br>Proxies can do all sorts of things, but we just need to understand the fundamentals right now.

┌─────────────┐<br>│ 66.66.66.66 │<br>┌────────┐ conn A │ proxy │ conn B ┌────────┐<br>│10.0.5.1├────────────► ├────────────►10.0.6.1│<br>│ client ◄────────────┤ terminates ◄────────────┤ server │<br>└────────┘ │ and │ └────────┘<br>│ forwards │<br>└─────────────┘

The above is what, I think, most people imagine when they think 'proxy', though technically, this is a reverse proxy where the proxy sits in front of the server.<br>To understand what a transparent proxy is they must think about what IP addresses the client will use to connect to the server, and the client IPs seen by the server.

┌─────────────┐<br>│ 66.66.66.66 │<br>┌────────┐ conn A │ proxy │ conn B ┌────────┐<br>│10.0.5.1├────────────► ├────────────►10.0.6.1│<br>│ client ◄────────────┤ terminates ◄────────────┤ server │<br>└────────┘ │ and │ └────────┘<br>┌───────────────────────┐ │ forwards │ ┌───────────────────────┐<br>│ Conn A │ └─────────────┘ │ Conn B │<br>│10.0.5.1 -> 66.66.66.66│ │66.66.66.66 -> 10.0.6.1│<br>└───────────────────────┘ └───────────────────────┘

Let's use the non-transparent proxy example above, both the client connection and the server's connections are well aware a proxy server sits between them.<br>A transparent proxy is fundamentally different.<br>┌─────────────┐<br>│ 66.66.66.66 │<br>┌────────┐ conn A │ proxy │ conn B ┌────────┐<br>│10.0.5.1├────────────► ├────────────►10.0.6.1│<br>│ client ◄────────────┤ terminates ◄────────────┤ server │<br>└────────┘ │ and │ └────────┘<br>┌───────────────────────┐ │ forwards │ ┌───────────────────────┐<br>│ Conn A │ └─────────────┘ │ Conn B │<br>│ 10.0.5.1 -> 10.0.6.1 │ │ 10.0.5.1 -> 10.0.6.1 │<br>└───────────────────────┘ └───────────────────────┘

Notice, in the transparent case the client perceives itself connected directly to the server and the server perceives itself connected directly to the client.<br>Both client and server maintain their IP addresses, across both "legs" of the proxy.<br>The example above is sometimes called a "fully transparent proxy" since the source IP of the client is maintained from the server's perspective.<br>It's not uncommon for the server to see the proxy's original IP when, for example, routing the client's IP back to the proxy is difficult, among other reasons.<br>While the explanation is simple, like a lot of things with networking, the devil's in the details.<br>Let's start building a proxy server which we will modify into a transparent socket as we explain how we can accomplish the above.<br>The topology

We need a small testing environment which creates separate layer 3 networks for the client, proxy, and server.<br>We can do this with just Linux network namespaces, the ip tool, and veths.

┌───────────┐ ┌──────────────┐ ┌──────────────┐<br>│ client ns │ │ proxy ns │ │ server ns │<br>│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │<br>│ │ ┼────────┼ │ │ ┼────────┼ │ │<br>└──────┴────┘ └────┘────└────┘ └────┘─────────┘<br>10.0.5.1 10.0.5.10 10.0.6.10 10.0.6.1

The above diagram illustrates the topology, three network namespaces connected by veths.<br>Makefile

LINK = ip link<br>NETNS = ip netns<br>NETNS_EXEC = ip netns exec<br>NETNS_CLIENT = client<br>NETNS_PROXY = proxy<br>NETNS_SERVER = server

topology:<br># create network namespaces<br>$(NETNS) add $(NETNS_CLIENT)<br>$(NETNS) add $(NETNS_PROXY)<br>$(NETNS) add $(NETNS_SERVER)

# wire them together with veths<br>$(LINK) add name client-a type veth peer name proxy-a<br>$(LINK) set dev client-a netns $(NETNS_CLIENT)<br>$(LINK) set dev proxy-a netns $(NETNS_PROXY)

$(LINK) add name proxy-b type veth peer name server-a<br>$(LINK) set dev proxy-b netns $(NETNS_PROXY)<br>$(LINK) set dev server-a netns $(NETNS_SERVER)

# configure IP networking<br>$(NETNS_EXEC) $(NETNS_CLIENT) ip addr add 10.0.5.1/24 dev client-a<br>$(NETNS_EXEC) $(NETNS_CLIENT) ip link set dev client-a up<br>$(NETNS_EXEC)...

proxy server client transparent conn netns

Related Articles