How Linux Proxy ARP Works in the Routing Stack
Proxy ARP in Linux allows a system to respond to Address Resolution Protocol (ARP) requests on behalf of other devices on distinct network segments. This article explains the internal mechanics of Proxy ARP within the Linux network stack, detailing the sysctl configuration flags, how the kernel's ARP processing routine interacts with the Forwarding Information Base (FIB), the ingress-to-egress validation rules that prevent network loops, and the complete packet lifecycle from initial ARP request to forwarded IP traffic.
Enabling Proxy ARP in the Kernel
Proxy ARP operates at Layer 2/Layer 3 boundaries and is governed by
kernel runtime parameters exposed via sysctl. It is
disabled by default for security and performance reasons. To activate
it, administrators toggle interface-specific flags under the
/proc/sys/net/ipv4/conf/ directory:
net.ipv4.conf.<interface>.proxy_arp: Enables standard Proxy ARP on the specified interface.net.ipv4.conf.all.proxy_arp: Globally enables Proxy ARP across all interfaces if both the global and per-interface values permit it.net.ipv4.conf.<interface>.proxy_arp_pvlan: Enables Private VLAN proxy ARP, allowing the kernel to reply to ARP requests even when the target IP resolves out of the same physical or logical interface.
Kernel ARP Processing Pipeline
When an Ethernet frame carrying an ARP request arrives at a network
interface, the network device driver passes the sk_buff
(socket buffer) to the core networking layer via
netif_receive_skb(). The packet dispatcher identifies
protocol ETH_P_ARP and routes the buffer to
arp_process() in the net/ipv4/arp.c kernel
source file.
The arp_process() routine evaluates whether to generate
an ARP reply. If the requested target IP does not match any local
address assigned to the receiving interface, the kernel checks whether
Proxy ARP should intervene.
Route Lookup and Validation Logic
Before synthesizing an ARP reply for a foreign IP address, the Linux kernel subjects the request to a strict set of routing and forwarding conditions:
- IP Forwarding Verification: The kernel confirms
that routing is enabled on the host
(
net.ipv4.ip_forward = 1). A system that cannot forward IPv4 datagrams will not proxy ARP requests. - Forwarding Information Base (FIB) Query: The kernel performs an internal route lookup for the requested IP address. It queries the routing tables to see if a valid route exists to the destination. If the target address is unreachable or drops into a blackhole route, the kernel discards the ARP request.
- Interface Separation Check (Split Horizon): Under
standard
proxy_arp, the kernel checks the egress interface determined by the FIB lookup. The egress interface must be different from the ingress interface where the ARP request was received. This rule prevents network broadcast storms and routing loops. Ifproxy_arp_pvlanis enabled, this check is relaxed to accommodate isolated hosts on the same subnet.
ARP Reply Generation
If the packet satisfies all lookup and filtering criteria, the kernel
builds an ARPOP_REPLY packet:
- Sender Hardware Address (SHA): Populated with the MAC address of the interface that received the ARP request, not the MAC address of the actual target device.
- Sender Protocol Address (SPA): Populated with the target IP address requested by the client.
- Target Hardware Address (THA): Set to the MAC address of the querying host.
- Target Protocol Address (TPA): Set to the IP address of the querying host.
The generated reply is transmitted directly out of the receiving
interface back to the requester via dev_queue_xmit().
Subsequent Data Path Forwarding
Once the querying client receives the proxy ARP reply, it updates its local ARP cache, binding the target IP address to the Linux router's interface MAC address.
When subsequent IP datagrams leave the client, they arrive at the
Linux host with the router's Layer 2 destination MAC and the final
host's Layer 3 destination IP. The Linux kernel strips the Layer 2
header, inspects the Layer 3 header, recognizes the destination as
non-local, decrements the TTL, and forwards the packet toward the true
destination via ip_forward() and standard IP routing
pathways.