routewright

Kubernetes CNI · standard Linux only

Kubernetes networking that shows up in show route.

Routewright runs your cluster network the way you run the rest of your network: BGP to the fabric, routed pods, ECMP with BFD failover. No eBPF. No overlay. No black boxes.

· Embedded BGP speaker · IPVS & nftables dataplane · Dual-stack, per-family sessions

GitHub stars
0
eBPF in the datapath
~1s
ECMP failover · BFD
4
binaries · one Go module

Why routed

Pods are routes, not tunnels.

Most modern CNIs reach for eBPF and bespoke overlays. You debug that datapath with a decompiler.

Routewright takes the other road. It composes the routing, filtering, and load-balancing subsystems the Linux kernel has shipped for decades, and speaks BGP to your fabric the way a router does. Every pod prefix is a real route your top-of-rack switches resolve over the ECMP they already run. No overlay, no encapsulation, no second control plane between your packets and the wire.

You inspect it with the tools already in your muscle memory. A pod prefix is another entry in the RIB. A Service is an IPVS virtual server with real connection tracking. A NetworkPolicy is a set of nftables rules you can follow. When a session flaps at 3 a.m., the failure is in the routing table, not compiled into a program you cannot single-step.

01 · routing

The kernel FIB

Pod and service prefixes are advertised over BGP and installed as multipath routes in the kernel FIB. Forwarding stays the kernel's job, with L4 hashing across equal-cost next-hops. No VXLAN, no encapsulation tax, no MTU accounting.

02 · load balancing

IPVS for Services

ClusterIP, NodePort, and LoadBalancer are IPVS virtual servers: real schedulers, real connection tracking, in the kernel. It replaces kube-proxy outright, and you read the live state with ipvsadm.

03 · filtering

nftables policy

NetworkPolicy compiles to nftables sets and chains you can list and follow. Masquerade, mark, and hairpin live in the same ruleset. No hidden program, no bytecode to decompile.

04 · control plane

Embedded BGP speaker

One BGP speaker per node, the only speaker on the box. eBGP to the fabric, BFD for sub-second failure detection, TCP-MD5 on every adjacency, and a session per family for clean dual-stack.

Boot to reachable

The node is on the fabric before kubelet starts.

A per-node daemon is the sole BGP speaker on the box. It starts from an immutable node.yaml before kubelet, opens eBGP and BFD to each top-of-rack switch, brings the node's management address up over BGP, and installs an ECMP default into the kernel FIB. The cluster control plane never touches the management plane, so it carries zero blast radius against node reachability.

ToR-AAS 65000ToR-BAS 65000eBGP + BFDeBGP + BFDroutewright-host · sole BGP speakermgmt IP · ECMP FIB · kubelet --node-iplocal gRPCroutewright-agentPodsvethServicesIPVSLoadBalancerVIPNetworkPolicynftables
1 · Before kubelet
The daemon reads node.yaml and opens eBGP plus BFD to each top-of-rack switch, one session per address family, authenticated with TCP-MD5. It comes up as a systemd unit ordered ahead of the container runtime, so the routing layer is live before anything tries to schedule a pod.
2 · Reachable first
It advertises the management prefix and installs a multipath default across both uplinks. The node's --node-ip is routable from the fabric before kubelet ever starts, so the node never registers from an address the rest of the cluster cannot reach.
3 · Readiness
A flag file, a local socket, and an rwctl --ready probe report the management IP as live and both sessions as established. The orchestrator gates on that signal and waits for the network instead of racing it, which keeps half-routed nodes out of the scheduler.
4 · Pods and services
Once kubelet is up, the agent hands pod CIDRs and Service VIPs to the daemon over the local socket, and the daemon originates them to the fabric. It stays the only process on the node that holds a BGP session, so every prefix the cluster announces goes through one auditable speaker.

Guarantees

Invariants the cluster can't override.

The management plane is fixed at boot from node.yaml. CRDs are additive: the cluster brings new peers and advertisements and drives the dataplane, but nothing it applies at runtime can rewrite the management address or pull a node off the fabric. These are the rules that hold underneath everything else the cluster does.

management plane

The cluster can't pull a node off the fabric

node.yaml fixes the management address, prefix, and bootstrap peers at boot. CRDs are additive only, new peers and advertisements, and SIGHUP reloads just those fields. A bad CRD cannot withdraw the management route.

failover

Failover tracks the session, not the route

A peer that drops sends no per-prefix withdrawal. The daemon watches session and BFD state, then prunes the dead next-hop from the FIB itself, near one second, without waiting on the kernel's link-down flag.

dual-stack

One BGP session per address family

IPv6 NLRI on a v4 session resolves to a next-hop the kernel rejects, so each AFI gets its own adjacency. A v6 link-local session can also carry v4 reachability with RFC 5549.

advertisement hygiene

One unambiguous path per prefix

When a pod CIDR already covers the node's management /32, the daemon advertises the host route alone. It never originates the aggregate beside a redundant component, so the fabric resolves a single path.

convergence

Sub-second on failure, clean on a drain

BFD and graceful restart conflict in the in-process model, so on uplinks BFD wins. A planned drain advertises GRACEFUL_SHUTDOWN per RFC 8326 and the ToR honors it through local-pref.

authentication

TCP-MD5 on every adjacency

Every BGP session carries TCP-MD5. The stack has no TCP-AO, so MD5 is the contract with the fabric. Keys live in node.yaml, never in a CRD and never in cluster state.

Where it stands

What's proven, and what's next.

The per-node BGP host daemon is complete and proven end-to-end against real BGP-speaking top-of-rack switches. The harness stands a node up dual-homed to two ToRs and asserts the behavior that matters: sessions and BFD establish, the management prefix lands in both ToR RIBs, the kernel default route goes multipath, a downed uplink is pruned in about a second, and a graceful stop drains with a clean withdrawal. It passes on IPv4, IPv6, and dual-stack.

The controller, agent, IPAM, and CNI plugin are in progress. Pod networking first, then services to replace kube-proxy, then NetworkPolicy. Each stage is proven against real hardware behavior before the next one begins.

Routewright is for people who would rather operate a routing domain than trust a black box. The code and the test harness are the honest record of where it stands.

See the version roadmap

Bring your routing table to your cluster.

No eBPF. No overlay. BGP, the kernel FIB, IPVS, and nftables: the network you already know how to operate.