How Linux Handles PPPoE Connections
The Linux operating system handles Point-to-Point Protocol over Ethernet (PPPoE) through a hybrid architecture combining user-space control daemons and in-kernel packet processing. By bridging the data link layer (Ethernet) and the network layer (PPP), Linux allows systems to authenticate and communicate over broadband networks such as DSL and fiber. This article explains how Linux initiates connections, offloads packet forwarding to the kernel, and manages transmission constraints like MTU sizing.
The Two-Phase PPPoE Process
Linux processes PPPoE connections in two distinct stages: Discovery and Session.
- Discovery Phase: Because Ethernet is a multi-access
network without built-in point-to-point semantics, Linux must first
locate an Access Concentrator (AC). A user-space utility (such as
pppoefrom the Roaring Penguin suite orNetworkManager) broadcasts a PPPoE Active Discovery Initiation (PADI) packet. When reachable concentrators reply with Offer (PADO) packets, the Linux client selects one, sends a Request (PADR), and waits for a Session-confirmation (PADS) containing a unique 16-bit Session ID. - Session Phase: Once the Session ID is established,
standard Point-to-Point Protocol (PPP) encapsulation begins. The Linux
pppddaemon negotiates link parameters using the Link Control Protocol (LCP), performs credential verification via PAP or CHAP, and negotiates network settings (IP addresses, default routes, DNS servers) using the Internet Protocol Control Protocol (IPCP).
User-Space Control vs. Kernel-Space Data Path
Early Linux PPPoE implementations handled all packet encapsulation in user space. This created significant CPU overhead because every network frame required a context switch between the kernel network stack and the user daemon.
Modern Linux systems use a dedicated kernel module
(pppoe, often compiled as part of
CONFIG_PPPOE) alongside the ppp_generic
subsystem:
- User Space (
pppdand plugins): Manages the state machine, renegotiates links, checks link quality using LCP Echo requests, and terminates sessions gracefully. - Kernel Space (
AF_PPPOXsocket): Once the user-space daemon negotiates the session, it attaches the PPPoE session ID and network interface to a kernel-level socket. The kernel then takes over the data path. Outgoing IP packets are encapsulated directly into PPPoE and Ethernet frames inside the kernel, and incoming PPPoE frames are stripped and passed directly to the standard TCP/IP stack without user-space intervention.
This separation ensures full line-rate throughput with minimal CPU overhead.
Network Interface Creation
When the session negotiation succeeds, the kernel creates a virtual
point-to-point network interface, typically designated as
ppp0. This interface functions identically to any other
standard network interface in Linux:
- It receives its own IP address and subnet mask from the ISP.
- It is assigned routes in the kernel routing table, usually serving as the system's default gateway.
- Standard Linux firewalling tools (
iptablesandnftables) can filter or shape traffic on this interface directly.
Handling MTU and MSS Clamping
Standard Ethernet frames have a Maximum Transmission Unit (MTU) of 1500 bytes. Because the PPPoE header consumes 6 bytes and the PPP protocol ID consumes 2 bytes, the maximum payload for an IP packet over PPPoE is reduced to 1492 bytes.
To prevent packet fragmentation or dropped packets caused by path MTU
discovery issues, the Linux kernel relies on Maximum Segment Size (MSS)
clamping. Network tools or firewall configurations inject rules via
iptables or nftables using the
TCPMSS target:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
This instruction forces Linux to rewrite the TCP MSS option in outgoing SYN packets to match the 1492-byte limit automatically, ensuring transparent and reliable communication across the PPPoE tunnel.