Bonding and WAN uplinks on Saphira Linux with Open vSwitch

Bonding and WAN uplinks on Saphira Linux with Open vSwitch

Combining two uplinks into one resilient path is a solved problem in theory and a fragile one in practice. The classic recipe (Linux bonding driver, a bridge, a VPN on top) works until the day you enslave the wrong NIC, lose the management path, and drive to the site. Saphira Linux approaches this with a small package, saphira-network, and a family of Open vSwitch do-one-thing commands with pre-flight checks and automatic rollback.

The standard approach

Linux offers the bonding driver (modes include balance-slb and 802.3ad LACP), bridging, and Open vSwitch with `ovs-vsctl add-bond`. Tunnels enter through VXLAN or GRE ports on the bridge, and a VPN can ride on top. Multipath TCP covers a different case: one TCP connection across interfaces, not whole-link resilience. All of this is documented upstream and works. What the manuals leave you is the operational half: refusing dangerous moves, surviving a lost management link, and reproducing the setup on the next machine.

What Saphira ships today

saphira-network 0.1-r6 (Business Source License 1.1) provides static per-interface configuration files, one per file in lexical order:

TYPE=physical
INTERFACE=eth0

IPV4_MODE=static
IPV4_CIDR=192.0.2.10/24
IPV4_GATEWAY=192.0.2.1

IPV6_MODE=off
DNS_SERVERS="192.0.2.53 2001:db8::53"

A file becomes ACTIVE once renamed to `*.conf`. Where no active configs exist, the tool auto-reads the legacy `/etc/network.d` directory, so upgraded systems migrate without edits. `TYPE` also supports `ovs-bridge`, `ovs-port` and `ovs-internal`, and both IP modes accept `static`, `dhcp` or `off`.

For virtualisation hosts, `ovs-vmswitch` provisions a complete OVS network for libvirt VMs in one command:

ovs-vmswitch \
  --switch test0 \
  --port tgw0 \
  --ipv4 10.100.0.1/24 \
  --ipv6 2a02:8012:bc57:100::1/64

This creates the OVS bridge plus internal gateway port in a single atomic `ovs-vsctl` transaction, sets MTU 9000, writes the systemd-networkd configs, and defines, starts and autostarts the persistent libvirt network. It integrates with systemd-networkd only, which makes it the SaphiraD (systemd) flavour of the tooling; OpenRC systems use `saphira-network-config`.

The wider command family (proposal status)

The package also carries `OVS_PROPOSAL.md`: a roadmap wrapping the `ovs-vsctl` surface into memorable verbs (`ovs-bridge`, `ovs-port`, `ovs-uplink`, `ovs-bond`, `ovs-tunnel`, `ovs-patch`, `ovs-net`, `ovs-status`, plus `ovs-rollback`). Be clear about status: this is a proposal, and only `ovs-vmswitch` is implemented. Bonds today are still built with `ovs-vsctl` directly, for example:

ovs-vsctl add-bond br0 bond0 eth0 eth1 bond_mode=balance-slb

The design is worth studying because of its safety model. Every mutating command must pre-flight: refuse if the NIC is already enslaved, refuse if a systemd-networkd file currently matches the device, and refuse if the target already exists. It then asks for confirmation (moving a NIC drops its links for a second or two), applies, and arms a rollback timer that restores the previous OVS database snapshot and networkd files unless the operator confirms reachability. Intent is written to `/etc/ovs/switches.d/` before anything is applied, so networkd configs and OVS rows are reproducible from it. A `--remote HOST` flag replays the same grammar over SSH for the far end of a tunnel pair.

Why it works

The atomic transaction matters most. A bridge plus gateway port created in one `ovs-vsctl` call cannot strand you halfway. The MAC-clone behaviour on uplink moves (copying the NIC hardware address onto the bridge port) preserves external ARP state and DHCP leases across the move. Bridges default to standalone fail mode with spanning tree off, which is the correct default for a host switch that must never participate in a wider spanning-tree domain by accident.

Failure modes and things not to assume

LACP (`--mode lacp`) requires switch cooperation; without it the bond will not form, which is why balance-slb is the default. Moving a live NIC always drops its links briefly, so never do this without a second management path. The proposal commands (`ovs-bond`, `ovs-tunnel`, `ovs-rollback`) do not exist yet: do not script against them. And the classic bonding driver recipe the original draft of this article described still works on any distro; the Saphira tooling is about surviving the operation, not replacing the mechanism.

Conclusion

Bonding and WAN uplinks are standard Linux mechanisms. What Saphira adds is operational discipline around them: declarative interface files with legacy migration, one-command OVS provisioning, and a command family designed around pre-flight refusal and automatic rollback. Start with `saphira-network-config` files, use `ovs-vmswitch` for VM hosts, and treat the wider OVS verbs as a roadmap to watch.

References

- Open vSwitch documentation: https://www.openvswitch.org/support/ - Linux bonding driver: https://www.kernel.org/doc/Documentation/networking/bonding.txt - Saphira Linux: https://saphira.vm2.uk/ - Saphira recipes (public): https://github.com/SaphiraLinux/recipes