labsim: add IPv6 BGP config layer for the Gateway-API public-v6 rehearsal
Some checks failed
CI/CD / lint (push) Failing after 24s
CI/CD / typecheck (push) Failing after 22s
CI/CD / test (push) Failing after 23s
CI/CD / build (push) Has been skipped
CI/CD / publish-rpm (push) Has been skipped
CI/CD / publish-deb (push) Has been skipped

The sim's eBGP was IPv4-only; extend it to the v6 family so labsim can rehearse
Cilium BGP advertising a public-style v6 LoadBalancer /64 to the VyOS pair before
it touches production (approved plan: public IPv6 via Cilium BGP + Gateway API).

- sim-net-config.py: bgp6 inside bgp() -- ipv6-unicast peer-group K8S6 over
  fd00:2::11/12/13, prefix-list6 (le 128, /128 host routes), K8S-IN6/OUT6 route-maps
  (export deny -- never hand the cluster a default). ULA fd61:1e00::/64 as the sim
  LB range so nothing leaks into the real HE /48. All v6 lines pass the
  sim-net-apply.sh whitelist.
- sim-ha-config.py: VLANS6 = {2: fd00:2/64} -> routers hold fd00:2::252/253 on
  bond0.2 so they can peer the nodes over v6 (no v6 VRRP VIP; BGP peers the per-box
  address, as production).
- k8s-up.sh: nodes get fd00:2::11/12/13 + dual-stack k3s (dual node-ip, dual
  cluster/service CIDR). Peers line up with sim-net-config K8S_NODES_V6.

Config-gen verified for all three. Live chunk (Cilium v6+BGP+Gateway install,
bring-up, datapath/#26847 proof) is next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DMVzWZgiKW2wquf5z8S1yH
This commit is contained in:
Michal
2026-09-10 01:23:03 +01:00
parent a4dcc9b379
commit c729275961
3 changed files with 60 additions and 6 deletions

View File

@@ -38,6 +38,14 @@ K8S_VLAN="${K8S_VLAN:-2}"
K8S_PREFIX="${K8S_PREFIX:-172.31.2}"
K8S_NODES="${K8S_NODES:-3}"
K8S_FIRST_OCTET="${K8S_FIRST_OCTET:-11}"
# Dual-stack, mirroring production's shape (not its addresses). VLAN 2 v6 is a
# ULA so nothing here can leak into the real HE /48; node fd00:2::1x matches the
# BGP peers in sim-net-config.py (K8S_NODES_V6). cluster/service v6 are ULAs too;
# the LB pool fd61:1e00::/64 is what Cilium advertises (SERVICE_CIDR_V6 there).
K8S_PREFIX_V6="${K8S_PREFIX_V6:-fd00:2}" # nodes fd00:2::11/12/13, router ::252/::253
K8S_ROUTER_V6="${K8S_ROUTER_V6:-fd00:2::252}" # v6 next-hop (primary router bond0.2)
CLUSTER_CIDR_V6="${CLUSTER_CIDR_V6:-fd00:42::/56}"
SERVICE_CIDR_V6="${SERVICE_CIDR_V6:-fd00:43::/112}"
K8S_MEM="${K8S_MEM:-4096}" # MB — k3s + cilium + a workload
K8S_CPUS="${K8S_CPUS:-2}"
K8S_DISK_GB="${K8S_DISK_GB:-12}"
@@ -53,6 +61,7 @@ CILIUM_VERSION="${CILIUM_VERSION:-1.19.1}"
node_name() { echo "labsim-k8s$1"; }
node_ip() { echo "${K8S_PREFIX}.$((K8S_FIRST_OCTET + $1 - 1))"; }
node_ip6() { echo "${K8S_PREFIX_V6}::$((K8S_FIRST_OCTET + $1 - 1))"; }
# --- base image -----------------------------------------------------------
ensure_base_image() {
@@ -75,7 +84,7 @@ ensure_base_image() {
# before joining, because cloud-init ordering across VMs is not guaranteed and
# a failed join leaves an agent that never retries.
build_k8s_seed() {
local iso="$1" vm="$2" ip="$3" role="$4" server_ip="$5" pubkey="$6"
local iso="$1" vm="$2" ip="$3" role="$4" server_ip="$5" pubkey="$6" ip6="$7" server_ip6="$8"
local tmp; tmp="$(mktemp -d)"
cat > "$tmp/meta-data" <<EOF
@@ -89,7 +98,7 @@ ethernets:
enp1s0:
match:
name: "en*"
addresses: [$ip/24]
addresses: [$ip/24, $ip6/64]
routes:
- to: default
via: ${K8S_PREFIX}.1
@@ -100,10 +109,13 @@ EOF
local k3s_exec
if [ "$role" = "server" ]; then
# flannel/servicelb/traefik off: Cilium is the CNI under test, and k3s's
# own ServiceLB would fight Cilium for LoadBalancer addresses.
k3s_exec="server --flannel-backend=none --disable-network-policy --disable=servicelb --disable=traefik --node-ip=$ip --tls-san=$ip --cluster-init"
# own ServiceLB would fight Cilium for LoadBalancer addresses. Dual-stack:
# both families in cluster-cidr/service-cidr and a dual node-ip, mirroring
# production. Pod IPs come from Cilium cluster-pool (node.spec.podCIDRs is
# immutable), so cluster-cidr v6 just marks the cluster dual-stack.
k3s_exec="server --flannel-backend=none --disable-network-policy --disable=servicelb --disable=traefik --node-ip=$ip,$ip6 --tls-san=$ip --cluster-cidr=10.42.0.0/16,${CLUSTER_CIDR_V6} --service-cidr=10.43.0.0/16,${SERVICE_CIDR_V6} --cluster-init"
else
k3s_exec="agent --server https://${server_ip}:6443 --node-ip=$ip"
k3s_exec="agent --server https://${server_ip}:6443 --node-ip=$ip,$ip6"
fi
cat > "$tmp/user-data" <<EOF
@@ -170,8 +182,10 @@ create_node() {
local n="$1" pubkey="$2"
local vm; vm="$(node_name "$n")"
local ip; ip="$(node_ip "$n")"
local ip6; ip6="$(node_ip6 "$n")"
local role="agent"; [ "$n" -eq 1 ] && role="server"
local server_ip; server_ip="$(node_ip 1)"
local server_ip6; server_ip6="$(node_ip6 1)"
if virsh_q dominfo "$vm" >/dev/null 2>&1; then
local state; state="$(virsh_q domstate "$vm" 2>/dev/null | head -1 | tr -d '\n')"
@@ -189,7 +203,7 @@ create_node() {
log "creating $vm ($ip, $role, ${K8S_MEM}MB/${K8S_CPUS}cpu)"
sudo qemu-img create -q -f qcow2 -F qcow2 -b "$DEB_BASE" "$disk" "${K8S_DISK_GB}G" >/dev/null
build_k8s_seed "$seed" "$vm" "$ip" "$role" "$server_ip" "$pubkey"
build_k8s_seed "$seed" "$vm" "$ip" "$role" "$server_ip" "$pubkey" "$ip6" "$server_ip6"
# Access port on the k8s VLAN — same broadcast domain as the routers'
# bond0.2 leg, so BGP peering is directly connected exactly as in production.