frontière : le devis rattrape la bascule SDN, et deux devis se contredisaient

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 `<NOEUD-DE-SORTIE-EVPN>` 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 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-03 08:40:20 -04:00
parent 87697597fb
commit 58fdaa5540
3 changed files with 62 additions and 5 deletions

View file

@ -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 `<NOEUD-DE-SORTIE-EVPN>` 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

View file

@ -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 `<NOEUD-DE-SORTIE-EVPN>` 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

View file

@ -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 = "<PROCHAIN-SAUT-SWITCH>"
NOEUD_SORTIE = "<NOEUD-DE-SORTIE-EVPN>"
IF_TRANSIT = "<IF-TRANSIT>"
IF_WAN = "<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"]