Zum Hauptinhalt springen

3. Netzwerkzeitprotokoll

Dieser Abschnitt besteht aus einer formalen Definition des Netzwerkzeitprotokolls, einschließlich seiner Datenformate, Entitäten, Zustandsvariablen, Ereignisse und Ereignisverarbeitungsverfahren. Die Spezifikation basiert auf dem in Abbildung 1 dargestellten Implementierungsmodell, aber es ist nicht beabsichtigt, dass dies das einzige Modell ist, auf dem eine Spezifikation basieren kann. Insbesondere soll die Spezifikation die intrinsischen Operationen von NTP veranschaulichen und klären sowie als Grundlage für eine strengere, umfassendere und überprüfbarere Spezifikation dienen.

3.1. Datenformate

Alle hier ausgedrückten oder impliziten mathematischen Operationen sind in Zweier-Komplement-Festkomma-Arithmetik. Daten werden als ganze Zahlen oder Festkomma-Größen angegeben, wobei Bits in Big-Endian-Weise ab Null beginnend von der linken oder höchstwertigen Position nummeriert sind. Da verschiedene Implementierungen extern abgeleitete Größen für den internen Gebrauch skalieren können, sind weder die Präzision noch die Dezimalpunktplatzierung für Festkomma-Größen spezifiziert. Sofern nicht anders angegeben, sind alle Größen vorzeichenlos und können die volle Feldbreite mit einer impliziten Null vor Bit Null belegen. Hardware- und Softwarepakete, die für die Arbeit mit vorzeichenbehafteten Größen entwickelt wurden, liefern daher überraschende Ergebnisse, wenn das höchstwertige (Vorzeichen-) Bit gesetzt ist. Es wird vorgeschlagen, dass extern abgeleitete, vorzeichenlose Festkomma-Größen wie Zeitstempel für den internen Gebrauch um ein Bit nach rechts verschoben werden, da die durch die volle Feldbreite dargestellte Präzision selten gerechtfertigt ist.

Da NTP-Zeitstempel wertvolle Daten sind und tatsächlich das Hauptprodukt des Protokolls darstellen, wurde ein spezielles Zeitstempelformat etabliert. NTP-Zeitstempel werden als 64-Bit-vorzeichenlose Festkommazahl dargestellt, in Sekunden relativ zu 0h am 1. Januar 1900. Der ganzzahlige Teil befindet sich in den ersten 32 Bits und der Bruchteil in den letzten 32 Bits. Dieses Format ermöglicht bequeme Mehrfachpräzisions-Arithmetik und Konvertierung in Zeitprotokoll-Darstellung (Sekunden), kompliziert jedoch die Konvertierung in ICMP-Zeitstempelnachrichten-Darstellung (Millisekunden). Die Präzision dieser Darstellung beträgt etwa 200 Pikosekunden, was selbst für die exotischsten Anforderungen ausreichend sein sollte.

Zeitstempel werden bestimmt, indem der aktuelle Wert der lokalen Uhr zu einem Zeitstempel kopiert wird, wenn ein bedeutendes Ereignis, wie die Ankunft einer Nachricht, auftritt. Um die höchste Genauigkeit aufrechtzuerhalten, ist es wichtig, dass dies so nah wie möglich am Hardware- oder Softwaretreiber erfolgt, der mit dem Ereignis verbunden ist. Insbesondere sollten Abfahrtszeitstempel für jede Neuübertragung auf Verbindungsebene neu bestimmt werden. In einigen Fällen ist ein bestimmter Zeitstempel möglicherweise nicht verfügbar, z. B. wenn der Host neu gestartet wird oder das Protokoll zum ersten Mal gestartet wird. In diesen Fällen wird das 64-Bit-Feld auf Null gesetzt, was anzeigt, dass der Wert ungültig oder undefiniert ist.

Beachten Sie, dass seit einiger Zeit im Jahr 1968 das höchstwertige Bit (Bit 0 des ganzzahligen Teils) gesetzt wurde und dass das 64-Bit-Feld irgendwann im Jahr 2036 überlaufen wird. Sollte NTP im Jahr 2036 noch verwendet werden, werden externe Mittel erforderlich sein, um die Zeit relativ zu 1900 und die Zeit relativ zu 2036 (und andere Vielfache von 136 Jahren) zu qualifizieren. Mit Zeitstempel versehene Daten, die eine solche Qualifikation erfordern, werden so wertvoll sein, dass geeignete Mittel leicht verfügbar sein sollten. Es wird ein 200-Pikosekunden-Intervall existieren, das hiernach ignoriert wird, alle 136 Jahre, wenn das 64-Bit-Feld Null ist und daher als ungültig betrachtet wird.

3.2. Zustandsvariablen und Parameter

Im Folgenden finden Sie eine Zusammenfassung der verschiedenen Zustandsvariablen und Parameter, die vom Protokoll verwendet werden. Sie sind in Klassen von Systemvariablen unterteilt, die sich auf die Betriebssystemumgebung und den lokalen Uhrenmechanismus beziehen; Peer-Variablen, die den Zustand der protokollspezifischen Maschine für jeden Peer darstellen; Paketvariablen, die den Inhalt der NTP-Nachricht darstellen; und Parameter, die feste Konfigurationskonstanten für alle Implementierungen der aktuellen Version darstellen. Für jede Klasse folgt der Beschreibung der Variablen ihr Name und das Verfahren oder der Wert, der sie steuert. Beachten Sie, dass Variablen in Kleinbuchstaben sind, während Parameter in Großbuchstaben sind. Zusätzliche Details zu Formaten und Verwendung werden in späteren Abschnitten und Anhängen präsentiert.

3.2.1. Gemeinsame Variablen

Die folgenden Variablen sind zwei oder mehreren der System-, Peer- und Paketklassen gemeinsam. Zusätzliche Variablen sind spezifisch für den optionalen Authentifizierungsmechanismus, der in Anhang C beschrieben wird. Wenn es notwendig ist, zwischen gemeinsamen Variablen desselben Namens zu unterscheiden, wird der Variablenbezeichner verwendet.

Peer-Adresse (Peer Address,peer.peeraddr, pkt.peeraddr), Peer-Port (Peer Port,peer.peerport, pkt.peerport): Dies sind die 32-Bit-Internetadresse und die 16-Bit-Portnummer des Peers.

Host-Adresse (Host Address,peer.hostaddr, pkt.hostaddr), Host-Port (Host Port,peer.hostport, pkt.hostport): Dies sind die 32-Bit-Internetadresse und die 16-Bit-Portnummer des Hosts. Sie sind in den Zustandsvariablen enthalten, um Multi-Homing zu unterstützen.

Schaltsekundenindikator (Leap Indicator,sys.leap, peer.leap, pkt.leap): Dies ist ein Zwei-Bit-Code, der vor einer bevorstehenden Schaltsekunde warnt, die in die NTP-Zeitskala eingefügt werden soll. Die Bits werden vor 23:59 am Tag der Einfügung gesetzt und nach 00:00 am folgenden Tag zurückgesetzt. Dies führt dazu, dass die Anzahl der Sekunden (Rollover-Intervall) am Tag der Einfügung um eins erhöht oder verringert wird. Im Falle primärer Server werden die Bits durch Eingreifen des Operators gesetzt, während im Falle sekundärer Server die Bits durch das Protokoll gesetzt werden. Die zwei Bits, Bit 0 und Bit 1, sind wie folgt kodiert:

WertBedeutung
00keine Warnung
01letzte Minute hat 61 Sekunden
10letzte Minute hat 59 Sekunden
11Alarmzustand (Uhr nicht synchronisiert)

In allen Fällen außer dem Alarmzustand (11₂) macht NTP selbst nichts mit diesen Bits, außer sie an die Zeitkonvertierungsroutinen weiterzugeben, die nicht Teil von NTP sind. Der Alarmzustand tritt auf, wenn aus irgendeinem Grund die lokale Uhr nicht synchronisiert ist, z. B. beim ersten Hochfahren oder nach einer längeren Zeit, in der keine primäre Referenzquelle verfügbar ist.

Modus (Mode,peer.mode, pkt.mode): Dies ist eine ganze Zahl, die den Assoziationsmodus angibt, mit wie folgt kodierten Werten:

WertModus
0nicht spezifiziert
1symmetrisch aktiv
2symmetrisch passiv
3Client
4Server
5Broadcast
6für NTP-Steuerungsnachrichten reserviert
7für private Verwendung reserviert

Schicht (Stratum,sys.stratum, peer.stratum, pkt.stratum): Dies ist eine ganze Zahl, die die Schicht der lokalen Uhr angibt, mit wie folgt definierten Werten:

WertBedeutung
0nicht spezifiziert
1primäre Referenz (z.B. kalibrierte Atomuhr, Funkuhr)
2-255sekundäre Referenz (über NTP)

Zu Vergleichszwecken wird ein Wert von Null als größer als jeder andere Wert betrachtet. Beachten Sie, dass der Maximalwert der als Paketvariable kodierten ganzen Zahl durch den Parameter NTP.MAXSTRATUM begrenzt ist.

3.2.2. Systemvariablen

Tabelle 1 zeigt den vollständigen Satz von Systemvariablen. Zusätzlich zu den zuvor beschriebenen gemeinsamen Variablen verwendet das Betriebssystem die folgenden Variablen, um die lokale Uhr zu synchronisieren.

Lokale Uhr (Local Clock,sys.clock): Dies ist die aktuelle lokale Zeit im Zeitstempelformat. Die lokale Zeit wird von der Hardwareuhr der jeweiligen Maschine abgeleitet und inkrementiert in Intervallen, die vom verwendeten Design abhängen. Ein geeignetes Design, einschließlich Anpassungs- und Schiefkompensationsmechanismen, wird in Abschnitt 5 beschrieben.

Uhrenquelle (Clock Source,sys.peer): Dies ist ein Selektor, der die aktuelle Synchronisationsquelle identifiziert. Normalerweise ist dies ein Zeiger auf eine Struktur, die die Peer-Variablen enthält. Der spezielle Wert NULL zeigt an, dass derzeit keine gültige Synchronisationsquelle vorhanden ist.

3.2.3. Peer-Variablen

Tabelle 2 zeigt den vollständigen Satz von Peer-Variablen. Zusätzlich zu den zuvor beschriebenen gemeinsamen Variablen verwenden die Peer-Verwaltungs- und Messfunktionen die folgenden Variablen.

Konfigurationsbit (Configured Bit,peer.config): Dies ist ein Bit, das anzeigt, dass die Assoziation aus Konfigurationsinformationen erstellt wurde und nicht demobilisiert werden sollte, wenn der Peer unerreichbar wird.

Aktualisierungszeitstempel (Update Timestamp,peer.update): Dies ist die lokale Zeit im Zeitstempelformat, wenn die neueste NTP-Nachricht empfangen wurde. Sie wird zur Berechnung der Schiefedispersion verwendet.

Erreichbarkeitsregister (Reachability Register,peer.reach): Dies ist ein Schieberegister mit NTP.WINDOW Bits, das verwendet wird, um den Erreichbarkeitsstatus des Peers zu bestimmen, wobei Bits vom am wenigsten signifikanten (rechten) Ende eintreten. Ein Peer wird als erreichbar betrachtet, wenn mindestens ein Bit in diesem Register auf eins gesetzt ist.

Peer-Timer (Peer Timer,peer.timer): Dies ist ein ganzzahliger Zähler, der verwendet wird, um das Intervall zwischen übertragenen NTP-Nachrichten zu steuern. Sobald er auf einen Wert ungleich Null gesetzt ist, dekrementiert der Zähler in Ein-Sekunden-Intervallen, bis er Null erreicht, wobei zu diesem Zeitpunkt das Übertragungsverfahren aufgerufen wird. Beachten Sie, dass der Betrieb dieses Timers unabhängig von lokalen Uhrenaktualisierungen ist, was impliziert, dass das Zeitmesssystem und die Intervall-Timer-Systemarchitektur unabhängig voneinander sein müssen.

3.2.4. Paketvariablen

Tabelle 3 zeigt den vollständigen Satz von Paketvariablen. Zusätzlich zu den zuvor beschriebenen gemeinsamen Variablen sind die folgenden Variablen definiert.

Versionsnummer (Version Number,pkt.version): Dies ist eine ganze Zahl, die die Versionsnummer des Senders angibt. NTP-Nachrichten werden immer mit der aktuellen Versionsnummer NTP.VERSION gesendet und werden immer akzeptiert, wenn die Versionsnummer mit NTP.VERSION übereinstimmt. Ausnahmen können von Fall zu Fall empfohlen werden, wenn die Versionsnummer geändert wird. Spezifische Richtlinien für die Interoperation zwischen dieser Version und früheren Versionen von NTP sind in Anhang D zusammengefasst.

3.2.5. Uhrenfilter-Variablen

Wenn die in Abschnitt 4 vorgeschlagenen Filter- und Auswahlalgorithmen verwendet werden, sind zusätzlich zu den zuvor beschriebenen Variablen die folgenden Zustandsvariablen definiert.

Filterregister (Filter Register,peer.filter): Dies ist ein Schieberegister mit NTP.SHIFT Stufen, wobei jede Stufe ein Tripel speichert, das aus der gemessenen Verzögerung, dem gemessenen Versatz und der berechneten Streuung besteht, die einer einzelnen Beobachtung zugeordnet sind. Diese Tripel treten vom höchstwertigen (linken) Ende ein und werden zum niedrigstwertigen (rechten) Ende verschoben und schließlich verworfen, wenn neue Beobachtungen eintreffen.

Zähler gültiger Daten (Valid Data Counter,peer.valid): Dies ist ein ganzzahliger Zähler, der die im Filterregister verbleibenden gültigen Stichproben angibt. Er wird verwendet, um den Erreichbarkeitszustand zu bestimmen und wann das Abfrageintervall erhöht oder verringert werden sollte.

Versatz (Offset,peer.offset): Dies ist eine vorzeichenbehaftete Festkommazahl, die den Versatz der Peer-Uhr relativ zur lokalen Uhr in Sekunden angibt.

Verzögerung (Delay,peer.delay): Dies ist eine vorzeichenbehaftete Festkommazahl, die die Umlaufverzögerung der Peer-Uhr relativ zur lokalen Uhr über den Netzwerkpfad zwischen ihnen in Sekunden angibt. Beachten Sie, dass diese Variable je nach Uhrengenauigkeit und Akkumulation von Driftfehlern sowohl positive als auch negative Werte annehmen kann.

Streuung (Dispersion,peer.dispersion): Dies ist eine vorzeichenbehaftete Festkommazahl, die den maximalen Fehler der Peer-Uhr relativ zur lokalen Uhr über den Netzwerkpfad zwischen ihnen in Sekunden angibt. Nur positive Werte größer als null sind möglich.

3.2.6. Authentifizierungsvariablen

Wenn der in Anhang C vorgeschlagene Authentifizierungsmechanismus verwendet wird, sind zusätzlich zu den zuvor beschriebenen Variablen die folgenden Zustandsvariablen definiert. Diese Variablen werden nur verwendet, wenn der in Anhang C beschriebene optionale Authentifizierungsmechanismus implementiert ist.

Authentifizierung-aktiviert-Bit (Authentication Enabled Bit,peer.authenable): Dies ist ein Bit, das angibt, dass die Assoziation im authentifizierten Modus betrieben werden soll.

Authentifiziert-Bit (Authenticated Bit,peer.authentic): Dies ist ein Bit, das angibt, dass die letzte vom Peer empfangene Nachricht korrekt authentifiziert wurde.

Schlüsselkennung (Key Identifier,peer.hostkeyid,peer.peerkeyid,pkt.keyid): Dies ist eine ganze Zahl, die den kryptografischen Schlüssel identifiziert, der zur Erzeugung des Nachrichtenauthentifizierungscodes verwendet wird.

Kryptografische Schlüssel (Cryptographic Keys,sys.key): Dies ist ein Satz von 64-Bit-DES-Schlüsseln. Jeder Schlüssel wird wie in den Berkeley-Unix-Distributionen konstruiert und besteht aus acht Oktetten, wobei die sieben niederwertigen Bits jedes Oktetts den DES-Bits 1-7 entsprechen und das höchstwertige Bit dem DES-Paritätsbit 8 (ungerade Parität) entspricht.

Krypto-Prüfsumme (Crypto-Checksum,pkt.check): Dies ist eine kryptografische Prüfsumme, die vom Verschlüsselungsverfahren berechnet wird.

3.2.7. Parameter

Tabelle 4 zeigt die Parameter, die für alle im Internet-System betriebenen Implementierungen angenommen werden. Es ist notwendig, sich auf die Werte dieser Parameter zu einigen, um unnötigen Netzwerk-Overhead zu vermeiden und stabile Peer-Assoziationen zu gewährleisten. Die folgenden Parameter werden als fest angenommen und gelten für alle Assoziationen.

Versionsnummer (NTP.VERSION): Dies ist die aktuelle NTP-Versionsnummer (3).

NTP-Port (NTP.PORT): Dies ist die Portnummer (123), die NTP von der Internet Assigned Numbers Authority zugewiesen wurde.

Maximales Stratum (NTP.MAXSTRATUM): Dies ist der maximale Stratum-Wert, der als Paketvariable kodiert werden kann, auch interpretiert als „unendlich" oder unerreichbar durch den Subnetz-Routing-Algorithmus.

Maximales Uhrenalter (NTP.MAXAGE): Dies ist das maximale Intervall in Sekunden, in dem eine Referenzuhr nach ihrer letzten Aktualisierung als gültig betrachtet wird.

Maximale Drift (NTP.MAXSKEW): Dies ist der maximale Versatzfehler aufgrund der Drift der lokalen Uhr über das durch NTP.MAXAGE bestimmte Intervall in Sekunden. Das Verhältnis φ = NTP.MAXSKEW / NTP.MAXAGE wird als die maximal mögliche Driftrate aufgrund aller Ursachen interpretiert.

Maximale Distanz (NTP.MAXDISTANCE): Wenn der in Abschnitt 4 vorgeschlagene Auswahlalgorithmus verwendet wird, ist dies die maximale Synchronisationsdistanz für zur Synchronisation akzeptable Peers.

Minimales Abfrageintervall (NTP.MINPOLL): Dies ist das minimale Abfrageintervall, das von jedem Peer des Internet-Systems zugelassen wird, in Sekunden als Zweierpotenz.

Maximales Abfrageintervall (NTP.MAXPOLL): Dies ist das maximale Abfrageintervall, das von jedem Peer des Internet-Systems zugelassen wird, in Sekunden als Zweierpotenz.

Minimale Anzahl ausgewählter Uhren (NTP.MINCLOCK): Wenn der in Abschnitt 4 vorgeschlagene Auswahlalgorithmus verwendet wird, ist dies die minimale Anzahl von zur Synchronisation akzeptablen Peers.

Maximale Anzahl ausgewählter Uhren (NTP.MAXCLOCK): Wenn der in Abschnitt 4 vorgeschlagene Auswahlalgorithmus verwendet wird, ist dies die maximale Anzahl von für die Auswahl berücksichtigten Peers.

Minimale Streuung (NTP.MINDISPERSE): Wenn der in Abschnitt 4 vorgeschlagene Filteralgorithmus verwendet wird, ist dies das minimale Streuungsinkrement für jede Stratum-Ebene in Sekunden.

Maximale Streuung (NTP.MAXDISPERSE): Wenn der in Abschnitt 4 vorgeschlagene Filteralgorithmus verwendet wird, ist dies die maximale Peer-Streuung und die für fehlende Daten angenommene Streuung in Sekunden.

Größe des Erreichbarkeitsregisters (NTP.WINDOW): Dies ist die Größe des Erreichbarkeitsregisters (peer.reach) in Bits.

Filtergröße (NTP.SHIFT): Wenn der in Abschnitt 4 vorgeschlagene Filteralgorithmus verwendet wird, ist dies die Größe des Uhrenfilter-Schieberegisters (peer.filter) in Stufen.

Filtergewicht (NTP.FILTER): Wenn der in Abschnitt 4 vorgeschlagene Filteralgorithmus verwendet wird, ist dies das Gewicht, das zur Berechnung der Filterstreuung verwendet wird.

Auswahlgewicht (NTP.SELECT): Wenn der in Abschnitt 4 vorgeschlagene Auswahlalgorithmus verwendet wird, ist dies das Gewicht, das zur Berechnung der Auswahlstreuung verwendet wird.

3.3. Betriebsmodi

Außer im Broadcast-Modus wird eine NTP-Assoziation gebildet, wenn zwei Peers Nachrichten austauschen und einer oder beide eine Instanziierung der Protokollmaschine, genannt Assoziation, erstellen und pflegen. Die Assoziation kann in einem von fünf Modi arbeiten, wie durch die Host-Modus-Variable (peer.mode) angezeigt: symmetrisch aktiv, symmetrisch passiv, Client, Server und Broadcast, die wie folgt definiert sind:

Symmetrisch Aktiv (Symmetric Active,1): Ein Host, der in diesem Modus arbeitet, sendet periodische Nachrichten unabhängig vom Erreichbarkeitszustand oder der Schicht seines Peers. Durch den Betrieb in diesem Modus kündigt der Host seine Bereitschaft an, zu synchronisieren und vom Peer synchronisiert zu werden.

Symmetrisch Passiv (Symmetric Passive,2): Dieser Assoziationstyp wird normalerweise beim Eintreffen einer Nachricht von einem Peer erstellt, der im symmetrisch aktiven Modus arbeitet, und besteht nur so lange, wie der Peer erreichbar ist und auf einer Schicht arbeitet, die kleiner oder gleich dem Host ist; andernfalls wird die Assoziation aufgelöst. Die Assoziation wird jedoch immer bestehen bleiben, bis mindestens eine Nachricht als Antwort gesendet wurde. Durch den Betrieb in diesem Modus kündigt der Host seine Bereitschaft an, zu synchronisieren und vom Peer synchronisiert zu werden.

Client (Client,3): Ein Host, der in diesem Modus arbeitet, sendet periodische Nachrichten unabhängig vom Erreichbarkeitszustand oder der Schicht seines Peers. Durch den Betrieb in diesem Modus kündigt der Host, normalerweise eine LAN-Workstation, seine Bereitschaft an, vom Peer synchronisiert zu werden, aber nicht den Peer zu synchronisieren.

Server (Server,4): Dieser Assoziationstyp wird normalerweise beim Eintreffen einer Client-Anforderungsnachricht erstellt und existiert nur, um auf diese Anforderung zu antworten, wonach die Assoziation aufgelöst wird. Durch den Betrieb in diesem Modus kündigt der Host, normalerweise ein LAN-Zeitserver, seine Bereitschaft an, zu synchronisieren, aber nicht vom Peer synchronisiert zu werden.

Broadcast (Broadcast,5): Ein Host, der in diesem Modus arbeitet, sendet periodische Nachrichten unabhängig vom Erreichbarkeitszustand oder der Schicht der Peers. Durch den Betrieb in diesem Modus kündigt der Host, normalerweise ein LAN-Zeitserver, der auf einem Hochgeschwindigkeits-Broadcast-Medium arbeitet, seine Bereitschaft an, alle Peers zu synchronisieren, aber nicht von einem von ihnen synchronisiert zu werden.

Ein im Client-Modus betriebener Host sendet gelegentlich eine NTP-Nachricht an einen im Server-Modus betriebenen Host, möglicherweise unmittelbar nach dem Neustart und danach in periodischen Abständen. Der Server antwortet einfach, indem er die Adressen und Ports vertauscht, die erforderlichen Informationen einträgt und die Nachricht an den Client zurücksendet. Server müssen zwischen Client-Anfragen keine Zustandsinformationen speichern, während Clients das Intervall für das Senden von NTP-Nachrichten frei verwalten können, um sich an lokale Bedingungen anzupassen. In diesen Modi kann die in diesem Dokument beschriebene Protokollmaschine erheblich zu einem einfachen Remote-Procedure-Call-Mechanismus vereinfacht werden, ohne dass ein wesentlicher Verlust an Genauigkeit oder Robustheit entsteht, insbesondere beim Betrieb in einem Hochgeschwindigkeits-LAN.

In den symmetrischen Modi entfällt die Client/Server-Unterscheidung (fast). Der symmetrisch-passive Modus ist für die Verwendung durch Zeitserver vorgesehen, die in der Nähe der Wurzelknoten (niedrigstes Stratum) des Synchronisations-Subnetzes und mit einer relativ großen Anzahl von Peers auf intermittierender Basis betrieben werden. In diesem Modus muss die Identität des Peers nicht im Voraus bekannt sein, da die Assoziation mit ihren Zustandsvariablen erst beim Eintreffen einer NTP-Nachricht erstellt wird. Darüber hinaus kann der Zustandsspeicher wiederverwendet werden, wenn der Peer unerreichbar wird oder auf einer höheren Stratum-Ebene arbeitet und somit als Synchronisationsquelle nicht in Frage kommt.

Der symmetrisch-aktive Modus ist für die Verwendung durch Zeitserver vorgesehen, die in der Nähe der Endknoten (höchstes Stratum) des Synchronisations-Subnetzes betrieben werden. Ein zuverlässiger Zeitdienst kann in der Regel mit zwei Peers auf der nächstniedrigeren Stratum-Ebene und einem Peer auf derselben Stratum-Ebene aufrechterhalten werden, sodass die Rate der kontinuierlichen Abfrage normalerweise nicht signifikant ist, selbst wenn die Konnektivität verloren geht und jede Abfrage eine Fehlermeldung zurückgibt.

Normalerweise arbeitet ein Peer im aktiven Modus (symmetrisch-aktiver, Client- oder Broadcast-Modus), wie durch eine Startdatei konfiguriert, während der andere im passiven Modus (symmetrisch-passiver oder Server-Modus) arbeitet, oft ohne vorherige Konfiguration. Es können jedoch beide Peers für den Betrieb im symmetrisch-aktiven Modus konfiguriert werden. Ein Fehlerzustand tritt auf, wenn beide Peers im selben Modus arbeiten, aber nicht im symmetrisch-aktiven Modus. In einem solchen Fall ignoriert jeder Peer Nachrichten vom anderen, sodass frühere Assoziationen, falls vorhanden, aufgrund eines Erreichbarkeitsfehlers aufgelöst werden.

Der Broadcast-Modus ist für den Betrieb in Hochgeschwindigkeits-LANs mit zahlreichen Workstations vorgesehen, bei denen keine höchste Genauigkeit erforderlich ist. Im typischen Szenario senden ein oder mehrere Zeitserver im LAN periodische Broadcasts an die Workstations, die dann die Zeit auf der Grundlage einer vorkonfigurierten Verzögerung in der Größenordnung von wenigen Millisekunden bestimmen. Wie in den Client/Server-Modi kann die Protokollmaschine in diesem Modus erheblich vereinfacht werden; eine modifizierte Form des Uhrenauswahlalgorithmus kann jedoch in Fällen nützlich sein, in denen mehrere Zeitserver zur Erhöhung der Zuverlässigkeit eingesetzt werden.

3.4. Ereignisverarbeitung

Die wichtigen Ereignisse von Interesse in NTP treten beim Ablauf eines Peer-Timers (peer.timer) auf, von denen einer jedem Peer mit einer aktiven Assoziation gewidmet ist, und beim Eintreffen einer NTP-Nachricht von den verschiedenen Peers. Ein Ereignis kann auch als Ergebnis eines Operatorbefehls oder eines erkannten Systemfehlers auftreten, wie z. B. eines Ausfalls der primären Referenzquelle. Dieser Abschnitt beschreibt die Verfahren, die aufgerufen werden, wenn diese Ereignisse auftreten.

3.4.1. Notationskonventionen

Die NTP-Filter- und Auswahlalgorithmen operieren auf einem Satz von Variablen: Uhrenversatz (θ, THETA), Umlaufverzögerung (δ, DELTA) und Streuung (ε, EPSILON). Wenn eine Unterscheidung erforderlich ist, werden griechische Kleinbuchstaben für Variablen relativ zu einem Peer verwendet, während griechische Großbuchstaben für Variablen relativ zur primären Referenzquelle verwendet werden, d. h. über den Peer zur Wurzel des Synchronisations-Subnetzes. Indizes werden verwendet, um einen bestimmten Peer zu identifizieren, wenn der Kontext nicht eindeutig ist. Diese Algorithmen basieren auf einer Größe, die als Synchronisationsabstand (λ, LAMBDA) bezeichnet und aus der Umlaufverzögerung und der Streuung wie unten beschrieben berechnet wird.

Wie in Anhang H beschrieben, umfasst die Peer-Streuung ε die folgenden Beiträge: Messfehler ρ = 1 << sys.precision, Akkumulation des Driftfehlers φτ (wobei φ = NTP.MAXSKEW / NTP.MAXAGE die maximale Driftrate und τ = sys.clock - peer.update das Intervall seit der letzten Aktualisierung ist) sowie die vom Uhrenfilteralgorithmus berechnete Filter-(Stichproben-)Streuung ε_σ. Die Wurzelstreuung EPSILON umfasst die Beiträge der ausgewählten Peer-Streuung ε und der Akkumulation des Driftfehlers φτ sowie die Wurzelstreuung des Peers selbst. Die Systemstreuung umfasst die vom Uhrenauswahlalgorithmus berechnete Auswahl-(Stichproben-)Streuung ε_ξ und den absoluten anfänglichen Uhrenversatz |THETA|, der dem lokalen Uhrenalgorithmus bereitgestellt wird. Sowohl ε als auch EPSILON sind dynamische Größen, da sie von der seit der letzten Aktualisierung verstrichenen Zeit τ sowie von den durch die Algorithmen berechneten Stichprobenstreuungen abhängen.

Immer wenn die relevanten Peer-Variablen aktualisiert werden, werden alle mit diesem Peer verbundenen Streuungen aktualisiert, um die Akkumulation des Driftfehlers widerzuspiegeln. Die Berechnungen lassen sich wie folgt zusammenfassen:

  • θ = peer.offset
  • δ = peer.delay
  • ε = peer.dispersion = ρ + φτ + ε_σ
  • λ = ε + |δ| / 2

wobei τ das Intervall zwischen dem Zeitpunkt, zu dem der ursprüngliche Zeitstempel (aus dem θ und δ bestimmt wurden) gesendet wurde, und der Gegenwart ist und ε_σ die Filterstreuung ist (siehe Uhrenfilterverfahren unten). Die Variablen relativ zur Wurzel des Synchronisations-Subnetzes über Peer i werden wie folgt bestimmt:

  • THETA_i = θ_i
  • DELTA_i = peer.rootdelay + δ_i
  • EPSILON_i = peer.rootdispersion + ε_i + φτ_i
  • LAMBDA_i = EPSILON_i + |DELTA_i| / 2

wobei alle Variablen als zum i-ten Peer gehörend verstanden werden. Unter der Annahme, dass der i-te Peer für die Synchronisation ausgewählt wird, werden die Systemvariablen schließlich wie folgt bestimmt:

  • THETA = kombinierter endgültiger Versatz
  • DELTA = DELTA_i
  • EPSILON = EPSILON_i + ε_ξ + |THETA|
  • LAMBDA = LAMBDA_i

wobei ε_ξ die Auswahlstreuung ist (siehe Uhrenauswahlverfahren unten).

Ein informeller Pseudocode, der diese Berechnungen durchführt, ist unten dargestellt. Beachten Sie, dass der Pseudocode in keiner bestimmten Sprache ausgedrückt ist, obwohl er viele Ähnlichkeiten mit der Sprache C aufweist. Spezifische Details wichtiger Algorithmen werden durch die C-Sprachroutinen in Anhang I weiter veranschaulicht.

3.4.2. Sendeverfahren

Das Sendeverfahren wird ausgeführt, wenn der Peer-Timer auf null herunterzählt, und zwar für alle Modi außer dem Client-Modus mit einem Broadcast-Server und dem Server-Modus in allen Fällen. Im Client-Modus mit einem Broadcast-Server werden niemals Nachrichten gesendet. Im Server-Modus werden Nachrichten nur als Antwort auf empfangene Nachrichten gesendet. Dieses Verfahren wird auch vom Empfangsverfahren aufgerufen, wenn eine NTP-Nachricht eintrifft, die nicht zu einer dauerhaften Assoziation führt.

begin transmit procedure

pkt.peeraddr <- peer.hostaddr; /* copy system and peer variables */
pkt.peerport <- peer.hostport;
pkt.hostaddr <- peer.peeraddr;
pkt.hostport <- peer.peerport;
pkt.leap <- sys.leap;
pkt.version <- NTP.VERSION;
pkt.mode <- peer.mode;
pkt.stratum <- sys.stratum;
pkt.poll <- peer.hostpoll;
pkt.precision <- sys.precision;
pkt.rootdelay <- sys.rootdelay;
if (sys.leap = 11₂ or (sys.clock - sys.reftime) > NTP.MAXAGE)
skew <- NTP.MAXSKEW;
else
skew <- φ(sys.clock - sys.reftime);
pkt.rootdispersion <- sys.rootdispersion + (1 << sys.precision) + skew;
pkt.refid <- sys.refid;
pkt.reftime <- sys.reftime;

pkt.org <- peer.org; /* copy timestamps */
pkt.rec <- peer.rec;
pkt.xmt <- sys.clock;
peer.xmt <- pkt.xmt;

#ifdef (authentication implemented) /* see Appendix C */
call encrypt;
#endef
send packet;

peer.reach <- peer.reach << 1; /* update reachability */
if (peer.reach = 0 and peer.config = 0)
begin
demobilize association;
exit;
endif

if (peer.reach & 6 != 0) /* test two low-order bits */
if (peer.valid < NTP.SHIFT) /* valid data received */
peer.valid <- peer.valid + 1;
else peer.hostpoll <- peer.hostpoll + 1;
else begin
peer.valid <- peer.valid - 1; /* nothing heard */
peer.hostpoll <- peer.hostpoll - 1;
call clock-filter(0, 0, NTP.MAXDISPERSE);
call clock-select; /* select clock source */
endif
call poll-update;
end transmit procedure;

3.4.3. Empfangsverfahren

Das Empfangsverfahren wird beim Eintreffen einer NTP-Nachricht ausgeführt. Es validiert die Nachricht, interpretiert die verschiedenen Modi und ruft andere Verfahren auf, um die Daten zu filtern und die Synchronisationsquelle auszuwählen. Wenn die Versionsnummer im Paket nicht mit der aktuellen Version übereinstimmt, kann die Nachricht verworfen werden; empfohlene Ausnahmen können jedoch bei Versionsänderungen von Fall zu Fall gemacht werden. Wenn die in Anhang B beschriebenen NTP-Steuernachrichten implementiert sind und der Paketmodus 6 (Steuerung) ist, wird das Steuernachrichtenverfahren aufgerufen. Die Quell- und Zieladressen sowie Ports im IP- und UDP-Header werden dem richtigen Peer zugeordnet. Wenn keine Übereinstimmung vorliegt, wird eine neue Instanz der Protokollmaschine erstellt und die Assoziation mobilisiert.

begin receive procedure
if (pkt.version != NTP.VERSION) exit;
#ifdef (control messages implemented)
if (pkt.mode = 6) call control-message;
#endef
for (all associations) /* access control goes here */
match addresses and ports to associations;
if (no matching association)
call receive-instantiation procedure; /* create association */

#ifdef (authentication implemented) /* see Appendix C */
call decrypt;
#endef

if (pkt.mode = 0) /* for compatibility */
mode <- (see Section 3.3);
else
mode <- pkt.mode;

case (mode, peer.hostmode) /* see Table 5 */

error: if (peer.config = 0) demobilize association;
break;

recv: call packet; /* process packet */
if (valid header) begin
peer.reach <- peer.reach | 1;
if (valid data) call clock-update;
endif
else
if (peer.config = 0) demobilize association;
break;

xmit: call packet; /* process packet */
peer.hostpoll <- peer.peerpoll;
call poll-update;
call transmit;
if (peer.config = 0) demobilize association;
break;

pkt: call packet; /* process packet */
if (valid header) begin
peer.reach <- peer.reach | 1;
if (valid data) call clock-update;
endif
else if (peer.config = 0) begin
peer.hostpoll <- peer.peerpoll;
call poll-update;
call transmit;
demobilize association;
endif
endcase
end receive procedure;

3.4.4. Paketverfahren

Das Paketverfahren prüft die Gültigkeit der Nachricht, berechnet Verzögerungs-/Versatz-Stichproben und ruft andere Verfahren auf, um die Daten zu filtern und die Synchronisationsquelle auszuwählen. Test 1 verlangt, dass der Sendezeitstempel nicht mit dem zuletzt vom selben Peer empfangenen übereinstimmt; andernfalls könnte die Nachricht ein altes Duplikat sein. Test 2 verlangt, dass der Ursprungszeitstempel mit dem zuletzt an denselben Peer gesendeten übereinstimmt; andernfalls könnte die Nachricht außerhalb der Reihenfolge, gefälscht oder schlimmer sein. Im Fall des Broadcast-Modus (5) ist die scheinbare Umlaufverzögerung null und die volle Genauigkeit der Zeitübertragungsoperation wird möglicherweise nicht erreicht. Die erzielte Genauigkeit kann jedoch für die meisten Zwecke ausreichend sein. Das Abfrage-Aktualisierungsverfahren wird mit dem Argument peer.hostpoll aufgerufen (peer.peerpoll hat sich möglicherweise geändert).

begin packet procedure
peer.rec <- sys.clock; /* capture receive timestamp */
if (pkt.mode != 5) begin
test1 <- (pkt.xmt != peer.org); /* test 1 */
test2 <- (pkt.org = peer.xmt); /* test 2 */
endif
else begin
pkt.org <- peer.rec; /* fudge missing timestamps */
pkt.rec <- pkt.xmt;
test1 <- true; /* fake tests */
test2 <- true;
endif
peer.org <- pkt.xmt; /* update originate timestamp */
peer.peerpoll <- pkt.poll; /* adjust poll interval */
call poll-update(peer.hostpoll);

test3 <- (pkt.org != 0 and pkt.rec != 0); /* test 3 */

δ <- (T_i - T_(i-3)) - (T_(i-1) - T_(i-2))
θ <- ((T_(i-2) - T_(i-3)) + (T_(i-1) - T_i)) / 2
ε <- (1 << sys.precision) + φ(T_i - T_(i-3))

test4 <- (|δ| < NTP.MAXDISPERSE and ε < NTP.MAXDISPERSE); /* test 4 */

#ifdef (authentication implemented) /* test 5 */
test5 <- ((peer.config = 1 and peer.authenable = 0) or peer.authentic = 1);
#endef

test6 <- (pkt.leap != 11₂ and /* test 6 */
pkt.reftime <= pkt.xmt < pkt.reftime + NTP.MAXAGE)
test7 <- (pkt.stratum <= sys.stratum and /* test 7 */
pkt.stratum < NTP.MAXSTRATUM);
test8 <- (|pkt.rootdelay| < NTP.MAXDISPERSE and /* test 8 */
pkt.rootdispersion < NTP.MAXDISPERSE);

if (not valid header) exit;
peer.leap <- pkt.leap; /* copy packet variables */
peer.stratum <- pkt.stratum;
peer.precision <- pkt.precision;
peer.rootdelay <- pkt.rootdelay;
peer.rootdispersion <- pkt.rootdispersion;
peer.refid <- pkt.refid;
peer.reftime <- pkt.reftime;
if (valid data) call clock-filter(θ, δ, ε); /* process sample */
end packet procedure;

3.4.5. Uhrenaktualisierungsverfahren

Das Uhrenaktualisierungsverfahren wird vom Empfangsverfahren aufgerufen, wenn gültige Uhrenversatz-, Verzögerungs- und Streuungsdaten vom Uhrenfilterverfahren für den aktuellen Peer bestimmt wurden. Das Ergebnis der Uhrenauswahl- und Uhrenkombinationsverfahren ist der endgültige Uhrenkorrekturwert THETA, der vom lokalen Uhrenverfahren zur Aktualisierung der lokalen Uhr verwendet wird. Wenn keine Kandidaten diese Verfahren überleben, wird das Uhrenaktualisierungsverfahren ohne weitere Maßnahmen beendet.

begin clock-update procedure
call clock-select; /* select clock source */
if (sys.peer != peer) exit;

LAMBDA <- distance(peer); /* update system variables */
if (LAMBDA >= NTP.MAXDISTANCE) exit;
sys.leap <- peer.leap;
sys.stratum <- peer.stratum + 1;
sys.refid <- peer.peeraddr;
call local-clock;
if (local clock reset) begin /* if reset, clear state variables */
sys.leap <- 11₂;
for (all peers) call clear;
endif
else begin
sys.peer <- peer; /* if not, adjust local clock */
sys.rootdelay <- DELTA;
sys.rootdispersion <- EPSILON + max(ε_ξ + |THETA|, NTP.MINDISPERSE);
endif
sys.reftime <- sys.clock;
end clock-update procedure;

In einigen Systemkonfigurationen ist eine präzise Quelle von Timing-Informationen in Form eines Zugs von Timing-Impulsen im Abstand von einer Sekunde verfügbar. Normalerweise wird dies zusätzlich zu einer Zeitcode-Informationsquelle bereitgestellt, wie z. B. einer Funkuhr oder sogar NTP selbst, um die Sekunden, Minuten, Stunden und Tage zu nummerieren. In diesen Konfigurationen werden die Systemvariablen so gesetzt, dass sie auf die Quelle verweisen, von der die Impulse abgeleitet werden. Bei Konfigurationen, die eine primäre Referenzquelle wie eine Funkuhr oder eine kalibrierte Atomuhr unterstützen, wird das Stratum auf 1 gesetzt, solange dies die tatsächliche Synchronisationsquelle ist, unabhängig davon, ob das Primäruhrenverfahren verwendet wird oder nicht.

Die Spezifikationen der Uhrenauswahl- und lokalen Uhrenalgorithmen sind kein integraler Bestandteil der NTP-Spezifikation, da andere Algorithmen mit gleichwertiger Leistung existieren können. Ein Uhrenauswahlalgorithmus, der sich in der Internet-Umgebung als gut funktionierend erwiesen hat, wird jedoch in Abschnitt 4 beschrieben, und ein lokaler Uhrenalgorithmus wird in Abschnitt 5 beschrieben, und ihre Verwendung wird empfohlen. Der in Abschnitt 4 beschriebene Uhrenauswahlalgorithmus wählt in der Regel den Peer mit dem niedrigsten Stratum und dem minimalen Synchronisationsabstand unter allen verfügbaren Peers aus, es sei denn, dieser Peer erscheint als Falschticker (falseticker). Das Ergebnis ist, dass alle Algorithmen zusammenwirken, um einen minimal gewichteten Spannbaum relativ zu den primären Referenzzeitservern zu konstruieren, was ein hierarchisches Master-Slave-Synchronisations-Subnetz ergibt.

3.4.6. Primäruhrenverfahren

Wenn eine primäre Referenzquelle, wie z. B. eine Funkuhr, mit dem Host verbunden ist, ist es zweckmäßig, ihre Informationen in die Datenbank aufzunehmen, als ob die Uhr als gewöhnlicher Peer dargestellt würde. Im Primäruhrenverfahren wird die Uhr etwa einmal pro Minute abgefragt und der zurückgegebene Zeitcode verwendet, um eine neue Aktualisierung für die lokale Uhr zu erzeugen. Wenn der peer.timer des Primäruhren-Peers auf null herunterzählt, wird das Sendeverfahren nicht aufgerufen; stattdessen wird die Funkuhr abgefragt, in der Regel mit einer für diesen Zweck festgelegten ASCII-Zeichenfolge. Wenn ein gültiger Zeitcode von der Funkuhr empfangen wird, wird er in das NTP-Zeitstempelformat umgewandelt und die Peer-Variablen werden aktualisiert. Der Wert von peer.leap wird entsprechend dem Zustand der Schaltsekunden-Warnbits im Zeitcode gesetzt, falls verfügbar, oder manuell durch den Operator. Der Wert von peer.peeraddr, der zum Wert von sys.refid wird, wenn das Uhrenaktualisierungsverfahren aufgerufen wird, wird auf eine ASCII-Zeichenfolge gesetzt, die den Uhrentyp beschreibt (siehe Anhang A).

begin primary-clock-update procedure
peer.leap <- "from" radio or operator; /* copy variables */
peer.peeraddr <- ASCII identifier;
peer.rec <- radio timestamp;
peer.reach <- 1;
call clock-filter(sys.clock - peer.rec, 0, 1 << peer.precision);
call clock-update; /* update local clock */
end primary-clock-update procedure;

3.4.7. Initialisierungsverfahren

Die Initialisierungsverfahren dienen dazu, das System, seine Peers und Assoziationen einzurichten und zu initialisieren.

3.4.7.1. Initialisierungsverfahren

Das Initialisierungsverfahren wird beim Neustart oder Wiederanlauf des NTP-Daemons aufgerufen. Die lokale Uhr ist beim Neustart vermutlich undefiniert; in einigen Geräten ist jedoch eine Schätzung aus der Neustartumgebung verfügbar, wie z. B. eine batteriegepufferte Uhr/Kalender. Die Präzisionsvariable wird durch die inhärente Architektur der lokalen Hardware-Uhr bestimmt. Die Authentifizierungsvariablen werden nur verwendet, wenn der in Anhang C beschriebene Authentifizierungsmechanismus implementiert ist. Die Werte dieser Variablen werden mit Verfahren bestimmt, die über den Rahmen von NTP selbst hinausgehen.

begin initialization procedure
#ifdef (authentication implemented) /* see Appendix C */
sys.keys <- as required;
#endef;
sys.leap <- 11₂; /* copy variables */
sys.stratum <- 0 (undefined);
sys.precision <- host precision;
sys.rootdelay <- 0 (undefined);
sys.rootdispersion <- 0 (undefined);
sys.refid <- 0 (undefined);
sys.reftime <- 0 (undefined);
sys.clock <- external reference;
sys.peer <- NULL;
sys.poll <- NTP.MINPOLL;
for (all configured peers) /* create configured associations */
call initialization-instantiation procedure;
end initialization procedure;

3.4.7.2. Initialisierungs-Instanziierungsverfahren

Dieses implementierungsspezifische Verfahren wird vom Initialisierungsverfahren aufgerufen, um eine Assoziation zu definieren. Die Adresse und der Modus des Peers werden aus Informationen bestimmt, die während des Neustartverfahrens gelesen werden, oder als Ergebnis von Operatorbefehlen. Die Authentifizierungsvariablen werden nur verwendet, wenn der in Anhang C beschriebene Authentifizierungsmechanismus implementiert ist. Die Werte dieser Variablen werden mit Verfahren bestimmt, die über den Rahmen von NTP selbst hinausgehen. Wenn das Authentifizierungsbit wie vorgeschlagen gesetzt ist, können nur ordnungsgemäß authentifizierte Peers zur Synchronisationsquelle werden.

begin initialization-instantiation procedure
peer.config <- 1;
#ifdef (authentication implemented) /* see Appendix C */
peer.authenable <- 1 (suggested);
peer.authentic <- 0;
peer.hostkeyid <- as required;
peer.peerkeyid <- 0;
#endef;
peer.peeraddr <- peer IP address; /* copy variables */
peer.peerport <- NTP.PORT;
peer.hostaddr <- host IP address;
peer.hostport <- NTP.PORT;
peer.mode <- host mode;
peer.peerpoll <- 0 (undefined);
peer.timer <- 0;
peer.delay <- 0 (undefined);
peer.offset <- 0 (undefined);
call clear; /* initialize association */
end initialization-instantiation procedure;

3.4.7.3. Empfangs-Instanziierungsverfahren

Das Empfangs-Instanziierungsverfahren wird vom Empfangsverfahren aufgerufen, wenn ein neuer Peer entdeckt wird. Es initialisiert die Peer-Variablen und mobilisiert die Assoziation. Wenn die Nachricht von einem im Client-Modus (3) betriebenen Peer stammt, wird der Host-Modus auf den Server-Modus (4) gesetzt; andernfalls wird er auf den symmetrisch-passiven Modus (2) gesetzt. Die Authentifizierungsvariablen werden nur verwendet, wenn der in Anhang C beschriebene Authentifizierungsmechanismus implementiert ist. Falls implementiert, können nur ordnungsgemäß authentifizierte, nicht konfigurierte Peers zur Synchronisationsquelle werden.

begin receive-instantiation procedure
#ifdef (authentication implemented) /* see Appendix C */
peer.authenable <- 0;
peer.authentic <- 0;
peer.hostkeyid <- as required;
peer.peerkeyid <- 0;
#endef
peer.config <- 0; /* copy variables */
peer.peeraddr <- pkt.peeraddr;
peer.peerport <- pkt.peerport;
peer.hostaddr <- pkt.hostaddr;
peer.hostport <- pkt.hostport;
if (pkt.mode = 3) /* determine mode */
peer.mode <- 4;
else
peer.mode <- 2;
peer.peerpoll <- 0 (undefined);
peer.timer <- 0;
peer.delay <- 0 (undefined);
peer.offset <- 0 (undefined);
call clear; /* initialize association */
end receive-instantiation procedure;

3.4.7.4. Primäruhren-Instanziierungsverfahren

Dieses Verfahren wird vom Initialisierungsverfahren aufgerufen, um die Zustandsvariablen für die Primäruhr einzurichten. Der Wert von peer.precision wird aus den Spezifikationen der Funkuhr und der Hardware-Schnittstelle bestimmt. Der Wert von peer.rootdispersion beträgt nominell das Zehnfache des inhärenten maximalen Fehlers der Funkuhr; zum Beispiel 10 μs für eine kalibrierte Atomuhr, 10 ms für eine WWVB- oder GOES-Funkuhr und 100 ms für eine weniger präzise WWV-Funkuhr.

begin clock-instantiation procedure
peer.config <- 1; /* copy variables */
peer.peeraddr <- 0 undefined;
peer.peerport <- 0 (not used);
peer.hostaddr <- 0 (not used);
peer.hostport <- 0 (not used);
peer.leap <- 11₂;
peer.mode <- 0 (not used);
peer.stratum <- 0;
peer.peerpoll <- 0 (undefined);
peer.precision <- clock precision;
peer.rootdelay <- 0;
peer.rootdispersion <- clock dispersion;
peer.refid <- 0 (not used);
peer.reftime <- 0 (undefined);
peer.timer <- 0;
peer.delay <- 0 (undefined);
peer.offset <- 0 (undefined);
call clear; /* initialize association */
end clock-instantiation procedure;

In einigen Konfigurationen mit einer kalibrierten Atomuhr oder einem LORAN-C-Empfänger liefert die primäre Referenzquelle möglicherweise nur Sekundenimpulse und es fehlt ein vollständiger Zeitcode, aus dem die Nummerierung der Sekunden usw. abgeleitet werden kann. In diesen Konfigurationen kann die Sekundennummerierung aus anderen Quellen abgeleitet werden, wie z. B. einer Funkuhr oder sogar anderen NTP-Peers. In diesen Konfigurationen sollten die Primäruhrenvariablen die primäre Referenzquelle widerspiegeln, nicht die Sekundennummerierungsquelle; wenn jedoch die Sekundennummerierungsquelle ausfällt oder bekanntermaßen nicht korrekt arbeitet, sollten Aktualisierungen von der primären Referenzquelle unterdrückt werden, als ob sie selbst ausgefallen wäre.

3.4.8. Löschverfahren

Das Löschverfahren wird aufgerufen, wenn ein Ereignis eintritt, das zu einer erheblichen Änderung des Erreichbarkeitszustands oder einer möglichen Unterbrechung der lokalen Uhr führt.

begin clear procedure
peer.org <- 0 (undefined); /* mark timestamps undefined */
peer.rec <- 0 (undefined);
peer.xmt <- 0 (undefined);
peer.reach <- 0; /* reset state variables */
peer.filter <- [0, 0, NTP.MAXDISPERSE]; /* all stages */
peer.valid <- 0;
peer.dispersion <- NTP.MAXDISPERSE;
peer.hostpoll <- NTP.MINPOLL; /* reset poll interval */
call poll-update;
call clock-select; /* select clock source */
end clear procedure;

3.4.9. Abfrage-Aktualisierungsverfahren

Das Abfrage-Aktualisierungsverfahren wird aufgerufen, wenn ein signifikantes Ereignis eintritt, das zu einer Änderung des Abfrageintervalls oder des Peer-Timers führen kann. Es überprüft die Werte des Host-Abfrageintervalls (peer.hostpoll) und des Peer-Abfrageintervalls (peer.peerpoll) und begrenzt jeden auf seinen gültigen Bereich. Wenn der Peer für die Synchronisation ausgewählt ist, wird der Wert weiter als Funktion der berechneten Konformität begrenzt (siehe Abschnitt 5).

begin poll-update procedure
temp <- peer.hostpoll; /* determine host poll interval */
if (peer = sys.peer)
temp <- min(temp, sys.poll, NTP.MAXPOLL);
else
temp <- min(temp, NTP.MAXPOLL);
peer.hostpoll <- max(temp, NTP.MINPOLL);
temp <- 1 << min(peer.hostpoll, max(peer.peerpoll, NTP.MINPOLL));

if (peer.timer = 0) /* reset peer timer */
peer.timer <- temp;
else if (peer.timer > temp)
peer.timer <- (sys.clock & (temp - 1)) + 1;
end poll-update procedure;

3.5. Synchronisationsabstandsverfahren

Das Abstandsverfahren berechnet die Synchronisationsdistanz aus den Peer-Variablen für den Peer peer.

begin distance(peer) procedure;
DELTA &lt;- peer.rootdelay + |peer.delay|;
EPSILON &lt;- peer.rootdispersion + peer.dispersion + φ(sys.clock - peer.update);
LAMBDA &lt;- EPSILON + |DELTA| / 2;
end distance procedure;

Beachten Sie, dass DELTA in einigen Fällen negativ sein kann, EPSILON und LAMBDA jedoch immer positiv sind.

3.6. Zugriffssteuerungsprobleme

Das NTP-Design ist so, dass versehentliche oder böswillige Datenänderungen (Manipulation, tampering) oder Zerstörung (Störung, jamming) an einem Zeitserver im Allgemeinen nicht zu Zeitmessfehlern an anderer Stelle im Synchronisationssubnetz führen sollten. Der Erfolg dieses Ansatzes hängt jedoch von redundanten Zeitservern und vielfältigen Netzwerkpfaden sowie der Annahme ab, dass Manipulation oder Störung nicht gleichzeitig an vielen Zeitservern im gesamten Synchronisationssubnetz auftreten werden. Im Prinzip kann die Subnetz-Verwundbarkeit durch die Auswahl von als vertrauenswürdig bekannten Zeitservern und das Zulassen nur dieser Zeitserver als Synchronisationsquelle konstruiert werden. Die in Anhang C beschriebenen Authentifizierungsverfahren stellen einen Mechanismus dar, um dies durchzusetzen; die Verschlüsselungsalgorithmen können jedoch sehr CPU-intensiv sein und die Genauigkeit ernsthaft beeinträchtigen, es sei denn, Vorsichtsmaßnahmen wie in der Beschreibung des Übertragungsverfahrens erwähnt werden getroffen.

Obwohl es keine erforderliche Funktion von NTP selbst ist, können einige Implementierungen eine Zugriffssteuerungsfunktion enthalten, die unbefugten Zugriff verhindert und steuert, welche Peers die lokale Uhr aktualisieren dürfen. Zu diesem Zweck ist es nützlich, zwischen drei Kategorien von Zugriff zu unterscheiden: solche, die als vertrauenswürdig vorautorisiert sind, als freundlich vorautorisiert sind und alle anderen (nicht vorautorisierte) Zugriffe. Vermutlich wird die Vorautorisierung durch Einträge in der Konfigurationsdatei oder eine Art Ticket-Verwaltungssystem wie Kerberos [STE88] erreicht. In diesem Modell können nur vertrauenswürdige Zugriffe dazu führen, dass der Peer zur Synchronisationsquelle wird. Während freundliche Zugriffe nicht dazu führen können, dass der Peer zur Synchronisationsquelle wird, werden NTP-Nachrichten und Zeitstempel wie angegeben zurückgegeben.

Es scheint nicht nützlich, eine geheime Uhr zu pflegen, wie sie sich aus der Einschränkung nicht vorautorisierter Zugriffe ergeben würde, es sei denn, die Absicht besteht darin, die Existenz des Zeitservers selbst zu verbergen. Von gut verhaltenen Internet-Hosts wird erwartet, dass sie eine ICMP-Service-nicht-verfügbar-Fehlermeldung zurückgeben, wenn ein Dienst nicht implementiert ist oder Ressourcen nicht verfügbar sind; im Falle von NTP sind jedoch die erforderlichen Ressourcen minimal, sodass kaum Bedarf besteht, Anfragen zu beschränken, die nur dazu dienen, die Uhr zu lesen. Ein einfacher, aber effektiver Zugriffssteuerungsmechanismus besteht dann darin, alle Assoziationen, die in einem symmetrischen Modus oder Client-Modus (Modi 1, 2 und 3) vorkonfiguriert sind, als vertrauenswürdig zu betrachten und alle anderen Assoziationen, vorkonfiguriert oder nicht, als freundlich.

Wenn ein umfassenderes Vertrauensmodell erforderlich ist, kann das Design auf einer Zugriffssteuerungsliste basieren, wobei jeder Eintrag aus einer 32-Bit-Internetadresse, einer 32-Bit-Maske und einem 3-Bit-Modus besteht. Wenn das logische UND der Quelladresse (pkt.peeraddr) und der Maske in einem Eintrag mit der entsprechenden Adresse im Eintrag übereinstimmt und der Modus (pkt.mode) mit dem Modus im Eintrag übereinstimmt, wird der Zugriff zugelassen; andernfalls wird eine ICMP-Fehlermeldung an den Anforderer zurückgegeben. Durch geeignete Wahl der Maske ist es möglich, Anfragen nach Modus auf einzelne Adressen, ein bestimmtes Subnetz oder Netzadressen zu beschränken oder überhaupt keine Einschränkung zu haben. Die Zugriffssteuerungsliste würde dann als Filter dienen, der steuert, welche Peers Assoziationen erstellen können.