Skip to content
Thomas Mangin edited this page Aug 16, 2026 · 6 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";
    auth-method chap-md5;
    cookie-timeout 5;
    max-sessions 65535;
    padi-rate-limit 100;
    interface eth0 {
    }
    interface eth0.100 {
        service-name "vlan100";
        max-sessions 1000;
    }
}

The subsystem starts only when enabled is true and the block lists at least one interface. A pppoe block with no interface entry registers nothing and answers no PADI.

Subscriber authentication

auth-method is the PPP Auth-Protocol the access concentrator puts in its own LCP Configure-Request.

Leaf Default Values Description
auth-method chap-md5 none, pap, chap-md5, ms-chap-v2 Auth-Protocol advertised in the AC's LCP Configure-Request (RFC 1661 Section 6.2)
allow-no-auth false boolean Admit a subscriber whose LCP ends with no Auth-Protocol negotiated

auth-method none requires allow-no-auth true beside it: an access concentrator that asks nobody who they are is a decision, not a default. That combination, and a value the PPP driver does not know, are both refused when the daemon starts. The default matches the L2TP LNS default, so a credential configured for one transport is treated the same way on the other.

The credential comes from the same auth plugins the L2TP LNS uses. l2tp-auth-local verifies it and l2tp-pool supplies the IPCP address, so a PPPoE-only BNG configures those two under l2tp {} and needs neither l2tp enabled nor an L2TP listener.

l2tp {
    auth {
        local {
            user alice {
                password "s3cr3t";
            }
        }
    }
    pool {
        ipv4 {
            gateway 10.20.0.1;
            start 10.20.0.2;
            end 10.20.0.254;
        }
    }
}

The PPP auth handler slot holds one handler, and configuration decides its owner: l2tp-auth-radius claims it when a RADIUS server is configured, and l2tp-auth-local keeps it otherwise. A box with local users and no RADIUS block therefore authenticates against those users, on PPPoE and on L2TP.

Interoperability

Two Docker lab scenarios run Ze in each PPPoE role. make ze-deployment-docker-pppoe-accel-test runs both.

Scenario Ze role Peer Proves
01-pppoe-chap-ipv4 client accel-ppp Discovery, LCP, CHAP-MD5, the IPCP address on a kernel pppN interface, a ping to the AC gateway through the session, and a clean teardown
02-ze-ac-pppd-client access concentrator pppd 2.5.1 with the rp-pppoe plugin Discovery, LCP demanding CHAP-MD5, the credential accepted, an IPCP pool address, ICMP across the session, and a wrong password refused before IPCP

make ze-qemu-pppoe-accel-test runs the client half in QEMU, and make ze-qemu-pppoe-test runs the access concentrator's own functional suite on Ze's runtime kernel, because a stock Alpine kernel has no CONFIG_PPPOE.

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