From 917b22ccc81339125bb22b4309d994109899c182 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Thu, 6 Aug 2026 16:12:38 -0400 Subject: [PATCH] sdn : emet le noeud de sortie PRIMAIRE, et trois noeuds pour t17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `proxmox_sdn.sortie_primaire` etait declare et utilise par `devis_opnsense` pour deriver le prochain saut de la frontiere, mais `devis_sdn` ne l'emettait jamais : une zone creee depuis ce devis n'aurait pas eu de primaire, et les deux devis se seraient contredits. Le devis l'emet, et refuse un primaire absent de la liste des noeuds de sortie. Cluster aligne : t17 passe a asgard,gandalf,vishnu (primaire asgard), strophe FRR posee sur les trois noeuds, six VRF portant leur defaut. Note de methode : une adresse ANYCAST ne peut pas servir de source de test. Le ping depuis gandalf semblait mort a 100 % ; la capture a montre asgard recevant les quatre reponses — la frontiere route le supernet vers le primaire, qui porte la meme passerelle localement. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ scripts/devis_sdn.py | 20 +++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee644a7..607d04a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ ## 2026-08-06 — le chemin nord-sud devient dérivable +### Trois nœuds de sortie, et le primaire enfin émis + +`t17` passe à `asgard,gandalf,vishnu`, primaire `asgard` — `t11` l'était déjà. La strophe FRR +est posée sur les trois nœuds, et les six VRF (deux zones × trois nœuds) portent leur défaut. + +**Un défaut du devis découvert au passage.** `proxmox_sdn.sortie_primaire` était déclaré et +**utilisé** par `devis_opnsense` pour dériver le prochain saut des routes de la frontière, +mais `devis_sdn` ne l'émettait jamais. Une zone créée depuis ce devis n'aurait pas eu de +primaire, et les deux devis se seraient contredits : la frontière aurait pointé un nœud que le +SDN n'avait pas désigné. Le devis l'émet désormais, et refuse un primaire absent de la liste +des nœuds de sortie. + +**Une mesure qui accusait à tort.** Depuis `gandalf` et `vishnu`, la sortie semblait morte : +100 % de perte là où `asgard` passait. La capture a tranché — pendant que `gandalf` pingait, +**`asgard` recevait les quatre réponses** : + +``` +IP 10.0.4.1 > 10.27.19.1: ICMP echo reply, seq 1..4 (vu sur asgard) +``` + +La source du test, `10.27.19.1`, est la passerelle **anycast** : elle existe à l'identique sur +les trois nœuds. La frontière route `10.27.0.0/16` par sa route statique unique vers +`10.0.4.41`, et `asgard` consomme les réponses. Une VM tenant a une adresse unique, et le +relais EVPN la lui rend — mécanisme déjà observé (`10.27.19.21 via 10.0.5.41`). + +Ce retour par le primaire n'est pas un défaut : c'est le sens de « primaire », et la +conséquence d'une route statique unique côté frontière. À retenir pour les mesures futures : +**une adresse anycast ne peut pas servir de source de test** — elle ne désigne pas le nœud +d'où l'on part. + + ### Les tenants sortent — et le chemin est entièrement dérivé Bout en bout, mesuré depuis `vrf_t17` puis `vrf_t11` sur `asgard` : diff --git a/scripts/devis_sdn.py b/scripts/devis_sdn.py index 3030aaf..1df194d 100644 --- a/scripts/devis_sdn.py +++ b/scripts/devis_sdn.py @@ -151,6 +151,10 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict: "controleur": sdn.get("controleur") or "", "asn": sdn.get("asn"), "noeuds_de_sortie": sdn.get("noeuds_de_sortie") or [], + # Le PRIMAIRE doit etre emis : `devis_opnsense` en derive le prochain saut des + # routes de la frontiere. Declare d'un cote et tu par l'autre, les deux devis se + # contrediraient — la frontiere pointerait un noeud que le SDN n'a pas designe. + "sortie_primaire": str(sdn.get("sortie_primaire") or "").strip(), "anciennes": list(ANCIEN_NOMMAGE), "frontiere": str(_tr.get("passerelle_sortie") or "").strip(), "zones": blocs, @@ -192,8 +196,22 @@ def rendre(devis: dict) -> str: f"pvesh create /cluster/sdn/zones --zone {b['zone']} --type evpn \\", f" --controller {devis['controleur']} --vrf-vxlan {b['vrf_vxlan']} \\", f" --mtu {b['mtu']} --ipam pve \\", - f" --exitnodes {','.join(sortie) if sortie else MARQUEUR_SORTIE}", + f" --exitnodes {','.join(sortie) if sortie else MARQUEUR_SORTIE}" + + (f" \\\n --exitnodes-primary {devis['sortie_primaire']}" + if devis.get("sortie_primaire") else ""), ] + if sortie and not devis.get("sortie_primaire"): + out += [ + "# /!\\ Aucun `proxmox_sdn.sortie_primaire` declare. La frontiere derive son", + "# prochain saut de cette valeur : sans elle, ses routes tenants restent", + "# en marqueur et aucun trafic entrant n'aboutit.", + ] + elif devis.get("sortie_primaire") and devis["sortie_primaire"] not in sortie: + out += [ + f"# /!\\ `sortie_primaire` = {devis['sortie_primaire']}, ABSENT des noeuds de", + "# sortie. Proxmox refuserait, et la frontiere pointerait un noeud sans", + "# chemin vers le tenant.", + ] if not sortie: out += [ "# /!\\ AUCUN noeud de sortie declare chez l'hebergeur.",