Skip to content
Thomas Mangin edited this page Jul 13, 2026 · 4 revisions

Pre-Alpha. This page describes behavior that may change.

Ze implements an RFC 2516 PPPoE access concentrator for direct-attach subscriber access. PPPoE is the alternative to L2TP: subscribers connect over Ethernet to the BNG without an intermediate LAC/LNS tunnel.

Architecture

PPPoE uses the same transport-agnostic PPP driver as L2TP. The PPPoE component handles discovery (PADI/PADO/PADR/PADS/PADT) and creates kernel PPPoE sessions via AF_PPPOX. The resulting /dev/ppp file descriptors feed into the PPP driver, which runs LCP, authentication, and IPCP/IPv6CP identically to L2TP sessions.

PPPoE and L2TP run concurrently on the same daemon. Both share the same PPP driver, auth handlers, IP pools, and shaper plugins.

Configuration

pppoe {
    enabled true;
    ac-name "my-bng";
    service-name "internet";
    cookie-timeout 5;
    max-sessions 65535;
    padi-rate-limit 100;
    interface eth0 {
    }
    interface eth0.100 {
        service-name "vlan100";
        max-sessions 1000;
    }
}

CLI

Command Description
show pppoe Subsystem summary
show pppoe sessions List active sessions
show pppoe session <sid> Show one session
show pppoe statistics Per-interface counters
show pppoe interfaces Configured access interfaces

Security

  • AC-Cookie: HMAC-SHA256 cookie in PADO/PADR prevents PADR flooding. Cookies expire after cookie-timeout seconds (default 5).
  • PADI rate limiting: Per-source-MAC rate limit prevents discovery flooding. Configurable via padi-rate-limit (default 100/s).
  • Service-Name filtering: Only PADIs matching configured service names are accepted. Empty list means accept any.
  • MAC binding: Sessions are bound to the subscriber MAC from PADR. PADTs from other MACs are rejected.

PPPoE to L2TP relay (LAC role)

Instead of terminating PPP locally, a PPPoE subscriber can be relayed straight into an L2TP incoming call, turning the box into an L2TP Access Concentrator (LAC). When a subscriber completes discovery and its PPPoE Service-Name matches a configured relay binding, Ze originates an L2TP incoming call (ICRQ) toward the bound remote instead of starting a local PPP session.

The relay bindings live on the L2TP side under l2tp { relay ... }, each referencing an l2tp { remote ... } dial target. See L2TP for the dial-target and relay configuration.

l2tp {
    remote lns-retail {
        address 203.0.113.10;
        shared-secret <secret>;
    }
    relay internet {            // PPPoE Service-Name to relay
        remote lns-retail;
    }
}

On the data plane, the subscriber's PPPoE channel and the L2TP pppol2tp channel are cross-connected in the kernel, so PPP frames flow directly between them and no local PPP unit runs for the subscriber. This kernel channel bridge is Linux-only.

The PPPoE and L2TP packages do not import each other. The relay decision crosses a neutral call-sink seam: the L2TP subsystem registers a sink at startup, and the PPPoE server looks it up for each PADS-completed subscriber.

Fallback to local termination happens when no relay binding matches the service, when no L2TP subsystem is running, or when a matched remote cannot be dialed. In each of these cases the subscriber terminates PPP locally as normal.

PPPoE client

Ze also supports PPPoE as a client (CPE side). PPPoE client interfaces are configured under interface { pppoe-client <name> { ... } } and use the same PPP driver. See Interfaces for the full config reference.

interface {
    pppoe-client pppoe0 {
        source-interface eth2;
        authentication {
            username "user@isp.example";
            password "secret";
        }
    }
}

The client dials an access concentrator over the named physical interface, negotiates LCP/auth/IPCP, and presents the PPP session as a routable interface with server-assigned addresses.

See also

Adapted from main/docs/guide/pppoe.md.

Home

About

First Steps

Configuration

Operation

Interfaces

Plugins

Plugin Development

Chaos Testing

Blueprints

Development

Reference

Clone this wiki locally