Der Tag, an dem meine Topologie mich anlog
Eine technische Nachbetrachtung zum eigenen Produkt, und das Labor, das den Fehler fand
Michel Wijnberg
Ich habe Osprey gebaut, damit es mir die Wahrheit über Netzwerke sagt. Dann fand ich heraus, dass es mich angelogen hatte. Selbstbewusst, schriftlich, auf einem Canvas, den ich Leuten vorgeführt hatte.
Das hier ist, was passiert ist, wie ich es gefunden habe und was ich geändert habe. Es ist der nützlichste Beitrag dieser Reihe, denn alles andere, was ich über Ehrlichkeit geschrieben habe, ist wohlfeil, solange nicht die eigene Arbeit geprüft wird.
Was ich glaubte
Ospreys Pfad-Engine tat, was fast jedes Pfadwerkzeug tut. Zu einer Quelle und einem Ziel ließ sie einen area-bewussten Dijkstra von der Quelle aus über die OSPF-Topologie laufen und zeichnete die billigste Route.
Ich hatte gute Gründe, ihr zu vertrauen. Sie beachtete die Regeln aus RFC 2328, die mir wichtig waren: intra-area vor inter-area, inter-area über das Backbone, E1 vor E2, ECMP aufgezählt statt zusammengefasst. Sie hatte Tests. Sie passte zu meinem mentalen Modell des Labors. Wenn ich zwei Router anklickte, leuchtete ein vernünftiger Pfad auf.
Sie beschrieb außerdem bei jeder achten Inter-Area-Abfrage eine Route, die kein Paket je nehmen würde.
Wie ich es herausfand
Nicht durch einen Fehlerbericht. Durch eine Fleißarbeit.
Ich baute gerade ein Validierungsgerüst für etwas ganz anderes und wollte einen
Datensatz mit gesicherter Wahrheit, gegen den ich prüfen konnte. Das Labor hat
64 Router in AS 200, alle über SSH erreichbar, alle bereit, ihre eigene
Routing-Tabelle auszugeben. Also tat ich das Naheliegende und Stumpfe: Ich
meldete mich auf allen 64 an und zog von jedem show ip route ospf.
Dann verglich ich, statt es zu überfliegen, die echte Tabelle jedes Routers mit dem, was Ospreys Pfad-Engine als Pfad behauptete, und zwar für alle 4.032 geordneten Quelle/Ziel-Paare.
360 davon widersprachen.
Nicht “die Kosten stimmen leicht nicht”. Der gezeichnete Pfad enthielt einen Router, den das Paket nachweislich nie besucht.
Der kanonische Fall
Hier ist einer, jede Zeile am Gerät gemessen statt modelliert:
| Router | Sagt über das Ziel | Leitet weiter an |
|---|---|---|
dfw1-gw2 | metric 20031, type inter area | dfw1-cr1 |
dfw1-cr1 | metric 30001, type intra area | ord1-cr1 ← nicht dfw2-cr1 |
ord1-cr1 | metric 20001, type intra area | jfk1-cr1 |
jfk1-cr1 | metric 10001, type intra area | lhr1-cr1 |
lhr1-cr1 | metric 1, connected | zugestellt |
Osprey zeichnete:
dfw1-gw2 → dfw1-cr1 → dfw2-cr1 → iad2-cr1 → ams1-cr1 → lhr1-cr1
Das Paket erreicht dfw2-cr1 nie. Ospreys Pfad war billiger. Er war nur kein
Pfad, den irgendein Router bauen würde.
Zwei voneinander unabhängige Regeln aus RFC 2328 sorgen dafür, dass dfw1-cr1
den billigen Korridor ablehnt, und ich hatte keine von beiden auf der Ebene
eines einzelnen Zwischenrouters umgesetzt:
§16 Schritt (3) / §16.2 ¶1: das Backbone-Tor. Ein Router, der an mehreren
Areas hängt, betrachtet ausschließlich Summary-LSAs aus dem Backbone. Das
billigere Summary von dfw2-cr1 mit Kosten 20011 liegt tatsächlich in der
Datenbank von dfw1-cr1 für Area 4.6.23.0 (ich habe das mit
show ip ospf database summary bestätigt, weil ich es selbst nicht glaubte),
aber es ist kein Kandidat, weil dfw1-cr1 ein ABR ist und dieses Summary nicht
über Area 0 eingetroffen ist. RFC 3509 §2.1/§2.2, das ABR-Verhalten von Cisco,
das auf diesen Geräten tatsächlich läuft, knüpft das an eine aktive
Backbone-Verbindung, die dfw1-cr1 hat.
§16.2 Schritt (6): intra schlägt inter, Punkt. “If the paths present in the
table are intra-area paths, do nothing with the LSA.” dfw1-cr1 hält bereits
einen Intra-Area-Pfad mit Metrik 30001. Das Summary mit 20011 verliert dagegen.
Die Metrik kommt im Vergleich gar nicht vor. Das ist eine Regel über den
Pfadtyp.
Jede der beiden Regeln allein erledigt den Korridor. Meine Engine konnte keine
von beiden sehen, denn sie fragte dfw1-cr1 überhaupt nichts. Sie fragte die
Weltsicht des Quellrouters ab und zog dann eine Linie durch acht andere Router,
als würden die sie teilen.
Die Form des Fehlers
Das lohnt eine genaue Benennung, denn das konkrete OSPF-Detail ist weniger interessant als die Kategorie.
Ich hatte einen Pfad modelliert, während das Netzwerk eine Folge unabhängiger Entscheidungen umsetzt. Diese beiden Dinge fallen genau so lange zusammen, wie jeder Router auf dem Weg dieselbe Entscheidungsregel über denselben Eingangsdaten anwendet. Innerhalb einer einzelnen OSPF-Area tun sie das (dieselbe LSDB, derselbe Algorithmus, dasselbe Ergebnis), und deshalb lautete die Fehlerzahl für Intra-Area-Paare:
0 von 1.193.
Sobald ein ABR beteiligt ist, sind die Eingangsdaten nicht mehr geteilt: Ein ABR sieht mit Absicht auf eine andere Menge von LSAs als die Router um ihn herum. Mein Modell hatte keinen Ort für diese Tatsache, denn mein Modell hatte einen Graphen und einen Dijkstra, nicht vierundsechzig Router mit je einer eigenen Meinung.
| Paare | Mit einem Hop, den kein Router nimmt | |
|---|---|---|
| Intra-Area | 1.193 | 0 |
| Inter-Area | 2.839 | 360 (12,68 %) |
| Alle | 4.032 | 360 (8,93 %) |
Die Korrektur, und woher ich weiß, dass sie wirkt
Die Engine läuft die Kette inzwischen ab. Bei jedem Hop wertet sie die eigene installierte Route dieses Routers zur Zieladresse aus, mit dessen eigener LSDB, dessen Area-Mitgliedschaften, dessen ABR-Status und dessen Mischung administrativer Distanzen, folgt dann dem Next Hop, den er tatsächlich wählt, und wiederholt das.
Dann ließ ich dieselben 4.032 Paare erneut gegen dieselbe gesicherte Wahrheit laufen:
| Prüfung | Ergebnis |
|---|---|
| Eigene Metrik des Routers exakt reproduziert | 4.032 / 4.032 |
| Pfadtyp (intra / inter / extern) exakt | 4.032 / 4.032 |
| Vollständige installierte ECMP-Next-Hop-Menge identisch | 4.032 / 4.032 |
Kette endet mit delivered am angefragten Ziel | 4.032 / 4.032 |
Nicht “verbessert”. Bei jedem Paar identisch, einschließlich der vollständigen Menge gleichwertiger Next Hops statt eines willkürlich gewählten Mitglieds daraus.
Das ist dieselbe Abfrage, die früher einen Hop erfand, und sie zeigt jetzt jeden
Router, der tatsächlich eine Entscheidung trifft, die Area, in der er sie trifft,
und die physischen Ports auf beiden Seiten. Die Summe, 30061, ist die selbst
installierte Metrik der Quelle: buchstäblich die Zahl, die show ip route auf
lax1-gw1 ausgibt.
Und dort, wo die Kette nicht berechenbar ist (OSPFv3, IS-IS, EIGRP, ein
unvollständiger Bereich, eine Area mit einem Virtual Link), fällt die Engine
nicht stillschweigend auf das alte Bild zurück. Sie liefert die von der Quelle
gewurzelte Sicht mit einem model_note, der den Grund benennt und als eigener
Erklärungsschritt dargestellt wird. Diese Verweigerung ist das Thema von
Beitrag vier, und es gibt sie wegen dieses
Fehlers.
Ein kleinerer, zum Ausgleich
Nicht jede selbst zugefügte Wunde ist ein Algorithmus. Hier eine mit zwei Zeilen, die mich einen Nachmittag gekostet hat.
Ospreys GRE-Recorder baut eine echte OSPF-Nachbarschaft auf, muss also ein Router-LSA erzeugen. Anfangs kündigte er das innere Subnetz des Tunnels als Stub-Netzwerk an: sah richtig aus, war RFC-konform und völlig vernünftig.
Daraufhin installierte jeder Router in der Area eine Route in Richtung des Tunnel-Präfixes. Diese Route überstimmte den Underlay-Pfad, auf dem der GRE-Tunnel selbst ritt. Der äußere Transport brach. Die Nachbarschaft fiel weg. Der Recorder verband sich neu, erzeugte das LSA erneut und tat es wieder.
Die Korrektur steht jetzt im Codekommentar, damit sie niemand aus Versehen entfernt:
// Only the P2P link is advertised. NO stub network for the tunnel
// inner subnet. Advertising the tunnel subnet (even at max cost) causes SPF
// on other routers to install a route for the tunnel prefix, which can
// disrupt the GRE outer path and kill the adjacency.
Ein Monitoring-Werkzeug, das genau das lahmlegt, was es überwacht, ist eine ganz eigene Art von peinlich, und auf einem Diagramm hätte ich es nie gefunden.
Wofür das Labor wirklich da ist
Früher habe ich das Labor als den Ort beschrieben, an dem ich Osprey vorführe. Das war die falsche Stellenbeschreibung, und sie richtigzustellen hat verändert, wie ich baue.
Das Labor ist nicht dazu da, Osprey gut aussehen zu lassen. Es ist dazu da, Osprey zu widerlegen.
Das heißt, das Labor ist genau so viel wert, wie es mir widersprechen kann: Es ist deshalb mit Absicht unbequem. Drei Carrier, die kein Protokoll teilen. Fünfzehn OSPF-Areas, wo drei sich besser vorführen ließen. Eine IS-IS-Domäne mit siebzehn Level-1-Areas. EIGRP, das überhaupt keine Link-State-Datenbank hat und jede Annahme des OSPF-Codes bricht. Dual-Stack-Links, deren v4- und v6-Kosten bei allen 124 um den Faktor 10 auseinanderliegen.
Nichts davon ergibt einen schöneren Screenshot. Alles davon hat etwas gefunden.
Die 360 erfundenen Korridore wurden nicht durch sorgfältiges Lesen gefunden und auch nicht durch einen Test, den ich klug genug war vorher zu schreiben. Sie wurden gefunden, weil ich 64 echte Router hatte, die man fragen konnte, was sie tatsächlich denken, und weil ich sie endlich alle auf einmal fragte, statt den zwei oder drei zu vertrauen, die ich stichprobenartig geprüft hatte.
Was die naheliegende Frage aufwirft, und die ist das Thema des nächsten Beitrags: Wenn das Netzwerk das Einzige ist, was Ihnen sagen kann, ob Ihr Modell stimmt, wie machen Sie daraus eine Testsuite?
Als Nächstes: Die Unit-Tests eines Netzwerkingenieurs.