メインコンテンツまでスキップ

3. ネットワークタイムプロトコル

このセクションは、データ形式、エンティティ、状態変数、イベント、およびイベント処理手順を含む、ネットワークタイムプロトコルの正式な定義で構成されています。この仕様は図1に示された実装モデルに基づいていますが、これが仕様の基礎となる唯一のモデルであることを意図していません。特に、この仕様はNTPの本質的な動作を説明および明確化し、より厳密で包括的で検証可能な仕様の基盤として機能することを目的としています。

3.1. データ形式

ここで明示的または暗黙的に表現されるすべての数学演算は、2の補数固定小数点演算です。データは整数または固定小数点量として指定され、ビットは左側または上位位置から始まるゼロからビッグエンディアン方式で番号が付けられます。さまざまな実装が内部使用のために外部から派生した量をスケーリングする可能性があるため、固定小数点量の精度も小数点配置も指定されていません。特に指定がない限り、すべての量は符号なしであり、ビットゼロの前に暗黙のゼロを持つ完全なフィールド幅を占有できます。符号付き量で動作するように設計されたハードウェアおよびソフトウェアパッケージは、最上位(符号)ビットが設定されている場合、驚くべき結果をもたらします。完全なフィールド幅で表される精度が正当化されることはめったにないため、タイムスタンプなどの外部から派生した符号なし固定小数点量を内部使用のために右に1ビットシフトすることをお勧めします。

NTPタイムスタンプは大切なデータであり、実際にはプロトコルの主要な製品を表すため、特別なタイムスタンプ形式が確立されています。NTPタイムスタンプは、1900年1月1日0時を基準とした秒単位の64ビット符号なし固定小数点数として表されます。整数部分は最初の32ビットにあり、小数部分は最後の32ビットにあります。この形式により、便利な多倍長演算とタイムプロトコル表現(秒)への変換が可能になりますが、ICMPタイムスタンプメッセージ表現(ミリ秒)への変換は複雑になります。この表現の精度は約200ピコ秒であり、最もエキゾチックな要件にも十分対応できるはずです。

タイムスタンプは、メッセージの到着などの重要なイベントが発生したときに、ローカルクロックの現在値をタイムスタンプにコピーすることによって決定されます。最高の精度を維持するためには、イベントに関連付けられたハードウェアまたはソフトウェアドライバーにできるだけ近い場所でこれを実行することが重要です。特に、出発タイムスタンプはリンクレベルの再送信ごとに再決定する必要があります。ホストが再起動されたときやプロトコルが最初に起動したときなど、特定のタイムスタンプが利用できない場合があります。これらの場合、64ビットフィールドはゼロに設定され、値が無効または未定義であることを示します。

1968年のある時点以降、最上位ビット(整数部分のビット0)が設定されており、64ビットフィールドは2036年のある時点でオーバーフローすることに注意してください。2036年にNTPが使用されている場合、1900年に対する時刻と2036年に対する時刻(および136年の他の倍数)を修飾するための外部手段が必要になります。そのような修飾を必要とするタイムスタンプ付きデータは非常に貴重であるため、適切な手段が容易に利用可能であるべきです。136年ごとに64ビットフィールドがゼロになり、したがって無効と見なされる200ピコ秒の間隔が存在します(以降無視されます)。

3.2. 状態変数とパラメータ

以下は、プロトコルで使用されるさまざまな状態変数とパラメータの要約です。これらは、オペレーティングシステム環境とローカルクロックメカニズムに関連するシステム変数、各ピアに固有のプロトコルマシンの状態を表すピア変数、NTPメッセージの内容を表すパケット変数、および現在のバージョンのすべての実装の固定構成定数を表すパラメータのクラスに分けられます。各クラスについて、変数の説明の後にその名前とそれを制御する手順または値が続きます。変数は小文字で、パラメータは大文字であることに注意してください。形式と使用に関する追加の詳細は、後のセクションと付録に示されています。

3.2.1. 共通変数

以下の変数は、システム、ピア、およびパケットクラスの2つ以上で共通です。追加の変数は、付録Cで説明されているオプションの認証メカニズムに固有です。同じ名前の共通変数を区別する必要がある場合は、変数識別子が使用されます。

ピアアドレス(Peer Address,peer.peeraddr, pkt.peeraddr)、ピアポート(Peer Port,peer.peerport, pkt.peerport): これらはピアの32ビットインターネットアドレスと16ビットポート番号です。

ホストアドレス(Host Address,peer.hostaddr, pkt.hostaddr)、ホストポート(Host Port,peer.hostport, pkt.hostport): これらはホストの32ビットインターネットアドレスと16ビットポート番号です。マルチホーミング(multi-homing)をサポートするために状態変数に含まれています。

閏秒インジケータ(Leap Indicator,sys.leap, peer.leap, pkt.leap): これは、NTPタイムスケールに挿入される差し迫った閏秒を警告する2ビットコードです。これらのビットは挿入日の23:59前に設定され、翌日の00:00後にリセットされます。これにより、挿入日の秒数(ロールオーバー間隔)が1だけ増加または減少します。プライマリサーバーの場合、これらのビットはオペレーターの介入によって設定されますが、セカンダリサーバーの場合、これらのビットはプロトコルによって設定されます。2ビット、ビット0とビット1は、それぞれ次のようにコード化されています。

意味
00警告なし
01最後の分は61秒
10最後の分は59秒
11アラーム条件(クロック非同期)

アラーム条件(11₂)を除くすべての場合、NTP自体はこれらのビットで何もせず、NTPの一部ではない時間変換ルーチンに渡すだけです。アラーム条件は、最初の起動時やプライマリ参照ソースが利用できない長期間の後など、何らかの理由でローカルクロックが同期されていない場合に発生します。

モード(Mode,peer.mode, pkt.mode): これは関連モードを示す整数で、値は次のようにコード化されています。

モード
0未指定
1対称アクティブ
2対称パッシブ
3クライアント
4サーバー
5ブロードキャスト
6NTP制御メッセージ用に予約
7私的使用のために予約

ストラタム(Stratum,sys.stratum, peer.stratum, pkt.stratum): これはローカルクロックのストラタムを示す整数で、値は次のように定義されています。

意味
0未指定
1プライマリ参照(例:校正された原子時計、無線時計)
2-255セカンダリ参照(NTP経由)

比較目的では、ゼロの値は他のどの値よりも大きいと見なされます。パケット変数としてエンコードされた整数の最大値はパラメータNTP.MAXSTRATUMによって制限されることに注意してください。

3.2.2. システム変数

表1は、完全なシステム変数のセットを示しています。前述の共通変数に加えて、オペレーティングシステムはローカルクロックを同期するために以下の変数を使用します。

ローカルクロック(Local Clock,sys.clock): これは現在のローカル時刻で、タイムスタンプ形式です。ローカル時刻は特定のマシンのハードウェアクロックから派生し、使用される設計に応じて間隔で増分します。セクション5では、調整およびスキュー補償メカニズムを含む適切な設計について説明しています。

クロックソース(Clock Source,sys.peer): これは現在の同期ソースを識別するセレクターです。通常、これはピア変数を含む構造体へのポインタになります。特別な値NULLは、現在有効な同期ソースがないことを示します。

3.2.3. ピア変数

表2は、完全なピア変数のセットを示しています。前述の共通変数に加えて、ピア管理および測定機能は以下の変数を使用します。

構成ビット(Configured Bit,peer.config): これは、関連が構成情報から作成されたことを示すビットで、ピアが到達不能になった場合でも解除されるべきではありません。

更新タイムスタンプ(Update Timestamp,peer.update): これは、最新のNTPメッセージが受信されたときのローカル時刻で、タイムスタンプ形式です。スキュー分散の計算に使用されます。

到達可能性レジスタ(Reachability Register,peer.reach): これはNTP.WINDOWビットのシフトレジスタで、ピアの到達可能性ステータスを判定するために使用され、ビットは最下位(最右端)から入ります。このレジスタの少なくとも1つのビットが1に設定されている場合、ピアは到達可能と見なされます。

ピアタイマー(Peer Timer,peer.timer): これは、送信されるNTPメッセージ間の間隔を制御するために使用される整数カウンタです。ゼロ以外の値に設定されると、カウンタは1秒間隔でゼロに達するまで減分され、その時点で送信手順が呼び出されます。このタイマーの動作はローカルクロック更新から独立していることに注意してください。これは、計時システムと間隔タイマーシステムアーキテクチャが互いに独立している必要があることを意味します。

3.2.4. パケット変数

表3は、完全なパケット変数のセットを示しています。前述の共通変数に加えて、以下の変数が定義されています。

バージョン番号(Version Number,pkt.version): これは送信者のバージョン番号を示す整数です。NTPメッセージは常に現在のバージョン番号NTP.VERSIONで送信され、バージョン番号がNTP.VERSIONと一致する場合は常に受け入れられます。バージョン番号が変更されたときには、ケースバイケースで例外が勧告される場合があります。このバージョンと以前のバージョンのNTPとの相互運用に関する具体的なガイドラインは、付録Dに要約されています。

3.2.5. クロックフィルタ変数

セクション4で提案されているフィルタおよび選択アルゴリズムを使用する場合、前述の変数に加えて以下の状態変数が定義されます。

フィルタレジスタ(Filter Register,peer.filter): これはNTP.SHIFT段のシフトレジスタで、各段は単一の観測に関連付けられた測定遅延、測定オフセット、および計算された分散からなる3つ組を格納します。これらの3つ組は最上位(最左端)から入り、最下位(最右端)に向かってシフトされ、新しい観測が到着すると最終的に破棄されます。

有効データカウンタ(Valid Data Counter,peer.valid): これはフィルタレジスタに残っている有効なサンプルを示す整数カウンタです。到達可能性状態の判定、およびポーリング間隔を増加または減少させるべき時期の判定に使用されます。

オフセット(Offset,peer.offset): これは、ローカルクロックに対するピアクロックのオフセットを秒単位で示す符号付き固定小数点数です。

遅延(Delay,peer.delay): これは、両者間のネットワークパス上での、ローカルクロックに対するピアクロックの往復遅延を秒単位で示す符号付き固定小数点数です。この変数は、クロック精度とスキュー誤差の累積に応じて、正と負の両方の値を取り得ることに注意してください。

分散(Dispersion,peer.dispersion): これは、両者間のネットワークパス上での、ローカルクロックに対するピアクロックの最大誤差を秒単位で示す符号付き固定小数点数です。ゼロより大きい正の値のみが可能です。

3.2.6. 認証変数

付録Cで提案されている認証メカニズムを使用する場合、前述の変数に加えて以下の状態変数が定義されます。これらの変数は、付録Cで説明されているオプションの認証メカニズムが実装されている場合にのみ使用されます。

認証有効ビット(Authentication Enabled Bit,peer.authenable): これは、関連が認証モードで動作することを示すビットです。

認証済みビット(Authenticated Bit,peer.authentic): これは、ピアから受信した最後のメッセージが正しく認証されたことを示すビットです。

キー識別子(Key Identifier,peer.hostkeyid,peer.peerkeyid,pkt.keyid): これは、メッセージ認証コードの生成に使用される暗号鍵を識別する整数です。

暗号鍵(Cryptographic Keys,sys.key): これは64ビットDES鍵のセットです。各鍵はBerkeley Unixディストリビューションと同様に構成され、8オクテットからなり、各オクテットの下位7ビットがDESビット1〜7に対応し、上位ビットがDES奇数パリティビット8に対応します。

暗号チェックサム(Crypto-Checksum,pkt.check): これは暗号化手順によって計算される暗号チェックサムです。

3.2.7. パラメータ

表4は、インターネットシステムで動作するすべての実装で想定されるパラメータを示しています。不要なネットワークオーバーヘッドを回避し、安定したピア関連を維持するために、これらのパラメータの値について合意することが必要です。以下のパラメータは固定であり、すべての関連に適用されると想定されます。

バージョン番号(NTP.VERSION): これは現在のNTPバージョン番号(3)です。

NTPポート(NTP.PORT): これはInternet Assigned Numbers AuthorityによってNTPに割り当てられたポート番号(123)です。

最大ストラタム(NTP.MAXSTRATUM): これはパケット変数としてエンコードできる最大ストラタム値であり、サブネットルーティングアルゴリズムによって「無限大」または到達不能とも解釈されます。

最大クロック経過時間(NTP.MAXAGE): これは、参照クロックが最後の更新後に有効と見なされる最大間隔(秒)です。

最大スキュー(NTP.MAXSKEW): これは、NTP.MAXAGEによって決定される間隔にわたるローカルクロックのスキューに起因する最大オフセット誤差(秒)です。比 φ = NTP.MAXSKEW / NTP.MAXAGE は、すべての原因に起因する最大可能スキュー率として解釈されます。

最大距離(NTP.MAXDISTANCE): セクション4で提案されている選択アルゴリズムを使用する場合、これは同期に許容されるピアの最大同期距離です。

最小ポーリング間隔(NTP.MINPOLL): これは、インターネットシステムの任意のピアによって許可される最小ポーリング間隔であり、2のべき乗の秒数で表されます。

最大ポーリング間隔(NTP.MAXPOLL): これは、インターネットシステムの任意のピアによって許可される最大ポーリング間隔であり、2のべき乗の秒数で表されます。

最小選択クロック数(NTP.MINCLOCK): セクション4で提案されている選択アルゴリズムを使用する場合、これは同期に許容されるピアの最小数です。

最大選択クロック数(NTP.MAXCLOCK): セクション4で提案されている選択アルゴリズムを使用する場合、これは選択のために考慮されるピアの最大数です。

最小分散(NTP.MINDISPERSE): セクション4で提案されているフィルタアルゴリズムを使用する場合、これは各ストラタムレベルの最小分散増分(秒)です。

最大分散(NTP.MAXDISPERSE): セクション4で提案されているフィルタアルゴリズムを使用する場合、これは最大ピア分散、および欠落データに対して想定される分散(秒)です。

到達可能性レジスタサイズ(NTP.WINDOW): これは到達可能性レジスタ(peer.reach)のサイズ(ビット単位)です。

フィルタサイズ(NTP.SHIFT): セクション4で提案されているフィルタアルゴリズムを使用する場合、これはクロックフィルタ(peer.filter)シフトレジスタのサイズ(段数)です。

フィルタ重み(NTP.FILTER): セクション4で提案されているフィルタアルゴリズムを使用する場合、これはフィルタ分散の計算に使用される重みです。

選択重み(NTP.SELECT): セクション4で提案されている選択アルゴリズムを使用する場合、これは選択分散の計算に使用される重みです。

3.3. 動作モード

ブロードキャストモードを除き、NTP関連は2つのピアがメッセージを交換し、それらの1つまたは両方が関連と呼ばれるプロトコルマシンのインスタンス化を作成および維持するときに形成されます。関連は、ホストモード変数(peer.mode)によって示される5つのモードのいずれかで動作できます:対称アクティブ、対称パッシブ、クライアント、サーバー、およびブロードキャスト。これらは次のように定義されます。

対称アクティブ(Symmetric Active,1): このモードで動作するホストは、ピアの到達可能性状態またはストラタムに関係なく定期的なメッセージを送信します。このモードで動作することにより、ホストはピアによって同期され、ピアを同期する意思を表明します。

対称パッシブ(Symmetric Passive,2): このタイプの関連は、通常、対称アクティブモードで動作するピアからのメッセージが到着したときに作成され、ピアが到達可能でホスト以下のストラタムレベルで動作している限り持続します。それ以外の場合、関連は解消されます。ただし、関連は少なくとも1つのメッセージが返信として送信されるまで常に持続します。このモードで動作することにより、ホストはピアによって同期され、ピアを同期する意思を表明します。

クライアント(Client,3): このモードで動作するホストは、ピアの到達可能性状態またはストラタムに関係なく定期的なメッセージを送信します。このモードで動作することにより、ホスト(通常はLANワークステーション)はピアによって同期される意思を表明しますが、ピアを同期しません。

サーバー(Server,4): このタイプの関連は、通常、クライアント要求メッセージが到着したときに作成され、その要求に応答するためにのみ存在し、その後関連は解消されます。このモードで動作することにより、ホスト(通常はLANタイムサーバー)は同期する意思を表明しますが、ピアによって同期されません。

ブロードキャスト(Broadcast,5): このモードで動作するホストは、ピアの到達可能性状態またはストラタムに関係なく定期的なメッセージを送信します。このモードで動作することにより、ホスト(通常は高速ブロードキャストメディア上で動作するLANタイムサーバー)はすべてのピアを同期する意思を表明しますが、それらのいずれによっても同期されません。

クライアントモードで動作するホストは、おそらく再起動直後、およびその後定期的な間隔で、サーバーモードで動作するホストにNTPメッセージを時折送信します。サーバーは単にアドレスとポートを交換し、必要な情報を埋めてメッセージをクライアントに返すことで応答します。サーバーはクライアント要求間で状態情報を保持する必要がありませんが、クライアントはローカル条件に合わせてNTPメッセージ送信間隔を自由に管理できます。これらのモードでは、本文書で説明されているプロトコルマシンは、特に高速LAN上で動作する場合、精度や堅牢性を大きく損なうことなく、単純なリモートプロシージャコール機構に大幅に簡略化できます。

対称モードでは、クライアント/サーバーの区別は(ほぼ)なくなります。対称パッシブモードは、同期サブネットのルートノード付近(最下位ストラタム)で動作し、比較的多数のピアを断続的に持つタイムサーバーでの使用を意図しています。このモードでは、状態変数を伴う関連はNTPメッセージが到着したときにのみ作成されるため、ピアの識別情報を事前に知る必要はありません。さらに、ピアが到達不能になったり、より高いストラタムレベルで動作して同期ソースとして不適格になったりした場合、状態ストレージを再利用できます。

対称アクティブモードは、同期サブネットのエンドノード付近(最上位ストラタム)で動作するタイムサーバーでの使用を意図しています。信頼できる時刻サービスは通常、1つ下のストラタムレベルの2つのピアと同じストラタムレベルの1つのピアで維持できるため、接続が失われてポーリングごとにエラーメッセージが返される場合でも、継続的なポーリングの頻度は通常それほど重要ではありません。

通常、一方のピアは起動ファイルで構成されたアクティブモード(対称アクティブ、クライアント、またはブロードキャストモード)で動作し、もう一方は多くの場合事前の構成なしにパッシブモード(対称パッシブまたはサーバーモード)で動作します。ただし、両方のピアを対称アクティブモードで動作するように構成することもできます。両方のピアが同じモード(ただし対称アクティブモードではない)で動作する場合、エラー状態が発生します。そのような場合、各ピアは他方からのメッセージを無視するため、以前の関連があれば到達可能性の失敗により解除されます。

ブロードキャストモードは、多数のワークステーションがあり、最高の精度が要求されない高速LANでの運用を意図しています。典型的なシナリオでは、LAN上の1つ以上のタイムサーバーがワークステーションに定期的なブロードキャストを送信し、ワークステーションは数ミリ秒程度の事前構成された遅延に基づいて時刻を決定します。クライアント/サーバーモードと同様に、このモードではプロトコルマシンを大幅に簡略化できます。ただし、信頼性向上のために複数のタイムサーバーを使用する場合には、クロック選択アルゴリズムの修正形式が有用となる可能性があります。

3.4. イベント処理

NTPで関心のある重要なイベントは、アクティブな関連を持つ各ピア専用のピアタイマー(peer.timer)の期限切れ時、およびさまざまなピアからのNTPメッセージの到着時に発生します。イベントは、オペレーターコマンドまたはプライマリ参照ソース障害などの検出されたシステム障害の結果としても発生する可能性があります。このセクションでは、これらのイベントが発生したときに呼び出される手順について説明します。

3.4.1. 記法規約

NTPのフィルタリングおよび選択アルゴリズムは、クロックオフセット(θ、THETA)、往復遅延(δ、DELTA)、分散(ε、EPSILON)の変数セットに対して作用します。それらを区別する必要がある場合、小文字のギリシャ文字はピアに対する変数に使用され、大文字のギリシャ文字はプライマリ参照ソースに対する変数、すなわちピアを経由して同期サブネットのルートに至る変数に使用されます。文脈から明らかでない場合は、添字を使用して特定のピアを識別します。これらのアルゴリズムは、以下に説明するように往復遅延と分散から計算される同期距離(λ、LAMBDA)と呼ばれる量に基づいています。

付録Hで説明されているように、ピア分散εには、測定誤差 ρ = 1 << sys.precision、スキュー誤差の累積 φτ(ここで φ = NTP.MAXSKEW / NTP.MAXAGE は最大スキュー率、τ = sys.clock - peer.update は最後の更新からの間隔)、およびクロックフィルタアルゴリズムによって計算されるフィルタ(サンプル)分散 ε_σ による寄与が含まれます。ルート分散EPSILONには、選択されたピア分散εとスキュー誤差の累積φτによる寄与、およびピア自身のルート分散が含まれます。システム分散には、クロック選択アルゴリズムによって計算される選択(サンプル)分散 ε_ξ と、ローカルクロックアルゴリズムに提供される絶対初期クロックオフセット |THETA| が含まれます。εとEPSILONはいずれも動的な量です。これは、最後の更新からの経過時間τ、およびアルゴリズムによって計算されるサンプル分散に依存するためです。

関連するピア変数が更新されるたびに、そのピアに関連付けられたすべての分散がスキュー誤差の累積を反映するように更新されます。計算は次のように要約できます。

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

ここで、τは元のタイムスタンプ(これからθとδが決定された)が送信されてから現在までの間隔であり、ε_σ はフィルタ分散です(以下のクロックフィルタ手順を参照)。ピアiを経由した同期サブネットのルートに対する変数は次のように決定されます。

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

ここで、すべての変数はi番目のピアに関するものと理解されます。最後に、i番目のピアが同期用に選択されたと仮定すると、システム変数は次のように決定されます。

  • THETA = 結合された最終オフセット
  • DELTA = DELTA_i
  • EPSILON = EPSILON_i + ε_ξ + |THETA|
  • LAMBDA = LAMBDA_i

ここで、ε_ξ は選択分散です(以下のクロック選択手順を参照)。

これらの計算を実行する非形式的な疑似コードを以下に示します。この疑似コードは特定の言語で表現されているわけではありませんが、C言語と多くの類似点があることに注意してください。重要なアルゴリズムの具体的な詳細は、付録IのC言語ルーチンでさらに説明されています。

3.4.2. 送信手順

送信手順は、ブロードキャストサーバーを伴うクライアントモードおよびすべての場合のサーバーモードを除くすべてのモードで、ピアタイマーがゼロまで減分したときに実行されます。ブロードキャストサーバーを伴うクライアントモードでは、メッセージは決して送信されません。サーバーモードでは、受信したメッセージに応答する場合にのみメッセージが送信されます。この手順は、持続的な関連をもたらさないNTPメッセージが到着したときに、受信手順からも呼び出されます。

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. 受信手順

受信手順は、NTPメッセージの到着時に実行されます。メッセージを検証し、さまざまなモードを解釈し、データをフィルタリングして同期ソースを選択するために他の手順を呼び出します。パケット内のバージョン番号が現在のバージョンと一致しない場合、メッセージは破棄される可能性があります。ただし、バージョンが変更される時期には、ケースバイケースで例外が勧告される場合があります。付録Bで説明されているNTP制御メッセージが実装されており、パケットモードが6(制御)の場合、制御メッセージ手順が呼び出されます。IPおよびUDPヘッダー内の送信元および宛先のインターネットアドレスとポートが正しいピアと照合されます。一致するものがない場合、プロトコルマシンの新しいインスタンスが作成され、関連が起動されます。

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. パケット手順

パケット手順は、メッセージの妥当性を検査し、遅延/オフセットのサンプルを計算し、データをフィルタリングして同期ソースを選択するために他の手順を呼び出します。テスト1は、送信タイムスタンプが同じピアから受信した最後のものと一致しないことを要求します。そうでなければ、そのメッセージは古い重複である可能性があります。テスト2は、発信タイムスタンプが同じピアに送信した最後のものと一致することを要求します。そうでなければ、そのメッセージは順序が乱れているか、偽物か、あるいはさらに悪いものである可能性があります。ブロードキャストモード(5)の場合、見かけの往復遅延はゼロとなり、時刻転送操作の完全な精度は達成できない可能性があります。ただし、達成される精度はほとんどの目的には十分である可能性があります。ポーリング更新手順は引数peer.hostpollとともに呼び出されます(peer.peerpollが変更されている可能性があります)。

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. クロック更新手順

クロック更新手順は、現在のピアについてクロックフィルタ手順によって有効なクロックオフセット、遅延、分散のデータが決定されたときに、受信手順から呼び出されます。クロック選択手順およびクロック結合手順の結果が最終的なクロック補正値THETAであり、これがローカルクロック手順によってローカルクロックの更新に使用されます。これらの手順を生き残った候補がない場合、クロック更新手順はそれ以上何もせずに終了します。

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;

一部のシステム構成では、1秒間隔で並んだタイミングパルス列の形で正確なタイミング情報源が利用できます。通常、これは秒、分、時、日に番号を付けるための、無線時計やNTP自体などのタイムコード情報源に加えて提供されます。これらの構成では、システム変数はパルスの導出元となるソースを参照するように設定されます。無線時計や校正された原子時計などのプライマリ参照ソースをサポートする構成では、それが実際の同期ソースである限り、プライマリクロック手順が使用されるかどうかにかかわらず、ストラタムは1に設定されます。

クロック選択アルゴリズムおよびローカルクロックアルゴリズムの仕様はNTP仕様の不可欠な部分ではありません。同等の性能を提供する他のアルゴリズムが存在する可能性があるためです。ただし、インターネット環境で良好に機能することがわかっているクロック選択アルゴリズムはセクション4で説明され、ローカルクロックアルゴリズムはセクション5で説明されており、それらの使用が推奨されます。セクション4で説明されているクロック選択アルゴリズムは通常、利用可能なすべてのピアの中で最も低いストラタムかつ最小の同期距離を持つピアを選択します。ただし、そのピアがフォールスティッカー(falseticker)であると思われる場合を除きます。その結果、これらのアルゴリズムはすべて、プライマリ参照タイムサーバーに対する最小重み全域木、すなわち階層的マスタースレーブ同期サブネットを構築するように機能します。

3.4.6. プライマリクロック手順

無線時計などのプライマリ参照ソースがホストに接続されている場合、そのクロックが通常のピアとして表現されているかのように、その情報をデータベースに組み込むと便利です。プライマリクロック手順では、クロックは1分に1回程度ポーリングされ、返されたタイムコードがローカルクロックの新しい更新の生成に使用されます。プライマリクロックピアのpeer.timerがゼロまで減分したとき、送信手順は呼び出されません。代わりに、通常この目的のために指定されたASCII文字列を使用して無線時計がポーリングされます。無線時計から有効なタイムコードを受信すると、それはNTPタイムスタンプ形式に変換され、ピア変数が更新されます。peer.leapの値は、利用可能であればタイムコード内の閏秒警告ビットの状態に応じて設定されるか、オペレーターによって手動で設定されます。クロック更新手順が呼び出されたときにsys.refidの値となるpeer.peeraddrの値は、クロックタイプを記述するASCII文字列に設定されます(付録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. 初期化手順

初期化手順は、システム、そのピア、および関連をセットアップして初期化するために使用されます。

3.4.7.1. 初期化手順

初期化手順は、NTPデーモンの再起動または再始動時に呼び出されます。ローカルクロックは再起動時にはおそらく未定義です。ただし、一部の機器では、バッテリーバックアップ式のクロック/カレンダーなど、再起動環境から推定値が利用できます。精度変数は、ローカルハードウェアクロックの固有のアーキテクチャによって決定されます。認証変数は、付録Cで説明されている認証メカニズムが実装されている場合にのみ使用されます。これらの変数の値は、NTP自体の範囲を超える手順を使用して決定されます。

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. 初期化インスタンス化手順

この実装固有の手順は、関連を定義するために初期化手順から呼び出されます。ピアのアドレスとモードは、再起動手順中に読み取られた情報、またはオペレーターコマンドの結果として決定されます。認証変数は、付録Cで説明されている認証メカニズムが実装されている場合にのみ使用されます。これらの変数の値は、NTP自体の範囲を超える手順を使用して決定されます。認証ビットが提案どおりに設定されている場合、適切に認証されたピアのみが同期ソースになることができます。

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. 受信インスタンス化手順

受信インスタンス化手順は、新しいピアが発見されたときに受信手順から呼び出されます。ピア変数を初期化し、関連を起動します。メッセージがクライアントモード(3)で動作するピアからのものである場合、ホストモードはサーバーモード(4)に設定されます。そうでない場合は、対称パッシブモード(2)に設定されます。認証変数は、付録Cで説明されている認証メカニズムが実装されている場合にのみ使用されます。実装されている場合、適切に認証された非構成ピアのみが同期ソースになることができます。

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. プライマリクロックインスタンス化手順

この手順は、プライマリクロックの状態変数をセットアップするために初期化手順から呼び出されます。peer.precisionの値は、無線時計の仕様とハードウェアインターフェースから決定されます。peer.rootdispersionの値は、名目上、無線時計の固有の最大誤差の10倍です。例えば、校正された原子時計では10 μs、WWVBまたはGOES無線時計では10 ms、精度の低いWWV無線時計では100 msです。

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;

校正された原子時計やLORAN-C受信機が関与する一部の構成では、プライマリ参照ソースが秒パルスのみを提供し、秒などの番号付けを導出できる完全なタイムコードを欠いている場合があります。これらの構成では、秒の番号付けは無線時計や他のNTPピアなど、他のソースから導出できます。これらの構成では、プライマリクロック変数は秒番号付けソースではなく、プライマリ参照ソースを反映する必要があります。ただし、秒番号付けソースが故障した場合、または正しく動作していないことがわかっている場合は、プライマリ参照ソースからの更新は、それが故障したかのように抑制されるべきです。

3.4.8. クリア手順

クリア手順は、到達可能性状態の重大な変化またはローカルクロックの潜在的な中断をもたらす何らかのイベントが発生したときに呼び出されます。

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. ポーリング更新手順

ポーリング更新手順は、ポーリング間隔またはピアタイマーの変更をもたらす可能性のある重大なイベントが発生したときに呼び出されます。ホストポーリング間隔(peer.hostpoll)とピアポーリング間隔(peer.peerpoll)の値を検査し、それぞれを有効範囲内にクランプします。ピアが同期用に選択されている場合、その値は計算された適合度の関数としてさらにクランプされます(セクション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. 同期距離手順

距離手順は、ピア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;

DELTAは場合によっては負になる可能性がありますが、EPSILONとLAMBDAは常に正であることに注意してください。

3.6. アクセス制御の問題

NTP設計は、タイムサーバーでの偶発的または悪意のあるデータ変更(改ざん、tampering)または破壊(妨害、jamming)が、一般的に同期サブネット内の他の場所で計時エラーをもたらすべきではないようなものです。ただし、このアプローチの成功は、冗長タイムサーバーと多様なネットワークパス、および改ざんまたは妨害が同時に同期サブネット全体の多くのタイムサーバーで発生しないという仮定に依存しています。原則として、サブネットの脆弱性は、信頼できることが知られているタイムサーバーの選択を通じて、およびそれらのタイムサーバーのみが同期ソースになることを許可することによって設計できます。付録Cで説明されている認証手順は、これを実施するための1つのメカニズムを表します。ただし、暗号化アルゴリズムは非常にCPU集約的であり、送信手順の説明で言及されているような予防措置を講じない限り、精度を深刻に低下させる可能性があります。

NTP自体の必須機能ではありませんが、一部の実装には、不正アクセスを防ぎ、どのピアがローカルクロックを更新できるかを制御するアクセス制御機能が含まれる場合があります。この目的のために、3つのアクセスカテゴリを区別することが有用です:信頼できるものとして事前承認されたもの、友好的なものとして事前承認されたもの、およびその他すべての(事前承認されていない)アクセス。おそらく、事前承認は構成ファイルのエントリまたはKerberos [STE88]のような何らかのチケット管理システムによって達成されます。このモデルでは、信頼できるアクセスのみがピアが同期ソースになることをもたらすことができます。友好的なアクセスはピアが同期ソースになることをもたらすことはできませんが、NTPメッセージとタイムスタンプは指定どおりに返されます。

タイムサーバー自体の存在を隠すことが意図されていない限り、事前承認されていないアクセスを制限することによって生じる秘密のクロックを維持することは有用ではないようです。行儀の良いインターネットホストは、サービスが実装されていない場合やリソースが利用できない場合にICMPサービス利用不可エラーメッセージを返すことが期待されます。ただし、NTPの場合、必要なリソースは最小限であるため、クロックを読み取ることのみを目的とした要求を制限する必要はほとんどありません。次に、単純で効果的なアクセス制御メカニズムは、対称モードまたはクライアントモード(モード1、2、および3)で事前構成されたすべての関連を信頼できるものと見なし、他のすべての関連(事前構成されているかどうかに関係なく)を友好的と見なすことです。

より包括的な信頼モデルが必要な場合、設計は32ビットインターネットアドレス、32ビットマスク、および3ビットモードで構成される各エントリを持つアクセス制御リストに基づくことができます。ソースアドレス(pkt.peeraddr)とエントリのマスクの論理ANDがエントリの対応するアドレスと一致し、モード(pkt.mode)がエントリのモードと一致する場合、アクセスが許可されます。それ以外の場合、ICMPエラーメッセージが要求者に返されます。マスクの適切な選択により、モードによる要求を個々のアドレス、特定のサブネットまたはネットアドレスに制限するか、まったく制限しないことが可能です。次に、アクセス制御リストは、どのピアが関連を作成できるかを制御するフィルターとして機能します。