From 58fdaa55408c97ddf1f23da5f6d28ba32bedb0aa Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 3 Aug 2026 08:40:20 -0400 Subject: [PATCH] =?UTF-8?q?fronti=C3=A8re=20:=20le=20devis=20rattrape=20la?= =?UTF-8?q?=20bascule=20SDN,=20et=20deux=20devis=20se=20contredisaient?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le prochain saut des routes tenants pointait le SVI du commutateur. En EVPN il ne route plus les tenants : la route arriverait sur un équipement sans chemin vers le tenant — configuration qui s'applique sans erreur et ne fonctionne pas. Le devis émet `` et dit pourquoi. `underlay.passerelle_sortie` garde son sens : adresse du pare-feu sur le lien de transit, donc sortie de l'UNDERLAY. Deux choses distinctes. Corrigé aussi une contradiction antérieure au SDN : la section 0 demandait de router l'administration vers 10.0.4.6, le SVI du commutateur lui-même, alors que `devis-reseau` émet 10.0.4.1, l'adresse du pare-feu — tout en affirmant que l'autre devis « émet déjà ces routes ». Elles coïncident maintenant, vérifié ligne à ligne. Et deux commentaires qui affirmaient l'inverse de la décision se dérivent du mode de routage. Preuves : 24 OK, 0 échec. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ docs/sdn-evpn.md | 12 ++++++++++-- scripts/devis_opnsense.py | 30 +++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70437cf..6c14b31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # CHANGELOG — Set-OPS +## 2026-08-03 (suite) — le devis frontière rattrape la bascule SDN + +Trois affirmations du devis frontière étaient devenues fausses, dont une qui cassait le +routage. + +### Corrigé — le prochain saut des routes tenants +Elles pointaient le SVI du commutateur (`10.0.4.6`). En EVPN, il ne route plus les tenants : +une route pointée là arriverait sur un équipement **sans chemin vers le tenant**. Configuration +qui s'applique sans erreur et ne fonctionne pas — la signature qu'on traque depuis deux jours. + +Le devis émet désormais `` et dit pourquoi, à figer après le spike. + +`underlay.passerelle_sortie` **garde** son sens : c'est l'adresse du pare-feu sur le lien de +transit, donc la sortie de l'**underlay**. Deux choses distinctes qu'il ne faut pas confondre. + +### Corrigé — une contradiction entre les deux devis, antérieure au SDN +La section 0 du devis frontière demandait de router les réseaux d'administration **vers +`10.0.4.6`** — le SVI du commutateur lui-même. `make devis-reseau` émet `10.0.4.1`, l'adresse +du pare-feu. Les deux devis se contredisaient, alors que l'un affirmait que l'autre « émet déjà +ces routes ». Elles coïncident maintenant, vérifié ligne à ligne. + +### Corrigé — deux commentaires qui affirmaient l'inverse de la décision +L'en-tête (« les passerelles de zone restent sur les switches L3 ») et la description des +routes (« routage inter-zone sur les switches L3 ») se dérivent maintenant du mode de routage. + ## 2026-08-03 — le SDN prend le routage tenant, le devis switch se vide Trois décisions, et le devis les applique déjà : **une zone EVPN par tenant** ; le routage diff --git a/docs/sdn-evpn.md b/docs/sdn-evpn.md index 5240fe8..ce7ea32 100644 --- a/docs/sdn-evpn.md +++ b/docs/sdn-evpn.md @@ -80,8 +80,16 @@ tenants. **Le nœud de sortie remplace le prochain saut.** En EVPN, le trafic quitte le VRF par un ou plusieurs *exit nodes* désignés. Ce sont eux, et non plus le commutateur, que l'OPNsense voit -comme voisins. `underlay.passerelle_sortie` changera de nature : il désignera l'entrée du -tenant vers la bordure, côté hyperviseur. +comme voisins. + +C'est **appliqué** : en mode `sdn`, le devis frontière n'émet plus le SVI du commutateur comme +prochain saut des routes tenants — il émet le marqueur `` et dit +pourquoi. Une route pointée vers le SVI arriverait sur un équipement qui n'a aucun chemin vers +le tenant : c'est le genre de configuration qui s'applique sans erreur et ne fonctionne pas. + +À noter : `underlay.passerelle_sortie` **garde** son sens — c'est l'adresse du pare-feu sur le +lien de transit, donc la sortie de l'**underlay**, indépendante du routage tenant. Ce sont +deux choses distinctes qu'il ne faut pas confondre. **Le MTU est un prérequis vérifié, pas un conseil.** VXLAN ajoute 50 octets ; `make underlay` **refuse** un réseau de transport sous 1550 dès que `routage_tenants: sdn`. C'est le premier diff --git a/scripts/devis_opnsense.py b/scripts/devis_opnsense.py index 53a9afa..512d8ad 100644 --- a/scripts/devis_opnsense.py +++ b/scripts/devis_opnsense.py @@ -59,6 +59,7 @@ import underlay as underlay_mod # noqa: E402 # (meme idiome que devis_reseau). Le panneau « Intrants de base » du GUI les ecrit # dans group_vars/opnsense.yml : le devis se complete tout seul ensuite. PROCHAIN_SAUT = "" +NOEUD_SORTIE = "" IF_TRANSIT = "" IF_WAN = "" @@ -122,6 +123,11 @@ def prochain_saut(transit: dict | None) -> str: fois dans l'underlay) ouvrirait la porte a deux valeurs contradictoires — c'est exactement le mode de panne qu'on ferme. Marqueur tant qu'aucun transit n'existe. """ + if underlay_mod.routage_tenants(underlay_mod.charger()) == "sdn": + # En EVPN, le commutateur ne route plus les tenants : le voisin de la frontiere + # est un NOEUD DE SORTIE (hyperviseur), pas le SVI. Emettre le SVI produirait + # des routes vers un equipement qui n'a aucun chemin vers le tenant. + return NOEUD_SORTIE return str((transit or {}).get("passerelle") or "").strip() or PROCHAIN_SAUT # Le socle est porte par TOUS les hotes : sa destination n'est pas une poignee @@ -286,7 +292,10 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict: { "reseau": supernet_de(n["index"]), "prochain_saut": saut, - "description": f"{nom} (index {n['index']}) — routage inter-zone sur les switches L3", + "description": f"{nom} (index {n['index']}) — routage inter-zone " + + ("dans la zone EVPN du tenant" + if underlay_mod.routage_tenants(underlay_mod.charger()) == "sdn" + else "sur les switches L3"), } for nom, _pfx, n in tenants ] @@ -314,8 +323,12 @@ def construire(tenants: list[tuple[str, str, dict]]) -> dict: "adresse_frontiere": (transit or {}).get("passerelle_sortie"), "nom_frontiere": nom_frontiere(transit), } if transit else None, + # Le switch route vers l'ADRESSE DU PARE-FEU sur le lien de transit + # (`passerelle_sortie`), pas vers son propre SVI ni vers un noeud de sortie : + # c'est la sortie de l'underlay. `devis_reseau` emet exactement ces lignes. "reciproque_switch": [ - f"ip route {cidr} -> {saut} (retour vers l'administration)" + f"ip route {cidr} -> {(transit or {}).get('passerelle_sortie') or PROCHAIN_SAUT}" + f" (retour vers l'administration)" for cidr in admin ], } @@ -327,7 +340,9 @@ def rendre(devis: dict) -> str: "# DEVIS OPNSENSE — frontiere nord/sud (Set-OPS)", "# Genere par scripts/devis_opnsense.py. NE PAS EDITER A LA MAIN.", "# Derive du registre des flux (pair: externe) + des nomenclatures.", - "# Les passerelles de zone restent sur les switches L3 : aucun SVI ici.", + ("# Les passerelles de zone sont anycast sur les hyperviseurs (SDN EVPN) : aucun SVI ici." + if underlay_mod.routage_tenants(underlay_mod.charger()) == "sdn" + else "# Les passerelles de zone restent sur les switches L3 : aucun SVI ici."), "# ============================================================", "", "## 0. Prerequis reciproques — COTE SWITCHES", @@ -373,6 +388,15 @@ def rendre(devis: dict) -> str: out += [ "## 2. Routes statiques (vers les supernets tenants)", ] + if devis["routes"] and devis["routes"][0]["prochain_saut"] == NOEUD_SORTIE: + out += [ + "# /!\\ LE PROCHAIN SAUT N'EST PAS LE COMMUTATEUR. En SDN EVPN, il ne route plus", + "# les tenants : une route pointee vers son SVI arriverait sur un equipement sans", + "# chemin vers le tenant. Le voisin de la frontiere est un NOEUD DE SORTIE — un", + "# hyperviseur qui porte la sortie du VRF. A figer apres le spike EVPN.", + ] + out += [ + ] for r in devis["routes"]: out.append(f"route add {r['reseau']} via {r['prochain_saut']} # {r['description']}") out += ["", "## 3. Alias"]