underlay : le MTU du modèle était faux, et la garde le validait

En préparant le déplacement des VTEP, la lecture des interfaces a montré que
`underlay.yml` annonçait 9000 alors que vmbr3 est à 1500. La garde P23
exigeait >= 1550 et passait parce que le fichier mentait. Une garde qui
valide une déclaration plutôt qu'une réalité donne un faux confort — pire
qu'une garde absente, qui au moins n'endort personne.

Le seuil ne peut pas être fixe non plus : 1550 aurait rejeté à tort un
transport à 1500 portant un overlay à 1450, qui tient exactement. Il dérive
d'un `mtu_overlay` déclaré : transport >= overlay + 50. Exercé.

Trouvé aussi : vmbr3 n'est pas VLAN-aware. L'adresse du VTEP y est non
étiquetée et vit dans le VLAN natif du port. Déplacer le VTEP n'est donc pas
un changement d'adresse — il faut un VLAN natif 10 ou une interface étiquetée
dédiée. C'est pourquoi le déplacement n'a pas été effectué.

Preuves : 24 OK, 0 échec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-08-03 10:28:16 -04:00
parent 6a80e5b55c
commit 89dc5ed3d4
4 changed files with 56 additions and 11 deletions

View file

@ -1,5 +1,31 @@
# CHANGELOG — Set-OPS
## 2026-08-03 (suite 7) — le modèle déclarait un MTU que le matériel n'a pas
En préparant le déplacement des adresses de VTEP, la lecture des interfaces a montré deux
choses que le modèle ignorait.
### `underlay.yml` annonçait 9000, `vmbr3` est à 1500
La garde P23 validait donc **une déclaration fausse** : elle exigeait ≥ 1550 et passait parce
que le fichier disait 9000. **Une garde qui valide une déclaration plutôt qu'une réalité donne
un faux confort** — c'est pire qu'une garde absente, qui au moins n'endort personne.
Le seuil ne peut pas non plus être fixe : 1550 aurait rejeté à tort un transport à 1500
portant un overlay à 1450, qui tient exactement. Il **dérive** désormais d'un
`mtu_overlay` déclaré (1450 par défaut) : transport ≥ overlay + 50.
Le fichier dit maintenant la vérité — 1500 — et la garde passe pour la bonne raison. Exercé :
un overlay porté à 1500 sur ce transport est refusé.
### `vmbr3` n'est pas *VLAN-aware*
Pas de `bridge_vlan_aware`, contrairement à `vmbr2`. L'adresse du VTEP y est **non
étiquetée** : elle vit dans le VLAN natif du port de commutateur. Déplacer le VTEP vers
l'underlay n'est donc pas un changement d'adresse — il faut soit un VLAN natif 10, soit une
interface étiquetée dédiée (`bond3.10`).
C'est la raison pour laquelle le déplacement n'a **pas** été effectué : le geste demandé
suppose une décision de câblage qui n'est pas prise.
## 2026-08-03 (suite 6) — les hyperviseurs entrent au modèle, à leur adresse cible
Redresser les pairs EVPN vers l'underlay suppose d'abord que les hyperviseurs **existent dans

View file

@ -91,8 +91,8 @@ le tenant : c'est le genre de configuration qui s'applique sans erreur et ne fon
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.
**L'overlay est plafonné à 1450** (décision du 2026-08-03), donc le transport doit dépasser
1500. Conséquence à ne pas manquer : sous 1500, **tout ce qui traverse la frontière dépend de
**L'overlay est plafonné à 1450** (décision du 2026-08-03) parce que le transport réel — le
pont `vmbr3` des hyperviseurs — est à **1500** : 1450 + 50 de VXLAN y tient exactement. Conséquence à ne pas manquer : sous 1500, **tout ce qui traverse la frontière dépend de
la découverte de MTU de chemin**, qui a besoin de l'ICMP « fragmentation nécessaire ».
Or le registre des flux ne connaissait que TCP et UDP : ce message ne pouvait pas être
@ -154,6 +154,11 @@ garde détecte la faute avant qu'on ne la documente.
dernier octet conservé comme sur `vmbr0`. Le déplacement réel de l'adresse sur les nœuds reste
à faire : c'est une modification du réseau d'un hyperviseur en service.
**Le pont `vmbr3` n'est pas *VLAN-aware*** (pas de `bridge_vlan_aware`, contrairement à
`vmbr2`). L'adresse du VTEP y est donc **non étiquetée** : elle vit dans le VLAN natif du port
de commutateur. Déplacer le VTEP vers l'underlay suppose soit un VLAN natif 10, soit une
interface étiquetée dédiée (`bond3.10`) — ce n'est pas qu'un changement d'adresse.
**`vishnu` n'est pas câblé.** Son `vmbr3` n'a **aucun port physique** : le pont existe, porte
une adresse, et ne mène nulle part. Un pair VXLAN pointé sur lui ne fonctionnera jamais.

View file

@ -82,10 +82,18 @@ STP_TOPOLOGIES = ("etoile", "anneau", "maille")
ROUTAGE_TENANTS = ("switch", "sdn")
# VXLAN ajoute 50 octets d'encapsulation. Sous ce seuil, le transport casse
# PARTIELLEMENT : le ping passe, les transferts echouent — la panne la plus couteuse
# a diagnostiquer. On refuse donc un underlay qui ne peut pas porter le SDN.
MTU_MINIMAL_VXLAN = 1550
# VXLAN coute 50 octets d'encapsulation : le TRANSPORT doit valoir au moins l'overlay
# plus cette surcharge. Sous ce seuil, il casse PARTIELLEMENT — le ping passe, les
# transferts echouent — la panne la plus couteuse a diagnostiquer.
SURCOUT_VXLAN = 50
MTU_OVERLAY_DEFAUT = 1450 # ce que voit une VM ; regle par zone dans Proxmox SDN
def mtu_overlay(underlay: dict | None) -> int:
"""MTU annonce aux VM par les VNet. Declare, parce qu'il commande le minimum du
transport : un overlay a 1450 tient dans un transport a 1500, pas un overlay a 1500."""
v = (underlay or {}).get("mtu_overlay")
return int(v) if isinstance(v, int) else MTU_OVERLAY_DEFAUT
def routage_tenants(underlay: dict | None) -> str:
@ -386,15 +394,16 @@ def valider(underlay: dict | None,
if rt is not None and str(rt) not in ROUTAGE_TENANTS:
erreurs.append(f"routage_tenants '{rt}' inconnu (attendu : {', '.join(ROUTAGE_TENANTS)})")
if routage_tenants(underlay) == "sdn":
# Le transport du VXLAN doit encaisser l'encapsulation, sinon panne partielle.
# Le transport doit valoir l'overlay DECLARE plus la surcharge d'encapsulation.
minimal = mtu_overlay(underlay) + SURCOUT_VXLAN
fab = fabric_du_routeur(underlay)
for r in reseaux_de_fabric(underlay, fab):
mtu = int(r.get("mtu") or 1500)
if mtu < MTU_MINIMAL_VXLAN:
if mtu < minimal:
erreurs.append(
f"reseau '{r.get('nom','?')}': MTU {mtu} < {MTU_MINIMAL_VXLAN} alors que "
f"`routage_tenants: sdn` — VXLAN ajoute 50 octets ; sous ce seuil le ping "
f"passe et les transferts echouent")
f"reseau '{r.get('nom','?')}': MTU {mtu} < {minimal} "
f"(overlay {mtu_overlay(underlay)} + {SURCOUT_VXLAN} de VXLAN) — sous ce "
f"seuil le ping passe et les transferts echouent")
d = dialecte(underlay)
if d and d not in DIALECTES:

View file

@ -35,6 +35,11 @@ underlay:
# MTU >= 1550 (VXLAN ajoute 50 octets) — `make underlay` le refuse sinon.
#routage_tenants: switch
# MTU annonce aux VM par les VNet, en mode `sdn`. Il commande le minimum du TRANSPORT :
# `make underlay` refuse un reseau de fabric sous `mtu_overlay + 50` (surcout VXLAN).
# 1450 tient dans un transport a 1500 ; viser 1500 d'overlay exige du jumbo.
#mtu_overlay: 1450
# Isolation inter-tenant par ACL de commutateur. `true` par defaut.
# Mettre `false` quand le materiel ne sait pas lier une ACL a une interface de
# routage : le devis cesse alors d'emettre des ACL qui ne seraient jamais liees.