Designing a modern IPv6-first data-center fabric: Clos leaf-spine topology, BGP routing, unnumbered links, addressing plans, and L3 vs EVPN/VXLAN overlays.
Modern data-center networks abandoned the classic three-tier (access/aggregation/core) design with large spanning-tree L2 domains in favor of a routed Clos fabric: a regular, horizontally scalable mesh where every server-facing switch is one or two hops from every other. Building it IPv6-first turns the fabric's biggest operational pain — per-link addressing — into a non-problem via unnumbered links.
The whole design reduces to four decisions: topology (folded Clos / leaf-spine, scaled with pods and a super-spine), routing (eBGP everywhere, ECMP for width — RFC 7938), addressing (unnumbered/RFC 5549 so links carry no IP to manage), and overlay-or-not (pure L3 to the host vs EVPN/VXLAN for tenant isolation and mobility).
A Clos network (Charles Clos, 1953) is a multistage non-blocking switching fabric. In the DC it appears as a folded Clos — the ingress and egress stages are collapsed into the same leaf switches, with spines as the middle stage. Every leaf connects to every spine; leaves never connect to leaves, spines never connect to spines.
3-stage folded Clos (leaf-spine)
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Spine1 │ │ Spine2 │ │ Spine3 │ │ Spine4 │ middle stage
└───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘
│ ╲ ╲ │ ╲ ╱ │ ╱ ╱ │ full mesh:
│ ╲ ╲ │ ╲ ╱ │ ╱ ╱ │ every leaf → every spine
│ ╲ ╲ │ ╱ ╲ │ ╱ ╱ │ (wide ECMP)
┌───┴────┐ ┌───┴────┐ ┌───┴────┐ ┌───┴────┐
│ Leaf1 │ │ Leaf2 │ │ Leaf3 │ │ Leaf4 │ ingress/egress
│ (ToR) │ │ (ToR) │ │ (ToR) │ │ (ToR) │ (folded together)
└──┬──┬──┘ └──┬──┬──┘ └──┬──┬──┘ └──┬──┬──┘
│ │ │ │ │ │ │ │
srv srv srv srv srv srv srv srv servers / hosts
Key properties an interviewer wants named:
1/N of the
inter-leaf bandwidth (graceful degradation), not a whole tier.A single leaf-spine tier (3-stage Clos) is bounded by spine radix (port count): the number of leaves ≤ ports on a spine. To scale past that, replicate the leaf-spine block into a pod and add a super-spine (a.k.a. spine-of-spines / fabric-plane) tier on top — a 5-stage Clos.
| Design | Stages | Hop count (worst case) | Scale limit |
|---|---|---|---|
| Leaf-spine (single pod) | 3-stage | leaf → spine → leaf (2 hops) | Spine radix (# leaves) |
| Pods + super-spine | 5-stage | leaf → spine → super-spine → spine → leaf (4 hops) | Super-spine radix (# pods) |
The defining choice of the modern fabric is pushing the L2/L3 boundary down to the leaf (ToR). Each leaf is a routed node; the fabric between leaves is pure L3.
| Big L2 domain (classic) | L3-to-the-leaf (Clos) |
|---|---|
| Spanning Tree blocks links → only one active path, wasted capacity | ECMP uses all links simultaneously |
| Broadcast/flood domain spans the whole domain → large failure blast radius | Broadcast domain ends at the leaf → tiny failure domains |
| MAC tables must hold every host in the domain | Fabric holds only prefixes/loopbacks; scales by summarization |
| Convergence via STP — slow, fragile, hard to reason about | Convergence via routing protocol — fast, deterministic, well understood |
| Topology changes ripple across the domain | Adding a rack is a local, templated operation |
If you need L2 adjacency between racks (VM mobility, legacy clustering that assumes a shared subnet), you reintroduce it as an overlay (EVPN/VXLAN) on top of the L3 underlay — never by widening the L2 domain in the physical fabric.
RFC 7938 ("Use of BGP for Routing in Large-Scale Data Centers") is the canonical reference. It argues for eBGP as the single routing protocol for the fabric, over an IGP, because:
The standard scheme: each leaf gets its own ASN; spines within a pod share an ASN (or each gets
its own); super-spines get another. Because a Clos is regular, this is templatable. Private ASNs (2-byte
64512–65534) run out fast in a big fabric, so use 4-byte private ASNs
(4200000000–4294967294).
| Tier | ASN scheme | Peers with |
|---|---|---|
| Leaf (ToR) | Unique ASN per leaf (or per rack) | All spines in its pod |
| Spine | One ASN per pod (all spines share), or unique per spine | All leaves in pod + all super-spines |
| Super-spine | Unique ASN(s) for the super-spine plane | All pod spines |
maximum-paths (BGP multipath) so all equal-cost paths across the spines are programmed into the
FIB. Some stacks require bgp bestpath as-path multipath-relax so paths with different
neighbor ASNs (but equal AS-path length) still count as ECMP — critical when each spine has a distinct ASN.allowas-in: needed when a device must accept a route whose AS-path already
contains its own ASN — e.g. shared-ASN leaves, or a route that must transit back. Use sparingly; it defeats
the natural loop protection, so bound it (allow the local AS at most once or twice).This is where IPv6 pays off operationally. In an IPv4 fabric every point-to-point fabric link needs a subnet, which you must plan, allocate, document, and configure on both ends — thousands of tiny subnets to manage. IPv6 removes that entirely.
| Approach | How it works | Cost |
|---|---|---|
| /127 per link (RFC 6164) | Assign a /127 to each p2p link (from a /64 reserved per link). RFC 6164 blesses /127 for p2p to avoid the ping-pong/neighbor-cache DoS issues of using a full /64. | Still per-link addressing to manage — better than IPv4 only in address abundance. |
| Fully unnumbered | Fabric links carry no global/ULA address at all. BGP sessions run over the interface's auto-configured link-local address (fe80::/10), discovered via Router Advertisements. | Zero per-link addressing. The automation win. |
BGP unnumbered is the headline feature. Instead of configuring a neighbor by IP, you configure the neighbor by interface:
# Config intent (FRR / Cumulus / SONiC style)
neighbor swp1 interface remote-as external
neighbor swp2 interface remote-as external
address-family ipv6 unicast
neighbor swp1 activate
neighbor swp2 activate
maximum-paths 64 # program all ECMP spines
exit-address-family
# What happens on the wire:
# 1. Each side enables ICMPv6 Router Advertisements on the link.
# 2. RA advertises the router's link-local (fe80::...) address.
# 3. Peer learns the neighbor's link-local from the RA (no static IP).
# 4. eBGP session forms over IPv6 link-local — no interface addressing.
remote-as external means "whoever answers,"
so the same template drops onto every leaf and spine. Cabling can change without config changes.The one-line pitch: an unnumbered IPv6 fabric has no link addresses to allocate, no per-neighbor BGP config, and no ARP — every switch runs the same template, and RFC 5549/8950 still lets it carry IPv4 to the hosts. That is the biggest single automation and operational simplification IPv6 brings to the DC.
With links unnumbered, the addresses you actually plan are loopbacks and host/tenant segments.
| Object | Typical allocation | Purpose |
|---|---|---|
| Router loopback | /128 from a per-fabric loopback block | Router-ID, BGP source, management, iBGP/EVPN endpoint. The only address a fabric switch truly needs. |
| VTEP address | /128 loopback (often the same or a second loopback) | VXLAN tunnel source/destination in an overlay; anycast VTEP shared by an MLAG pair. |
| Host segment | /64 per subnet / per rack | One /64 per L2 segment is the IPv6 norm (required for SLAAC). Never subnet a /64 for hosts. |
| Fabric p2p link | Unnumbered, or /127 (RFC 6164) | Prefer unnumbered; /127 only if a tool/vendor forces per-link IP. |
2000::/3) is globally
routable — use it for anything that talks to the internet or across sites. ULA
(fc00::/7, practically fd00::/8) is site-local/private — good for the
underlay loopbacks and infrastructure that must never be internet-reachable, giving you a
stable internal numbering independent of your provider-assigned GUA. Many designs: ULA underlay + GUA for
tenant/host prefixes./60 or /56 so it can carve its own /64s). Useful at the edge, for
container hosts, or handing tenants a routable block instead of NAT.The underlay is the physical L3 Clos that provides loopback-to-loopback reachability (BGP + ECMP). What rides on top depends on whether you need tenant isolation and L2 mobility.
/128s or a host /64.
In BGP-to-the-host, a routing daemon (FRR/BIRD) runs on the server and peers with the
leaf — services announce their own anycast /128, enabling per-service ECMP and instant
drain/migration.A Clos only delivers its bisection bandwidth if traffic is spread evenly across the spines. That is the job of ECMP hashing.
remote-as external mean the same config on every port,
so zero-touch provisioning (ZTP) is straightforward.AS-path prepend, set a max-metric,
lower LOCAL_PREF/MED, tag with a GRACEFUL_SHUTDOWN community
(RFC 8326), or administratively shut the BGP sessions. Traffic re-hashes onto the remaining
ECMP paths before you reboot — no drops. Undrain to restore.1/N of inter-leaf bandwidth. Prefix summarization at pod
boundaries bounds how far a churn event propagates. Design so no single failure removes a whole tier.maximum-paths + multipath-relax)remote-as external)