When building enterprise edge proxies, Web Application Firewalls (WAF), service meshes, or telemetry inspection engines on Linux, traditional traffic interception methods introduce major operational tradeoffs:
- Standard DNAT / Port Forwarding (
iptables -j REDIRECT): Rewrites destination ports cleanly, but breaks UDP non-local socket semantics and adds connection tracking (conntrack) memory bloat. - Reverse Proxying with SNAT (
nginx proxy_pass): Rewrites source IP addresses, completely obscuring the original client IP unless custom headers likeX-Forwarded-Foror the PROXY protocol are supported by both endpoints.
To intercept both TCP and UDP traffic transparently—while preserving the original client IP and destination port without NAT overhead—the Linux kernel provides TPROXY (Transparent Proxy).
In this deep-dive systems engineering guide, we explore how TPROXY works, configure Linux policy routing (ip rule), and build high-performance transparent proxying using Envoy and eBPF on Dedicated Servers in Pakistan.
The Architecture: Why TPROXY Outperforms DNAT/REDIRECT
[Standard REDIRECT / NAT]
Client (203.0.113.5:54122) ──► Server (198.51.100.10:443)
│
▼ (iptables -j REDIRECT --to-ports 8443)
Dest IP rewritten to 127.0.0.1:8443 (Kernel rewrites packet headers!)
(Non-local socket bindings fail; conntrack overhead on millions of flows)
[TPROXY Transparent Redirection]
Client (203.0.113.5:54122) ──► Server (198.51.100.10:443)
│
▼ (iptables -j TPROXY --tproxy-mark 0x1/0x1 --on-port 15001)
Packet headers remain COMPLETELY UNTOUCHED!
Kernel routes packet to user-space proxy socket bound with IP_TRANSPARENT!
(Proxy inspects original Client IP + original Dest IP with zero header mutation!)
With TPROXY, the kernel delivers the unmodified packet to a local listening socket that has the IP_TRANSPARENT socket option enabled. The proxy application can read the original destination IP and port using getsockname() and see the real remote client IP using getpeername().
Step 1: Configuring Linux Policy Routing (ip rule & ip route)
TPROXY uses Netfilter firewall marks (fwmark) to route intercepted packets to the local loopback interface:
# 1. Create a dedicated routing table (table 100) that routes marked packets locally
sudo ip route add local default dev lo table 100
# 2. Add an ip rule directing packets marked with 0x1 to routing table 100
sudo ip rule add fwmark 0x1 lookup 100
Verify that the policy rule is active:
ip rule show
Output:
0: from all lookup local
100: from all fwmark 0x1 lookup 100
32766: from all lookup main
32767: from all lookup default
Step 2: Injecting TPROXY Rules via iptables / nftables
Instruct Netfilter to match incoming TCP packets destined for HTTP/HTTPS ports (80/443), assign firewall mark 0x1, and redirect them to local proxy port 15001:
Using iptables (mangle table):
# Create custom TPROXY chain in mangle table
sudo iptables -t mangle -N TPROXY_CHAIN
# Skip loopback and private management traffic
sudo iptables -t mangle -A TPROXY_CHAIN -d 127.0.0.0/8 -j RETURN
sudo iptables -t mangle -A TPROXY_CHAIN -d 10.0.0.0/8 -j RETURN
# Intercept TCP traffic on ports 80 and 443
sudo iptables -t mangle -A TPROXY_CHAIN -p tcp -m multiport --dports 80,443 \
-j TPROXY --tproxy-mark 0x1/0x1 --on-port 15001
# Attach chain to PREROUTING
sudo iptables -t mangle -A PREROUTING -j TPROXY_CHAIN
Step 3: Enabling IP_TRANSPARENT Socket Option in Code
For an application (written in C, Go, or Rust) to accept transparently redirected connections, it must set the IP_TRANSPARENT socket flag:
In C / C++:
int fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
// Enable non-local IP binding and transparent socket acceptance
if (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt)) < 0) {
perror("setsockopt IP_TRANSPARENT failed");
exit(1);
}
// Bind to 0.0.0.0 on port 15001
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(15001);
addr.sin_addr.s_addr = INADDR_ANY;
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
listen(fd, 4096);
In Envoy Proxy (envoy.yaml):
Envoy supports TPROXY natively. In your listener configuration, set transparent: true:
static_resources:
listeners:
- name: tproxy_listener
address:
socket_address:
address: 0.0.0.0
port_value: 15001
transparent: true
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: origin_cluster }
Step 4: Accelerating Socket Dispatch with eBPF (sock_ops & sk_msg)
On high-concurrency Dedicated Servers, you can pair TPROXY with eBPF socket redirection.
By attaching an eBPF sock_ops program to the root cgroup, the Linux kernel shortcuts packet processing: when the transparent proxy forwards data to the local backend, eBPF redirects the TCP stream directly from the client socket buffer to the backend socket buffer, completely bypassing TCP/IP stack re-traversal!
This drops proxy latency by up to 40%, delivering near-line-rate performance for enterprise security proxies and API gateways in Pakistan.
Deploy Low-Latency Edge Proxies with NextGen Bare Metal
Run your WAF, Envoy gateways, and eBPF network pipelines on dedicated infrastructure. NextGen Dedicated Servers in Pakistan offer 10Gbps unmetered connectivity, enterprise AMD EPYC processors, and direct peering across local telecom backbones.
