Guide

VRRP

VRRP gives a LAN a gateway address that survives the loss of the router holding it. Two or more routers share a virtual IP; one is Active (Master) and answers for it, the others wait. If the Active router dies, a Backup takes the address over, and the hosts on the segment never change their default gateway.

Ze implements RFC 9568 (VRRPv3, IPv4 and IPv6) by default, and RFC 3768 (VRRPv2, IPv4 only) as a per-group opt-in for talking to older equipment.

Status: Experimental. The implementation is complete, its wire format is asserted against golden frames under QEMU, and it interoperates with keepalived 2.3.1 across election, node-death failover, and graceful-stop scenarios (including virtual-MAC ownership of the virtual IP), but it has not yet been proven in production. See RFC status.

Configuration

A VRRP group lives under the interface unit's address family, next to the addresses it backs:

interface {
    backend netlink;
    ethernet eth0 {
        unit 0 {
            ipv4 {
                address [ 192.0.2.251/24 ];
                vrrp {
                    group uplink {
                        vrid 10;
                        virtual-address [ 192.0.2.1 ];
                        priority 200;
                    }
                }
            }
        }
    }
}

group uplink is a name you choose, for your own readability: it names the group in show vrrp output, in log lines, and in the group metric label. It carries no protocol meaning. The protocol identity is vrid, which is mandatory, must be 1..255, and must match on every router in the group.

The two routers in a group differ only in priority. Give the router you want to win the higher number.

Leaf Default Meaning
vrid none (mandatory) Virtual Router ID, 1..255. The same value on every router in the group.
virtual-address none (at least one) The address(es) the group backs. A leaf-list: [ 192.0.2.1 192.0.2.2 ].
priority 100 1..254. Highest wins. 255 is reserved for the address owner and is assigned by ze, never by you.
preempt true Whether a higher-priority router takes mastership back when it returns.
preempt-delay-seconds 0 0..3600. How long a returning higher-priority router waits before preempting. Useful when a router's uplinks converge more slowly than it boots, so it does not take the gateway back before it can forward.
accept-mode false VRRPv3 only. Records whether a non-owner Active should accept packets addressed to the virtual IP. Not enforced on the dataplane yet: see the limitation below.
advertise-interval-milliseconds 1000 How often the Active router advertises.
version 3 2 opts this group into VRRPv2.

Choosing a version

VRRPv3 is the default and is what you want unless something else on the segment cannot speak it. Set version 2 per group for legacy peers:

vrrp {
    group legacy {
        vrid 20;
        version 2;
        virtual-address [ 192.0.2.5 ];
        advertise-interval-milliseconds 2000;
    }
}

VRRPv2 is IPv4-only and has no accept-mode; ze rejects a config that combines them rather than ignoring the leaf. The two versions also encode the advertise interval differently, which constrains what you can ask for:

Version Interval encoding Valid advertise-interval-milliseconds
3 centiseconds any multiple of 10
2 whole seconds any multiple of 1000

Ze refuses an interval it cannot put on the wire instead of silently rounding it, because a rounded interval is a timing mismatch you would only discover as a flapping election.

RFC 3768's authentication types are deliberately not implemented: RFC 9568 Section 9 removed them, as they protect against no real attack.

IPv6

An IPv6 group's first virtual address must be the link-local one, because RFC 9568 Section 5.2.9 makes the first address the advert's source identity:

ipv6 {
    vrrp {
        group uplink-v6 {
            vrid 10;
            virtual-address [ fe80::1 2001:db8::1 ];
            priority 200;
        }
    }
}

IPv4 and IPv6 groups are independent state machines even when they share a vrid, so a group of each on one unit is normal and correct.

IPv6 needs none of the ARP-flux sysctls IPv4 needs: Neighbor Solicitation for the virtual IP targets its solicited-node multicast group, which only the virtual-MAC macvlan joins (the parent does not hold the virtual IP), so the parent never competes to answer. Ze does disable Duplicate Address Detection on that macvlan (accept_dad=0): a virtual IP lives on one router at a time, so DAD would only add a tentative window during which the address is unreachable right after a promotion. IPv6 interoperability is verified against keepalived under QEMU (election, node-death failover, and virtual-MAC resolution of the virtual IP).

The address owner

If a group's virtual address equals an address configured on the unit, that router owns the address and ze assigns it priority 255 automatically (RFC 9568 Section 5.2.4). The owner always wins. Do not set priority 255 yourself; ze rejects it, because the owner is derived from the addresses, and a hand-set 255 on a non-owner would claim an authority the router does not have.

The virtual MAC

Each group gets its own macvlan device carrying the RFC virtual MAC:

Family Virtual MAC
IPv4 00:00:5e:00:01:{vrid}
IPv6 00:00:5e:00:02:{vrid}

This is why failover works without the hosts noticing. The virtual MAC moves with the virtual IP, so a host's ARP or neighbour entry for the gateway stays valid: the address it points at simply arrives from a different router. Ze additionally sends gratuitous ARP (IPv4) or unsolicited Neighbor Advertisements (IPv6) on promotion so switches relearn the port immediately.

The macvlan is created and destroyed with the group. It belongs to the plugin, not to your config, and you do not declare it.

A host on the segment that resolves the virtual IP gets the virtual MAC, not the router's real MAC, so failover is transparent at L2: the address moves to a different router without the host's ARP or neighbour entry changing. Achieving this on Linux, where the macvlan's parent holds a real address in the same subnet, requires the macvlan to be the sole ARP responder for the virtual IP. Ze arranges that automatically when it creates the macvlan (you configure nothing): the device is created in macvlan private mode, the virtual IP is installed with the parent's subnet prefix, and a small set of arp_ignore, arp_filter, and rp_filter sysctls stop the parent answering for the virtual IP while letting the macvlan answer with the virtual MAC. This mirrors what keepalived's use_vmac does, and ze restores the sysctls it changed when the last group on an interface goes away. This behaviour is verified against keepalived under QEMU (ze-test's VRRP interop lab).

A few consequences of this mechanism are worth knowing:

Operating

Command Shows
show vrrp Every group: state, priority, vrid, virtual addresses.
show vrrp interface name eth0 Only the groups on one interface.
show vrrp statistics Per-group counters, including rejected packets and why.
clear vrrp statistics Resets those counters.

State changes are logged as vrrp: state change with from, to, and a reason, so a failover leaves a record of what triggered it.

Replayable Ze terminal lab

Keep the gateway reachable while Ze stops

Inspect the active and live VRRP state, stop the higher-priority Ze router, and prove keepalived takes the same reachable VIP.

Reproducible
Ze 26.07.182 minutes 54 secondsLinux namespace labTerminalVHS 0.11.0 Plain-text transcript
Read the demonstration transcript
An operator needs to stop the active router without changing the default gateway on every host.

$ ze config show demos/terminal/vrrp-failover/ze.conf interface ethernet eth0 unit 0 ipv4 vrrp group gateway
The daemon configuration shows VRID 10, VIP 192.0.2.1, priority 200, and the advertisement interval.

$ grep -E 'interface|virtual_router_id|priority|192.0.2.1' keepalived.conf
The peer is configured as BACKUP on the same interface, VRID, and VIP with a lower priority of 100.

$ ze cli -c "show vrrp"
The complete live state shows Ze is master.

$ ip -n vrrp-ze -o addr show | grep 192.0.2.1
The kernel shows the VIP on Ze's RFC virtual-MAC interface.

$ cat failover-proof.sh
$ bash -x failover-proof.sh
The recording displays and traces the exact commands that kill Ze, remove its namespace, inspect the VIP on keepalived, and send two probes.

The final kernel output shows 192.0.2.1 on keepalived's `vrrp.10` interface, and both probes succeed after failover.

For Prometheus:

Metric Meaning
ze_vrrp_state{device,group,vrid,family} Current state of each group.
ze_vrrp_transitions_total State transitions, for spotting a flapping group.

Requirements and limits

accept-mode is not enforced on the dataplane. RFC 9568 Section 6.4.3 says a non-owner Active with Accept_Mode false must not accept packets addressed to the virtual IP, other than the adverts themselves. Ze records the leaf, validates it (rejecting it under version 2), and reports it in show vrrp, but does not yet install the filtering: the virtual address is configured on the group's macvlan while the router is Active, so the kernel answers ping and other traffic for it whichever way you set the leaf. In practice this means an Active ze router answers on the virtual IP, which is what accept-mode true asks for and what most deployments want. If you are relying on accept-mode false to make the virtual IP unreachable except as a forwarding next hop, it will not do that today.

No priority tracking. A group's priority is fixed at the value you configure. Ze does not decrement it in response to an uplink going down, a route disappearing, or a health check failing, so it will not on its own hand the gateway to a Backup when an upstream path fails while the VRRP interface itself stays up. The interface, route, and script tracking that Junos, Nokia, and VyOS use to drive that priority-decrement failover is not implemented in this release; failover is driven only by VRRP's own triggers: loss of the Active router's adverts, carrier loss on the VRRP interface, or a graceful stop.

Netlink backend only. VRRP needs macvlan devices and raw sockets, which the VPP backend does not provide. A VPP-backed config carrying a VRRP group is rejected at validation rather than started in a degraded state.

Raw socket privileges. Ze needs CAP_NET_RAW and CAP_NET_ADMIN to send adverts (IP protocol 112) and to manage the macvlan.

Multicast must reach the segment. Adverts go to 224.0.0.18 (IPv4) or ff02::12 (IPv6) with TTL/hop-limit 255, and RFC 9568 requires receivers to discard anything arriving with a lower TTL. A device that rewrites the TTL or filters link-local multicast will break the election.

Diagnostics

ze doctor runs the same verifier a commit runs, so the two can never disagree.

Code Meaning
doctor-vrrp-config-invalid A cross-leaf rule was broken (for example accept-mode with version 2).
doctor-vrrp-backend-unusable The tree configures VRRP on a backend that cannot run it.
doctor-vrrp-raw-socket The raw socket VRRP needs cannot be opened, usually missing CAP_NET_RAW.

Run ze explain <code> for the remediation.