简单网络时间协议

**網路時間協定**（英語：**N**etwork **T**ime **P**rotocol，縮寫：**NTP**）是在資料網路潛伏時間可變的電腦系統之間通過封包交換進行時鐘同步的一個網路協定，位於 OSI 模型的應用層。自 1985 年以來，NTP 是目前仍在使用的最古老的網際網路協定之一。NTP 由德拉瓦大學的 David L. Mills 設計。

NTP 意圖將所有參與電腦的協調世界時（UTC）時間同步到幾毫秒的誤差內。它使用 Marzullo 演算法的修改版來選擇準確的時間伺服器，其設計旨在減輕可變網路延遲造成的影響。NTP 通常可以在公共網際網路保持幾十毫秒的誤差，並且在理想的區域網路環境中可以實現超過 1 毫秒的精度。不對稱路由和擁塞控制可能導致 100 毫秒（或更高）的誤差。

該協定通常描述為一種主從式架構，但它也可以用在對等網路中，對等體雙方可將另一端認定為潛在的時間源。傳送和接收時間戳採用使用者資料報協定（UDP）的通訊埠 123 實現。這也可以使用廣播或多播，其中的客戶端在最初的往返校準交換後被動地監聽時間更新。NTP 提供一個即將到來閏秒調整的警告，但不會傳輸有關本地時區或夏時制的資訊。

當前協定為版本 4（NTPv4），這是一個 RFC 5905 文件中的建議標準。它向下相容指定於 RFC 1305 的版本 3。

## 歷史

1979 年，網路時間同步技術在紐約的國家電腦會議上於執行在跨大西洋衛星網路的網際網路服務上公開演示，這可能是該技術的首次公開演示。該技術後在 1981 年網際網路工程筆記（IEN）173 中描述，並根據 RFC 778 文件開發為一個公開協定。該技術首先被部署在一個本地網路，作為 Hello 路由協定的一部分，並在 Fuzzball（一個用於網路原型的實驗作業系統，已執行多年）中實現。

現在還有其他的相關網路工具。這包括 Daytime 和 Time 協定用以記錄事件時間，以及網際網路控制訊息協定和 IP 時間戳選項（RFC 781）。更多完整的同步系統，雖然缺乏 NTP 的資料分析和時鐘規律演算法，包括 Unix 守護行程 timed 在內的軟體其使用選舉演算法為所有客戶端指定伺服器。以及數字時間同步服務（Digital Time Synchronization Service，DTSS）使用類似構 NTP 階層模型的伺服器階層。

1985 年，NTPv0 被實現於 Fuzzball 和 Unix，文件化於 RFC 958 的 NTP 封包頭、和往返延遲和偏移計算都被留存至 NTPv4。儘管當時可用的電腦和網路相對較慢，但在跨大西洋鏈路上也取得了優於 100 毫秒的精度，在乙太網路網路上準確度為幾十毫秒。

1988 年，一個更完整的 NTPv1 協定規範及相關的演算法發表在 RFC 1059。它利用了 RFC 956 的文獻中的實驗結果和時鐘濾波演算法，並是第一個描述客戶端 - 伺服器和對等網路模型的版本。1991 年，NTPv1 架構、協定和演算法通過 David L. Mills 在 IEEE Transactions on Communications 發布的一篇文章得到了工程學界的更廣泛關注。

1989 年，RFC 1119 發布通過有限狀態機定義的 NTPv2，使用虛擬碼來描述其操作。它引入了一個管理協定和加密認證方案，它們留存至 NTPv4。社群批評 NTP 的設計缺乏形式正確性原則。他們的替代設計包括 Marzullo 演算法，其修改版本已及時添加到 NTP。這個時代的大多數演算法也大部分留存至 NTPv4。

1992 年，RFC 1305 定義了 NTPv3。該 RFC 包括一個對所有錯誤來源的分析，從參考時鐘至最終客戶端，這使幫助度量和選擇最佳伺服器成為可能（在其中幾個候選者反對的情況下）。廣播模式被引入。

在接下來的幾年裡，隨著新特性的添加和演算法的改進，顯然還需要一個新的協定版本。2010 年，RFC 5905 發布了一個對 NTPv4 的建議規範，但該協定自那時以來已經顯著改變，截至 2014 年，更新的 RFC 尚未發布。在 Mills 從德拉瓦大學退休後，該參考實現目前由 Harlan Stenn 保持為一個開放原始碼專案。

## 時鐘層

NTP 使用一個分層、半分層的時間源系統。該層次的每個級別被稱為「stratum」，頂層分配為數字 0。一個通過階層 n 同步的伺服器將執行在階層 n + 1。數字表示與參考時鐘的距離，用於防止階層中的迴圈依賴性。階層並不總是指示品質或可靠性；在階層 3 的時間源得到比階層 2 時間源更高的時間品質也很常見。電信系統對時鐘層使用不同的定義。以下提供了階層 0、1、2、3 的簡要描述。

**階層 0（Stratum 0）**
: 這些是高精度計時裝置，例如原子鐘（如銫、銣）、GPS 時鐘或其他無線電時鐘。它們生成非常精確的脈衝秒訊號，觸發所連接電腦上的中斷和時間戳。階層 0 裝置也稱為參考（基準）時鐘。

**階層 1**
: 這些與階層 0 裝置相連、在幾微秒誤差內同步系統時鐘的電腦。階層 1 伺服器可能與其他階層 1 伺服器對等相連，以進行完整性檢查和備份。它們也被稱為主要（primary）時間伺服器。

**階層 2**
: 這些電腦通過網路與階層 1 伺服器同步。提供階層 2 的電腦將查詢多個階層 1 伺服器。階層 2 電腦也可能與其他階層 2 電腦對等相連，為對等組中的所有裝置提供更健全穩定的時間。

**階層 3**
: 這些電腦與階層 2 的伺服器同步。它們使用與階層 2 相同的演算法進行對等和資料採樣，並可以自己作為伺服器擔任階層 4 電腦，以此類推。

階層的上限為 15；階層 16 被用於標識裝置未同步。每台電腦上的 NTP 演算法相互構造一個貝爾曼 - 福特演算法最短路徑生成樹，以最小化所有客戶端到階層 1 伺服器的累積往返延遲。

## 時間戳

NTP 使用 64 位元的時間戳，其中 32 位元表示秒，32 位元表示秒的小數，給出一個每 232 秒（136 年）才會翻轉的時間尺度，理論解析度 2−32 秒（233 皮秒）。NTP 以 1900 年 1 月 1 日作為開始時間，因此第一次翻轉將在 2036 年 2 月 7 日發生。

NTP 的未來版本可能將時間表示擴充到 128 位元：其中 64 位元表示秒，64 位元表示秒的小數。當前的 NTPv4 格式支援「時代數字」（Era Number）和「時代偏移」（Era Offset），正確使用它們應該有助於解決日期翻轉問題。據 Mills 稱：「64 位元的秒小數足以分辨光子以光速通過電子所需的時間。64 位元的秒足以提供明確的時間表示，直到宇宙變暗。」

## 時鐘同步演算法

典型的 NTP 客戶端將定期輪詢不同網路上的三個或更多伺服器。為同步其時鐘，客戶端必須計算其時間偏移量和來回通訊延遲。時間偏移「θ」定義為：

　
: $\theta ={(t_{1}-t_{0})+(t_{2}-t_{3}) \over 2}$ ，

往返延遲「δ」為：

　
: $\delta ={(t_{3}-t_{0})-(t_{2}-t_{1})}$ ，

其中：

　
: _t_0 是請求封包傳輸的客戶端時間戳，

　
: _t_1 是請求封包回覆的伺服器時間戳，

　
: _t_2 是回應封包傳輸的伺服器時間戳

　
: _t_3 是回應封包回覆的客戶端時間戳。

「θ」和「δ」的值通過過濾器並進行統計分析。異常值被剔除，並從最好的三個剩餘候選中匯出估算的時間偏移。然後調整時脈頻率以逐漸減小偏移，建立一個回饋迴路。

當客戶端和伺服器之間的輸入和輸出路由都具有對稱的標稱延遲時，同步是正確的。如果路由沒有共同的標稱延遲，則將差異取半作為測量誤差。

## 軟體實現

### 參考實現

NTP 參考實現連同協定的開發已持續發展了 20 多年。隨著新功能的添加，向下相容性仍保持不變。它包含幾個敏感的演算法，尤其是時鐘規律，在同步到使用不同演算法的伺服器時可能會發生錯誤。該軟體已移植到幾乎各個計算平台，包括個人電腦。它在 Unix 上執行名為 ntpd 的守護行程，或在 Windows 上執行為一個 Windows 服務。支援參考時鐘，並且以與遠端伺服器相同的方式對偏移進行過濾和分析，儘管它們通常更頻繁地輪詢。

### SNTP

一個不太複雜的 NTP 實現，使用相同的協定但不需要儲存較長時間的狀態，也稱簡單網路時間協定（Simple Network Time Protocol，SNTP）。它使用在某些嵌入式系統和不需要高精度時間的應用中。

### Windows 時間服務

從 Windows 2000 起的所有 Microsoft Windows 版本都包括 Windows 時間服務（W32Time），其具有將電腦時鐘同步到 NTP 伺服器的能力。

W32Time 服務最初是為實現 Kerberos 第五版的身分驗證協定，它需要誤差 5 分鐘內正確時間值以防止重放攻擊。Windows 2000 和 Windows XP 中只實現了簡單的 NTP，並在幾個方面違反了 NTP 第 3 版的標準。從 Windows Server 2003 和 Windows Vista 開始，已包括符合完整 NTP 的實現。微軟稱 W32Time 服務不能可靠地將同步時間保持在 1 至 2 秒的範圍內。如果需要更高的精度，微軟建議使用其他 NTP 實現。

Windows 10 與 Windows Server 2016 由版本 1607 開始，提供高精度的系統時間，支援 1ms 的時間精度。

### Ntimed

一個新的 NTP 客戶端 **ntimed** 由 Poul-Henning Kamp 在 2014 年開始編寫。新的實現由 Linux 基金會贊助，作為參考實現的一個替代，因為它決定更容易地從頭開始編寫新的實現，而不是修復現有大型代碼庫的現有問題。截至 2015 年 6 月，它尚未正式發布，但 **ntimed** 可以可靠地同步時鐘。**ntimed** 在 Debian 和 FreeBSD 上工作，但也被移植到 Windows 和 Mac OS。

## 閏秒

在閏秒事件的當天，ntpd 從設定檔、附加參考時鐘或遠端伺服器收到通知。因為時間必須是單調遞增，所以閏秒的插入方式為：23:59:59、23:59:60、00:00:00。雖然時鐘實際上在事件期間停止，但任何查詢系統時間的行程都會使它增加微小的量，以保持事件的順序。如果必要，在序列中刪除閏秒的形式為：23:59:58、00:00:00，跳過 23:59:59。

## 2038 年問題

## 安全問題

2014 年底，幾個安全問題被發現。在以前，研究人員發現 NTP 伺服器可能受到中間人攻擊的影響，除非封包被加密和簽章以驗證身分。但這所涉及的計算量在繁忙的伺服器上可能是不切實際的，尤其是容易遭遇阻斷服務攻擊。NTP 訊息欺騙可以被用來偏移客戶端電腦上的時鐘，並配合過期的加密金鑰來完成其他攻擊。可能受到偽造 NTP 訊息影響的服務包括 TLS、DNSSEC、各類快取方案（例如 DNS 快取）、比特幣以及許多持久登入方案。

有著 25 年以上歷史的 NTP 代碼庫中的參考實現中只發現了幾個其他的安全問題，但最近出現的幾個引起了人們的強烈關注。該協定正在對其整個歷史進行修訂和審查。截至 2011 年 1 月，在 NTP 規範中沒有安全修訂，也沒有在 CERT 協調中心的報告。幾年來，該參考實現的當前代碼庫已經被多個來源進行安全審計，在當前發布的軟體中沒有已知的高風險漏洞。

多種 NTP 伺服器誤用和濫用實踐的存在導致了對網路時間協定（NTP）伺服器的破壞或劣化。

NTP 已經被用於分散式阻斷服務（DDoS）攻擊，方法是將一個具有偽造的返回位址的小的查詢傳送到 NTP 伺服器。類似 DNS 放大攻擊，伺服器將以比攻擊者傳送資料量大很多倍的資料量回覆給偽造的目標。為了避免參與攻擊，伺服器可以組態為忽略外部查詢，或者升級到 4.2.7p26 或更高版本。

一個緩衝區溢位漏洞已經被發現，並自 2014 年 12 月 19 日提供修補程式。漏洞覆蓋 NTP 第四版所有版本，自 4.2.8 修復。蘋果公司首次使用自動更新功能解決該問題，不過這僅適用最近的 MacOS 版本。在 10.6.8 版本中，使用者可以手動修復伺服器版本，以及普通使用者可以在系統偏好設定 - 日期和時間中關閉自動更新時間。研究人員認為該協定的設計是相當好的，缺陷出現在協定的實現中。某些錯誤很基礎，例如常式中缺少返回語句，這可能導致某些執行在 root 權限下的 NTP 版本被利用以無限制的存取系統。守護行程不使用 root 權限的系統（例如 BSD）不受此缺陷的影響。

## 參見

* Allan variance
* 時鐘網路
* 原子時
* NITZ
* NTP pool
* Ntpdate
* OpenNTPD
* 精確時間協定（IEEE 1588 PTP）

## 備註

## 參考資料

## 拓展閱讀

* Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4). RFC 5907.
* Network Time Protocol (NTP) Server Option for DHCPv6. RFC 5908.

## 外部連結

* 官方網站 （英文）
* 公共 NTP 伺服器列表 （頁面存檔備份，存於網際網路檔案館） （英文）
* NTP 節點鏡像群 （頁面存檔備份，存於網際網路檔案館）
* IETF NTP working group （頁面存檔備份，存於網際網路檔案館）
* Time and NTP paper （頁面存檔備份，存於網際網路檔案館）
* NTP Survey 2005
* Current NIST leap seconds file compatible with ntpd
* David L. Mills, A Brief History of NTP Time: Confessions of an Internet Timekeeper （頁面存檔備份，存於網際網路檔案館）
* Keith Marzullo's homepage （頁面存檔備份，存於網際網路檔案館）
* NTP Server Test Online Tool （頁面存檔備份，存於網際網路檔案館）