From 17448af2b8a8d13e8ab8d43f5cee5398262e9896 Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Sat, 6 Jun 2026 13:25:13 -0400 Subject: [PATCH 1/3] chore: move lab environments to deploy/containerlab/ Consolidates the gvpc ContainerLab environment from lab/gvpc/ into deploy/containerlab/ and removes the standalone network lab (lab/network/). Updates README to reflect the new location and adds restructured Kustomize resources with separate underlay, overlay, and cosmos subdirectories. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 7 +- {lab/gvpc => deploy/containerlab}/README.md | 86 ++++---- deploy/containerlab/Taskfile.yaml | 173 ++++++++++++++++ deploy/containerlab/containers/frr/Dockerfile | 12 ++ .../containerlab/containers/gobgp/Dockerfile | 13 ++ .../kindest-node-galactic/Dockerfile | 6 +- .../kindest-node-galactic/kubectl-wrapper | 0 .../kindest-node-galactic/scripts/install.sh | 8 + .../containerlab}/group_files/common/hosts | 0 .../group_files/common/startup-lib.sh | 0 .../group_files/common/vtysh.conf | 0 .../containerlab}/group_files/transit/daemons | 0 .../containerlab}/gvpc.clab.yaml | 1 + .../containerlab}/node_files/iad/config.yaml | 0 .../node_files/infra/config.yaml | 0 .../containerlab}/node_files/sjc/config.yaml | 0 .../containerlab}/node_files/tr1/frr.conf | 0 .../containerlab}/node_files/tr1/startup.sh | 0 .../containerlab}/node_files/tr2/frr.conf | 0 .../containerlab}/node_files/tr2/startup.sh | 0 .../containerlab}/node_files/tr3/frr.conf | 0 .../containerlab}/node_files/tr3/startup.sh | 0 .../containerlab}/node_files/tr4/frr.conf | 0 .../containerlab}/node_files/tr4/startup.sh | 0 .../resources/cosmos/iad/bgpinstance.yaml | 17 ++ .../resources/cosmos/iad/bgpprovider.yaml | 12 ++ .../resources/cosmos/iad/bgpsession.yaml | 23 +++ .../resources/cosmos/iad/kustomization.yaml | 4 + .../resources/cosmos/sjc/bgpinstance.yaml | 17 ++ .../resources/cosmos/sjc/bgpprovider.yaml | 12 ++ .../resources/cosmos/sjc/bgpsession.yaml | 23 +++ .../resources/cosmos/sjc/kustomization.yaml | 4 + .../resources/overlay/base/daemonset.yaml | 35 ++++ .../resources/overlay/base/kustomization.yaml | 2 + .../overlay/iad/daemonset/kustomization.yaml | 5 + .../overlay/iad/daemonset/namespace.yaml | 4 + .../resources/overlay/iad/kustomization.yaml | 3 + .../resources/overlay/iad/nad.yaml | 18 ++ .../overlay/sjc/daemonset/kustomization.yaml | 5 + .../overlay/sjc/daemonset/namespace.yaml | 4 + .../resources/overlay/sjc/kustomization.yaml | 3 + .../resources/overlay/sjc/nad.yaml | 18 ++ .../resources/underlay/base/daemonset.yaml | 99 ++------- .../underlay/base/kustomization.yaml | 2 + .../resources/underlay/iad/configmap.yaml | 63 ++++++ .../resources/underlay/iad/kustomization.yaml | 6 + .../resources/underlay/iad/namespace.yaml | 4 + .../resources/underlay/infra/configmap.yaml | 82 ++++++++ .../underlay/infra/kustomization.yaml | 6 + .../resources/underlay/infra/namespace.yaml | 4 + .../resources/underlay/sjc/configmap.yaml | 63 ++++++ .../resources/underlay/sjc/kustomization.yaml | 6 + .../resources/underlay/sjc/namespace.yaml | 4 + .../containerlab}/scripts/host-setup.sh | 21 +- .../containerlab/scripts/install-overlay.sh | 21 ++ .../containerlab/scripts/install-underlay.sh | 19 ++ lab/README.md | 66 ------ lab/gvpc/Taskfile.yaml | 115 ----------- lab/gvpc/resources/iad-overlay.k8s.yaml | 105 ---------- .../resources/infra-control-plane.k8s.yaml | 175 ---------------- lab/gvpc/resources/sjc-overlay.k8s.yaml | 105 ---------- lab/gvpc/resources/sjc-underlay.k8s.yaml | 156 -------------- lab/gvpc/scripts/install-overlay.sh | 17 -- lab/gvpc/scripts/install-underlay.sh | 18 -- lab/network/README.md | 194 ------------------ lab/network/Taskfile.yaml | 70 ------- lab/network/containers/gobgp-pe/Dockerfile | 22 -- lab/network/group_files/common/hosts | 11 - lab/network/group_files/common/startup-lib.sh | 57 ----- lab/network/group_files/common/vtysh.conf | 1 - lab/network/group_files/control/daemons | 22 -- lab/network/group_files/pe/daemons | 22 -- lab/network/group_files/transit/daemons | 22 -- lab/network/network.clab.yaml | 147 ------------- lab/network/network.drawio.xml | 64 ------ lab/network/node_files/pe1/frr.conf | 27 --- lab/network/node_files/pe1/gobgp.conf | 27 --- lab/network/node_files/pe1/startup.sh | 41 ---- lab/network/node_files/pe2/frr.conf | 27 --- lab/network/node_files/pe2/gobgp.conf | 27 --- lab/network/node_files/pe2/startup.sh | 41 ---- lab/network/node_files/rr1/frr.conf | 38 ---- lab/network/node_files/rr1/startup.sh | 17 -- lab/network/node_files/rr2/frr.conf | 38 ---- lab/network/node_files/rr2/startup.sh | 17 -- lab/network/node_files/tr1/frr.conf | 47 ----- lab/network/node_files/tr1/startup.sh | 21 -- lab/network/node_files/tr2/frr.conf | 47 ----- lab/network/node_files/tr2/startup.sh | 21 -- lab/network/node_files/tr3/frr.conf | 47 ----- lab/network/node_files/tr3/startup.sh | 21 -- lab/network/node_files/tr4/frr.conf | 47 ----- lab/network/node_files/tr4/startup.sh | 21 -- lab/network/scripts/host-setup.sh | 58 ------ 94 files changed, 767 insertions(+), 2167 deletions(-) rename {lab/gvpc => deploy/containerlab}/README.md (68%) create mode 100644 deploy/containerlab/Taskfile.yaml create mode 100644 deploy/containerlab/containers/frr/Dockerfile create mode 100644 deploy/containerlab/containers/gobgp/Dockerfile rename {lab/gvpc => deploy/containerlab}/containers/kindest-node-galactic/Dockerfile (75%) rename {lab/gvpc => deploy/containerlab}/containers/kindest-node-galactic/kubectl-wrapper (100%) rename {lab/gvpc => deploy/containerlab}/containers/kindest-node-galactic/scripts/install.sh (86%) rename {lab/gvpc => deploy/containerlab}/group_files/common/hosts (100%) rename {lab/gvpc => deploy/containerlab}/group_files/common/startup-lib.sh (100%) rename {lab/gvpc => deploy/containerlab}/group_files/common/vtysh.conf (100%) rename {lab/gvpc => deploy/containerlab}/group_files/transit/daemons (100%) rename {lab/gvpc => deploy/containerlab}/gvpc.clab.yaml (99%) rename {lab/gvpc => deploy/containerlab}/node_files/iad/config.yaml (100%) rename {lab/gvpc => deploy/containerlab}/node_files/infra/config.yaml (100%) rename {lab/gvpc => deploy/containerlab}/node_files/sjc/config.yaml (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr1/frr.conf (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr1/startup.sh (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr2/frr.conf (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr2/startup.sh (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr3/frr.conf (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr3/startup.sh (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr4/frr.conf (100%) rename {lab/gvpc => deploy/containerlab}/node_files/tr4/startup.sh (100%) create mode 100644 deploy/containerlab/resources/cosmos/iad/bgpinstance.yaml create mode 100644 deploy/containerlab/resources/cosmos/iad/bgpprovider.yaml create mode 100644 deploy/containerlab/resources/cosmos/iad/bgpsession.yaml create mode 100644 deploy/containerlab/resources/cosmos/iad/kustomization.yaml create mode 100644 deploy/containerlab/resources/cosmos/sjc/bgpinstance.yaml create mode 100644 deploy/containerlab/resources/cosmos/sjc/bgpprovider.yaml create mode 100644 deploy/containerlab/resources/cosmos/sjc/bgpsession.yaml create mode 100644 deploy/containerlab/resources/cosmos/sjc/kustomization.yaml create mode 100644 deploy/containerlab/resources/overlay/base/daemonset.yaml create mode 100644 deploy/containerlab/resources/overlay/base/kustomization.yaml create mode 100644 deploy/containerlab/resources/overlay/iad/daemonset/kustomization.yaml create mode 100644 deploy/containerlab/resources/overlay/iad/daemonset/namespace.yaml create mode 100644 deploy/containerlab/resources/overlay/iad/kustomization.yaml create mode 100644 deploy/containerlab/resources/overlay/iad/nad.yaml create mode 100644 deploy/containerlab/resources/overlay/sjc/daemonset/kustomization.yaml create mode 100644 deploy/containerlab/resources/overlay/sjc/daemonset/namespace.yaml create mode 100644 deploy/containerlab/resources/overlay/sjc/kustomization.yaml create mode 100644 deploy/containerlab/resources/overlay/sjc/nad.yaml rename lab/gvpc/resources/iad-underlay.k8s.yaml => deploy/containerlab/resources/underlay/base/daemonset.yaml (50%) create mode 100644 deploy/containerlab/resources/underlay/base/kustomization.yaml create mode 100644 deploy/containerlab/resources/underlay/iad/configmap.yaml create mode 100644 deploy/containerlab/resources/underlay/iad/kustomization.yaml create mode 100644 deploy/containerlab/resources/underlay/iad/namespace.yaml create mode 100644 deploy/containerlab/resources/underlay/infra/configmap.yaml create mode 100644 deploy/containerlab/resources/underlay/infra/kustomization.yaml create mode 100644 deploy/containerlab/resources/underlay/infra/namespace.yaml create mode 100644 deploy/containerlab/resources/underlay/sjc/configmap.yaml create mode 100644 deploy/containerlab/resources/underlay/sjc/kustomization.yaml create mode 100644 deploy/containerlab/resources/underlay/sjc/namespace.yaml rename {lab/gvpc => deploy/containerlab}/scripts/host-setup.sh (76%) create mode 100755 deploy/containerlab/scripts/install-overlay.sh create mode 100755 deploy/containerlab/scripts/install-underlay.sh delete mode 100644 lab/README.md delete mode 100644 lab/gvpc/Taskfile.yaml delete mode 100644 lab/gvpc/resources/iad-overlay.k8s.yaml delete mode 100644 lab/gvpc/resources/infra-control-plane.k8s.yaml delete mode 100644 lab/gvpc/resources/sjc-overlay.k8s.yaml delete mode 100644 lab/gvpc/resources/sjc-underlay.k8s.yaml delete mode 100755 lab/gvpc/scripts/install-overlay.sh delete mode 100755 lab/gvpc/scripts/install-underlay.sh delete mode 100644 lab/network/README.md delete mode 100644 lab/network/Taskfile.yaml delete mode 100644 lab/network/containers/gobgp-pe/Dockerfile delete mode 100644 lab/network/group_files/common/hosts delete mode 100755 lab/network/group_files/common/startup-lib.sh delete mode 100644 lab/network/group_files/common/vtysh.conf delete mode 100644 lab/network/group_files/control/daemons delete mode 100644 lab/network/group_files/pe/daemons delete mode 100644 lab/network/group_files/transit/daemons delete mode 100644 lab/network/network.clab.yaml delete mode 100644 lab/network/network.drawio.xml delete mode 100644 lab/network/node_files/pe1/frr.conf delete mode 100644 lab/network/node_files/pe1/gobgp.conf delete mode 100755 lab/network/node_files/pe1/startup.sh delete mode 100644 lab/network/node_files/pe2/frr.conf delete mode 100644 lab/network/node_files/pe2/gobgp.conf delete mode 100755 lab/network/node_files/pe2/startup.sh delete mode 100644 lab/network/node_files/rr1/frr.conf delete mode 100755 lab/network/node_files/rr1/startup.sh delete mode 100644 lab/network/node_files/rr2/frr.conf delete mode 100755 lab/network/node_files/rr2/startup.sh delete mode 100644 lab/network/node_files/tr1/frr.conf delete mode 100755 lab/network/node_files/tr1/startup.sh delete mode 100644 lab/network/node_files/tr2/frr.conf delete mode 100755 lab/network/node_files/tr2/startup.sh delete mode 100644 lab/network/node_files/tr3/frr.conf delete mode 100755 lab/network/node_files/tr3/startup.sh delete mode 100644 lab/network/node_files/tr4/frr.conf delete mode 100755 lab/network/node_files/tr4/startup.sh delete mode 100755 lab/network/scripts/host-setup.sh diff --git a/README.md b/README.md index 73e35b1..defe98e 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,9 @@ Under the hood, Galactic uses Segment Routing over IPv6 (SRv6) for efficient, de ## Getting Started -Two ContainerLab environments are available under [`lab/`](./lab/): +A ContainerLab environment is available under [`deploy/containerlab/`](./deploy/containerlab/): -- **[`lab/network/`](./lab/network/)** — Standalone SRv6 underlay lab (FRR + GoBGP, no Kubernetes). Good starting point for understanding the routing layer. -- **[`lab/gvpc/`](./lab/gvpc/)** — Three Kind clusters wired over an SRv6 transit mesh. The full GVPC multi-cluster environment. +- **[`deploy/containerlab/`](./deploy/containerlab/)** — Three Kind clusters wired over an SRv6 transit mesh. The full GVPC multi-cluster environment with FRR underlay and GoBGP L3VPN overlay. See the [galactic DevContainer](./.devcontainer/galactic/) for development environment setup. On ARM64 / OrbStack, use the [containerlab DevContainer](./.devcontainer/containerlab-dood/) to run ContainerLab via Docker-out-of-Docker. @@ -67,7 +66,7 @@ task ci:e2etest # full e2e lifecycle — spins up a Kind cluster, builds an `task ci:unittest` is the fast path for development; it runs the same command as the CI `test-unit` job. `task ci:e2etest` requires Docker and Kind and mirrors the CI `test-e2e` job exactly, including automatic cluster cleanup via a `trap` on exit. -Lab environments have their own `Taskfile.yaml`; run `task` from the relevant directory (`lab/network/` or `lab/gvpc/`) to see available tasks. +The lab environment has its own `Taskfile.yaml`; run `task` from `deploy/containerlab/` to see available tasks. ## License diff --git a/lab/gvpc/README.md b/deploy/containerlab/README.md similarity index 68% rename from lab/gvpc/README.md rename to deploy/containerlab/README.md index 93165c3..dc7c9ba 100644 --- a/lab/gvpc/README.md +++ b/deploy/containerlab/README.md @@ -1,4 +1,4 @@ -# GVPC Lab +# Galactic VPC Lab Deployment Three Kind clusters connected over an IPv6 SRv6 transit mesh. Each cluster runs FRR as a node routing daemon (hostNetwork DaemonSet) to peer with the transit layer via @@ -111,17 +111,17 @@ Worker SRv6 node SIDs (on `lo-galactic`): ## Lab layout ``` -gvpc/ +deploy/containerlab/ ├── gvpc.clab.yaml ├── Taskfile.yaml ├── containers/ -│ └── kindest-node-galactic/ # Custom Kind node image (Cilium, Multus, cert-manager) +│ ├── kindest-node-galactic/ # Custom Kind node image (Cilium, Multus, cert-manager, galactic) +│ ├── gobgp/ # GoBGP container built from upstream release binary +│ └── frr/ # FRR container built from Alpine edge ├── resources/ -│ ├── iad-underlay.k8s.yaml # FRR DaemonSet — iad cluster PE -│ ├── sjc-underlay.k8s.yaml # FRR DaemonSet — sjc cluster PE -│ ├── infra-control-plane.k8s.yaml # FRR DaemonSet — infra cluster route reflector -│ ├── iad-overlay.k8s.yaml # GoBGP DaemonSet — iad cluster L3VPN PE -│ └── sjc-overlay.k8s.yaml # GoBGP DaemonSet — sjc cluster L3VPN PE +│ ├── underlay/ # FRR DaemonSet kustomize overlays (iad, sjc, infra) +│ ├── overlay/ # GoBGP DaemonSet kustomize overlays (iad, sjc) +│ └── cosmos/ # Cosmos BGP CRs (BGPInstance, BGPSession, BGPProvider) ├── node_files/ │ ├── iad/ config.yaml │ ├── sjc/ config.yaml @@ -132,8 +132,6 @@ gvpc/ │ └── tr4/ frr.conf startup.sh ├── group_files/ │ ├── common/ hosts vtysh.conf startup-lib.sh -│ ├── control/ daemons -│ ├── pe/ daemons │ └── transit/ daemons └── scripts/ ├── host-setup.sh @@ -151,25 +149,39 @@ gvpc/ ## Quick start ```bash -task up # build Kind node image, apply host sysctls, deploy lab -task underlay # apply FRR DaemonSets to all three clusters -task overlay # apply GoBGP DaemonSets to iad and sjc clusters +cd deploy/containerlab +task deploy # build all images, apply host sysctls, deploy lab end-to-end +``` + +To tear down and start fresh: + +```bash +task destroy # remove all lab containers and Kind clusters +task clean # destroy + delete built images and lab artifacts +task deploy ``` ## Tasks -| Task | Description | -|------------------|-----------------------------------------------------------| -| `build` | Build the custom `kindest/node:galactic` image | -| `up` | Build, apply host sysctls, and deploy the lab | -| `down` | Destroy the lab and remove state | -| `reload` | Full rebuild — destroy then redeploy | -| `inspect` | Show running nodes and management addresses | -| `graph` | Generate a draw.io diagram for the topology | -| `host-setup` | Apply required host sysctls (IPv6 forwarding etc.) | -| `underlay` | Apply FRR DaemonSets to all three clusters | -| `overlay` | Pull GoBGP image, load into clusters, apply DaemonSets | -| `clean` | Destroy lab, remove state, and delete the Kind node image | +| Task | Description | +|--------------------|----------------------------------------------------------------| +| `build` | Build all container images (node, cosmos, gobgp, frr) | +| `build:node` | Build the custom `kindest/node:galactic` image | +| `build:cosmos` | Build the Cosmos BGP operator image from source | +| `build:gobgp` | Build the GoBGP container from upstream release binary | +| `build:frr` | Build the FRR container from Alpine edge | +| `deploy` | Build images, apply host sysctls, and deploy the lab | +| `deploy:topology` | Deploy the ContainerLab topology (transit routers + clusters) | +| `deploy:images` | Load images into Kind clusters and wait for cosmos rollout | +| `deploy:underlay` | Apply FRR DaemonSets to all three clusters | +| `deploy:overlay` | Apply GoBGP DaemonSets and Cosmos BGP CRs to iad and sjc | +| `destroy` | Destroy the lab and remove all Kind clusters | +| `reload` | Full rebuild — destroy then redeploy | +| `inspect` | Show running nodes and management addresses | +| `graph` | Generate a draw.io diagram for the topology | +| `host-setup` | Apply required host sysctls (IPv6 forwarding, inotify limits) | +| `clean` | Destroy lab, delete built images, and remove lab artifacts | +| `test` | Run all verification checks | ## Verification @@ -177,11 +189,11 @@ task overlay # apply GoBGP DaemonSets to iad and sjc clusters ```bash # iBGP full mesh — expect all sessions Established -docker exec tr1 vtysh -c "show bgp ipv6 unicast summary" +docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast summary" # Worker SRv6 prefixes should be present on all TR nodes -docker exec tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff01::/48" -docker exec tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff02::/48" +docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff01::/48" +docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff02::/48" ``` ### FRR DaemonSets (eBGP underlay) @@ -190,10 +202,10 @@ docker exec tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff02::/48" # Check pods are running docker exec iad-control-plane kubectl get pods -n iad-underlay docker exec sjc-control-plane kubectl get pods -n sjc-underlay -docker exec infra-control-plane kubectl get pods -n infra-control-plane +docker exec infra-control-plane kubectl get pods -n infra-underlay # Run vtysh inside a pod -docker exec iad-control-plane kubectl exec -n iad-underlay deploy/iad-underlay \ +docker exec iad-control-plane kubectl exec -n iad-underlay ds/iad-underlay \ -- vtysh -c "show bgp ipv6 unicast summary" ``` @@ -205,14 +217,12 @@ docker exec iad-control-plane kubectl get pods -n iad-overlay docker exec sjc-control-plane kubectl get pods -n sjc-overlay # Check iBGP session to infra-control-plane -docker exec iad-control-plane kubectl exec -n iad-overlay deploy/iad-overlay \ - -- gobgp neighbor -docker exec sjc-control-plane kubectl exec -n sjc-overlay deploy/sjc-overlay \ - -- gobgp neighbor - -# Inspect VRF RIB -docker exec iad-control-plane kubectl exec -n iad-overlay deploy/iad-overlay \ - -- gobgp vrf +docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp neighbor +docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp neighbor + +# Inspect VPN RIB +docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp global rib -a vpnv6 +docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp global rib -a vpnv6 ``` ## Notes diff --git a/deploy/containerlab/Taskfile.yaml b/deploy/containerlab/Taskfile.yaml new file mode 100644 index 0000000..f38865d --- /dev/null +++ b/deploy/containerlab/Taskfile.yaml @@ -0,0 +1,173 @@ +version: '3' + +vars: + LAB: + sh: awk '/^name:/ {print $2; exit}' *.clab.yaml + TOPO: + sh: echo *.clab.yaml + COSMOS_IMAGE: ghcr.io/milo-os/cosmos:latest + GOBGP_VERSION: "3.37.0" + GOBGP_IMAGE: gobgp:{{.GOBGP_VERSION}} + FRR_VERSION: "10.6.1" + FRR_IMAGE: frr:{{.FRR_VERSION}} + +tasks: + default: + silent: true + cmds: + - task --list + + build: + desc: Build all container images + cmds: + - task: "build:node" + - task: "build:cosmos" + - task: "build:gobgp" + - task: "build:frr" + + "build:node": + desc: Build the Kind node image with the galactic CNI plugin + cmds: + - docker build --network=host -t kindest/node:galactic -f containers/kindest-node-galactic/Dockerfile ../.. + + "build:cosmos": + desc: Build the cosmos BGP operator image from source + status: + - docker image inspect {{.COSMOS_IMAGE}} > /dev/null 2>&1 + cmds: + - | + if [ ! -d ".cosmos-src" ]; then + git clone --depth=1 https://github.com/milo-os/cosmos .cosmos-src + fi + docker build -t {{.COSMOS_IMAGE}} -f .cosmos-src/build/Dockerfile .cosmos-src + + "build:gobgp": + desc: Build the GoBGP container using the pre-built v{{.GOBGP_VERSION}} release binary + status: + - docker image inspect {{.GOBGP_IMAGE}} > /dev/null 2>&1 + cmds: + - docker build --build-arg GOBGP_VERSION={{.GOBGP_VERSION}} -t {{.GOBGP_IMAGE}} containers/gobgp/ + + "build:frr": + desc: Build the FRR container from Alpine edge (v{{.FRR_VERSION}}) + status: + - docker image inspect {{.FRR_IMAGE}} > /dev/null 2>&1 + cmds: + - docker build --build-arg FRR_VERSION={{.FRR_VERSION}} -t {{.FRR_IMAGE}} containers/frr/ + + deploy: + desc: Build images and deploy the full lab end-to-end + deps: [build, host-setup] + cmds: + - task: "deploy:topology" + - task: "deploy:images" + - task: "deploy:underlay" + - task: "deploy:overlay" + + "deploy:topology": + desc: Deploy the ContainerLab topology (transit routers + Kind clusters) + cmds: + - sudo containerlab deploy -t {{.TOPO}} + + "deploy:images": + desc: Load all container images into the Kind clusters and wait for cosmos + cmds: + - kind load docker-image {{.COSMOS_IMAGE}} --name iad + - kind load docker-image {{.COSMOS_IMAGE}} --name sjc + - kind load docker-image {{.GOBGP_IMAGE}} --name iad + - kind load docker-image {{.GOBGP_IMAGE}} --name sjc + - kind load docker-image {{.FRR_IMAGE}} --name iad + - kind load docker-image {{.FRR_IMAGE}} --name sjc + - kind load docker-image {{.FRR_IMAGE}} --name infra + - docker exec iad-control-plane kubectl -n bgp-system rollout status daemonset bgp --timeout=120s + - docker exec sjc-control-plane kubectl -n bgp-system rollout status daemonset bgp --timeout=120s + + destroy: + desc: Destroy the lab + cmds: + - sudo containerlab destroy -t {{.TOPO}} --cleanup + + reload: + desc: Full rebuild and redeploy + cmds: + - task: destroy + - task: deploy + + inspect: + desc: Inspect deployed nodes and management addresses + cmds: + - sudo containerlab inspect -t {{.TOPO}} + + graph: + desc: Generate a draw.io diagram for the current topology + cmds: + - sudo containerlab graph -t {{.TOPO}} --drawio --drawio-dir . + + host-setup: + desc: Apply host sysctls for this dual-stack lab + cmds: + - sudo ./scripts/host-setup.sh --no-persist + + "deploy:underlay": + desc: Install the FRR underlay DaemonSets + cmds: + - ./scripts/install-underlay.sh + + "deploy:overlay": + desc: Install the GoBGP overlay DaemonSets and cosmos BGP resources + cmds: + - ./scripts/install-overlay.sh + + test: + desc: Run all verification checks + cmds: + - task: "test:bgp-transit" + - task: "test:bgp-underlay" + - task: "test:srv6" + - task: "test:l3vpn" + + "test:bgp-transit": + desc: Verify transit router BGP sessions (iBGP full mesh) + cmds: + - | + for r in tr1 tr2 tr3 tr4; do + echo "--- $r ---" + docker exec clab-gvpc-$r vtysh -c "show bgp ipv6 unicast summary" + done + + "test:bgp-underlay": + desc: Verify underlay BGP sessions on iad and sjc workers + cmds: + - | + docker exec iad-control-plane \ + kubectl exec -n iad-underlay ds/iad-underlay \ + -- vtysh -c "show bgp ipv6 unicast summary" + - | + docker exec sjc-control-plane \ + kubectl exec -n sjc-underlay ds/sjc-underlay \ + -- vtysh -c "show bgp ipv6 unicast summary" + + "test:srv6": + desc: Verify SRv6 forwarding prefixes on tr1 + cmds: + - docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff01::/48" + - docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff02::/48" + - docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff03::/48" + + "test:l3vpn": + desc: Verify GoBGP L3VPN neighbors and RIB on iad and sjc + cmds: + - docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp neighbor + - docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp neighbor + - docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp global rib -a vpnv6 + - docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp global rib -a vpnv6 + + clean: + desc: Destroy the lab and delete artifacts + cmds: + - task: destroy + - docker rmi kindest/node:galactic || true + - docker rmi {{.GOBGP_IMAGE}} || true + - docker rmi {{.FRR_IMAGE}} || true + - docker rmi {{.COSMOS_IMAGE}} || true + - rm -rf clab-{{.LAB}} diff --git a/deploy/containerlab/containers/frr/Dockerfile b/deploy/containerlab/containers/frr/Dockerfile new file mode 100644 index 0000000..23130a3 --- /dev/null +++ b/deploy/containerlab/containers/frr/Dockerfile @@ -0,0 +1,12 @@ +ARG FRR_VERSION=10.6.1 +FROM alpine:edge +ARG FRR_VERSION + +# alpine:edge is a rolling release — base OS packages update on every rebuild. +# FRR is pinned via "frr=${FRR_VERSION}-r0". If Alpine bumps the package to -r1 +# for the same FRR version, the build will fail; drop or adjust the -r0 suffix. +RUN apk add --no-cache \ + "frr=${FRR_VERSION}-r0" \ + frr-pythontools \ + iproute2 \ + tcpdump diff --git a/deploy/containerlab/containers/gobgp/Dockerfile b/deploy/containerlab/containers/gobgp/Dockerfile new file mode 100644 index 0000000..29e2d94 --- /dev/null +++ b/deploy/containerlab/containers/gobgp/Dockerfile @@ -0,0 +1,13 @@ +ARG GOBGP_VERSION=4.5.0 +FROM alpine:3.20 AS fetch +ARG GOBGP_VERSION +RUN apk add --no-cache wget ca-certificates && \ + wget -O /tmp/gobgp.tar.gz \ + "https://github.com/osrg/gobgp/releases/download/v${GOBGP_VERSION}/gobgp_${GOBGP_VERSION}_linux_amd64.tar.gz" && \ + tar -xzf /tmp/gobgp.tar.gz -C /usr/local/bin gobgpd gobgp + +FROM alpine:3.20 +COPY --from=fetch /usr/local/bin/gobgpd /usr/local/bin/gobgpd +COPY --from=fetch /usr/local/bin/gobgp /usr/local/bin/gobgp +EXPOSE 50051 179 +ENTRYPOINT ["gobgpd"] diff --git a/lab/gvpc/containers/kindest-node-galactic/Dockerfile b/deploy/containerlab/containers/kindest-node-galactic/Dockerfile similarity index 75% rename from lab/gvpc/containers/kindest-node-galactic/Dockerfile rename to deploy/containerlab/containers/kindest-node-galactic/Dockerfile index 2f402b7..ef5be3f 100644 --- a/lab/gvpc/containers/kindest-node-galactic/Dockerfile +++ b/deploy/containerlab/containers/kindest-node-galactic/Dockerfile @@ -13,16 +13,18 @@ SHELL ["/bin/bash", "-eo", "pipefail", "-c"] RUN apt-get update \ && apt-get install -y --no-install-recommends \ + git \ tcpdump \ && rm -rf /var/lib/apt/lists/* WORKDIR /galactic COPY --from=builder /src/bin/galactic ./bin/galactic -COPY --chmod=0755 lab/gvpc/containers/kindest-node-galactic/scripts/ ./scripts/ +COPY deploy/containerlab/resources/ ./resources/ +COPY --chmod=0755 deploy/containerlab/containers/kindest-node-galactic/scripts/ ./scripts/ # Wrap kubectl to work around OrbStack DooD: kind runs `kubectl apply` for the # StorageClass step immediately after kubeadm init, but the apiserver briefly # goes offline while OrbStack's bridge finishes coming up, causing worker nodes # to never reach kubeadm join. The wrapper polls /healthz before passing through. -COPY --chmod=0755 lab/gvpc/containers/kindest-node-galactic/kubectl-wrapper /usr/local/bin/kubectl +COPY --chmod=0755 deploy/containerlab/containers/kindest-node-galactic/kubectl-wrapper /usr/local/bin/kubectl diff --git a/lab/gvpc/containers/kindest-node-galactic/kubectl-wrapper b/deploy/containerlab/containers/kindest-node-galactic/kubectl-wrapper similarity index 100% rename from lab/gvpc/containers/kindest-node-galactic/kubectl-wrapper rename to deploy/containerlab/containers/kindest-node-galactic/kubectl-wrapper diff --git a/lab/gvpc/containers/kindest-node-galactic/scripts/install.sh b/deploy/containerlab/containers/kindest-node-galactic/scripts/install.sh similarity index 86% rename from lab/gvpc/containers/kindest-node-galactic/scripts/install.sh rename to deploy/containerlab/containers/kindest-node-galactic/scripts/install.sh index d2c41f4..64f6a0d 100644 --- a/lab/gvpc/containers/kindest-node-galactic/scripts/install.sh +++ b/deploy/containerlab/containers/kindest-node-galactic/scripts/install.sh @@ -28,6 +28,14 @@ if hostname |grep -q control-plane; then # control-plane # Multus kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/refs/tags/${MULTUS_VERSION}/deployments/multus-daemonset-thick.yml kubectl -n kube-system rollout status daemonset kube-multus-ds + + # Cosmos BGP operator (image loaded into Kind by task up after deploy) + kubectl apply -k https://github.com/milo-os/cosmos//config/crd + kubectl apply -k https://github.com/milo-os/cosmos//config/deploy + + # Cosmos BGP resources (BGPProvider, BGPInstance, BGPSession) + cluster=$(hostname | sed 's/-control-plane//') + kubectl apply -k /galactic/resources/cosmos/${cluster}/ fi else # worker diff --git a/lab/gvpc/group_files/common/hosts b/deploy/containerlab/group_files/common/hosts similarity index 100% rename from lab/gvpc/group_files/common/hosts rename to deploy/containerlab/group_files/common/hosts diff --git a/lab/gvpc/group_files/common/startup-lib.sh b/deploy/containerlab/group_files/common/startup-lib.sh similarity index 100% rename from lab/gvpc/group_files/common/startup-lib.sh rename to deploy/containerlab/group_files/common/startup-lib.sh diff --git a/lab/gvpc/group_files/common/vtysh.conf b/deploy/containerlab/group_files/common/vtysh.conf similarity index 100% rename from lab/gvpc/group_files/common/vtysh.conf rename to deploy/containerlab/group_files/common/vtysh.conf diff --git a/lab/gvpc/group_files/transit/daemons b/deploy/containerlab/group_files/transit/daemons similarity index 100% rename from lab/gvpc/group_files/transit/daemons rename to deploy/containerlab/group_files/transit/daemons diff --git a/lab/gvpc/gvpc.clab.yaml b/deploy/containerlab/gvpc.clab.yaml similarity index 99% rename from lab/gvpc/gvpc.clab.yaml rename to deploy/containerlab/gvpc.clab.yaml index f4e0069..161d9d1 100644 --- a/lab/gvpc/gvpc.clab.yaml +++ b/deploy/containerlab/gvpc.clab.yaml @@ -1,3 +1,4 @@ +--- name: gvpc mgmt: diff --git a/lab/gvpc/node_files/iad/config.yaml b/deploy/containerlab/node_files/iad/config.yaml similarity index 100% rename from lab/gvpc/node_files/iad/config.yaml rename to deploy/containerlab/node_files/iad/config.yaml diff --git a/lab/gvpc/node_files/infra/config.yaml b/deploy/containerlab/node_files/infra/config.yaml similarity index 100% rename from lab/gvpc/node_files/infra/config.yaml rename to deploy/containerlab/node_files/infra/config.yaml diff --git a/lab/gvpc/node_files/sjc/config.yaml b/deploy/containerlab/node_files/sjc/config.yaml similarity index 100% rename from lab/gvpc/node_files/sjc/config.yaml rename to deploy/containerlab/node_files/sjc/config.yaml diff --git a/lab/gvpc/node_files/tr1/frr.conf b/deploy/containerlab/node_files/tr1/frr.conf similarity index 100% rename from lab/gvpc/node_files/tr1/frr.conf rename to deploy/containerlab/node_files/tr1/frr.conf diff --git a/lab/gvpc/node_files/tr1/startup.sh b/deploy/containerlab/node_files/tr1/startup.sh similarity index 100% rename from lab/gvpc/node_files/tr1/startup.sh rename to deploy/containerlab/node_files/tr1/startup.sh diff --git a/lab/gvpc/node_files/tr2/frr.conf b/deploy/containerlab/node_files/tr2/frr.conf similarity index 100% rename from lab/gvpc/node_files/tr2/frr.conf rename to deploy/containerlab/node_files/tr2/frr.conf diff --git a/lab/gvpc/node_files/tr2/startup.sh b/deploy/containerlab/node_files/tr2/startup.sh similarity index 100% rename from lab/gvpc/node_files/tr2/startup.sh rename to deploy/containerlab/node_files/tr2/startup.sh diff --git a/lab/gvpc/node_files/tr3/frr.conf b/deploy/containerlab/node_files/tr3/frr.conf similarity index 100% rename from lab/gvpc/node_files/tr3/frr.conf rename to deploy/containerlab/node_files/tr3/frr.conf diff --git a/lab/gvpc/node_files/tr3/startup.sh b/deploy/containerlab/node_files/tr3/startup.sh similarity index 100% rename from lab/gvpc/node_files/tr3/startup.sh rename to deploy/containerlab/node_files/tr3/startup.sh diff --git a/lab/gvpc/node_files/tr4/frr.conf b/deploy/containerlab/node_files/tr4/frr.conf similarity index 100% rename from lab/gvpc/node_files/tr4/frr.conf rename to deploy/containerlab/node_files/tr4/frr.conf diff --git a/lab/gvpc/node_files/tr4/startup.sh b/deploy/containerlab/node_files/tr4/startup.sh similarity index 100% rename from lab/gvpc/node_files/tr4/startup.sh rename to deploy/containerlab/node_files/tr4/startup.sh diff --git a/deploy/containerlab/resources/cosmos/iad/bgpinstance.yaml b/deploy/containerlab/resources/cosmos/iad/bgpinstance.yaml new file mode 100644 index 0000000..4967b83 --- /dev/null +++ b/deploy/containerlab/resources/cosmos/iad/bgpinstance.yaml @@ -0,0 +1,17 @@ +apiVersion: bgp.miloapis.com/v1alpha1 +kind: BGPInstance +metadata: + name: overlay +spec: + providerSelector: + matchLabels: + bgp.datum.net/plane: overlay + bgp.miloapis.com/daemon: gobgp + asNumber: 65000 + routerIDSource: Manual + routerID: "10.255.255.2" + addressFamilies: + - afi: IPv4 + safi: VPNUnicast + - afi: IPv6 + safi: VPNUnicast diff --git a/deploy/containerlab/resources/cosmos/iad/bgpprovider.yaml b/deploy/containerlab/resources/cosmos/iad/bgpprovider.yaml new file mode 100644 index 0000000..ca85d3e --- /dev/null +++ b/deploy/containerlab/resources/cosmos/iad/bgpprovider.yaml @@ -0,0 +1,12 @@ +apiVersion: providers.bgp.miloapis.com/v1alpha1 +kind: BGPProvider +metadata: + name: iad-worker-gobgp + labels: + bgp.datum.net/plane: overlay + bgp.miloapis.com/daemon: gobgp + bgp.miloapis.com/node: iad-worker +spec: + type: GoBGP + gobgp: + endpoint: localhost:50051 diff --git a/deploy/containerlab/resources/cosmos/iad/bgpsession.yaml b/deploy/containerlab/resources/cosmos/iad/bgpsession.yaml new file mode 100644 index 0000000..7549a35 --- /dev/null +++ b/deploy/containerlab/resources/cosmos/iad/bgpsession.yaml @@ -0,0 +1,23 @@ +apiVersion: bgp.miloapis.com/v1alpha1 +kind: BGPSession +metadata: + name: iad-overlay-to-infra-rr +spec: + fromProviderSelector: + matchLabels: + bgp.datum.net/plane: overlay + bgp.miloapis.com/daemon: gobgp + fromInstanceRef: overlay + toPeers: + - address: "fc00:0:4::1" + asNumber: 65000 + instanceRef: overlay + routeReflectorClient: false + addressFamilies: + - afi: IPv4 + safi: VPNUnicast + - afi: IPv6 + safi: VPNUnicast + timers: + holdTime: 90 + keepalive: 30 diff --git a/deploy/containerlab/resources/cosmos/iad/kustomization.yaml b/deploy/containerlab/resources/cosmos/iad/kustomization.yaml new file mode 100644 index 0000000..bbc5e9b --- /dev/null +++ b/deploy/containerlab/resources/cosmos/iad/kustomization.yaml @@ -0,0 +1,4 @@ +resources: + - bgpprovider.yaml + - bgpinstance.yaml + - bgpsession.yaml diff --git a/deploy/containerlab/resources/cosmos/sjc/bgpinstance.yaml b/deploy/containerlab/resources/cosmos/sjc/bgpinstance.yaml new file mode 100644 index 0000000..517bc4c --- /dev/null +++ b/deploy/containerlab/resources/cosmos/sjc/bgpinstance.yaml @@ -0,0 +1,17 @@ +apiVersion: bgp.miloapis.com/v1alpha1 +kind: BGPInstance +metadata: + name: overlay +spec: + providerSelector: + matchLabels: + bgp.datum.net/plane: overlay + bgp.miloapis.com/daemon: gobgp + asNumber: 65000 + routerIDSource: Manual + routerID: "10.255.255.3" + addressFamilies: + - afi: IPv4 + safi: VPNUnicast + - afi: IPv6 + safi: VPNUnicast diff --git a/deploy/containerlab/resources/cosmos/sjc/bgpprovider.yaml b/deploy/containerlab/resources/cosmos/sjc/bgpprovider.yaml new file mode 100644 index 0000000..e333ffb --- /dev/null +++ b/deploy/containerlab/resources/cosmos/sjc/bgpprovider.yaml @@ -0,0 +1,12 @@ +apiVersion: providers.bgp.miloapis.com/v1alpha1 +kind: BGPProvider +metadata: + name: sjc-worker-gobgp + labels: + bgp.datum.net/plane: overlay + bgp.miloapis.com/daemon: gobgp + bgp.miloapis.com/node: sjc-worker +spec: + type: GoBGP + gobgp: + endpoint: localhost:50051 diff --git a/deploy/containerlab/resources/cosmos/sjc/bgpsession.yaml b/deploy/containerlab/resources/cosmos/sjc/bgpsession.yaml new file mode 100644 index 0000000..a3d5abb --- /dev/null +++ b/deploy/containerlab/resources/cosmos/sjc/bgpsession.yaml @@ -0,0 +1,23 @@ +apiVersion: bgp.miloapis.com/v1alpha1 +kind: BGPSession +metadata: + name: sjc-overlay-to-infra-rr +spec: + fromProviderSelector: + matchLabels: + bgp.datum.net/plane: overlay + bgp.miloapis.com/daemon: gobgp + fromInstanceRef: overlay + toPeers: + - address: "fc00:0:4::1" + asNumber: 65000 + instanceRef: overlay + routeReflectorClient: false + addressFamilies: + - afi: IPv4 + safi: VPNUnicast + - afi: IPv6 + safi: VPNUnicast + timers: + holdTime: 90 + keepalive: 30 diff --git a/deploy/containerlab/resources/cosmos/sjc/kustomization.yaml b/deploy/containerlab/resources/cosmos/sjc/kustomization.yaml new file mode 100644 index 0000000..bbc5e9b --- /dev/null +++ b/deploy/containerlab/resources/cosmos/sjc/kustomization.yaml @@ -0,0 +1,4 @@ +resources: + - bgpprovider.yaml + - bgpinstance.yaml + - bgpsession.yaml diff --git a/deploy/containerlab/resources/overlay/base/daemonset.yaml b/deploy/containerlab/resources/overlay/base/daemonset.yaml new file mode 100644 index 0000000..5e89a41 --- /dev/null +++ b/deploy/containerlab/resources/overlay/base/daemonset.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: overlay + namespace: overlay +spec: + selector: + matchLabels: + app.kubernetes.io/name: overlay + template: + metadata: + labels: + app.kubernetes.io/name: overlay + spec: + hostNetwork: true + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-role.kubernetes.io/control-plane + operator: DoesNotExist + containers: + - name: gobgp + image: gobgp:3.37.0 + imagePullPolicy: Never + command: + - gobgpd + - --api-hosts + - :50051 + securityContext: + capabilities: + add: + - NET_ADMIN + - NET_RAW diff --git a/deploy/containerlab/resources/overlay/base/kustomization.yaml b/deploy/containerlab/resources/overlay/base/kustomization.yaml new file mode 100644 index 0000000..0987901 --- /dev/null +++ b/deploy/containerlab/resources/overlay/base/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - daemonset.yaml diff --git a/deploy/containerlab/resources/overlay/iad/daemonset/kustomization.yaml b/deploy/containerlab/resources/overlay/iad/daemonset/kustomization.yaml new file mode 100644 index 0000000..14abb84 --- /dev/null +++ b/deploy/containerlab/resources/overlay/iad/daemonset/kustomization.yaml @@ -0,0 +1,5 @@ +namePrefix: iad- +namespace: iad-overlay +resources: + - ../../base + - namespace.yaml diff --git a/deploy/containerlab/resources/overlay/iad/daemonset/namespace.yaml b/deploy/containerlab/resources/overlay/iad/daemonset/namespace.yaml new file mode 100644 index 0000000..eb27140 --- /dev/null +++ b/deploy/containerlab/resources/overlay/iad/daemonset/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: overlay diff --git a/deploy/containerlab/resources/overlay/iad/kustomization.yaml b/deploy/containerlab/resources/overlay/iad/kustomization.yaml new file mode 100644 index 0000000..c519067 --- /dev/null +++ b/deploy/containerlab/resources/overlay/iad/kustomization.yaml @@ -0,0 +1,3 @@ +resources: + - daemonset + - nad.yaml diff --git a/deploy/containerlab/resources/overlay/iad/nad.yaml b/deploy/containerlab/resources/overlay/iad/nad.yaml new file mode 100644 index 0000000..25aa8c8 --- /dev/null +++ b/deploy/containerlab/resources/overlay/iad/nad.yaml @@ -0,0 +1,18 @@ +apiVersion: k8s.cni.cncf.io/v1 +kind: NetworkAttachmentDefinition +metadata: + name: galactic + namespace: default +spec: + config: | + { + "cniVersion": "0.3.1", + "name": "galactic", + "type": "galactic", + "vpc": "1", + "vpcattachment": "1", + "srv6_locator": "2001:db8:ff01::/48", + "gobgp": { + "address": "127.0.0.1:50051" + } + } diff --git a/deploy/containerlab/resources/overlay/sjc/daemonset/kustomization.yaml b/deploy/containerlab/resources/overlay/sjc/daemonset/kustomization.yaml new file mode 100644 index 0000000..638c481 --- /dev/null +++ b/deploy/containerlab/resources/overlay/sjc/daemonset/kustomization.yaml @@ -0,0 +1,5 @@ +namePrefix: sjc- +namespace: sjc-overlay +resources: + - ../../base + - namespace.yaml diff --git a/deploy/containerlab/resources/overlay/sjc/daemonset/namespace.yaml b/deploy/containerlab/resources/overlay/sjc/daemonset/namespace.yaml new file mode 100644 index 0000000..eb27140 --- /dev/null +++ b/deploy/containerlab/resources/overlay/sjc/daemonset/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: overlay diff --git a/deploy/containerlab/resources/overlay/sjc/kustomization.yaml b/deploy/containerlab/resources/overlay/sjc/kustomization.yaml new file mode 100644 index 0000000..c519067 --- /dev/null +++ b/deploy/containerlab/resources/overlay/sjc/kustomization.yaml @@ -0,0 +1,3 @@ +resources: + - daemonset + - nad.yaml diff --git a/deploy/containerlab/resources/overlay/sjc/nad.yaml b/deploy/containerlab/resources/overlay/sjc/nad.yaml new file mode 100644 index 0000000..7067299 --- /dev/null +++ b/deploy/containerlab/resources/overlay/sjc/nad.yaml @@ -0,0 +1,18 @@ +apiVersion: k8s.cni.cncf.io/v1 +kind: NetworkAttachmentDefinition +metadata: + name: galactic + namespace: default +spec: + config: | + { + "cniVersion": "0.3.1", + "name": "galactic", + "type": "galactic", + "vpc": "1", + "vpcattachment": "1", + "srv6_locator": "2001:db8:ff02::/48", + "gobgp": { + "address": "127.0.0.1:50051" + } + } diff --git a/lab/gvpc/resources/iad-underlay.k8s.yaml b/deploy/containerlab/resources/underlay/base/daemonset.yaml similarity index 50% rename from lab/gvpc/resources/iad-underlay.k8s.yaml rename to deploy/containerlab/resources/underlay/base/daemonset.yaml index 4e19b19..6cf589a 100644 --- a/lab/gvpc/resources/iad-underlay.k8s.yaml +++ b/deploy/containerlab/resources/underlay/base/daemonset.yaml @@ -1,94 +1,18 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: iad-underlay - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: iad-underlay-config - namespace: iad-underlay -data: - daemons: | - zebra=yes - bgpd=yes - ospfd=no - ospf6d=no - ripd=no - ripngd=no - isisd=no - pimd=no - ldpd=no - nhrpd=no - eigrpd=no - babeld=no - sharpd=no - pbrd=no - bfdd=no - fabricd=no - vrrpd=no - pathd=no - - vtysh_enable=yes - zebra_options=" -A 127.0.0.1 -s 90000000" - bgpd_options=" -A 127.0.0.1" - frr.conf: | - frr version 9.1 - frr defaults traditional - hostname iad-underlay - log syslog informational - - interface lo - ! /48 assigns the full SRv6 SID block to this node; eBGP advertises the /48 as the SID range. - ipv6 address fc00:0:2::1/48 - ! - interface eth1 - description tr1-facing - ! - - ipv6 route 2001:db8:ff01::/48 Null0 - ! - router bgp 65000 - bgp router-id 10.255.255.2 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - bgp log-neighbor-changes - neighbor eth1 interface remote-as 65100 - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth1 allowas-in 1 - network 2001:db8:ff01::/48 - network fc00:0:2::/48 - exit-address-family - ! - - ipv6 forwarding - vtysh.conf: | - service integrated-vtysh-config - ---- apiVersion: apps/v1 kind: DaemonSet metadata: - name: iad-underlay - namespace: iad-underlay - labels: - app.kubernetes.io/name: iad-underlay - app.kubernetes.io/instance: iad-underlay + name: underlay + namespace: underlay spec: selector: matchLabels: - app.kubernetes.io/name: iad-underlay - app.kubernetes.io/instance: iad-underlay + app.kubernetes.io/name: underlay template: metadata: labels: - app.kubernetes.io/name: iad-underlay - app.kubernetes.io/instance: iad-underlay + app.kubernetes.io/name: underlay spec: + hostNetwork: true affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -96,12 +20,12 @@ spec: - matchExpressions: - key: node-role.kubernetes.io/control-plane operator: DoesNotExist - hostNetwork: true initContainers: - name: frr-init - image: frrouting/frr:latest + image: frr:10.6.1 + imagePullPolicy: Never command: - - /bin/sh + - sh - -c - | cp /tmp/frr-config/* /etc/frr/ @@ -123,9 +47,10 @@ spec: mountPath: /var/log/frr containers: - name: frr - image: frrouting/frr:latest + image: frr:10.6.1 + imagePullPolicy: Never command: - - /bin/sh + - sh - -c - | /usr/lib/frr/frrinit.sh start @@ -146,7 +71,7 @@ spec: volumes: - name: frr-config-source configMap: - name: iad-underlay-config + name: underlay-config - name: frr-etc emptyDir: {} - name: frr-run diff --git a/deploy/containerlab/resources/underlay/base/kustomization.yaml b/deploy/containerlab/resources/underlay/base/kustomization.yaml new file mode 100644 index 0000000..0987901 --- /dev/null +++ b/deploy/containerlab/resources/underlay/base/kustomization.yaml @@ -0,0 +1,2 @@ +resources: + - daemonset.yaml diff --git a/deploy/containerlab/resources/underlay/iad/configmap.yaml b/deploy/containerlab/resources/underlay/iad/configmap.yaml new file mode 100644 index 0000000..d6f9d2e --- /dev/null +++ b/deploy/containerlab/resources/underlay/iad/configmap.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: underlay-config + namespace: underlay +data: + daemons: | + zebra=yes + bgpd=yes + ospfd=no + ospf6d=no + ripd=no + ripngd=no + isisd=no + pimd=no + ldpd=no + nhrpd=no + eigrpd=no + babeld=no + sharpd=no + pbrd=no + bfdd=no + fabricd=no + vrrpd=no + pathd=no + + vtysh_enable=yes + zebra_options=" -A 127.0.0.1 -s 90000000" + bgpd_options=" -A 127.0.0.1" + frr.conf: | + frr version 9.1 + frr defaults traditional + hostname iad-underlay + log syslog informational + + interface lo + ! /48 assigns the full SRv6 SID block to this node; eBGP advertises the /48 as the SID range. + ipv6 address fc00:0:2::1/48 + ! + interface eth1 + description tr1-facing + ! + + ipv6 route 2001:db8:ff01::/48 Null0 + ! + router bgp 65000 + bgp router-id 10.255.255.2 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + bgp log-neighbor-changes + neighbor eth1 interface remote-as 65100 + ! + address-family ipv6 unicast + neighbor eth1 activate + neighbor eth1 allowas-in 1 + network 2001:db8:ff01::/48 + network fc00:0:2::/48 + exit-address-family + ! + + ipv6 forwarding + vtysh.conf: | + service integrated-vtysh-config diff --git a/deploy/containerlab/resources/underlay/iad/kustomization.yaml b/deploy/containerlab/resources/underlay/iad/kustomization.yaml new file mode 100644 index 0000000..f9228ed --- /dev/null +++ b/deploy/containerlab/resources/underlay/iad/kustomization.yaml @@ -0,0 +1,6 @@ +namePrefix: iad- +namespace: iad-underlay +resources: + - ../base + - namespace.yaml + - configmap.yaml diff --git a/deploy/containerlab/resources/underlay/iad/namespace.yaml b/deploy/containerlab/resources/underlay/iad/namespace.yaml new file mode 100644 index 0000000..70d1c1e --- /dev/null +++ b/deploy/containerlab/resources/underlay/iad/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: underlay diff --git a/deploy/containerlab/resources/underlay/infra/configmap.yaml b/deploy/containerlab/resources/underlay/infra/configmap.yaml new file mode 100644 index 0000000..8d70bb2 --- /dev/null +++ b/deploy/containerlab/resources/underlay/infra/configmap.yaml @@ -0,0 +1,82 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: underlay-config + namespace: underlay +data: + daemons: | + zebra=yes + bgpd=yes + ospfd=no + ospf6d=no + ripd=no + ripngd=no + isisd=no + pimd=no + ldpd=no + nhrpd=no + eigrpd=no + babeld=no + sharpd=no + pbrd=no + bfdd=no + fabricd=no + vrrpd=no + pathd=no + + vtysh_enable=yes + zebra_options=" -A 127.0.0.1 -s 90000000" + bgpd_options=" -A 127.0.0.1" + frr.conf: | + frr version 9.1 + frr defaults traditional + hostname infra-underlay + log syslog informational + + interface lo + ipv6 address fc00:0:4::1/128 + ! + interface eth1 + description tr3-facing + ! + + ipv6 route 2001:db8:ff03::/48 Null0 + ! + router bgp 65000 + bgp router-id 10.255.255.1 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + bgp log-neighbor-changes + neighbor eth1 interface remote-as 65100 + neighbor fc00:0:2::1 remote-as 65000 + neighbor fc00:0:2::1 update-source lo + neighbor fc00:0:2::1 passive + neighbor fc00:0:3::1 remote-as 65000 + neighbor fc00:0:3::1 update-source lo + neighbor fc00:0:3::1 passive + ! + address-family ipv6 unicast + neighbor eth1 activate + neighbor eth1 allowas-in 1 + network 2001:db8:ff03::/48 + network fc00:0:4::1/128 + exit-address-family + ! + address-family ipv4 vpn + neighbor fc00:0:2::1 activate + neighbor fc00:0:2::1 route-reflector-client + neighbor fc00:0:3::1 activate + neighbor fc00:0:3::1 route-reflector-client + exit-address-family + ! + address-family ipv6 vpn + neighbor fc00:0:2::1 activate + neighbor fc00:0:2::1 route-reflector-client + neighbor fc00:0:3::1 activate + neighbor fc00:0:3::1 route-reflector-client + exit-address-family + ! + + ipv6 forwarding + vtysh.conf: | + service integrated-vtysh-config diff --git a/deploy/containerlab/resources/underlay/infra/kustomization.yaml b/deploy/containerlab/resources/underlay/infra/kustomization.yaml new file mode 100644 index 0000000..83590c0 --- /dev/null +++ b/deploy/containerlab/resources/underlay/infra/kustomization.yaml @@ -0,0 +1,6 @@ +namePrefix: infra- +namespace: infra-underlay +resources: + - ../base + - namespace.yaml + - configmap.yaml diff --git a/deploy/containerlab/resources/underlay/infra/namespace.yaml b/deploy/containerlab/resources/underlay/infra/namespace.yaml new file mode 100644 index 0000000..70d1c1e --- /dev/null +++ b/deploy/containerlab/resources/underlay/infra/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: underlay diff --git a/deploy/containerlab/resources/underlay/sjc/configmap.yaml b/deploy/containerlab/resources/underlay/sjc/configmap.yaml new file mode 100644 index 0000000..bb701b9 --- /dev/null +++ b/deploy/containerlab/resources/underlay/sjc/configmap.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: underlay-config + namespace: underlay +data: + daemons: | + zebra=yes + bgpd=yes + ospfd=no + ospf6d=no + ripd=no + ripngd=no + isisd=no + pimd=no + ldpd=no + nhrpd=no + eigrpd=no + babeld=no + sharpd=no + pbrd=no + bfdd=no + fabricd=no + vrrpd=no + pathd=no + + vtysh_enable=yes + zebra_options=" -A 127.0.0.1 -s 90000000" + bgpd_options=" -A 127.0.0.1" + frr.conf: | + frr version 9.1 + frr defaults traditional + hostname sjc-underlay + log syslog informational + + interface lo + ! /48 assigns the full SRv6 SID block to this node; eBGP advertises the /48 as the SID range. + ipv6 address fc00:0:3::1/48 + ! + interface eth1 + description tr2-facing + ! + + ipv6 route 2001:db8:ff02::/48 Null0 + ! + router bgp 65000 + bgp router-id 10.255.255.3 + no bgp default ipv4-unicast + no bgp ebgp-requires-policy + bgp log-neighbor-changes + neighbor eth1 interface remote-as 65100 + ! + address-family ipv6 unicast + neighbor eth1 activate + neighbor eth1 allowas-in 1 + network 2001:db8:ff02::/48 + network fc00:0:3::/48 + exit-address-family + ! + + ipv6 forwarding + vtysh.conf: | + service integrated-vtysh-config diff --git a/deploy/containerlab/resources/underlay/sjc/kustomization.yaml b/deploy/containerlab/resources/underlay/sjc/kustomization.yaml new file mode 100644 index 0000000..f5a8c1e --- /dev/null +++ b/deploy/containerlab/resources/underlay/sjc/kustomization.yaml @@ -0,0 +1,6 @@ +namePrefix: sjc- +namespace: sjc-underlay +resources: + - ../base + - namespace.yaml + - configmap.yaml diff --git a/deploy/containerlab/resources/underlay/sjc/namespace.yaml b/deploy/containerlab/resources/underlay/sjc/namespace.yaml new file mode 100644 index 0000000..70d1c1e --- /dev/null +++ b/deploy/containerlab/resources/underlay/sjc/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: underlay diff --git a/lab/gvpc/scripts/host-setup.sh b/deploy/containerlab/scripts/host-setup.sh similarity index 76% rename from lab/gvpc/scripts/host-setup.sh rename to deploy/containerlab/scripts/host-setup.sh index bf85bec..a641ed5 100755 --- a/lab/gvpc/scripts/host-setup.sh +++ b/deploy/containerlab/scripts/host-setup.sh @@ -3,21 +3,18 @@ set -euo pipefail -DUALSTACK=false PERSIST=true SYSCTL_CONF=/etc/sysctl.d/99-clab.conf usage() { - echo "Usage: $0 [--dualstack] [--no-persist]" + echo "Usage: $0 [--no-persist]" echo "" - echo " --dualstack Enable IPv4 forwarding in addition to IPv6" echo " --no-persist Apply sysctls now but do not write to ${SYSCTL_CONF}" exit 1 } for arg in "$@"; do case $arg in - --dualstack) DUALSTACK=true ;; --no-persist) PERSIST=false ;; --help|-h) usage ;; *) echo "Unknown argument: $arg"; usage ;; @@ -30,7 +27,6 @@ if [ "$(id -u)" -ne 0 ]; then fi echo "==> Containerlab host setup" -echo " dualstack : ${DUALSTACK}" echo " persist : ${PERSIST}" echo "" @@ -42,13 +38,8 @@ echo "--> Enabling IPv6 forwarding" sysctl -w net.ipv6.conf.all.forwarding=1 sysctl -w net.ipv6.conf.default.forwarding=1 -if [ "$DUALSTACK" = true ]; then - echo "--> Enabling IPv4 forwarding (dual-stack)" - sysctl -w net.ipv4.ip_forward=1 -else - echo "--> Disabling IPv4 forwarding (IPv6-only lab)" - sysctl -w net.ipv4.ip_forward=0 -fi +echo "--> Enabling IPv4 forwarding (required for Docker NAT)" +sysctl -w net.ipv4.ip_forward=1 if [ "$PERSIST" = true ]; then echo "--> Writing ${SYSCTL_CONF}" @@ -57,11 +48,7 @@ if [ "$PERSIST" = true ]; then echo "# Generated by host-setup.sh" echo "net.ipv6.conf.all.forwarding=1" echo "net.ipv6.conf.default.forwarding=1" - if [ "$DUALSTACK" = true ]; then - echo "net.ipv4.ip_forward=1" - else - echo "net.ipv4.ip_forward=0" - fi + echo "net.ipv4.ip_forward=1" echo "fs.inotify.max_user_instances=1024" echo "fs.inotify.max_user_watches=524288" } >"${SYSCTL_CONF}" diff --git a/deploy/containerlab/scripts/install-overlay.sh b/deploy/containerlab/scripts/install-overlay.sh new file mode 100755 index 0000000..1abcb82 --- /dev/null +++ b/deploy/containerlab/scripts/install-overlay.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +RESOURCES_DIR="${SCRIPT_DIR}/../resources" + +apply() { + local node="$1" + local site="$2" + echo "Applying overlay/${site} to ${node}..." + docker cp "${RESOURCES_DIR}/overlay" "${node}:/galactic/resources/" + docker exec "${node}" kubectl apply -k /galactic/resources/overlay/${site}/ + echo "Applying cosmos/${site} to ${node}..." + docker cp "${RESOURCES_DIR}/cosmos" "${node}:/galactic/resources/" + docker exec "${node}" kubectl apply -k /galactic/resources/cosmos/${site}/ +} + +apply iad-control-plane iad +apply sjc-control-plane sjc + +echo "Done." diff --git a/deploy/containerlab/scripts/install-underlay.sh b/deploy/containerlab/scripts/install-underlay.sh new file mode 100755 index 0000000..bcd0592 --- /dev/null +++ b/deploy/containerlab/scripts/install-underlay.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +RESOURCES_DIR="${SCRIPT_DIR}/../resources" + +apply() { + local node="$1" + local site="$2" + echo "Applying underlay/${site} to ${node}..." + docker cp "${RESOURCES_DIR}/underlay" "${node}:/galactic/resources/" + docker exec "${node}" kubectl apply -k /galactic/resources/underlay/${site}/ +} + +apply iad-control-plane iad +apply sjc-control-plane sjc +apply infra-control-plane infra + +echo "Done." diff --git a/lab/README.md b/lab/README.md deleted file mode 100644 index 203baaa..0000000 --- a/lab/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Galactic Lab - -Local development and integration-testing environments for [Galactic VPC](https://www.datum.net/docs/galactic-vpc/). - -``` -lab/ -├── network/ # ContainerLab SRv6 underlay — standalone BGP/SRv6 network with FRR + GoBGP -└── gvpc/ # ContainerLab multi-cluster lab — three Kind clusters over an SRv6 transit mesh -``` - ---- - -## `network/` — SRv6 Underlay Lab - -A ContainerLab topology that validates the BGP/SRv6 underlay that Galactic depends on. -Eight nodes across PE, transit, and route-reflector roles run FRR + GoBGP with SRv6 -uSID L3VPN. Use this to develop and test routing behaviour independently of Kubernetes. - -See [`network/README.md`](network/README.md) for topology details, addressing, and -verification commands. - -### Prerequisites - -- ContainerLab ≥ 0.54 -- Docker -- Linux kernel ≥ 5.18 (SRv6 `encap.red` support) - -### Quick start - -```bash -cd network -task build # build the gobgp-pe container image -task up # apply host sysctls and deploy the lab -task inspect # show node addresses -task down # tear down -``` - ---- - -## `gvpc/` — Multi-Cluster GVPC Lab - -A ContainerLab topology that connects three Kind clusters (`iad`, `sjc`, `infra`) over -an IPv6 SRv6 transit mesh. FRR runs as a node routing daemon on each cluster worker for -the eBGP underlay; GoBGP runs on `iad` and `sjc` workers to exchange L3VPN type-5 routes -with the `infra` route reflector over iBGP. Cilium, cert-manager, and Multus are -pre-installed on each cluster. - -See [`gvpc/README.md`](gvpc/README.md) for topology details, addressing, and -verification commands. - -### Prerequisites - -- ContainerLab ≥ 0.54 -- Docker -- `kind` CLI -- Host kernel with SRv6 support - -### Quick start - -```bash -cd gvpc -task up # build Kind node image, apply host sysctls, deploy lab -task underlay # apply FRR DaemonSets to all three clusters -task overlay # pull GoBGP image, load into clusters, apply DaemonSets -task down # tear down -``` diff --git a/lab/gvpc/Taskfile.yaml b/lab/gvpc/Taskfile.yaml deleted file mode 100644 index a471ca6..0000000 --- a/lab/gvpc/Taskfile.yaml +++ /dev/null @@ -1,115 +0,0 @@ -version: '3' - -vars: - LAB: - sh: awk '/^name:/ {print $2; exit}' *.clab.yaml - TOPO: - sh: echo *.clab.yaml - -tasks: - default: - silent: true - cmds: - - task --list - - build: - desc: Build container images (Kind node) - cmds: - - docker build --network=host -t kindest/node:galactic -f containers/kindest-node-galactic/Dockerfile ../.. - - up: - desc: Build the Kind node image and deploy the lab - deps: [build, host-setup] - cmds: - - sudo containerlab deploy -t {{.TOPO}} - - down: - desc: Destroy the lab and clean generated state - cmds: - - sudo containerlab destroy -t {{.TOPO}} --cleanup - - reload: - desc: Full rebuild and redeploy - cmds: - - task: down - - task: up - - inspect: - desc: Inspect deployed nodes and management addresses - cmds: - - sudo containerlab inspect -t {{.TOPO}} - - graph: - desc: Generate a draw.io diagram for the current topology - cmds: - - sudo containerlab graph -t {{.TOPO}} --drawio --drawio-dir . - - host-setup: - desc: Apply host sysctls for this dual-stack lab - cmds: - - sudo ./scripts/host-setup.sh --no-persist - - underlay: - desc: Apply FRR DaemonSets to all clusters - cmds: - - ./scripts/install-underlay.sh - - overlay: - desc: Pull GoBGP image, load into clusters, and apply DaemonSets to iad and sjc - cmds: - - docker pull osrg/gobgp:latest - - kind load docker-image osrg/gobgp:latest --name iad - - kind load docker-image osrg/gobgp:latest --name sjc - - ./scripts/install-overlay.sh - - test: - desc: Run all verification checks - cmds: - - task: test-bgp-transit - - task: test-bgp-underlay - - task: test-srv6 - - task: test-l3vpn - - test-bgp-transit: - desc: Verify transit router BGP sessions (iBGP full mesh) - cmds: - - | - for r in tr1 tr2 tr3 tr4; do - echo "--- $r ---" - docker exec clab-gvpc-$r vtysh -c "show bgp ipv6 unicast summary" - done - - test-bgp-underlay: - desc: Verify underlay BGP sessions on iad and sjc workers - cmds: - - | - docker exec iad-control-plane \ - kubectl exec -n iad-underlay ds/iad-underlay \ - -- vtysh -c "show bgp ipv6 unicast summary" - - | - docker exec sjc-control-plane \ - kubectl exec -n sjc-underlay ds/sjc-underlay \ - -- vtysh -c "show bgp ipv6 unicast summary" - - test-srv6: - desc: Verify SRv6 forwarding prefixes on tr1 - cmds: - - docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff01::/48" - - docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff02::/48" - - docker exec clab-gvpc-tr1 vtysh -c "show bgp ipv6 unicast 2001:db8:ff03::/48" - - test-l3vpn: - desc: Verify GoBGP L3VPN neighbors and RIB on iad and sjc - cmds: - - docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp neighbor - - docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp neighbor - - docker exec iad-control-plane kubectl exec -n iad-overlay ds/iad-overlay -- gobgp global rib -a vpnv6 - - docker exec sjc-control-plane kubectl exec -n sjc-overlay ds/sjc-overlay -- gobgp global rib -a vpnv6 - - clean: - desc: Destroy the lab, remove ContainerLab state, and delete container images - cmds: - - task: down - - docker rmi kindest/node:galactic || true - - docker rmi osrg/gobgp:latest || true - - rm -rf clab-{{.LAB}} diff --git a/lab/gvpc/resources/iad-overlay.k8s.yaml b/lab/gvpc/resources/iad-overlay.k8s.yaml deleted file mode 100644 index 57ea289..0000000 --- a/lab/gvpc/resources/iad-overlay.k8s.yaml +++ /dev/null @@ -1,105 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: iad-overlay - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: iad-overlay-config - namespace: iad-overlay -data: - gobgp.conf: | - [global.config] - as = 65000 - router-id = "10.255.255.2" - port = -1 - local-address-list = ["fc00:0:2::1"] - - [[neighbors]] - [neighbors.config] - neighbor-address = "fc00:0:4::1" - peer-as = 65000 - description = "infra-control-plane" - [neighbors.transport.config] - local-address = "fc00:0:2::1" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv6-unicast" - ---- -apiVersion: k8s.cni.cncf.io/v1 -kind: NetworkAttachmentDefinition -metadata: - name: galactic - namespace: default -spec: - config: | - { - "cniVersion": "0.3.1", - "name": "galactic", - "type": "galactic", - "vpc": "1", - "vpcattachment": "1", - "srv6_locator": "2001:db8:ff01::/48", - "gobgp": { - "address": "127.0.0.1:50051" - } - } - ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: iad-overlay - namespace: iad-overlay - labels: - app.kubernetes.io/name: iad-overlay - app.kubernetes.io/instance: iad-overlay -spec: - selector: - matchLabels: - app.kubernetes.io/name: iad-overlay - app.kubernetes.io/instance: iad-overlay - template: - metadata: - labels: - app.kubernetes.io/name: iad-overlay - app.kubernetes.io/instance: iad-overlay - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: node-role.kubernetes.io/control-plane - operator: DoesNotExist - hostNetwork: true - containers: - - name: gobgp - image: osrg/gobgp:latest - imagePullPolicy: Never - command: - - gobgpd - - -f - - /etc/gobgp/gobgp.conf - - --api-hosts - - :50051 - securityContext: - capabilities: - add: - - NET_ADMIN - - NET_RAW - volumeMounts: - - name: gobgp-config - mountPath: /etc/gobgp - readOnly: true - volumes: - - name: gobgp-config - configMap: - name: iad-overlay-config diff --git a/lab/gvpc/resources/infra-control-plane.k8s.yaml b/lab/gvpc/resources/infra-control-plane.k8s.yaml deleted file mode 100644 index 5c44eae..0000000 --- a/lab/gvpc/resources/infra-control-plane.k8s.yaml +++ /dev/null @@ -1,175 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: infra-control-plane - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: infra-control-plane-config - namespace: infra-control-plane -data: - daemons: | - zebra=yes - bgpd=yes - ospfd=no - ospf6d=no - ripd=no - ripngd=no - isisd=no - pimd=no - ldpd=no - nhrpd=no - eigrpd=no - babeld=no - sharpd=no - pbrd=no - bfdd=no - fabricd=no - vrrpd=no - pathd=no - - vtysh_enable=yes - zebra_options=" -A 127.0.0.1 -s 90000000" - bgpd_options=" -A 127.0.0.1" - frr.conf: | - frr version 9.1 - frr defaults traditional - hostname infra-control-plane - log syslog informational - - interface lo - ipv6 address fc00:0:4::1/128 - ! - interface eth1 - description tr3-facing - ! - - ipv6 route 2001:db8:ff03::/48 Null0 - ! - router bgp 65000 - bgp router-id 10.255.255.1 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - bgp log-neighbor-changes - neighbor eth1 interface remote-as 65100 - neighbor fc00:0:2::1 remote-as 65000 - neighbor fc00:0:2::1 update-source lo - neighbor fc00:0:2::1 passive - neighbor fc00:0:3::1 remote-as 65000 - neighbor fc00:0:3::1 update-source lo - neighbor fc00:0:3::1 passive - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth1 allowas-in 1 - network 2001:db8:ff03::/48 - network fc00:0:4::1/128 - exit-address-family - ! - address-family ipv4 vpn - neighbor fc00:0:2::1 activate - neighbor fc00:0:2::1 route-reflector-client - neighbor fc00:0:3::1 activate - neighbor fc00:0:3::1 route-reflector-client - exit-address-family - ! - address-family ipv6 vpn - neighbor fc00:0:2::1 activate - neighbor fc00:0:2::1 route-reflector-client - neighbor fc00:0:3::1 activate - neighbor fc00:0:3::1 route-reflector-client - exit-address-family - ! - - ipv6 forwarding - vtysh.conf: | - service integrated-vtysh-config - ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: infra-control-plane - namespace: infra-control-plane - labels: - app.kubernetes.io/name: infra-control-plane - app.kubernetes.io/instance: infra-control-plane -spec: - selector: - matchLabels: - app.kubernetes.io/name: infra-control-plane - app.kubernetes.io/instance: infra-control-plane - template: - metadata: - labels: - app.kubernetes.io/name: infra-control-plane - app.kubernetes.io/instance: infra-control-plane - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: node-role.kubernetes.io/control-plane - operator: DoesNotExist - hostNetwork: true - initContainers: - - name: frr-init - image: frrouting/frr:latest - command: - - /bin/sh - - -c - - | - cp /tmp/frr-config/* /etc/frr/ - install -d -o frr -g frr -m 775 /run/frr - install -d -o frr -g frr -m 775 /var/log/frr - securityContext: - capabilities: - add: - - NET_ADMIN - volumeMounts: - - name: frr-config-source - mountPath: /tmp/frr-config - readOnly: true - - name: frr-etc - mountPath: /etc/frr - - name: frr-run - mountPath: /run/frr - - name: frr-log - mountPath: /var/log/frr - containers: - - name: frr - image: frrouting/frr:latest - command: - - /bin/sh - - -c - - | - /usr/lib/frr/frrinit.sh start - exec tail -f /dev/null - securityContext: - capabilities: - add: - - NET_ADMIN - - NET_RAW - - SYS_ADMIN - volumeMounts: - - name: frr-etc - mountPath: /etc/frr - - name: frr-run - mountPath: /run/frr - - name: frr-log - mountPath: /var/log/frr - volumes: - - name: frr-config-source - configMap: - name: infra-control-plane-config - - name: frr-etc - emptyDir: {} - - name: frr-run - emptyDir: - medium: Memory - - name: frr-log - emptyDir: {} diff --git a/lab/gvpc/resources/sjc-overlay.k8s.yaml b/lab/gvpc/resources/sjc-overlay.k8s.yaml deleted file mode 100644 index 080d6b5..0000000 --- a/lab/gvpc/resources/sjc-overlay.k8s.yaml +++ /dev/null @@ -1,105 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: sjc-overlay - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: sjc-overlay-config - namespace: sjc-overlay -data: - gobgp.conf: | - [global.config] - as = 65000 - router-id = "10.255.255.3" - port = -1 - local-address-list = ["fc00:0:3::1"] - - [[neighbors]] - [neighbors.config] - neighbor-address = "fc00:0:4::1" - peer-as = 65000 - description = "infra-control-plane" - [neighbors.transport.config] - local-address = "fc00:0:3::1" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv6-unicast" - ---- -apiVersion: k8s.cni.cncf.io/v1 -kind: NetworkAttachmentDefinition -metadata: - name: galactic - namespace: default -spec: - config: | - { - "cniVersion": "0.3.1", - "name": "galactic", - "type": "galactic", - "vpc": "1", - "vpcattachment": "1", - "srv6_locator": "2001:db8:ff02::/48", - "gobgp": { - "address": "127.0.0.1:50051" - } - } - ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: sjc-overlay - namespace: sjc-overlay - labels: - app.kubernetes.io/name: sjc-overlay - app.kubernetes.io/instance: sjc-overlay -spec: - selector: - matchLabels: - app.kubernetes.io/name: sjc-overlay - app.kubernetes.io/instance: sjc-overlay - template: - metadata: - labels: - app.kubernetes.io/name: sjc-overlay - app.kubernetes.io/instance: sjc-overlay - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: node-role.kubernetes.io/control-plane - operator: DoesNotExist - hostNetwork: true - containers: - - name: gobgp - image: osrg/gobgp:latest - imagePullPolicy: Never - command: - - gobgpd - - -f - - /etc/gobgp/gobgp.conf - - --api-hosts - - :50051 - securityContext: - capabilities: - add: - - NET_ADMIN - - NET_RAW - volumeMounts: - - name: gobgp-config - mountPath: /etc/gobgp - readOnly: true - volumes: - - name: gobgp-config - configMap: - name: sjc-overlay-config diff --git a/lab/gvpc/resources/sjc-underlay.k8s.yaml b/lab/gvpc/resources/sjc-underlay.k8s.yaml deleted file mode 100644 index 9e36087..0000000 --- a/lab/gvpc/resources/sjc-underlay.k8s.yaml +++ /dev/null @@ -1,156 +0,0 @@ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: sjc-underlay - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: sjc-underlay-config - namespace: sjc-underlay -data: - daemons: | - zebra=yes - bgpd=yes - ospfd=no - ospf6d=no - ripd=no - ripngd=no - isisd=no - pimd=no - ldpd=no - nhrpd=no - eigrpd=no - babeld=no - sharpd=no - pbrd=no - bfdd=no - fabricd=no - vrrpd=no - pathd=no - - vtysh_enable=yes - zebra_options=" -A 127.0.0.1 -s 90000000" - bgpd_options=" -A 127.0.0.1" - frr.conf: | - frr version 9.1 - frr defaults traditional - hostname sjc-underlay - log syslog informational - - interface lo - ! /48 assigns the full SRv6 SID block to this node; eBGP advertises the /48 as the SID range. - ipv6 address fc00:0:3::1/48 - ! - interface eth1 - description tr2-facing - ! - - ipv6 route 2001:db8:ff02::/48 Null0 - ! - router bgp 65000 - bgp router-id 10.255.255.3 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - bgp log-neighbor-changes - neighbor eth1 interface remote-as 65100 - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth1 allowas-in 1 - network 2001:db8:ff02::/48 - network fc00:0:3::/48 - exit-address-family - ! - - ipv6 forwarding - vtysh.conf: | - service integrated-vtysh-config - ---- -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: sjc-underlay - namespace: sjc-underlay - labels: - app.kubernetes.io/name: sjc-underlay - app.kubernetes.io/instance: sjc-underlay -spec: - selector: - matchLabels: - app.kubernetes.io/name: sjc-underlay - app.kubernetes.io/instance: sjc-underlay - template: - metadata: - labels: - app.kubernetes.io/name: sjc-underlay - app.kubernetes.io/instance: sjc-underlay - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: node-role.kubernetes.io/control-plane - operator: DoesNotExist - hostNetwork: true - initContainers: - - name: frr-init - image: frrouting/frr:latest - command: - - /bin/sh - - -c - - | - cp /tmp/frr-config/* /etc/frr/ - install -d -o frr -g frr -m 775 /run/frr - install -d -o frr -g frr -m 775 /var/log/frr - securityContext: - capabilities: - add: - - NET_ADMIN - volumeMounts: - - name: frr-config-source - mountPath: /tmp/frr-config - readOnly: true - - name: frr-etc - mountPath: /etc/frr - - name: frr-run - mountPath: /run/frr - - name: frr-log - mountPath: /var/log/frr - containers: - - name: frr - image: frrouting/frr:latest - command: - - /bin/sh - - -c - - | - /usr/lib/frr/frrinit.sh start - exec tail -f /dev/null - securityContext: - capabilities: - add: - - NET_ADMIN - - NET_RAW - - SYS_ADMIN - volumeMounts: - - name: frr-etc - mountPath: /etc/frr - - name: frr-run - mountPath: /run/frr - - name: frr-log - mountPath: /var/log/frr - volumes: - - name: frr-config-source - configMap: - name: sjc-underlay-config - - name: frr-etc - emptyDir: {} - - name: frr-run - emptyDir: - medium: Memory - - name: frr-log - emptyDir: {} diff --git a/lab/gvpc/scripts/install-overlay.sh b/lab/gvpc/scripts/install-overlay.sh deleted file mode 100755 index 005706a..0000000 --- a/lab/gvpc/scripts/install-overlay.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -euo pipefail - -SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) -RESOURCES_DIR="${SCRIPT_DIR}/../resources" - -apply() { - local node="$1" - local file="$2" - echo "Applying ${file##*/} to ${node}..." - docker exec -i "${node}" kubectl apply -f - < "${file}" -} - -apply iad-control-plane "${RESOURCES_DIR}/iad-overlay.k8s.yaml" -apply sjc-control-plane "${RESOURCES_DIR}/sjc-overlay.k8s.yaml" - -echo "Done." diff --git a/lab/gvpc/scripts/install-underlay.sh b/lab/gvpc/scripts/install-underlay.sh deleted file mode 100755 index 0c4de8e..0000000 --- a/lab/gvpc/scripts/install-underlay.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -set -euo pipefail - -SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) -RESOURCES_DIR="${SCRIPT_DIR}/../resources" - -apply() { - local node="$1" - local file="$2" - echo "Applying ${file##*/} to ${node}..." - docker exec -i "${node}" kubectl apply -f - < "${file}" -} - -apply iad-control-plane "${RESOURCES_DIR}/iad-underlay.k8s.yaml" -apply sjc-control-plane "${RESOURCES_DIR}/sjc-underlay.k8s.yaml" -apply infra-control-plane "${RESOURCES_DIR}/infra-control-plane.k8s.yaml" - -echo "Done." diff --git a/lab/network/README.md b/lab/network/README.md deleted file mode 100644 index a2b090c..0000000 --- a/lab/network/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# BGP Route Reflector Lab — SRv6 uSID - -## Purpose - -Validates SRv6 uSID L3VPN between two PE nodes over a redundant transit mesh with -dual route reflectors. FRR carries the eBGP/iBGP underlay on every node. PE nodes run -GoBGP for VPNv4 overlay (L3VPN IPv4 unicast). Two FRR-only route reflectors (`rr1`, -`rr2`) reflect VPNv4 routes between the PEs. SRv6 encap mode is `encap.red` -(SRH-less, single-SID path). Transit nodes form an iBGP full mesh within AS 65100. - -## Topology - -See `network.drawio.xml` for the full topology diagram. - -Eight nodes across three roles: - -- **PE** (`pe1`, `pe2`) — edge routers running FRR (eBGP underlay) + GoBGP (VPNv4 overlay). Each connects to one transit node. -- **Transit** (`tr1`–`tr4`) — FRR-only, iBGP full mesh within AS 65100. Carry all underlay reachability and SRv6 data-plane traffic. -- **Control** (`rr1`, `rr2`) — FRR-only VPNv4 route reflectors. Each peers with both PEs as RR clients. Connected to `tr3` and `tr4` respectively. - -Underlay path: `pe1 — tr1 — (mesh) — tr2 — pe2`. SRv6 data plane follows the same path. - -## Addressing - -### Loopbacks and SRv6 locators - -| Node | Loopback | Role | BGP ASN (underlay) | BGP ASN (overlay) | -|------|-----------------|-----------|--------------------|--------------------| -| pe1 | fc00:0:2::1/48 | edge PE | 65101 | 65000 | -| pe2 | fc00:0:3::1/48 | edge PE | 65102 | 65000 | -| tr1 | fc00:0:1::1/128 | transit | 65100 | — | -| tr2 | fc00:0:5::1/128 | transit | 65100 | — | -| tr3 | fc00:0:6::1/128 | transit | 65100 | — | -| tr4 | fc00:0:7::1/128 | transit | 65100 | — | -| rr1 | fc00:0:4::1/128 | control | 65103 (local-as) | 65000 | -| rr2 | fc00:0:8::1/128 | control | 65104 (local-as) | 65000 | - -### SRv6 uSID plan - -uSID block: `fc00::/32` — 32-bit block | 16-bit node | 16-bit function - -| Node | uDT4 SID (VRF blue) | VPN prefix | -|------|---------------------|-------------| -| pe1 | fc00:0:2:e000:: | 10.0.0.1/32 | -| pe2 | fc00:0:3:e000:: | 10.0.0.2/32 | - -### Link subnets (underlay point-to-point) - -| Link | Subnet | Node A addr | Node B addr | -|--------------|---------------------|--------------------|--------------------| -| pe1 – tr1 | 2001:db8:0:1::/64 | tr1 eth1 ::1 | pe1 eth1 ::2 | -| pe2 – tr2 | 2001:db8:0:2::/64 | tr2 eth1 ::1 | pe2 eth1 ::2 | -| tr1 – tr2 | 2001:db8:0:12::/64 | tr1 eth2 ::1 | tr2 eth2 ::2 | -| tr1 – tr3 | 2001:db8:0:13::/64 | tr1 eth3 ::1 | tr3 eth1 ::3 | -| tr1 – tr4 | 2001:db8:0:14::/64 | tr1 eth4 ::1 | tr4 eth1 ::4 | -| tr2 – tr3 | 2001:db8:0:23::/64 | tr2 eth3 ::2 | tr3 eth2 ::3 | -| tr2 – tr4 | 2001:db8:0:24::/64 | tr2 eth4 ::2 | tr4 eth2 ::4 | -| tr3 – tr4 | 2001:db8:0:34::/64 | tr3 eth3 ::3 | tr4 eth3 ::4 | -| tr3 – rr1 | 2001:db8:0:3::/64 | tr3 eth4 ::1 | rr1 eth1 ::2 | -| tr4 – rr2 | 2001:db8:0:4::/64 | tr4 eth4 ::1 | rr2 eth1 ::2 | - -## Lab layout - -```text -. -├── network.clab.yaml -├── node_files/ -│ ├── pe1/ frr.conf gobgp.conf startup.sh -│ ├── pe2/ frr.conf gobgp.conf startup.sh -│ ├── tr1/ frr.conf startup.sh -│ ├── tr2/ frr.conf startup.sh -│ ├── tr3/ frr.conf startup.sh -│ ├── tr4/ frr.conf startup.sh -│ ├── rr1/ frr.conf startup.sh -│ └── rr2/ frr.conf startup.sh -├── group_files/ -│ ├── common/ hosts vtysh.conf startup-lib.sh -│ ├── pe/ daemons -│ ├── transit/ daemons -│ └── control/ daemons -├── containers/ -│ └── gobgp-pe/ Dockerfile -├── scripts/ -│ └── host-setup.sh -└── Taskfile.yaml -``` - -## Prerequisites - -- ContainerLab ≥ 0.54 -- Docker with access to `frrouting/frr:latest` -- Custom `gobgp-pe:latest` image (built locally via `task build`) -- Host kernel ≥ 5.18 for SRv6 `encap.red` support -- `task` and standard Linux utilities - -## Quick start - -```bash -task build # build gobgp-pe:latest from containers/gobgp-pe/Dockerfile -task up # apply host sysctls and deploy lab -task inspect # show node management addresses -``` - -## Tasks - -| Task | Description | -|----------------------|-----------------------------------------------------| -| `build` | Build the custom `gobgp-pe:latest` image | -| `up` | Apply host sysctls then deploy the lab | -| `down` | Destroy the lab and remove state | -| `reload` | Full rebuild — destroy then redeploy | -| `inspect` | Show running nodes and management addresses | -| `graph` | Generate a draw.io diagram for the current topology | -| `host-setup` | Apply required host sysctls (IPv6 forwarding etc.) | -| `clean` | Destroy the lab, remove state, and delete the image | - -## Verification - -### Underlay BGP - -```bash -# All transit iBGP and PE eBGP sessions — expect Established on all links -docker exec clab-network-tr1 vtysh -c "show bgp ipv6 unicast summary" -docker exec clab-network-tr3 vtysh -c "show bgp ipv6 unicast summary" - -# PE should learn all other loopbacks through the transit mesh -docker exec clab-network-pe1 vtysh -c "show bgp ipv6 unicast" -docker exec clab-network-pe1 ip -6 route show - -# RR loopbacks should be reachable from both PEs -docker exec clab-network-pe1 ping6 -c3 fc00:0:4::1 -docker exec clab-network-pe1 ping6 -c3 fc00:0:8::1 -``` - -### Overlay BGP (route reflectors) - -```bash -# Both PEs should be Established as RR clients -docker exec clab-network-rr1 vtysh -c "show bgp ipv4 vpn summary" -docker exec clab-network-rr2 vtysh -c "show bgp ipv4 vpn summary" - -# VPN table — expect 10.0.0.1/32 and 10.0.0.2/32 with SRv6 SID next-hops -docker exec clab-network-rr1 vtysh -c "show bgp ipv4 vpn" -``` - -### Overlay BGP (PEs) - -```bash -# Session state from the PE side — expect rr1 and rr2 Established -docker exec clab-network-pe1 gobgp neighbor -docker exec clab-network-pe2 gobgp neighbor - -# VRF blue RIB — expect both prefixes with correct SRv6 SID next-hops -docker exec clab-network-pe1 gobgp vrf blue rib -docker exec clab-network-pe2 gobgp vrf blue rib -``` - -### SRv6 data plane - -```bash -# Decap rule — End.DT4 seg6local entry for the local uDT4 SID -docker exec clab-network-pe1 ip -6 route show | grep seg6local - -# Encap rule — seg6 encap.red route in VRF blue toward the remote PE -docker exec clab-network-pe1 ip route show table 100 | grep seg6 -``` - -### End-to-end connectivity - -```bash -docker exec clab-network-pe1 ip vrf exec blue ping -c3 10.0.0.2 -docker exec clab-network-pe2 ip vrf exec blue ping -c3 10.0.0.1 -``` - -## Notes - -- PE startup scripts program both control and data planes before FRR starts: - - VRF blue kernel device + dummy0 for VPN endpoint advertisement - - `ip -6 route add encap seg6local action End.DT4 vrftable 100` — decap - - `ip route add vrf blue encap seg6 mode encap.red segs ` — encap (SRH-less) -- GoBGP runs on PEs with `port = -1` so FRR owns TCP 179 for the eBGP underlay session in the same namespace. -- GoBGP exposes gRPC on `:50051`; use `gobgp -u neighbor` to connect from outside the container. -- RR nodes (`rr1`, `rr2`) present AS 65103/65104 toward transit via `local-as no-prepend replace-as` while running AS 65000 for the overlay iBGP sessions. -- Both PEs peer with both RRs; there is no inter-RR session. Each RR independently reflects all PE client routes. -- The uSID block is `fc00::/32`. Node IDs occupy bits 33–48; function `0xe000` marks End.DT4 (uDT4). -- `encap.red` eliminates the SRH for single-SID paths; the outer IPv6 DA is the remote uDT4 SID directly. -- End.DT4 `flavors usd,next-csid` are not supported on kernel ≤ 6.19 — plain End.DT4 is used and is functionally equivalent for single-SID paths. - -## Known issues / limitations - -- No automated test suite; validation is manual via the commands above. -- End.DT4 uSD flavor not supported by kernel 6.19.12 (silently ignored). encap.red is applied. -- Transit iBGP full mesh does not scale beyond ~4 nodes; use route reflectors for larger topologies. -- VRF blue route targets are not enforced (both PEs use RT 65000:100); all VPN prefixes are mutually imported. diff --git a/lab/network/Taskfile.yaml b/lab/network/Taskfile.yaml deleted file mode 100644 index 01febd8..0000000 --- a/lab/network/Taskfile.yaml +++ /dev/null @@ -1,70 +0,0 @@ -version: '3' - -vars: - LAB: - sh: awk '/^name:/ {print $2; exit}' *.clab.yaml - TOPO: - sh: echo *.clab.yaml - ARCH: - sh: uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/' - -tasks: - default: - silent: true - cmds: - - task --list - - pull-base: - desc: Pull the base image for gobgp-pe using crane (avoids Docker Hub TLS 1.3 panic in OrbStack ARM64 builds) - cmds: - - | - if ! docker image inspect debian:bookworm-slim >/dev/null 2>&1; then - crane pull --platform linux/{{.ARCH}} debian:bookworm-slim /tmp/debian-bookworm-slim.tar - docker load -i /tmp/debian-bookworm-slim.tar - rm /tmp/debian-bookworm-slim.tar - fi - - build: - desc: Build the custom GoBGP + FRR image - deps: [pull-base] - cmds: - - docker build --pull=false -t gobgp-pe:latest containers/gobgp-pe - - up: - desc: Build the image and deploy the lab - deps: [build, host-setup] - cmds: - - sudo containerlab deploy -t {{.TOPO}} - - down: - desc: Destroy the lab and clean generated state - cmds: - - sudo containerlab destroy -t {{.TOPO}} --cleanup - - reload: - desc: Full rebuild and redeploy - cmds: - - task: down - - task: up - - inspect: - desc: Inspect deployed nodes and management addresses - cmds: - - sudo containerlab inspect -t {{.TOPO}} - - graph: - desc: Generate a draw.io diagram for the current topology - cmds: - - sudo containerlab graph -t {{.TOPO}} --drawio --drawio-dir . - - host-setup: - desc: Apply host sysctls for this dual-stack lab - cmds: - - sudo ./scripts/host-setup.sh --no-persist - - clean: - desc: Destroy the lab, remove ContainerLab state, and delete the gobgp-pe image - cmds: - - task: down - - docker rmi gobgp-pe:latest || true - - rm -rf clab-{{.LAB}} diff --git a/lab/network/containers/gobgp-pe/Dockerfile b/lab/network/containers/gobgp-pe/Dockerfile deleted file mode 100644 index e4ab95b..0000000 --- a/lab/network/containers/gobgp-pe/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM debian:bookworm-slim - -RUN apt-get update && apt-get install -y --no-install-recommends \ - iproute2 \ - iputils-ping \ - procps \ - ca-certificates \ - curl \ - gnupg \ - && rm -rf /var/lib/apt/lists/* - -RUN curl -s https://deb.frrouting.org/frr/keys.gpg \ - | gpg --dearmor > /usr/share/keyrings/frr.gpg \ - && echo "deb [signed-by=/usr/share/keyrings/frr.gpg] https://deb.frrouting.org/frr bookworm frr-stable" \ - > /etc/apt/sources.list.d/frr.list \ - && apt-get update && apt-get install -y --no-install-recommends frr \ - && rm -rf /var/lib/apt/lists/* - -ARG GOBGP_VERSION=4.5.0 -RUN curl -fsSL \ - "https://github.com/osrg/gobgp/releases/download/v${GOBGP_VERSION}/gobgp_${GOBGP_VERSION}_linux_amd64.tar.gz" \ - | tar -xz -C /usr/local/bin gobgp gobgpd diff --git a/lab/network/group_files/common/hosts b/lab/network/group_files/common/hosts deleted file mode 100644 index b194e0b..0000000 --- a/lab/network/group_files/common/hosts +++ /dev/null @@ -1,11 +0,0 @@ -127.0.0.1 localhost -::1 localhost ip6-localhost ip6-loopback - -fc00:0:2::1 pe1 -fc00:0:3::1 pe2 -fc00:0:1::1 tr1 -fc00:0:5::1 tr2 -fc00:0:6::1 tr3 -fc00:0:7::1 tr4 -fc00:0:4::1 rr1 -fc00:0:8::1 rr2 diff --git a/lab/network/group_files/common/startup-lib.sh b/lab/network/group_files/common/startup-lib.sh deleted file mode 100755 index a704a5d..0000000 --- a/lab/network/group_files/common/startup-lib.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh - -log() { - echo "$(hostname)-startup: $*" -} - -wait_for_interface() { - iface="$1"; timeout="${2:-30}"; i=0 - while [ "$i" -lt "$timeout" ]; do - if ip link show dev "$iface" >/dev/null 2>&1; then - return 0 - fi - i=$((i + 1)) - sleep 1 - done - log "interface $iface did not appear within ${timeout}s" - return 1 -} - -wait_for_gobgpd() { - timeout="${1:-30}"; i=0 - while [ "$i" -lt "$timeout" ]; do - if gobgp neighbor >/dev/null 2>&1; then - return 0 - fi - i=$((i + 1)) - sleep 1 - done - log "gobgpd did not become ready within ${timeout}s" - return 1 -} - -wait_for_addr() { - addr="$1"; timeout="${2:-30}"; i=0 - while [ "$i" -lt "$timeout" ]; do - if ip -6 addr show to "$addr" scope global 2>/dev/null | grep -v tentative | grep -q inet6; then - return 0 - fi - i=$((i + 1)) - sleep 1 - done - log "address $addr did not appear within ${timeout}s" - return 1 -} - -wait_for_route() { - addr="$1"; timeout="${2:-120}"; i=0 - while [ "$i" -lt "$timeout" ]; do - if ip -6 route get "$addr" >/dev/null 2>&1; then - return 0 - fi - i=$((i + 1)) - sleep 1 - done - log "route to $addr did not appear within ${timeout}s" - return 1 -} diff --git a/lab/network/group_files/common/vtysh.conf b/lab/network/group_files/common/vtysh.conf deleted file mode 100644 index e0ab9cb..0000000 --- a/lab/network/group_files/common/vtysh.conf +++ /dev/null @@ -1 +0,0 @@ -service integrated-vtysh-config diff --git a/lab/network/group_files/control/daemons b/lab/network/group_files/control/daemons deleted file mode 100644 index c18ff7e..0000000 --- a/lab/network/group_files/control/daemons +++ /dev/null @@ -1,22 +0,0 @@ -zebra=yes -bgpd=yes -ospfd=no -ospf6d=no -ripd=no -ripngd=no -isisd=no -pimd=no -ldpd=no -nhrpd=no -eigrpd=no -babeld=no -sharpd=no -pbrd=no -bfdd=no -fabricd=no -vrrpd=no -pathd=no - -vtysh_enable=yes -zebra_options=" -A 127.0.0.1 -s 90000000" -bgpd_options=" -A 127.0.0.1" diff --git a/lab/network/group_files/pe/daemons b/lab/network/group_files/pe/daemons deleted file mode 100644 index c18ff7e..0000000 --- a/lab/network/group_files/pe/daemons +++ /dev/null @@ -1,22 +0,0 @@ -zebra=yes -bgpd=yes -ospfd=no -ospf6d=no -ripd=no -ripngd=no -isisd=no -pimd=no -ldpd=no -nhrpd=no -eigrpd=no -babeld=no -sharpd=no -pbrd=no -bfdd=no -fabricd=no -vrrpd=no -pathd=no - -vtysh_enable=yes -zebra_options=" -A 127.0.0.1 -s 90000000" -bgpd_options=" -A 127.0.0.1" diff --git a/lab/network/group_files/transit/daemons b/lab/network/group_files/transit/daemons deleted file mode 100644 index c18ff7e..0000000 --- a/lab/network/group_files/transit/daemons +++ /dev/null @@ -1,22 +0,0 @@ -zebra=yes -bgpd=yes -ospfd=no -ospf6d=no -ripd=no -ripngd=no -isisd=no -pimd=no -ldpd=no -nhrpd=no -eigrpd=no -babeld=no -sharpd=no -pbrd=no -bfdd=no -fabricd=no -vrrpd=no -pathd=no - -vtysh_enable=yes -zebra_options=" -A 127.0.0.1 -s 90000000" -bgpd_options=" -A 127.0.0.1" diff --git a/lab/network/network.clab.yaml b/lab/network/network.clab.yaml deleted file mode 100644 index 801dff6..0000000 --- a/lab/network/network.clab.yaml +++ /dev/null @@ -1,147 +0,0 @@ -name: network - -mgmt: - network: mgmt - ipv4-subnet: 172.20.20.0/24 - -topology: - defaults: - kind: linux - binds: - - group_files/common/vtysh.conf:/etc/frr/vtysh.conf:ro - - group_files/common/startup-lib.sh:/opt/lab/startup-lib.sh:ro - - group_files/common/hosts:/etc/hosts:ro - - kinds: - linux: - image: frrouting/frr:latest - - nodes: - pe1: - group: pe - labels: - graph-group: pe - graph-level: 1 - graph-icon: router - image: gobgp-pe:latest - entrypoint: /node_files/startup.sh - env: - LOCAL_LOOPBACK: "fc00:0:2::1" - LOCAL_SID: "fc00:0:2:e000::" - REMOTE_SID: "fc00:0:3:e000::" - LOCAL_VPN_IP: 10.0.0.1/32 - REMOTE_VPN_PREFIX: 10.0.0.2/32 - RR_LOOPBACK: "fc00:0:4::1" - binds: - - group_files/pe/daemons:/etc/frr/daemons:ro - - node_files/pe1/startup.sh:/node_files/startup.sh:ro - - node_files/pe1/frr.conf:/etc/frr/frr.conf:ro - - node_files/pe1/gobgp.conf:/etc/gobgp/gobgp.conf:ro - - pe2: - group: pe - labels: - graph-group: pe - graph-level: 1 - graph-icon: router - image: gobgp-pe:latest - entrypoint: /node_files/startup.sh - env: - LOCAL_LOOPBACK: "fc00:0:3::1" - LOCAL_SID: "fc00:0:3:e000::" - REMOTE_SID: "fc00:0:2:e000::" - LOCAL_VPN_IP: 10.0.0.2/32 - REMOTE_VPN_PREFIX: 10.0.0.1/32 - RR_LOOPBACK: "fc00:0:4::1" - binds: - - group_files/pe/daemons:/etc/frr/daemons:ro - - node_files/pe2/startup.sh:/node_files/startup.sh:ro - - node_files/pe2/frr.conf:/etc/frr/frr.conf:ro - - node_files/pe2/gobgp.conf:/etc/gobgp/gobgp.conf:ro - - tr1: - group: transit - labels: - graph-group: transit - graph-level: 2 - graph-icon: router - entrypoint: /node_files/startup.sh - binds: - - group_files/transit/daemons:/etc/frr/daemons:ro - - node_files/tr1/startup.sh:/node_files/startup.sh:ro - - node_files/tr1/frr.conf:/etc/frr/frr.conf:ro - - tr2: - group: transit - labels: - graph-group: transit - graph-level: 2 - graph-icon: router - entrypoint: /node_files/startup.sh - binds: - - group_files/transit/daemons:/etc/frr/daemons:ro - - node_files/tr2/startup.sh:/node_files/startup.sh:ro - - node_files/tr2/frr.conf:/etc/frr/frr.conf:ro - - tr3: - group: transit - labels: - graph-group: transit - graph-level: 2 - graph-icon: router - entrypoint: /node_files/startup.sh - binds: - - group_files/transit/daemons:/etc/frr/daemons:ro - - node_files/tr3/startup.sh:/node_files/startup.sh:ro - - node_files/tr3/frr.conf:/etc/frr/frr.conf:ro - - tr4: - group: transit - labels: - graph-group: transit - graph-level: 2 - graph-icon: router - entrypoint: /node_files/startup.sh - binds: - - group_files/transit/daemons:/etc/frr/daemons:ro - - node_files/tr4/startup.sh:/node_files/startup.sh:ro - - node_files/tr4/frr.conf:/etc/frr/frr.conf:ro - - rr1: - group: control - labels: - graph-group: control - graph-level: 3 - graph-icon: router - entrypoint: /node_files/startup.sh - binds: - - group_files/control/daemons:/etc/frr/daemons:ro - - node_files/rr1/startup.sh:/node_files/startup.sh:ro - - node_files/rr1/frr.conf:/etc/frr/frr.conf:ro - - rr2: - group: control - labels: - graph-group: control - graph-level: 3 - graph-icon: router - entrypoint: /node_files/startup.sh - binds: - - group_files/control/daemons:/etc/frr/daemons:ro - - node_files/rr2/startup.sh:/node_files/startup.sh:ro - - node_files/rr2/frr.conf:/etc/frr/frr.conf:ro - - links: - # PE uplinks - - endpoints: ["pe1:eth1", "tr1:eth1"] - - endpoints: ["pe2:eth1", "tr2:eth1"] - # TR full mesh (iBGP within AS 65100) - - endpoints: ["tr1:eth2", "tr2:eth2"] - - endpoints: ["tr1:eth3", "tr3:eth1"] - - endpoints: ["tr1:eth4", "tr4:eth1"] - - endpoints: ["tr2:eth3", "tr3:eth2"] - - endpoints: ["tr2:eth4", "tr4:eth2"] - - endpoints: ["tr3:eth3", "tr4:eth3"] - # RR downlinks - - endpoints: ["rr1:eth1", "tr3:eth4"] - - endpoints: ["rr2:eth1", "tr4:eth4"] diff --git a/lab/network/network.drawio.xml b/lab/network/network.drawio.xml deleted file mode 100644 index 7a1e981..0000000 --- a/lab/network/network.drawio.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lab/network/node_files/pe1/frr.conf b/lab/network/node_files/pe1/frr.conf deleted file mode 100644 index 9fb0ee3..0000000 --- a/lab/network/node_files/pe1/frr.conf +++ /dev/null @@ -1,27 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname pe1 -log syslog informational - -interface lo - ! /48 assigns the full SRv6 SID block to this node; eBGP advertises the /48 as the SID range. - ipv6 address fc00:0:2::1/48 -! -interface eth1 - description tr1-facing - ipv6 address 2001:db8:0:1::2/64 -! - -router bgp 65101 - bgp router-id 10.255.255.2 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65100 - ! - address-family ipv6 unicast - neighbor eth1 activate - network fc00:0:2::/48 - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/pe1/gobgp.conf b/lab/network/node_files/pe1/gobgp.conf deleted file mode 100644 index 4d64ba8..0000000 --- a/lab/network/node_files/pe1/gobgp.conf +++ /dev/null @@ -1,27 +0,0 @@ -[global.config] - as = 65000 - router-id = "10.255.255.2" - port = -1 - local-address-list = ["fc00:0:2::1"] - -[[neighbors]] - [neighbors.config] - neighbor-address = "fc00:0:4::1" - peer-as = 65000 - description = "rr1" - [neighbors.transport.config] - local-address = "fc00:0:2::1" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" - -[[neighbors]] - [neighbors.config] - neighbor-address = "fc00:0:8::1" - peer-as = 65000 - description = "rr2" - [neighbors.transport.config] - local-address = "fc00:0:2::1" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" diff --git a/lab/network/node_files/pe1/startup.sh b/lab/network/node_files/pe1/startup.sh deleted file mode 100755 index efeac90..0000000 --- a/lab/network/node_files/pe1/startup.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -ip link set eth1 up - -sysctl -w net.vrf.strict_mode=1 -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.seg6_enabled=1 -sysctl -w net.ipv6.conf.eth1.seg6_enabled=1 - -# Program the local VRF and SRv6 data plane before starting control-plane daemons. -ip link add blue type vrf table 100 -ip link set blue up -ip link add dummy0 type dummy -ip link set dummy0 master blue -ip link set dummy0 up -ip addr add "$LOCAL_VPN_IP" dev dummy0 -ip -6 route add "$LOCAL_SID" encap seg6local action End.DT4 vrftable 100 dev eth1 -ip route add "$REMOTE_VPN_PREFIX" vrf blue encap seg6 mode encap.red segs "$REMOTE_SID" dev eth1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -wait_for_addr "$LOCAL_LOOPBACK" -wait_for_route "$RR_LOOPBACK" || log "rr1 loopback not yet reachable; gobgpd will retry" - -gobgpd -f /etc/gobgp/gobgp.conf --api-hosts :50051 --log-level=debug >/var/log/gobgpd.log 2>&1 & -GOBGP_PID=$! - -if wait_for_gobgpd; then - gobgp vrf add blue rd 65000:100 rt both 65000:100 || true - gobgp vrf blue rib add "$LOCAL_VPN_IP" nexthop "$LOCAL_SID" || true -else - log "skipping VRF route injection: gobgpd not ready" -fi - -wait "$GOBGP_PID" diff --git a/lab/network/node_files/pe2/frr.conf b/lab/network/node_files/pe2/frr.conf deleted file mode 100644 index 512f900..0000000 --- a/lab/network/node_files/pe2/frr.conf +++ /dev/null @@ -1,27 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname pe2 -log syslog informational - -interface lo - ! /48 assigns the full SRv6 SID block to this node; eBGP advertises the /48 as the SID range. - ipv6 address fc00:0:3::1/48 -! -interface eth1 - description tr2-facing - ipv6 address 2001:db8:0:2::2/64 -! - -router bgp 65102 - bgp router-id 10.255.255.3 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65100 - ! - address-family ipv6 unicast - neighbor eth1 activate - network fc00:0:3::/48 - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/pe2/gobgp.conf b/lab/network/node_files/pe2/gobgp.conf deleted file mode 100644 index 738d94d..0000000 --- a/lab/network/node_files/pe2/gobgp.conf +++ /dev/null @@ -1,27 +0,0 @@ -[global.config] - as = 65000 - router-id = "10.255.255.3" - port = -1 - local-address-list = ["fc00:0:3::1"] - -[[neighbors]] - [neighbors.config] - neighbor-address = "fc00:0:4::1" - peer-as = 65000 - description = "rr1" - [neighbors.transport.config] - local-address = "fc00:0:3::1" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" - -[[neighbors]] - [neighbors.config] - neighbor-address = "fc00:0:8::1" - peer-as = 65000 - description = "rr2" - [neighbors.transport.config] - local-address = "fc00:0:3::1" - [[neighbors.afi-safis]] - [neighbors.afi-safis.config] - afi-safi-name = "l3vpn-ipv4-unicast" diff --git a/lab/network/node_files/pe2/startup.sh b/lab/network/node_files/pe2/startup.sh deleted file mode 100755 index efeac90..0000000 --- a/lab/network/node_files/pe2/startup.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -ip link set eth1 up - -sysctl -w net.vrf.strict_mode=1 -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.seg6_enabled=1 -sysctl -w net.ipv6.conf.eth1.seg6_enabled=1 - -# Program the local VRF and SRv6 data plane before starting control-plane daemons. -ip link add blue type vrf table 100 -ip link set blue up -ip link add dummy0 type dummy -ip link set dummy0 master blue -ip link set dummy0 up -ip addr add "$LOCAL_VPN_IP" dev dummy0 -ip -6 route add "$LOCAL_SID" encap seg6local action End.DT4 vrftable 100 dev eth1 -ip route add "$REMOTE_VPN_PREFIX" vrf blue encap seg6 mode encap.red segs "$REMOTE_SID" dev eth1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -wait_for_addr "$LOCAL_LOOPBACK" -wait_for_route "$RR_LOOPBACK" || log "rr1 loopback not yet reachable; gobgpd will retry" - -gobgpd -f /etc/gobgp/gobgp.conf --api-hosts :50051 --log-level=debug >/var/log/gobgpd.log 2>&1 & -GOBGP_PID=$! - -if wait_for_gobgpd; then - gobgp vrf add blue rd 65000:100 rt both 65000:100 || true - gobgp vrf blue rib add "$LOCAL_VPN_IP" nexthop "$LOCAL_SID" || true -else - log "skipping VRF route injection: gobgpd not ready" -fi - -wait "$GOBGP_PID" diff --git a/lab/network/node_files/rr1/frr.conf b/lab/network/node_files/rr1/frr.conf deleted file mode 100644 index ec8bd2d..0000000 --- a/lab/network/node_files/rr1/frr.conf +++ /dev/null @@ -1,38 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname rr1 -log syslog informational - -interface lo - ipv6 address fc00:0:4::1/128 -! -interface eth1 - description tr3-facing - ipv6 address 2001:db8:0:3::2/64 -! - -router bgp 65000 - bgp router-id 10.255.255.1 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65100 - neighbor eth1 local-as 65103 no-prepend replace-as - neighbor fc00:0:2::1 remote-as 65000 - neighbor fc00:0:2::1 update-source lo - neighbor fc00:0:3::1 remote-as 65000 - neighbor fc00:0:3::1 update-source lo - ! - address-family ipv6 unicast - neighbor eth1 activate - network fc00:0:4::1/128 - exit-address-family - ! - address-family ipv4 vpn - neighbor fc00:0:2::1 activate - neighbor fc00:0:2::1 route-reflector-client - neighbor fc00:0:3::1 activate - neighbor fc00:0:3::1 route-reflector-client - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/rr1/startup.sh b/lab/network/node_files/rr1/startup.sh deleted file mode 100755 index 2f2dc4a..0000000 --- a/lab/network/node_files/rr1/startup.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -ip link set eth1 up - -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -wait_for_addr fc00:0:4::1 || log "rr1 loopback not yet programmed" - -exec tail -f /dev/null diff --git a/lab/network/node_files/rr2/frr.conf b/lab/network/node_files/rr2/frr.conf deleted file mode 100644 index a6c9e86..0000000 --- a/lab/network/node_files/rr2/frr.conf +++ /dev/null @@ -1,38 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname rr2 -log syslog informational - -interface lo - ipv6 address fc00:0:8::1/128 -! -interface eth1 - description tr4-facing - ipv6 address 2001:db8:0:4::2/64 -! - -router bgp 65000 - bgp router-id 10.255.255.4 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65100 - neighbor eth1 local-as 65104 no-prepend replace-as - neighbor fc00:0:2::1 remote-as 65000 - neighbor fc00:0:2::1 update-source lo - neighbor fc00:0:3::1 remote-as 65000 - neighbor fc00:0:3::1 update-source lo - ! - address-family ipv6 unicast - neighbor eth1 activate - network fc00:0:8::1/128 - exit-address-family - ! - address-family ipv4 vpn - neighbor fc00:0:2::1 activate - neighbor fc00:0:2::1 route-reflector-client - neighbor fc00:0:3::1 activate - neighbor fc00:0:3::1 route-reflector-client - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/rr2/startup.sh b/lab/network/node_files/rr2/startup.sh deleted file mode 100755 index b7d0dd7..0000000 --- a/lab/network/node_files/rr2/startup.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -ip link set eth1 up - -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -wait_for_addr fc00:0:8::1 || log "rr2 loopback not yet programmed" - -exec tail -f /dev/null diff --git a/lab/network/node_files/tr1/frr.conf b/lab/network/node_files/tr1/frr.conf deleted file mode 100644 index 454f350..0000000 --- a/lab/network/node_files/tr1/frr.conf +++ /dev/null @@ -1,47 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname tr1 -log syslog informational - -interface lo - ipv6 address fc00:0:1::1/128 -! -interface eth1 - description pe1-facing - ipv6 address 2001:db8:0:1::1/64 -! -interface eth2 - description tr2-facing - ipv6 address 2001:db8:0:12::1/64 -! -interface eth3 - description tr3-facing - ipv6 address 2001:db8:0:13::1/64 -! -interface eth4 - description tr4-facing - ipv6 address 2001:db8:0:14::1/64 -! - -router bgp 65100 - bgp router-id 10.255.255.100 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65101 - neighbor eth2 interface remote-as 65100 - neighbor eth3 interface remote-as 65100 - neighbor eth4 interface remote-as 65100 - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth2 activate - neighbor eth2 next-hop-self - neighbor eth3 activate - neighbor eth3 next-hop-self - neighbor eth4 activate - neighbor eth4 next-hop-self - network fc00:0:1::1/128 - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/tr1/startup.sh b/lab/network/node_files/tr1/startup.sh deleted file mode 100755 index 358d71a..0000000 --- a/lab/network/node_files/tr1/startup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -wait_for_interface eth2 -wait_for_interface eth3 -wait_for_interface eth4 -ip link set eth1 up -ip link set eth2 up -ip link set eth3 up -ip link set eth4 up - -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -exec tail -f /dev/null diff --git a/lab/network/node_files/tr2/frr.conf b/lab/network/node_files/tr2/frr.conf deleted file mode 100644 index aeb1b14..0000000 --- a/lab/network/node_files/tr2/frr.conf +++ /dev/null @@ -1,47 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname tr2 -log syslog informational - -interface lo - ipv6 address fc00:0:5::1/128 -! -interface eth1 - description pe2-facing - ipv6 address 2001:db8:0:2::1/64 -! -interface eth2 - description tr1-facing - ipv6 address 2001:db8:0:12::2/64 -! -interface eth3 - description tr3-facing - ipv6 address 2001:db8:0:23::2/64 -! -interface eth4 - description tr4-facing - ipv6 address 2001:db8:0:24::2/64 -! - -router bgp 65100 - bgp router-id 10.255.255.101 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65102 - neighbor eth2 interface remote-as 65100 - neighbor eth3 interface remote-as 65100 - neighbor eth4 interface remote-as 65100 - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth2 activate - neighbor eth2 next-hop-self - neighbor eth3 activate - neighbor eth3 next-hop-self - neighbor eth4 activate - neighbor eth4 next-hop-self - network fc00:0:5::1/128 - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/tr2/startup.sh b/lab/network/node_files/tr2/startup.sh deleted file mode 100755 index 358d71a..0000000 --- a/lab/network/node_files/tr2/startup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -wait_for_interface eth2 -wait_for_interface eth3 -wait_for_interface eth4 -ip link set eth1 up -ip link set eth2 up -ip link set eth3 up -ip link set eth4 up - -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -exec tail -f /dev/null diff --git a/lab/network/node_files/tr3/frr.conf b/lab/network/node_files/tr3/frr.conf deleted file mode 100644 index 36e81c7..0000000 --- a/lab/network/node_files/tr3/frr.conf +++ /dev/null @@ -1,47 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname tr3 -log syslog informational - -interface lo - ipv6 address fc00:0:6::1/128 -! -interface eth1 - description tr1-facing - ipv6 address 2001:db8:0:13::3/64 -! -interface eth2 - description tr2-facing - ipv6 address 2001:db8:0:23::3/64 -! -interface eth3 - description tr4-facing - ipv6 address 2001:db8:0:34::3/64 -! -interface eth4 - description rr1-facing - ipv6 address 2001:db8:0:3::1/64 -! - -router bgp 65100 - bgp router-id 10.255.255.102 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65100 - neighbor eth2 interface remote-as 65100 - neighbor eth3 interface remote-as 65100 - neighbor eth4 interface remote-as 65103 - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth1 next-hop-self - neighbor eth2 activate - neighbor eth2 next-hop-self - neighbor eth3 activate - neighbor eth3 next-hop-self - neighbor eth4 activate - network fc00:0:6::1/128 - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/tr3/startup.sh b/lab/network/node_files/tr3/startup.sh deleted file mode 100755 index 358d71a..0000000 --- a/lab/network/node_files/tr3/startup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -wait_for_interface eth2 -wait_for_interface eth3 -wait_for_interface eth4 -ip link set eth1 up -ip link set eth2 up -ip link set eth3 up -ip link set eth4 up - -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -exec tail -f /dev/null diff --git a/lab/network/node_files/tr4/frr.conf b/lab/network/node_files/tr4/frr.conf deleted file mode 100644 index 5323767..0000000 --- a/lab/network/node_files/tr4/frr.conf +++ /dev/null @@ -1,47 +0,0 @@ -frr version 9.1 -frr defaults traditional -hostname tr4 -log syslog informational - -interface lo - ipv6 address fc00:0:7::1/128 -! -interface eth1 - description tr1-facing - ipv6 address 2001:db8:0:14::4/64 -! -interface eth2 - description tr2-facing - ipv6 address 2001:db8:0:24::4/64 -! -interface eth3 - description tr3-facing - ipv6 address 2001:db8:0:34::4/64 -! -interface eth4 - description rr2-facing - ipv6 address 2001:db8:0:4::1/64 -! - -router bgp 65100 - bgp router-id 10.255.255.103 - no bgp default ipv4-unicast - no bgp ebgp-requires-policy - neighbor eth1 interface remote-as 65100 - neighbor eth2 interface remote-as 65100 - neighbor eth3 interface remote-as 65100 - neighbor eth4 interface remote-as 65104 - ! - address-family ipv6 unicast - neighbor eth1 activate - neighbor eth1 next-hop-self - neighbor eth2 activate - neighbor eth2 next-hop-self - neighbor eth3 activate - neighbor eth3 next-hop-self - neighbor eth4 activate - network fc00:0:7::1/128 - exit-address-family -! - -ipv6 forwarding diff --git a/lab/network/node_files/tr4/startup.sh b/lab/network/node_files/tr4/startup.sh deleted file mode 100755 index 358d71a..0000000 --- a/lab/network/node_files/tr4/startup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -set -eu -. /opt/lab/startup-lib.sh - -wait_for_interface eth1 -wait_for_interface eth2 -wait_for_interface eth3 -wait_for_interface eth4 -ip link set eth1 up -ip link set eth2 up -ip link set eth3 up -ip link set eth4 up - -sysctl -w net.ipv4.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.all.forwarding=1 - -install -d -o frr -g frr -m 775 /run/frr -install -d -o frr -g frr -m 775 /var/log/frr -/usr/lib/frr/frrinit.sh start - -exec tail -f /dev/null diff --git a/lab/network/scripts/host-setup.sh b/lab/network/scripts/host-setup.sh deleted file mode 100755 index 963f676..0000000 --- a/lab/network/scripts/host-setup.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -# host-setup.sh — Prepare the host for Containerlab labs. - -set -euo pipefail - -PERSIST=true -SYSCTL_CONF=/etc/sysctl.d/99-clab.conf - -usage() { - echo "Usage: $0 [--no-persist]" - echo "" - echo " --no-persist Apply sysctls now but do not write to ${SYSCTL_CONF}" - exit 1 -} - -for arg in "$@"; do - case $arg in - --no-persist) PERSIST=false ;; - --help|-h) usage ;; - *) echo "Unknown argument: $arg"; usage ;; - esac -done - -if [ "$(id -u)" -ne 0 ]; then - echo "ERROR: must run as root (use sudo)" >&2 - exit 1 -fi - -echo "==> Containerlab host setup" -echo " persist : ${PERSIST}" -echo "" - -echo "--> Enabling IPv6 forwarding" -sysctl -w net.ipv6.conf.all.forwarding=1 -sysctl -w net.ipv6.conf.default.forwarding=1 - -echo "--> Enabling IPv4 forwarding" -sysctl -w net.ipv4.ip_forward=1 - -if [ "$PERSIST" = true ]; then - echo "--> Writing ${SYSCTL_CONF}" - { - echo "# Containerlab host requirements" - echo "# Generated by host-setup.sh" - echo "net.ipv6.conf.all.forwarding=1" - echo "net.ipv6.conf.default.forwarding=1" - echo "net.ipv4.ip_forward=1" - } >"${SYSCTL_CONF}" - echo " written." -fi - -echo "" -echo "==> Verification" -echo " net.ipv6.conf.all.forwarding = $(sysctl -n net.ipv6.conf.all.forwarding)" -echo " net.ipv6.conf.default.forwarding = $(sysctl -n net.ipv6.conf.default.forwarding)" -echo " net.ipv4.ip_forward = $(sysctl -n net.ipv4.ip_forward)" -echo "" -echo "==> Done. Host is ready for Containerlab." From 6ea42018921f2f61422560eceb85a24ddbeeb66c Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Sat, 6 Jun 2026 13:27:42 -0400 Subject: [PATCH 2/3] chore: move cosmos-src from hidden dir to build/cosmos-src Co-Authored-By: Claude Sonnet 4.6 --- deploy/containerlab/Taskfile.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/containerlab/Taskfile.yaml b/deploy/containerlab/Taskfile.yaml index f38865d..133377a 100644 --- a/deploy/containerlab/Taskfile.yaml +++ b/deploy/containerlab/Taskfile.yaml @@ -36,10 +36,10 @@ tasks: - docker image inspect {{.COSMOS_IMAGE}} > /dev/null 2>&1 cmds: - | - if [ ! -d ".cosmos-src" ]; then - git clone --depth=1 https://github.com/milo-os/cosmos .cosmos-src + if [ ! -d "build/cosmos-src" ]; then + git clone --depth=1 https://github.com/milo-os/cosmos build/cosmos-src fi - docker build -t {{.COSMOS_IMAGE}} -f .cosmos-src/build/Dockerfile .cosmos-src + docker build -t {{.COSMOS_IMAGE}} -f build/cosmos-src/build/Dockerfile build/cosmos-src "build:gobgp": desc: Build the GoBGP container using the pre-built v{{.GOBGP_VERSION}} release binary From 64621519c665edcc51faa472bdb313735d08a610 Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Sat, 6 Jun 2026 13:28:34 -0400 Subject: [PATCH 3/3] chore: rename cosmos-src to cosmos, clean build/ on clean, ignore build/ dirs Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 3 +-- deploy/containerlab/Taskfile.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 3db593f..6f4aa1e 100644 --- a/.gitignore +++ b/.gitignore @@ -195,8 +195,7 @@ topology-data.json # ============================================================ # Lab and local dev overrides # ============================================================ -lab/gvpc/clab-*/ -lab/network/clab-*/ +build/ # ============================================================ # Miscellaneous diff --git a/deploy/containerlab/Taskfile.yaml b/deploy/containerlab/Taskfile.yaml index 133377a..f16d550 100644 --- a/deploy/containerlab/Taskfile.yaml +++ b/deploy/containerlab/Taskfile.yaml @@ -36,10 +36,10 @@ tasks: - docker image inspect {{.COSMOS_IMAGE}} > /dev/null 2>&1 cmds: - | - if [ ! -d "build/cosmos-src" ]; then - git clone --depth=1 https://github.com/milo-os/cosmos build/cosmos-src + if [ ! -d "build/cosmos" ]; then + git clone --depth=1 https://github.com/milo-os/cosmos build/cosmos fi - docker build -t {{.COSMOS_IMAGE}} -f build/cosmos-src/build/Dockerfile build/cosmos-src + docker build -t {{.COSMOS_IMAGE}} -f build/cosmos/build/Dockerfile build/cosmos "build:gobgp": desc: Build the GoBGP container using the pre-built v{{.GOBGP_VERSION}} release binary @@ -170,4 +170,4 @@ tasks: - docker rmi {{.GOBGP_IMAGE}} || true - docker rmi {{.FRR_IMAGE}} || true - docker rmi {{.COSMOS_IMAGE}} || true - - rm -rf clab-{{.LAB}} + - rm -rf clab-{{.LAB}} build/