Both were invisible to inspection and only appeared under the hard matrix: the flap damper tearing down an established session via the lease it stopped renewing, and a missing peers file being completely silent. The second has a production edge worth spelling out -- an unsaved commit reverts on reboot and takes pppoe0 with it, leaving a standby that can never take over while looking perfectly healthy.
7.3 KiB
PPPoE high availability
Proven in labsim. Not applied to production.
What it does
One consumer ISP account, two routers. The 10 gig lease is bound to a cloned MAC
(f0:9f:c2:12:9b:4f, the retired USG's) and the Vodafone line to a single
credential, so neither may be live on both boxes. The WAN follows VRRP
mastership — but the two halves use different control planes, and that is the
whole design:
| plane | why | |
|---|---|---|
bond0.53 (10 gig) |
VyOS config (disable) |
only config can move a MAC |
pppoe0 (Vodafone) |
systemd unit gate | see below |
Why PPPoE cannot live on the config plane
interfaces_pppoe.py treats disable and delete identically: both unlink
/etc/ppp/peers/pppoe0, call PPPoEIf.remove() (withdrawing the FRR default
route) and stop the unit. That path is pppd's own options file
(ExecStart=/usr/sbin/pppd call %I), so the resting state destroyed exactly what
the promotion path needed. ppp@pppoe0 then restart-looped against the missing
file — 47 restarts observed, zero sessions at the access concentrator — and
never tripped systemd's limiter, because RestartSec=5s against the default
10s/5-burst window is only two restarts per interval.
It also made op-mode connect interface pppoe0 unusable (it refuses without the
peers file), and put every failover behind a priority-322 commit where one
unrelated invalid node fails the whole thing.
The gate
pppoe0 is configured identically and enabled on both routers, so the peers
file always exists. Dialling is gated by
/etc/systemd/system/ppp@pppoe0.service.d/10-vrrp-wan-gate.conf:
ConditionPathExists=/run/vrrp-wan/may-dial
ConditionPathExists=/etc/ppp/peers/pppoe0
StartLimitIntervalSec=600
StartLimitBurst=6
/run is tmpfs, so the gate is shut at boot and neither box can dial before VRRP
has decided. This is load-bearing, not a nicety: with the node enabled,
interfaces_pppoe.py restarts ppp on every commit touching the pppoe subtree
when the daemon is not running — so the backup actively tries to dial whenever
anything commits (pulumi up, a hand commit, the boot-time config load). The
gate is the only thing making that a no-op, which is why vrrp-wan-reconcile
refuses to bless a box whose drop-in is missing: /etc is per-image, so a
VyOS upgrade silently removes the protection, and failing closed turns that into
"PPPoE never dials" rather than "both routers dial".
may-dial is a lease, not a flag. ConditionPathExists is evaluated at
start only — it can prevent a dial, never revoke one. vrrp-wan-reconcile
renews it every 30s; vrrp-wan-guard runs every 5s and only ever revokes, on
either "I do not hold the VIP" or "the lease is stale".
Measured in labsim
Clean failover (force-fault) |
pppoe0 moves in 20-26s, reproducible |
| 10 gig down → PPPoE | route falls to pppoe0; LAN back online in 5s |
| Stale lease | guard hangs up within ~5s |
| Missing peers file | NRestarts=0 — no loop |
| Flap holdoff vs live session | session survives a forged 900s holdoff |
| Invariant | AC never showed two simdsl sessions |
Two failure modes found by running it, not by reading it
The flap damper tore down a healthy WAN. ppp_dial() checked the hold-off
and returned before renewing may-dial. That file is a lease the guard
expires after LEASE_TTL, so tripping the damper stopped the renewal and the
guard hung up pppoe0 on the master ~80s later:
DIAL FLAP: >=6 attempts in 600s -- holding off 900s
GUARD: lease stale (81s > 75s) -- hanging up pppoe0
A damper meant to suppress repeated dials was destroying an established session instead. An active session now renews the lease and returns before every other check; everything below only decides whether to start a new session. T12 is the regression test.
A missing peers file is silent. /etc/ppp/peers/pppoe0 is both pppd's
options file and the gate's second condition, and it is only written by a commit
that touches the pppoe subtree. Without it systemd logs
skipped because of an unmet condition check exactly once and then nothing —
a router that cannot dial at all looks identical to a healthy backup.
ppp_dial() now says so on every tick, and distinguishes the two causes:
configured-but-not-rendered (re-commit the subtree) versus no pppoe0 in the
config at all.
The second cause is the one to watch in production: a commit that was never
saved reverts on reboot and takes pppoe0 with it. That is exactly how the
sim secondary lost its WAN and spent hours looking like an ISP problem. After
any hand commit to the pppoe subtree, save — or the next reboot produces a
standby that can never take over.
Deploying (not yet done)
sudo /config/vyos-known-good saveon both.migration/vrrp-wan-install --vip 192.168.1.1 --host vyos@10.0.1.253then the same for.252. Then--checkon both. No config change yet — verify nothing dials.- Confirm
/config/wan-secretsis present and identical on both. - vyos002 first (the non-master), on its own commit —
interfaces pppoeis priority 322 and one bad node fails everything:delete interfaces pppoe pppoe0 disable,commit-confirm 10. - Verify vyos002 did not dial — check on the wire, not from state:
sudo tcpdump -i bond0.51 -nn pppoedshould show no PADI. Thenconfirm;save. - vyos001: nothing to change; it already has
pppoe0enabled. - Confirm
vif 53 disableis in bothconfig.boots. - Add the drop-in re-install to the VyOS image-upgrade runbook.
Rollback, from either box: set interfaces pppoe pppoe0 disable on both and
rm /run/vrrp-wan/may-dial. That restores today's behaviour exactly.
What the sim cannot prove
- Vodafone's
session-control. The sim's accel-ppp defaults toreplace, so a new auth kills the old session immediately. A real BRAS maydenyand hold the session for its own dead-peer timer. The matrix runs all three modes to bracket the risk; it cannot tell you which one you will meet, and account rate-limiting or lockout on repeated dials has no sim analogue at all. The flap damper (6 dials / 600s → 15 min hold-off) exists for that. - Whether Vodafone honours our LCP Terminate / PADT on a graceful stop.
- The cloned-MAC lease — whether the 10 gig ISP re-issues
87.192.101.48tof0:9f:c2:12:9b:4farriving on a different switch port. That risk belongs tobond0.53, not PPPoE, and is the largest untested item in the failover. - Real dial time and MTU/MSS under load. PPPoE was proven on the USG; VyOS dialling Vodafone has never been done.
- Timing under load. The sim routers are idle 2-vCPU VMs; commit latency on
the VP2440s under kea + BGP + conntrack will be worse, and commit latency is
the dominant term in the
bond0.53half of a failover.
A model hazard to fix before applying
The imported baseline records the running state, not the safe one: vyos001
has no vif 53 disable (it is master), vyos002 does. So an apply performed while
vyos002 held the VIP would enable vyos001's WAN as well, putting the cloned MAC
on both boxes. An override must assert vif 53 disable on both routers, with
the reconciler re-enabling whichever holds the VIP. Note the consequence: an
apply then briefly disables the current master's 10 gig until the reconciler
restores it (≤30s).