PTP und die Suche nach der Mikrosekunde im Pakethaufen
Sollen die internen Uhren von Netzwerkteilnehmern präzise zu einer Master-Uhr synchronisiert werden, wird meist das Precision Time Protocol (kurz PTP) genutzt. Doch wie findet man die Ursache, wenn hierbei Synchroisierungsprobleme auftreten?
Genau vor einem solchen Problem stand ein Kunde, der uns um Hilfe zur Lösung des Problems bat.
Das Problem
In diesem konkreten Fall handelte es sich um Audiogeräte, die sich per PTP synchronisieren. Die Management-Software der Geräte meldete zyklisch Ausfälle der Geräte, da deren interne Uhr zu stark von der Master-Zeit abwich.
Nachdem die üblichen Verdächtigen, wie z. B. Paketverluste oder IGMP ausgeschlossen waren und der PTP-Traffic augenscheinlich vollständig und zuverlässig transportiert wurde, musste etwas tiefer gegraben werden.
Setzen wir eine korrekte PTP-Implementierung in den betroffenen Geräten voraus, könnte die Ursache vermutlich dennoch irgendwo auf dem Transportweg und somit im Netzwerk zu finden sein. Nun ist das Debugging und die Entwicklung hochgenauer Zeitgeber und -Nehmer nicht gerade unser „Daily Business“. Hierfür gibt es eigene Wirtschaftszweige mit Spezialisten, die über das nötige professionelle Besteck, wie z. B. Hardware-PTP-Tester, verfügen. Dennoch können wir versuchen, den Synchronisierungsablauf zu verstehen und diesen mit den vorhandenen Hilfsmitteln zu verifizieren.
Der Grundgedanke
Wenn die Ursache im Netzwerk liegt, muss diese anhand des Traffic an einem PTP-Slave ersichtlich sein. Hierzu wurde der Traffic zwischen Switch 1 und dem daran angeschlossenen PTP-Slave mitgeschnitten (vgl. Abb. 1). Die Aufzeichnung lief so lange, bis die Management-Software einen Fehler aufgrund angeblich fehlender Synchronität anzeigte und später wieder in den Normalzustand zurücksprang. Damit sollte der Norm-, der Fehler- und der erneute Normzustand gespeichert sein.
Info: Die Switche in diesem Aufbau verfügen über keine PTP-Funktionalität, es gibt also keine Boundary oder Transparent Clocks.
Anhand des Mitschnittes sollte es doch nun möglich sein, den PTP-Verkehr zu verifizieren. Hierzu ist es aber notwendig, einen PTP-Slave programmatisch zu simulieren.
PTP-Basics
PTP basiert auf einem Master-Slave-Konzept. Der Master – oder genauer die Grandmaster Clock – wird nach dem „Best Master“-Algorithmus ausgewählt. Vereinfacht ausgedrückt läuft der Algorithmus folgendermaßen ab:
Eine Uhr, die ans Netzwerk angeschlossen wird, wartet für einige Zeit auf eine Announce-Nachricht einer Grandmaster Clock. Eine solche Announce-Nachricht enthält sechs Eigenschaftsfelder, welche die Uhr beschreiben. Stellt die neu hinzugekommene Uhr fest, dass sich innerhalb einer bestimmten Zeit (Announce Timeout Interval) kein Grandmaster gemeldet hat oder dass sie selbst „bessere“ Eigenschaften besitzt als der aktuelle Grandmaster, so betrachtet sie sich selbst als eine solche und startet selbst mit dem Senden von Announce-Nachrichten mit ihren Eigenschaften und macht sich somit im Netzwerk als Grandmaster Clock bekannt. Sind ihre Eigenschaften jedoch „schlechter“, so akzeptiert sie den bereits vorhandenen Master.
Der Master sendet nun zyklisch Sync Messages, wobei er den Sendezeitpunkt t1 speichert. Der Slave speichert hingegen den Zeitpunkt des Erhalts t2 (vgl. Abb. 2).
Anschließend sendet der Master eine Follow-Up-Message, welche den zuvor gespeicherten Sendezeitpunkt t1 enthält. Zwischen dem Sende- und Empfangszeitpunkt der Sync-Nachricht ist jedoch auch Zeit vergangen – das Delay – welches bisher noch nicht bestimmt wurde.
Dennoch kann der Slave seine Uhr bereits jetzt annähernd genau korrigieren (mit Akzeptanz des unbekannten Fehlers „Delay“).
Delay = 0 (da unbekannt) Korrektur-Offset zur internen Uhr = t2 - t1 - Delay = t2 -t1 - 0
In einem weiteren Schritt sendet der Slave eine Delay-Request-Nachricht an den Master, wobei der Sendezeitpunkt t3 gespeichert wird. Der Master speichert den Empfangszeitpunkt t4 und teilt dem Slave diesen in der Delay-Response Nachricht mit.
Da PTP von einem symmetrischen Delay ausgeht – d. h. die Paketlaufzeit von Master zu Slave ist gleich groß wie von Slave zu Master – kann nun das Delay folgendermaßen bestimmt werden.
Delay = ( (t2 - t1) + (t4 - t3) ) / 2
Für die folgenden Sync/Follow-Up Sequenzen ist nun ebenfalls das Delay bekannt.
Info: Der hier beschriebene Ablauf trifft auf den PTP-Two-Step-Mode zu. Weiterhin gibt es den One-Step-Mode bei dem die Sendezeitpunkte von der Hardware on-the-fly in die Pakete eingefügt werden und somit ein Paket (oder Step) eingespart werden kann (gilt nicht für Delay-Berechnung).
Die Simulation
Um die Daten verifizieren zu können, muss der Traffic Paket für Paket analysiert und gleichzeitig ein PTP-Slave simuliert werden. Das heißt, jedes Paket soll auf PTP-Validität geprüft und dazu verwendet werden, den simulierten PTP-Slave auf die Master-Zeit zu synchronisieren.
Hinweis: PTP ermöglicht eine Sub-Mikrosekunden-Genauigkeit. Da hier der Zeitstempel der Mitschnittdatei ausgewertet wird, ist es absolut notwendig, dass dieser so genau wie möglich ist. Hier wurde ein Hardware-TAP zum Mitschneiden verwendet, dessen Zeitstempel auf 5 Nanosekunden genau sind. Ein Mitschnitt mit herkömmlichen Netzwerkkarten bzw. über Mirror-Ports verursacht zu große Zeitfehler und ist daher ungeeignet.
Grundlage für die Simulation ist ein Python-Programm, welches die erforderlichen Schritte durchführt.
Hinweis: Wie im Folgenden zu sehen ist, sind Timestamps in Pcap- bzw. PcapNG-Dateien im Unix-Format gespeichert. Da in diesem Beispiel mit einer Nanosekundenauflösung gerechnet wird, entstehen große Zahlen mit 9 Dezimalstellen. Hier muss besonders auf den Datentyp geachtet werden, da normale Float- oder Double-Werte hier feststellbare Ungenauigkeiten aufweisen. Für Python gilt hier: Keine Fließkomma- sondern Festkommaarithmetik anwenden (https://docs.python.org/2/library/decimal.html).
Schritt 1: PTP-Ablauf verifizieren
Hierbei werden die PTP-Pakete auf folgende Kriterien geprüft:
- Kommen Announce-Nachrichten vom erwarteten Master
- Sind die Sequence-Counter für die einzelnen Nachrichtentypen aufsteigend
Ergebnis:
Pkt. 1 PCAP:1573830018.812714556: OK Sequence: 13052 Master announced (desired Master) Pkt. 2 PCAP:1573830018.819787804: OK Sequence: 36416 Master Sync Message Pkt. 3 PCAP:1573830018.819832412: OK Sequence: 36416 Master Follow Up Message Pkt. 4 PCAP:1573830018.944830972: OK Sequence: 36417 Master Sync Message Pkt. 5 PCAP:1573830018.944881676: OK Sequence: 36417 Master Follow Up Message Pkt. 6 PCAP:1573830019.069850292: OK Sequence: 36418 Master Sync Message Pkt. 7 PCAP:1573830019.069893724: OK Sequence: 36418 Master Follow Up Message Pkt. 8 PCAP:1573830019.194890172: OK Sequence: 36419 Master Sync Message Pkt. 9 PCAP:1573830019.194941612: OK Sequence: 36419 Master Follow Up Message Pkt. 10 PCAP:1573830019.31990534: OK Sequence: 36420 Master Sync Message Pkt. 11 PCAP:1573830019.319951084: OK Sequence: 36420 Master Follow Up Message Pkt. 12 PCAP:1573830019.444940612: OK Sequence: 36421 Master Sync Message Pkt. 13 PCAP:1573830019.44499038: OK Sequence: 36421 Master Follow Up Message Pkt. 14 PCAP:1573830019.569967756: OK Sequence: 36422 Master Sync Message Pkt. 15 PCAP:1573830019.570016124: OK Sequence: 36422 Master Follow Up Message Pkt. 16 PCAP:1573830019.695021172: OK Sequence: 36423 Master Sync Message Pkt. 17 PCAP:1573830019.69507874: OK Sequence: 36423 Master Follow Up Message Pkt. 18 PCAP:1573830019.811931076: OK Sequence: 0 Slave Delay Request Pkt. 19 PCAP:1573830019.812083804: OK Sequence: 0 Slave Delay Response Pkt. 20 PCAP:1573830019.812734292: OK Sequence: 13053 Master announced (desired Master) Pkt. 21 PCAP:1573830019.820016276: OK Sequence: 36424 Master Sync Message Pkt. 22 PCAP:1573830019.82006038: OK Sequence: 36424 Master Follow Up Message [...]
Wie in diesem Ausschnitt zu sehen ist, hat jeder Nachrichtentyp seinen eigenen Sequence-Counter, der strikt aufsteigend ist. Hier geht ebenfalls hervor, dass
- der Master schon einige Zeit aktiv ist
- seine Grandmaster-Rolle akzeptiert wird
- der Slave seine erste Delay-Messung durchführt.
Im Praxisbeispiel konnten hier über den gesamten Mitschnitt keine Fehler festgestellt werden.
Schritt 2: PTP-Slave simulieren
Hierbei werden die Sync-, FollowUp-, DelayRequest- und DelayResponse-Pakete ausgewertet. Da der Traffic physisch direkt vor der Schnittstelle des Slaves mit sehr hoher Zeitpräzision aufgezeichnet wurde, nehmen wir an, dass der Paketzeitstempel dem Sende- bzw. Empfangszeitpunkt am Slave entspricht.
Die Aufgabe des Programms ist es nun, anhand der gespeicherten Pakete zu versuchen wie ein Slave zu handeln.
Bei der Programmierung besteht nur ein Problem: Da die Pakete nicht in Echtzeit sondern so schnell wie möglich durchlaufen werden, steht keine Echtzeituhr zur Verfügung, welche synchronisiert werden könnte. Es fehlt jede Relation zur Zeit. Wie soll also eine Uhr synchronisiert werden, wenn es keine gibt?
Zur Lösung des Problems wurde der Timestamp der Mitschnittdatei hergezogen. Da dies der einzig zuverlässige Zeitgeber in unserem Szenario ist, entspricht die interne „virtuelle Uhr“ des Programms einem errechneten Offset zur Zeit in der Mitschnittdatei. (Logisch? Ja, das muss man zwei mal lesen.)
In anderen Worten: Das Programm korrigiert keine Echtzeituhr. Stattdessen korrigiert das Programm einen Differenzwert zur Zeit in der Mitschnittdatei. Damit ist es möglich zu jedem Paketzeitpunkt eine passende PTP-Zeit am Slave (theoretische Slave-Echtzeituhr) zu errechnen.
Erste Sync-Sequenz:
Pkt. 2 PCAP:1573830018.819787804 PTP:1573830018.819787804: OK Sequence: 36416 Master Sync Message Pkt. 3 PCAP:1573830018.819832412 PTP:1573830018.819832412: OK Sequence: 36416 Master Follow Up Message -> Sync send time (Packet Master time) : 977914740.492970626 -> Current delay : 0 -> Current offset to absolute PCAP time : 0 -> New offset to absolute PCAP time : 595915278.326817178 -> Offset delta to previous : 595915278.326817155 -> Calculated PTP time (@Sync packet) : 977914740.492970626 -> Calculated PTP time (@now) : 977914740.493015234
Bei Paket Nr. 2 speichert das Programm den Zeitpunkt des Erhalts. Festzuhalten ist hier, dass die PCAP-Zeit (Zeitstempel Mitschnitt) der errechneten PTP-Zeit entspricht, da noch keinerlei Synchronisierung stattgefunden hat.
Bei Paket Nr. 3 ist die Zeitsituation die selbe, allerdings erhält das Programm hier den Sendezeitpunkt von Paket Nr. 2 (Sync send time).
Daraus kann nun das Offset zur PTP-Zeit errechnet und angeglichen werden, wohlgemerkt mit dem Fehler des noch nicht bekannten Delays.
Pkt. 4 PCAP:1573830018.944830972 PTP:977914740.618013794: OK Sequence: 36417 Master Sync Message Pkt. 5 PCAP:1573830018.944881676 PTP:977914740.618064498: OK Sequence: 36417 Master Follow Up Message -> Sync send time (Packet Master time) : 977914740.618014586 -> Current delay : 0 -> Current offset to absolute PCAP time : 595915278.326817178 -> New offset to absolute PCAP time : 595915278.326816386 -> Offset delta to previous : -0.000000792 -> Calculated PTP time (@Sync packet) : 977914740.618014586 -> Calculated PTP time (@now) : 977914740.618065290
Wie zu sehen ist, besteht bei Paket Nr. 4 nun schon eine PTP-Zeit, die weitaus genauer an der echten PTP-Zeit ist. Dennoch ist eine Abweichung von 792 Nanosekunden feststellbar, um die die virtuelle Uhr korrigiert wird.
Springen wir nun zu der ersten Delay-Berechnung.
Pkt. 18 PCAP:1573830019.811931076 PTP:977914741.485119378: OK Sequence: 0 Slave Delay Request Pkt. 19 PCAP:1573830019.812083804 PTP:977914741.485272106: OK Sequence: 0 Slave Delay Response -> Calculated delay Master -> Slave : -0.000002216 -> Calculated delay Slave -> Master : 0.000012312 -> Old delay : 0 -> New delay : 0.000005048 -> Delay delta : 0.000005048 Pkt. 20 PCAP:1573830019.812734292 PTP:977914741.485922594: OK Sequence: 13053 Master announced (desired Master) Pkt. 21 PCAP:1573830019.820016276 PTP:977914741.493204578: OK Sequence: 36424 Master Sync Message Pkt. 22 PCAP:1573830019.82006038 PTP:977914741.493248682: OK Sequence: 36424 Master Follow Up Message -> Sync send time (Packet Master time) : 977914741.493205322 -> Current delay : 0.000005048 -> Current offset to absolute PCAP time : 595915278.326811698 -> New offset to absolute PCAP time : 595915278.326805906 -> Offset delta to previous : -0.000005792 -> Calculated PTP time (@Sync packet) : 977914741.493205322 -> Calculated PTP time (@now) : 977914741.493249426
Hier wird ersichtlich, dass bei der ersten Delay-Berechnung eine recht große Differenz zwischen Master->Slave und Slave->Master Delay besteht. Diese rührt daher, da der Zeitstempel zu DelayRequest aufgrund des bisher fehlenden Delays noch recht ungenau ist. Da der Fehler zu diesem Zeitpunkt aber auch in umgekehrter Richtung besteht, hebt sich der Fehler auf.
Festzuhalten ist hier, dass in den darauffolgenden Sync-Sequenzen nun auch das Delay berücksichtigt wird und bei der ersten darauffolgenden Sequenz eine verhältnismäßig große Zeitkorrektur vorgenommen wird.
Pkt. 44 PCAP:1573830021.122773044 PTP:977914742.795974922: OK Sequence: 1 Slave Delay Request Pkt. 45 PCAP:1573830021.122909548 PTP:977914742.796111426: OK Sequence: 1 Slave Delay Response -> Calculated delay Master -> Slave : 0.000004280 -> Calculated delay Slave -> Master : 0.000006896 -> Old delay : 0.000005048 -> New delay : 0.000005588 -> Delay delta : 5.40E-7
Die Pakete 44/45 zeigen die nächste Delay-Messung nach der obigen (Pakete 18/19). Hier ist deutlich zu sehen, dass sich Master->Slave und Slave->Master Delay annähern, das berechnete Delay aber nur um 540 Nanosekunden von der ersten Berechnung abweicht.
Bewertung
Wie in den Listings oben zu sehen ist, errechnet das Programm nach jeder Sync- sowie Delay-Sequenz ein Delta zum jeweils vorherigen Wert. Diese Werte werden für eine spätere Auswertung von Bedeutung, da diese die Korrekturgrößen der Uhr wiederspiegeln. Starke Abweichungen, sprich starke Korrekturen, sprechen für eine Veränderung der Paketlaufzeit oder des Jitters.
Veränderungen der Paketlaufzeit können von PTP recht gut und schnell ausgeglichen werden, wohingegen starke Schwankungen der Laufzeit (Jitter) nicht sauber korrigiert werden können, da Delay-Berechnungen nicht zuverlässig anwendbar sind.
Hilfreich ist hier eine Visualisierung der Deltawerte:
Abb. 3 zeigt die Delta-Werte für Offset und Delay über den Verlauf der gesamten Aufzeichnung. Zu sehen ist hierbei, dass
- die virtuelle Uhr dauerhaft negativ korrigiert wird
- Ausreißer bei Offset und Delay vorhanden sind.
Im Diagramm ist ersichtlich, dass die rote Offset-Linie meist bei ca. -800 Nanosekunden liegt. Der Grund hierfür ist der natürliche Drift einer Uhr.
Stellt man zwei Armbanduhren auf die exakt gleiche Zeit und vergleicht sie eine Woche später, stellt man fest, dass die Uhren voneinander abweichen. Hier passiert genau das gleiche. Die Master-Uhr läuft langsamer als die virtuelle Uhr.
Die Sync-Sequenz findet hier alle 125 Millisekunden, also 8 mal pro Sekunde statt. Der Abweichungsfehler entspricht also ca. 6,4 ppm. Da übliche Oszillator-basierte Echtzeituhren einen Fehler von 3-20 ppm aufweisen, kann der Drift als Ursache der kontinuierlichen Korrektur angenommen werden.
Info: ppm = parts per million: Bezeichnet Teile unter 1 Million – In unserem Fall:
6,4 ppm ≈ 6,4 ns Fehler in 1.000.000 ns ≈ 6,4 ns Fehler in 1 ms ≈ 800 ns Fehler in 125 ms
Eine PTP-Implementierung korrigiert nicht nur laufend die Uhrzeit sondern zudem auch die Taktfrequenz des eigenen Zeitgebers. Hierdurch wird die Geschwindigkeit der eigenen Uhr an die des Masters angeglichen. Ohne diese Angleichung läuft die Slave-Uhr nach einer Sync-Sequenz sofort der Master-Uhr davon oder hinterher, wie hier zu sehen.
Korrigiert man nun das Offset um den Median aller Offset-Messwerte und gleicht somit den Drift aus, erhält man folgenden Graphen.
Hierbei wird ein Fehler in Kauf genommen, denn normalerweise würde sich die Frequenzkorrektur der Uhr auch auf die Berechnung des Delays auswirken. Dies wird hierbei vernachlässigt.
Info: Der Median ist der Zentralwert einer geordneten Liste. Dies ermöglicht die Feststellung eines Mittelwerts ohne das arithmetische Mittel (Durchschnitt) zu verwenden. Dieses wäre durch die Ausreißer zu stark beeinflusst.
Problemursache und -Lösung
Die Ursache des Problems lag hier an den verwendeten Switchen. Deren internes Paket-Forwarding verursachte den o. g. Jitter, welcher wiederum zur Folge hatte, dass das von den PTP-Slaves erwartete Master-Offset von <=100 ns nicht einzuhalten war.
Waren die Switche nun „schlecht“? Sie waren nicht schlecht, lediglich ungeeignet. Der oben beschriebene Aufbau wurde mit anderen Switchen im Labor nachgebaut. Wir konnten mit Switchen anderer Hersteller deutlich bessere Synchronisierungsprozesse nachstellen, auch über größere Layer-2-Strukturen hinweg. Das alleine sagt aber nicht zwangsläufig, dass ein Switch gut oder schlecht ist, lediglich dass er für diese Applikation ungeeignet ist.
Grundsätzlich sollte der Forwarding-Jitter nahezu 0 sein, wenn das selbe Paket immer und immer wieder verschickt wird, da die internen Abläufe im Switch (Policing, Queueing, Scheduling, Table-Lookups, etc.) stets die selben sind und annähernd gleich lange brauchen – vorausgesetzt der Switch befindet sich nicht unter Congestion.
Selbst in einem Überlast-Szenario sollte der Jitter minimal beeinflusst werden, wenn zusätzlich CoS/QoS angewandt wird und dafür gesorgt wird, dass die Pakete immer direkt an der besten Queue anstehen.
Bei diesem Switch war dies allerdings nicht der Fall. Die Vermutung liegt nahe, dass die CPU hier noch ihre Finger im Spiel hatte, womit das reine Switching im Chipsatz zeitlich beeinflusst wurde. Das ist aber nur eine Mutmaßung – der Hersteller wird seine Gründe für die verwendete Architektur haben.
Wie wurde das Problem schlussendlich gelöst? Es wurde tatsächlich ein Switchaustausch erforderlich. Um den Laufzeit-Jitter aus der Gleichung zu nehmen, wurden Switche mit Hardware-Unterstützung für PTP implementiert. Diese agieren als PTP-Transparent-Clock. Dem Switch ist es hiermit möglich die „Verweildauer“ (Residence Time) innerhalb des Switches zu messen und diese als Korrektur (Correction Factor) dem PTP-Paket anzufügen, so dass der Slave die Verweildauer kennt und in seiner Berechnung berücksichtigen kann.