127.0.0.1:5757, so on the public web it shows FABRIC ? — that is expected (there is no public backend). Device configs, topology and diagrams below are all live. Want the real running fabric on your own machine? ↳ Build your own Docker lab (step-by-step) · repo ↗
spine1 (RR), leaf2 (VTEP rack-1), leaf5 (VTEP rack-3)
SR Linux v24.10.3 — Full EVPN-MH with ESI-LAG, IRB anycast gateway, symmetric IRB routing via ip-vrf
spine2 (RR), leaf1 (VTEP rack-1), leaf4 (VTEP rack-2)
cEOS 4.33.1F — EVPN multihoming with Port-Channel ESI, virtual-router anycast MAC, VXLAN L3 VNI
spine3 (RR), leaf3 (VTEP rack-2), leaf6 (VTEP rack-3)
FRR 8.4 — EVPN-MH via es-id/es-sys-mac, Linux bridge VXLAN. L3 VNI limited (no kernel VRF in container)
Pure eBGP underlay (RFC 7938). Each leaf has unique ASN (65001-65006), spines share AS 65100. Loopbacks redistributed via route-map. No OSPF/IS-IS IGP.
EVPN overlay runs as eBGP with multihop between leaf loopbacks and spine route-reflectors. Per-leaf AS numbers preserved — no AS swap tricks needed.
Every host dual-homed to a leaf pair via LACP bond0. ESI (Ethernet Segment Identifier) ensures active-active forwarding with sub-second failover via EVPN Type-1/Type-4.
Symmetric IRB with shared anycast MAC (00:00:5E:00:53:01). L3 VNI 50001 in TENANT-A VRF. All hosts see the same gateway MAC regardless of which leaf they hit.
All 9 network nodes send SNMP traps to 172.20.20.1:1162. Community: GESH-DC1-RO. SRL/cEOS use native SNMP; FRR uses external snmpd + AgentX.
30/30 host-to-host paths verified. VLAN 10 (10.10.10.0/24), VLAN 20 (10.10.20.0/24), VLAN 30 (10.10.30.0/24). Full mesh via symmetric IRB over VXLAN.
┌─────────────────────────────────────────────────────────┐
│ SPINE LAYER (AS 65100) │
│ EVPN Route Reflectors │
│ │
│ spine1 spine2 spine3 │
│ Nokia SRL Arista cEOS FRR │
│ 10.255.0.1 10.255.0.2 10.255.0.3 │
│ 172.20.20.11 172.20.20.12 172.20.20.13 │
└───────┬──────────────────┬─────────────────┬────────────┘
┌───────────┼──────────────────┼─────────────────┼───────────┐
┌──────────┼───────────┼──────┬───────────┼──────┬──────────┼───────────┼──────────┐
│ │ │ │ │ │ │ │ │
┌─────┴────┐ ┌───┴─────┐ ┌──┴──────┴──┐ ┌─────┴────┐ ┌┴─────────┴─┐ ┌──────┴────────┐ │
│ leaf1 │ │ leaf2 │ │ leaf3 │ │ leaf4 │ │ leaf5 │ │ leaf6 │ │
│ cEOS │ │ SRL │ │ FRR │ │ cEOS │ │ SRL │ │ FRR │ │
│ AS65001 │ │ AS65002 │ │ AS65003 │ │ AS65004 │ │ AS65005 │ │ AS65006 │ │
│ VNI10010 │ │VNI10010 │ │ VNI10020 │ │VNI10020 │ │ VNI10030 │ │ VNI10030 │ │
└──┬───┬───┘ └──┬───┬──┘ └──┬────┬──┘ └──┬───┬───┘ └──┬────┬───┘ └──┬─────┬───┘ │
│ │ │ │ │ │ │ │ │ │ │ │ │
│ └────────┤ ├───────┘ └───────┤ ├────────┘ └────────┤ │ │
│ ESI-LAG │ │ ESI-LAG ESI-LAG│ │ ESI-LAG ESI-LAG│ │ │
┌──┴──┐ ┌───┴───┴──┐ ┌───┴───┴──┐ ┌───┴──┐ ┌────┴─────┴──┐ │
│host1│ │ host2 │ │ host3 │ │host4│ │ host5 │ │
│bond0│ │ bond0 │ │ bond0 │ │bond0 │ │ bond0 │ │
│LACP │ │ LACP │ │ LACP │ │LACP │ │ LACP │ │
│.10.1│ │ .10.2 │ │ .20.1 │ │.20.2 │ │ .30.1 │ │
└─────┘ └──────────┘ └──────────┘ └──────┘ └─────────────┘ │
│
┌──────────────────────────────────────────────────────────────────────────────┘
│ host6 — bond0 active-backup, primary=eth2 (leaf5 SRL) for L3 VNI routing
│ 10.10.30.2/24
└──────────────────────────────────────────────────────────────────────────────
| Device | Role | Vendor | ASN | Loopback | VTEP | Mgmt |
|---|---|---|---|---|---|---|
| spine1 | Spine RR | SRL | 65100 | 10.255.0.1 | — | 172.20.20.11 |
| spine2 | Spine RR | cEOS | 65100 | 10.255.0.2 | — | 172.20.20.12 |
| spine3 | Spine RR | FRR | 65100 | 10.255.0.3 | — | 172.20.20.13 |
| leaf1 | Leaf VTEP | cEOS | 65001 | 10.255.1.1 | 10.255.1.1 | 172.20.20.21 |
| leaf2 | Leaf VTEP | SRL | 65002 | 10.255.1.2 | 10.255.100.2 | 172.20.20.22 |
| leaf3 | Leaf VTEP | FRR | 65003 | 10.255.1.3 | 10.255.100.3 | 172.20.20.23 |
| leaf4 | Leaf VTEP | cEOS | 65004 | 10.255.1.4 | 10.255.1.4 | 172.20.20.24 |
| leaf5 | Leaf VTEP | SRL | 65005 | 10.255.1.5 | 10.255.100.5 | 172.20.20.25 |
| leaf6 | Leaf VTEP | FRR | 65006 | 10.255.1.6 | 10.255.100.6 | 172.20.20.26 |
Loopbacks: fd00:dc1::X/128 — P2P links: fd00:dc1:SPINE:LEAF::/127 (::0 = spine side, ::1 = leaf side)
| Device | IPv6 Loopback | P2P to spine1 | P2P to spine2 | P2P to spine3 |
|---|---|---|---|---|
| spine1 | fd00:dc1::1/128 | — | — | — |
| spine2 | fd00:dc1::2/128 | — | — | — |
| spine3 | fd00:dc1::3/128 | — | — | — |
| leaf1 | fd00:dc1::11/128 | fd00:dc1:1:1::1 | fd00:dc1:2:1::1 | fd00:dc1:3:1::1 |
| leaf2 | fd00:dc1::12/128 | fd00:dc1:1:2::1 | fd00:dc1:2:2::1 | fd00:dc1:3:2::1 |
| leaf3 | fd00:dc1::13/128 | fd00:dc1:1:3::1 | fd00:dc1:2:3::1 | fd00:dc1:3:3::1 |
| leaf4 | fd00:dc1::14/128 | fd00:dc1:1:4::1 | fd00:dc1:2:4::1 | fd00:dc1:3:4::1 |
| leaf5 | fd00:dc1::15/128 | fd00:dc1:1:5::1 | fd00:dc1:2:5::1 | fd00:dc1:3:5::1 |
| leaf6 | fd00:dc1::16/128 | fd00:dc1:1:6::1 | fd00:dc1:2:6::1 | fd00:dc1:3:6::1 |
| VLAN | L2 VNI | Subnet | Anycast GW | MAC | Leaf Pair | Hosts |
|---|---|---|---|---|---|---|
| 10 | 10010 | 10.10.10.0/24 | 10.10.10.254 | 00:00:5E:00:53:01 | leaf1 (cEOS) + leaf2 (SRL) | host1, host2 |
| 20 | 10020 | 10.10.20.0/24 | 10.10.20.254 | 00:00:5E:00:53:01 | leaf3 (FRR) + leaf4 (cEOS) | host3, host4 |
| 30 | 10030 | 10.10.30.0/24 | 10.10.30.254 | 00:00:5E:00:53:01 | leaf5 (SRL) + leaf6 (FRR) | host5, host6 |
| L3 VNI | 50001 | TENANT-A VRF — symmetric IRB inter-VLAN routing | ||||
| Host | Bond Mode | Primary Leaf | Partner Leaf | ESI |
|---|---|---|---|---|
| host1 | 802.3ad LACP | leaf1 (cEOS) | leaf2 (SRL) | 00:11:11:11:11:11:11:11:11:11 |
| host2 | 802.3ad LACP | leaf2 (SRL) | leaf1 (cEOS) | 00:11:11:11:11:11:11:11:22:22 |
| host3 | 802.3ad LACP | leaf3 (FRR) | leaf4 (cEOS) | 00:22:22:22:22:22:22:22:11:11 |
| host4 | 802.3ad LACP | leaf4 (cEOS) | leaf3 (FRR) | 00:22:22:22:22:22:22:22:22:22 |
| host5 | 802.3ad LACP | leaf5 (SRL) | leaf6 (FRR) | 00:33:33:33:33:33:33:33:11:11 |
| host6 | active-backup | leaf5 (SRL) * | leaf6 (FRR) | 00:33:33:33:33:33:33:33:22:22 |
* host6 uses active-backup with leaf5 (SRL) as primary because leaf6 FRR lacks kernel VRF for L3 VNI decapsulation
flowchart TB
subgraph SPINES["SPINE LAYER · AS 65100"]
S1["spine1 · Nokia SRL
e1-1..e1-6"]
S2["spine2 · Arista cEOS
et1..et6"]
S3["spine3 · FRR
eth3..eth8"]
end
subgraph LEAFS["LEAF LAYER · VTEPs"]
L1["leaf1 cEOS
et1/et2/et3"]
L2["leaf2 SRL
e1-1/e1-2/e1-3"]
L3["leaf3 FRR
eth1/eth2/eth3"]
L4["leaf4 cEOS
et2/et3/et4"]
L5["leaf5 SRL
e1-1/e1-2/e1-3"]
L6["leaf6 FRR
eth1/eth2/eth3"]
end
S1 ---|e1-1 : et1| L1
S1 ---|e1-2 : e1-1| L2
S1 ---|e1-3 : eth1| L3
S1 ---|e1-4 : et2| L4
S1 ---|e1-5 : e1-1| L5
S1 ---|e1-6 : eth1| L6
S2 ---|et1 : et2| L1
S2 ---|et2 : e1-2| L2
S2 ---|et3 : eth2| L3
S2 ---|et4 : et3| L4
S2 ---|et5 : e1-2| L5
S2 ---|et6 : eth2| L6
S3 ---|eth3 : et3| L1
S3 ---|eth4 : e1-3| L2
S3 ---|eth5 : eth3| L3
S3 ---|eth6 : et4| L4
S3 ---|eth7 : e1-3| L5
S3 ---|eth8 : eth3| L6
L1 -.->|et4/et5 LACP| H12["host1+host2
bond0 (eth1+eth2)"]
L2 -.->|e1-4/e1-5 LACP| H12
L3 -.->|eth4/eth5 LACP| H34["host3+host4
bond0"]
L4 -.->|et5/et6 LACP| H34
L5 -.->|e1-4/e1-5 LACP| H56["host5+host6
bond0"]
L6 -.->|eth4/eth5| H56
flowchart LR S1["spine1 SRL · AS65100
lo 10.255.0.1 · fd00:dc1::1"] L1["leaf1 cEOS · AS65001
lo 10.255.1.1 · fd00:dc1::11"] L2["leaf2 SRL · AS65002
lo 10.255.1.2 · fd00:dc1::12"] L3["leaf3 FRR · AS65003
lo 10.255.1.3 · fd00:dc1::13"] S1 ---|"10.0.1.0/31 · fd00:dc1:1:1::/127"| L1 S1 ---|"10.0.1.2/31 · fd00:dc1:1:2::/127"| L2 S1 ---|"10.0.1.4/31 · fd00:dc1:1:3::/127"| L3 S2["spine2 cEOS · AS65100
lo 10.255.0.2 · fd00:dc1::2"] S2 ---|"10.0.2.0/31 · fd00:dc1:2:1::/127"| L1 S2 ---|"10.0.2.2/31 · fd00:dc1:2:2::/127"| L2 S2 ---|"10.0.2.4/31 · fd00:dc1:2:3::/127"| L3 S3["spine3 FRR · AS65100
lo 10.255.0.3 · fd00:dc1::3"] S3 ---|"10.0.3.0/31 · fd00:dc1:3:1::/127"| L1 S3 ---|"10.0.3.2/31 · fd00:dc1:3:2::/127"| L2 S3 ---|"10.0.3.4/31 · fd00:dc1:3:3::/127"| L3
10.0.{spine}.{(leaf-1)*2}/31 (spine side, even) ↔ +1 (leaf side, odd).
leaf4–6 follow the same pattern on 10.0.{1,2,3}.{6,8,10}.
flowchart TB S1(["spine1 RR
AS65100"]) S2(["spine2 RR
AS65100"]) S3(["spine3 RR
AS65100"]) L1["leaf1 AS65001"] L2["leaf2 AS65002"] L3["leaf3 AS65003"] L4["leaf4 AS65004"] L5["leaf5 AS65005"] L6["leaf6 AS65006"] S1 --- L1 & L2 & L3 & L4 & L5 & L6 S2 --- L1 & L2 & L3 & L4 & L5 & L6 S3 --- L1 & L2 & L3 & L4 & L5 & L6 S1 -.EVPN.- L1 & L2 & L3 & L4 & L5 & L6 S2 -.EVPN.- L1 & L2 & L3 & L4 & L5 & L6 S3 -.EVPN.- L1 & L2 & L3 & L4 & L5 & L6
flowchart TB
subgraph VNI10["VLAN 10 · VNI 10010 · 10.10.10.0/24"]
direction LR
H1["host1
10.10.10.1"] --- P1["leaf1+leaf2
ESI 00:11:..:11:11"]
H2["host2
10.10.10.2"] --- P1
end
subgraph VNI20["VLAN 20 · VNI 10020 · 10.10.20.0/24"]
direction LR
H3["host3
10.10.20.1"] --- P2["leaf3+leaf4
ESI 00:22:..:22:22"]
H4["host4
10.10.20.2"] --- P2
end
subgraph VNI30["VLAN 30 · VNI 10030 · 10.10.30.0/24"]
direction LR
H5["host5
10.10.30.1"] --- P3["leaf5+leaf6
ESI 00:33:..:33:33"]
H6["host6
10.10.30.2"] --- P3
end
P1 --- VRF["TENANT-A VRF
L3 VNI 50001
anycast GW 00:00:5E:00:53:01"]
P2 --- VRF
P3 --- VRF
flowchart TB
subgraph R1["RACK 1 · VLAN 10"]
R1GW["GW 10.10.10.254
leaf1(cEOS)+leaf2(SRL)"]
R1H["host1 .1 · host2 .2"]
R1H --- R1GW
end
subgraph R2["RACK 2 · VLAN 20"]
R2GW["GW 10.10.20.254
leaf3(FRR)+leaf4(cEOS)"]
R2H["host3 .1 · host4 .2"]
R2H --- R2GW
end
subgraph R3["RACK 3 · VLAN 30"]
R3GW["GW 10.10.30.254
leaf5(SRL)+leaf6(FRR)"]
R3H["host5 .1 · host6 .2"]
R3H --- R3GW
end
R1GW --- T["TENANT-A VRF
VNI 50001
inter-VLAN routing"]
R2GW --- T
R3GW --- T
flowchart TB
subgraph CORE["CORE · multi-region eBGP"]
C1["de-fra-core-01
AS65001 · .11"]
C2["de-fra-core-02
AS65002 · .12"]
C3["uk-lon-core-01
AS65003 · .13"]
C4["nl-ams-core-01
AS65004 · .14"]
C5["us-nyc-core-01
AS65005 · .15"]
end
C1 --- C2 & C3 & C4
C2 --- C4 & C5
C3 --- C4 & C5
C1 --- C5
C1 --- E1["de-fra-edge-01
AS65006 · .21"]
C1 --- D1["de-fra-dist-01
AS65009 · .33"]
C2 --- E1 & D1
C3 --- E2["uk-lon-edge-01
AS65007 · .22"]
C3 --- DI2["uk-lon-dist-01
AS65010 · .31"]
C4 --- E3["nl-ams-edge-01
AS65008 · .23"]
C5 --- E2
D1 --- E1
Source: docs/fabric-diagram.html
| host1 V10 | host2 V10 | host3 V20 | host4 V20 | host5 V30 | host6 V30 | |
|---|---|---|---|---|---|---|
| host1 | — | OK | OK | OK | OK | OK |
| host2 | OK | — | OK | OK | OK | OK |
| host3 | OK | OK | — | OK | OK | OK |
| host4 | OK | OK | OK | — | OK | OK |
| host5 | OK | OK | OK | OK | — | OK |
| host6 | OK | OK | OK | OK | OK | — |
All 9 loopbacks (fd00:dc1::1 through fd00:dc1::16) are reachable over the IPv6 underlay. Each spine-leaf link carries both an IPv4 /31 and an IPv6 /127. Dedicated IPv6 BGP neighbors exchange IPv6 unicast routes independently of the IPv4 sessions. Verify with: ping6 -c1 fd00:dc1::1 from any leaf.
| Layer | Protocol | Details |
|---|---|---|
| Underlay | eBGP IPv4 + IPv6 dual-stack | 18 IPv4 sessions + 18 IPv6 sessions (3 spines × 6 leafs). Per-leaf ASN. Loopback redistribution via route-map EXPORT_LO. IPv6 uses dedicated native neighbors on global P2P addresses (fd00:dc1::/48) |
| Overlay | eBGP EVPN | 18 sessions (leaf loopbacks → spine RRs via multihop). Type-1 AD, Type-2 MAC/IP, Type-3 IMET, Type-4 ES, Type-5 Prefix |
| Data Plane | VXLAN | L2 VNIs: 10010 (VLAN10), 10020 (VLAN20), 10030 (VLAN30). L3 VNI: 50001 (TENANT-A) |
| Multihoming | EVPN-MH / ESI-LAG | 6 Ethernet Segments, all-active forwarding. LACP (802.3ad) or active-backup bonds |
| L3 Gateway | Anycast IRB | Virtual MAC 00:00:5E:00:53:01 shared across leaf pairs. Symmetric IRB via L3 VNI 50001 |
| Monitoring | SNMPv2c | Community GESH-DC1-RO, traps to 172.20.20.1:1162 |
src/drivers/)Deterministic per-vendor drivers handle data collection (no LLM guesses CLI syntax). The LLM only does what it's good at — root-cause synthesis on structured/raw output. This guarantees a predictable data frame before it ever reaches the analysis layer.
Every driver method returns a DriverResult with .normalized (dict → Grafana/InfluxDB metrics) AND .raw (text → LLM context). Normalize for machines, raw for the model.
Command tables + parsers live once in drivers/. Both health.py (dashboard) and clab_collector.py (telemetry) consume them — no more drift between the two.
┌──────────────────────────────────────────────┐
│ get_driver(vendor, transport) │
│ factory.py + aliases │
└───────────────────┬──────────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ BaseNetworkDriver (ABC) — base.py │
│ get_bgp_summary() get_interface_counters() │
│ get_health() run_command() → DriverResult │
│ (.normalized dict + .raw text) │
└───────┬──────────────┬──────────────┬────────┘
┌───────────────┘ │ └───────────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ FRRDriver │ │ EOSDriver │ │ SRLDriver │
│ _parse() │ │ _parse() │ │ _parse() │
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
└────────────────────────────┼────────────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ Transport (protocol) — transport.py │
│ DockerExecTransport → vtysh / Cli / sr_cli │
│ SSHRunnerTransport → wraps run_command() │
│ ScrapliTransport → (planned, SSH NOS) │
└──────────────────────────────────────────────┘
parsers.py: parse_bgp / ospf / interfaces / interface_counters / routes / version
vendor-tagged · soft-fail (never raise) · fixed normalized shapes
| Method | Returns | Normalized shape |
|---|---|---|
get_bgp_summary() | DriverResult | {peers[], established, total} |
get_ospf_neighbors() | DriverResult | {neighbors[], full, total} |
get_interface_status() | DriverResult | {list[], up, total} |
get_interface_counters() | DriverResult | {interfaces[{in_octets, out_octets, in_packets, ...}]} |
get_routes() | DriverResult | {total, by_protocol{}} |
get_health() | dict | {meta, version, bgp, ospf, interfaces, routes} |
run_command(cmd) | DriverResult | {} (raw passthrough) |
| Vendor | Driver | Transport argv | Output format |
|---|---|---|---|
| SRL nokia-srl | SRLDriver | sr_cli -d "<cmd>" | flat tables (text parse) |
| cEOS arista-eos | EOSDriver | Cli -p 15 -c "<cmd> | json" | JSON |
| FRR frr | FRRDriver | vtysh -c "<cmd> json" | JSON |
| JUNOS junos | JunosDriver | cli -c "<cmd> | display json" | JSON (deeply nested [{data}]) |
| IOS-XR cisco-iosxr | IOSXRDriver | "<cmd> | json" → text fallback | Cisco text tables (JSON 7.x best-effort) |
| Decision | Choice | Rationale |
|---|---|---|
| Collection | Deterministic drivers | LLM guessing CLI syntax or touching devices is an operational liability. Drivers enforce predictable commands. |
| Analysis | LLM on raw + normalized | Model does pattern-matching / root-cause synthesis on a known data frame — what it's actually good at. |
| Abstraction | ABC class pattern (not Jinja2) | Jinja2 is for config patches. OO driver classes are cleaner for operational retrieval — drop in a new vendor class without touching core routing. |
| Transport | Injected (not imported) | Drivers never import flask/paramiko/scrapli at module top — collector runs on a lab host without them. Transport is a constructor arg. |
| New vendor | ~24-line driver class | Add JunosDriver/IOSXRDriver: set vendor + commands, implement _parse(). Factory + registry pick it up. |
| Failure model | Soft-fail, never raise | Parsers return fixed empty shapes on bad input. One wedged command never gates the whole snapshot. |
61/61 driver tests pass. Wired into health.py (dashboard command tables) and clab_collector.py (telemetry probes + interface counters → InfluxDB).
This portal is served by a launchd agent com.geshlab.portal
(RunAtLoad + KeepAlive) bound to http://127.0.0.1:8099.
Script: containerlab-multivendor/scripts/start_portal.sh · Log: /tmp/geshlab_portal.log
launchctl list | grep geshlab.portal
launchctl bootout gui/$(id -u)/com.geshlab.portal
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.geshlab.portal.plist
tail -f /tmp/geshlab_portal.log
LABPATH=~/02_Projects/.../containerlab-multivendor
docker run --rm --privileged --network host --pid host \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /run/netns:/run/netns -v "$LABPATH:$LABPATH" \
-w "$LABPATH/topologies" --entrypoint /usr/bin/containerlab \
ghcr.io/srl-labs/clab:latest deploy -t clos-evpn.clab.yml
... ghcr.io/srl-labs/clab:latest destroy -t clos-evpn.clab.yml --cleanup
docker ps --filter "name=clab-clos" --format "table {{.Names}}\t{{.Status}}"
docker exec clab-clos-evpn-spine1 bash -c 'sr_cli "show network-instance default protocols bgp neighbor"'
docker exec clab-clos-evpn-spine2 Cli -p 15 -c "show bgp evpn summary"
docker exec clab-clos-evpn-spine3 vtysh -c "show bgp summary"
docker exec clab-clos-evpn-leaf2 bash -c 'sr_cli "show network-instance mac-vrf-10 bridge-table mac-table all"'
docker exec clab-clos-evpn-leaf1 Cli -p 15 -c "show bgp evpn"
docker exec clab-clos-evpn-leaf1 Cli -p 15 -c "show ip route vrf TENANT-A"
docker exec clab-clos-evpn-host1 cat /proc/net/bonding/bond0
for s in 1 2 3 4 5 6; do for d in 1 2 3 4 5 6; do [ $s -ne $d ] && echo -n "h${s}→h${d}: " && docker exec clab-clos-evpn-host$s ping -c1 -W1 10.10.$((( (d-1)/2 *10+10 ))).$(( (d-1)%2+1 )) 2>&1|grep -oE "[0-9]+ received"; done; done
Interactive visual architecture of the multi-vendor DC fabric with protocol overlays and node details.
Open architecture.html →Live topology view with spine-leaf connections, SNMP agents, and management IPs.
Open fabric-diagram.html →Complete IP addressing plan including loopbacks, P2P links, management, and VXLAN VTEPs.
Open IP_PLAN.md →| Decision | Choice | Rationale |
|---|---|---|
| Underlay | eBGP (not OSPF/IS-IS) | RFC 7938 — simpler ops, per-leaf failure domains, no IGP LSA floods |
| Overlay | eBGP EVPN (not iBGP) | Each leaf keeps its own AS for EVPN. Avoids AS-swap hacks needed for iBGP on FRR 8.4 |
| IRB model | Symmetric IRB | Single L3 VNI (50001) for all inter-VLAN traffic. Scales to N VLANs without N² tunnels |
| Anycast MAC | 00:00:5E:00:53:01 | Single shared MAC across all leafs — hosts see consistent gateway regardless of active leaf |
| host6 bond | active-backup (not LACP) | FRR 8.4 container lacks kernel VRF module — L3 VNI decap fails. Force traffic to SRL leaf5 |
| SRL LAG type | static (not LACP) | multitool host containers don't run lldpad/lacpd — LACP negotiation times out |
| clab deploy | --pid host | Docker Desktop macOS lacks /run/netns. --pid host gives clab container access to LinuxKit VM PID namespace |
| Issue | Impact | Workaround |
|---|---|---|
| FRR container no kernel VRF | L3 VNI 50001 can't be decapsulated on FRR leafs | host6 bond primary set to SRL leaf5; FRR handles L2 only |
| SRL startup-config not auto-loading | SRL nodes boot blank — need manual sr_cli push | Push configs via sr_cli heredoc after deploy |
| cEOS interface numbering | clab maps et1/et2/et3 which may not match config Ethernet1/2/3 | Verify with show ip interface brief after deploy |
| Docker Desktop netns | clab deploy fails without --pid host | Always use --pid host flag with clab Docker image |