Une banque. Migration d’un VLAN de production vers un fabric VXLAN/EVPN tout neuf. Six machines Nutanix à basculer, une passerelle qui reste côté legacy sur un firewall, un handoff L2 propre entre les Borders et les vieux switches Nexus.
Sur le papier, une migration de routine. On l’avait déjà faite sur d’autres VLAN.
Sauf que celui-là ne pingait pas. Et entre le premier test du matin et la commande qui a fini par tout expliquer, il s’est écoulé plus de douze heures.
Spoiler : deux ports, et un compteur réglé à dix. Vous pouvez rester quand même, c’est le chemin qui est intéressant.
Le symptôme qui rend fou
Depuis un switch Nexus du legacy, avec une IP posée dans le VLAN, on pingait les six machines. Toutes. Sans exception, sans latence anormale, sans perte.
Depuis le firewall, qui est la passerelle réelle de ces machines et qui se trouve dans le même VLAN, sur le même chemin, à un saut près : rien. Pas une réponse. Et surtout, pas une seule entrée ARP dans sa table.
Deux équipements dans le même domaine de broadcast. Même VLAN, même fabric, mêmes destinations. L’un voit tout, l’autre ne voit rien.
À ce stade, tu relis ton schéma trois fois en te demandant si tu n’as pas loupé un truc énorme et humiliant. Et surtout, c’est le genre de symptôme où quelqu’un finit toujours par dire la phrase.
La phrase qu’il ne fallait pas dire
« Le switch ping, donc le réseau est bon. Le problème est sur le firewall. »
Elle est tentante. Elle est même défendable sur le moment : le réseau prouve qu’il transporte le trafic, un autre équipement du même VLAN fonctionne parfaitement, donc la balle est dans le camp de l’équipe sécurité.
On ne l’a pas dite. Pas par héroïsme, plutôt par flemme d’avoir trois fois la même réunion. Mais c’est probablement la seule décision vraiment importante de toute cette histoire.
Parce que « ça marche depuis A mais pas depuis B » n’est pas un verdict. C’est une question. Et la question n’est pas qui est A et qui est B, mais ce que A et B font différemment.
Ce qu’on a écarté, méthodiquement
On a passé la journée dans le plan de contrôle. Tout, absolument tout, était vert :
- Les routes EVPN type-2 des six machines, importées sur les deux Borders avec les bons Route Targets.
- Les routes type-3 (inclusive multicast) qui construisent la liste d’inondation, valides et sélectionnées.
- Les tunnels VXLAN entre Borders et leafs, tous up, avec des uptimes de plusieurs jours.
- Les tables MAC, cohérentes des deux côtés, y compris celle du firewall, apprise localement sur les Borders et correctement annoncée dans l’EVPN.
- Le handoff L2, la sous-interface dot1q, le bridge-domain en mode Broadcast Forward.
Et on a enterré une par une les hypothèses, chacune par une mesure :
- La passerelle anycast qui aurait absorbé le trafic : hors sujet, la vraie passerelle est le firewall.
- Une asymétrie de Route Target : non, l’import est correct partout.
- La répartition de charge de l’agrégat qui enverrait les flux sur le mauvais Border : les journaux prouvent que les deux Borders étaient sains au moment des pannes.
- Le VTEP anycast partagé entre deux leafs, qui ne livrerait le broadcast qu’à un seul des deux : ce mécanisme prédit un échec partiel, or l’échec était total.
- Une suppression ARP au niveau du bridge-domain : la BD est explicitement en Forward.
- Une limitation des paquets remontés au CPU : compteurs à zéro, 1 948 082 requêtes ARP passées, aucune rejetée.
Six pistes, six impasses. Chacune a coûté une à deux heures : formuler l’hypothèse, trouver la commande qui la teste, la lancer sur quatre équipements, lire la sortie, l’enterrer. À la sixième, tu commences à parler tout seul devant le terminal.
Et en chemin, on a fait une chose qui a l’air brutale mais qui était la bonne : on a coupé un Border, désactivé un lien du legacy, arrêté les uplinks d’un leaf. Pas par énervement, pour forcer un chemin déterministe.
C’est le vrai problème de ces architectures quand elles tombent en panne. Entre l’ECMP de l’underlay, le VTEP anycast partagé par deux leafs, le M-LAG et la répartition de charge des agrégats, une trame ne suit pas un chemin : elle en suit un parmi plusieurs, choisi par des hashs qu’on ne contrôle pas. Un symptôme qui dépend d’un hash n’est pas reproductible, et un test non reproductible ne prouve rien. Tant qu’on n’a pas réduit la topologie à un seul chemin possible, on ne mesure pas, on devine.
Sauf qu’on a pu se le permettre pour une raison très précise, et il faut le dire : la fabric ne portait encore aucune production. C’était justement l’objet de la soirée, y migrer un premier VLAN. Couper un Border ne coûtait rien à personne.
Sur un fabric en service, cette porte est fermée. Il faut alors obtenir la même détermination sans toucher à la topologie : choisir des adresses de test qui fixent le résultat du hash, ou capturer simultanément sur tous les chemins possibles et recouper. C’est plus lent, plus laborieux, et c’est la vraie difficulté de ce genre de panne en production.
La contrepartie se paie quand même, et il faut la regarder en face : plus aucune redondance pendant la durée du test, et un point de comparaison qui bouge à chaque coupure. Donc on note ce qu’on coupe, on remet tout avant de conclure, et on ne compare jamais deux mesures prises sur deux topologies différentes.
À ce stade, on avait un fabric parfaitement configuré, parfaitement convergé, et parfaitement incapable de faire résoudre une adresse.
Le détail qui a tout renversé
Il était dans une capture faite en fin d’après-midi, et qu’on avait lue trop vite.
La capture, on l’a faite sur le Border lui-même, sur l’Eth-Trunk qui regarde le legacy. Une ACL de niveau 2, un filtre sur le VLAN :
acl number 4001
rule 5 permit source-mac 8c60-4f00-0001
rule 10 permit destination-mac 8c60-4f00-0001
quit
capture-packet interface Eth-Trunk40 vlan 100 destination terminal acl 4001 time-out 60 packet-num 100
L’ACL cible la MAC du SVI Nexus, en source comme en destination : on voulait voir tout ce que cet équipement échangeait dans le VLAN. Dix-sept trames en soixante secondes. Toutes des ARP, requêtes et réponses appariées avec les six machines. En apparence, un dialogue ARP en pleine forme. C’est d’ailleurs ce qu’on en avait conclu sur le moment, avant de passer à autre chose comme des champions.
Petite précision qui a son importance pour la suite : destination terminal ne produit aucun fichier, ça crache de l’hexadécimal sur la console. Le soir, on a repris ce dump et reconstruit un .pcap pour le relire proprement. Et là, dans le volet de détail :

Capture réelle sur le Border, filtrée sur le VLAN : dans la liste de paquets, la colonne Destination n’affiche que des adresses MAC unicast, pas une seule trame vers Broadcast. Le volet de gauche détaille la première trame, une requête ARP unicast dont le champ Target MAC est déjà renseigné. Le volet de droite montre, à titre de comparaison et clairement signalé comme une reconstitution, le rendu d’une requête ARP de résolution standard, adressée à Broadcast avec un Target MAC à zéro.
Deux choses sautaient aux yeux, dans cet ordre.
La première, dans le volet de détail : la MAC de destination n’est pas l’adresse de broadcast, c’est directement celle de la machine visée. Et le champ Target MAC de la charge ARP, celui qui est censé être à zéro puisqu’il représente précisément ce qu’on cherche, est déjà renseigné.
Autrement dit : le switch ne cherchait rien du tout. Il rafraîchissait des entrées qu’il possédait depuis des lustres. Une requête ARP unicast, adressée à quelqu’un dont il connaît déjà la réponse.
Les autres trames disaient la même chose. Parmi elles, huit requêtes ARP, dans les deux sens : six du switch vers les machines, deux de machines vers le switch. Aucune en diffusion. Le reste, ce sont des réponses, et une réponse ARP est unicast par construction : elle ne prouve rien.
Un détail au passage, qui a son utilité. Le switch renseigne le champ Target MAC de ses requêtes avec l’adresse qu’il a déjà en cache. Les machines, elles, le laissent à zéro. Aucune n’a tort : la RFC 826 considère ce champ comme non significatif dans une requête, chaque pile en fait ce qu’elle veut. C’est précisément pour ça qu’il ne faut pas s’en servir comme critère. Le seul qui ne mente jamais, c’est la MAC de destination Ethernet.
Et c’est la seconde chose, celle qu’on n’avait pas vue de l’après-midi : sur dix-sept trames ARP capturées en soixante secondes dans ce VLAN, pas une seule n’était adressée à `ff:ff:ff:ff:ff:ff`. Aucune diffusion. Pas une. Sur un domaine de broadcast en production.
Un mot sur le volet droit de la figure : c’est une reconstitution, le rendu Wireshark d’une requête ARP de résolution standard. La vraie requête du firewall, on ne l’a jamais capturée sur ce lien. On a longtemps mis ça sur le compte de la malchance. C’était le symptôme.
Reste que Wireshark ne vous aide pas autant qu’on croit. Dans la colonne Info, les deux trames racontent exactement la même chose : Who has … ? Tell …. Même protocole, même formulation. Et le champ Target MAC n’apparaît dans aucune colonne de la vue par défaut. Le discriminant existe pourtant, une colonne plus à gauche : la Destination, qui affiche une MAC précise dans un cas et Broadcast dans l’autre. Elle est sous vos yeux depuis le début. Personne ne la lit quand il fait défiler trois cents lignes d’ARP.
Si vous relisez des captures ARP un jour, ces deux filtres vous éviteront ma demi-journée :
arp.opcode == 1 && eth.dst == ff:ff:ff:ff:ff:ff
arp.opcode == 1 && eth.dst != ff:ff:ff:ff:ff:ff
Le premier isole les vraies résolutions, celles qui sont inondées. Le second isole les rafraîchissements, ceux qui ne prouvent strictement rien sur la santé de votre broadcast. Et si un compteur doit vous rassurer, c’est le premier : sur un VLAN de production vivant, il ne doit jamais rester vide.
Un piège à éviter au passage : filtrer sur arp.dst.hw_mac != 00:00:00:00:00:00 semble malin pour attraper les rafraîchissements, mais ça rate ceux qui laissent le champ à zéro tout en étant adressés en unicast. On en avait dans notre capture. Le seul critère fiable reste eth.dst.
Le problème n’était donc pas « le switch contre le firewall ». C’était l’unicast contre le broadcast. Et cette reformulation change tout : on ne cherche plus pourquoi un équipement échoue, on cherche ce qui, sur ce chemin, traite le broadcast différemment de l’unicast.
Trois faits sont alors devenus cohérents d’un coup :
- L’unicast traversait parfaitement, dans les deux sens, réponses ARP comprises puisqu’elles sont unicast elles aussi.
- Une entrée ARP statique posée sur le firewall pour une machine faisait immédiatement fonctionner le ping vers elle, et elle seule.
- Un proxy ARP activé en désespoir de cause côté legacy, en amont du lien, débloquait tout : la requête trouvait un répondeur avant d’avoir à traverser le trunk.
Trois contournements qui avaient tous le même point commun : ils évitaient le broadcast.
La configuration qu’on a trouvée, puis abandonnée à tort
En relisant les configurations, on tombe sur ceci, présent sur les 48 ports de chaque équipement du parc :
storm control broadcast min-rate 1 max-rate 10
storm control multicast min-rate 1 max-rate 10
storm control action block
Dix paquets de broadcast par seconde, avec blocage au dépassement. Sur un lien d’interconnexion qui transporte une douzaine de VLAN legacy, où 96,5 % du trafic entrant est précisément du broadcast, à environ cinquante paquets par seconde.
Et le détail qui transforme un incident en état permanent, c’est min-rate 1. Ce n’est pas un second seuil de déclenchement, c’est le seuil de sortie de punition. Le port ne redevient passant que si le broadcast retombe sous un paquet par seconde. Avec une cinquantaine de paquets par seconde en permanence sur ce trunk, il ne peut structurellement jamais en sortir. Ce n’est pas un blocage intermittent sous rafale, c’est un interrupteur poussé une fois, six jours plus tôt.
Il était vingt-deux heures passées. On tenait quelque chose. Alors on a cherché la preuve.
Journal des événements : rien. Pas un seul message de blocage sur six jours.
Compteurs de rejet CPU : zéro, sur tous les types de paquets.
Compteur de rejet d’interface : zéro également.
Trois sources concordantes qui disent qu’il ne se passe rien. On a écarté l’hypothèse.
C’était con. Il a fallu une commande de plus pour s’en rendre compte.
L’état, pas la configuration
La configuration dit ce qui est prévu. Elle ne dit pas ce qui se passe.
display storm control
PortName Type MaxRate Mode Action Punish-Status Trap Log Last Punish-Time
25GE1/0/45 BC 10 Pps Block Block Off Off J-6 16:45:38
25GE1/0/46 BC 10 Pps Block Block Off Off J-0 20:12:29
25GE1/0/1 BC 10 Pps Block Normal Off Off —
… les 45 autres ports : Normal
Deux ports sur quarante-huit en état Block. Exactement les deux membres du lien qui relie le Border au legacy, c’est-à-dire précisément l’endroit par lequel le broadcast du firewall entre dans le fabric.
Le premier port bloquait depuis six jours, et sans interruption depuis : en recoupant avec la date de dernière remise en service du port, le blocage s’était déclenché moins d’une minute après son passage en up.
Pourquoi personne n’avait rien vu ? La réponse tient dans deux colonnes de la même sortie : Trap Off, Log Off. Le mécanisme qui jetait le trafic était configuré pour ne le dire à personne. Et comme le rejet se fait en matériel au niveau du port, aucun des compteurs qu’on avait consultés ne le voyait passer.
Une panne parfaitement silencieuse. Par configuration. Bien joué.
La dernière pièce
Restait une question, et c’est elle qui rendait l’hypothèse difficile à croire : si un port jetait le broadcast, pourquoi le trunk en transportait-il encore ? Une capture filtrée sur ffff-ffff-ffff, sans filtre VLAN cette fois, remontait cent trames en deux minutes. Du broadcast, il en passait.
Le lien vers le legacy est un agrégat de deux ports physiques. Le premier bloquait depuis six jours, le second n’est passé en blocage que le jour même de la découverte.
Or la répartition de charge sur un agrégat est déterministe, et elle est calculée par l’équipement émetteur, pas par celui qui reçoit. Pour de l’ARP, qui n’a pas d’en-tête IP, l’algorithme retombe sur les adresses MAC. Conséquence : tout le broadcast du firewall sortait toujours par le même port. Celui des autres équipements, par l’autre.
La MAC du firewall tombait sur le port bloqué.
C’est aussi ce qui rendait la panne si nette. Réparti sur les deux membres, le broadcast du firewall serait passé une fois sur deux, et on aurait eu un ping intermittent, un symptôme qu’on aurait su lire. Un tirage déterministe perdu donne 0 %. Et 0 %, ça ressemble à une erreur de conception, pas à une limitation de débit.
Voilà. Douze heures de travail, six hypothèses, une plateforme à moitié démontée, et à l’arrivée : un tirage au sort perdu par le firewall. Ce n’était ni lui, ni le fabric, ni le design. C’était un port sur deux, et un compteur à dix.
Ce que je retiens
Un symptôme comparatif est une question, pas une conclusion. « Ça marche depuis A mais pas depuis B » invite à désigner un coupable. La bonne réaction est d’aller regarder ce que A et B émettent réellement sur le fil. Dans notre cas, la réponse dormait dans une capture qu’on avait déjà, depuis des heures.
L’absence de log n’est pas l’absence d’événement. On a écarté la bonne hypothèse parce que trois sources disaient « rien à signaler ». Elles disaient en réalité « je ne suis pas configuré pour te le dire ». Sur tout mécanisme de protection, la première question à poser est : est-ce que ce truc parle quand il agit ?
La configuration n’est pas l’état. display current-configuration montre l’intention. Il faut la commande qui montre l’application effective. Ce sont deux mondes différents, et on ne diagnostique que dans le second.
Un gabarit de durcissement s’applique dans un périmètre. Le document de conception prévoyait le storm control « sur ports accès ». Il s’est retrouvé sur les quarante-huit ports de chaque équipement, liens d’interconnexion compris. Sur les ports serveurs, aucun problème, ils n’émettent quasiment pas de broadcast. Sur un trunk legacy, il bloque en permanence.
Rendez le chemin déterministe avant de mesurer. Dans un fabric, ECMP, VTEP anycast, M-LAG et hashs d’agrégat font qu’une trame emprunte un chemin parmi plusieurs. Tant que ce choix vous échappe, vos tests ne sont pas reproductibles et ne prouvent rien. Couper pour réduire à un seul chemin est une méthode légitime, à condition de noter chaque coupure, de tout remettre avant de conclure, et de ne jamais comparer deux mesures prises sur deux topologies différentes. Encore faut-il pouvoir couper : on a eu ce luxe parce que rien ne tournait encore sur cette fabric. En service, il faut fixer le hash par le choix des adresses de test, ou capturer sur tous les chemins à la fois.
Et surtout : ne renvoyez pas la balle. La sortie la plus facile était de conclure que le réseau fonctionnait et que le problème appartenait à l’équipe firewall. C’était factuellement défendable, et complètement faux. Le prix de cette facilité, ç’aurait été quelques semaines de ping-pong entre équipes, avec la panne toujours là au bout.
Le correctif
Deux lignes, sur deux ports, sur deux équipements.
Mais avant les deux lignes, il y a eu douze heures, six pistes écartées, une topologie réduite à un chemin unique pour pouvoir mesurer quelque chose, une capture relue champ par champ, une bonne hypothèse abandonnée à tort, et une commande d’état qu’on aurait dû taper le matin même.
C’est souvent le ratio, dans ce métier. Deux lignes de configuration, une journée de vie.