RFC-0228:FDomain:遠端控制 Fuchsia 目標

RFC-0228:FDomain:Fuchsia 目標的遠端控制
狀態已接受
區域
  • 開發人員
  • 測試
說明

推出新通訊協定,可在遠端 Fuchsia 裝置上執行任意 FIDL

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2023-05-22
審查日期 (年-月-日)2023-10-04

摘要

本提案概述 FDomain,這是一種新機制,可透過開發主機與 Fuchsia 目標上的 FIDL 服務通訊。FDomain 將支援 FFX 指令列工具的功能,取代目前使用的 Overnet 通訊協定。

提振精神

FFX 可提供對元件階層中公開的 FIDL 通訊協定進行任意存取的權限。這讓開發人員可以輕鬆開發各種工具和外掛程式,並且有助於實現自動化整合測試和類似 sl4f 的功能。

FFX 會透過 Overnet 與裝置上的 FIDL 通訊。Overnet 是一種對等通訊協定,可讓 Fuchsia 核心句柄透過網路共用。Fuchsia 裝置可以透過 overnet 將管道傳送至開發人員主機,而主機端 Fuchsia 模擬程式庫可讓該管道盡可能在裝置上使用。Rust 的 FIDL 繫結可使用這些管道在主機上編譯,因此可用於從主機到目標的通訊。

Overnet 的做法有幾個缺點:

  • 主機上的核心句柄模擬功能必然不完美。句柄僅限於單一程序,因此必須透過其他 Overnet 連線,才能在主機上的程序之間移動句柄。某些機制 (例如 zx_channel_call) 完全缺少。
  • 透過句柄回報錯誤的設計並非以複雜且可能出錯的網路堆疊為前提。任何 Overnet 句柄發生的服務中斷問題,最後都會以 PEER_CLOSED 回報,且不會提供其他資訊。
  • 核心句柄並非設計用於透過網路進行 Proxy。在設計和實作 Overnet 時,並未充分諮詢核心團隊。因此,目前的 API 無法正確實作某些功能 (例如物件信號),而且可能無法繼續實作。
  • 由於我們承諾要代理句柄,並讓這些句柄在主機上遠端執行的行為與在目標上本機執行的行為大致相同,因此幾乎不可能支援特定物件 (例如 VMOs)。

除了上述方法的問題外,Overnet 的具體設計和實作方式也有重大缺點:

  • Overnet 的低階通訊協定 (用於協調串流) 是使用 FIDL 編寫,但並未按照 FIDL 的設計方式使用。相反地,FIDL 扮演的角色類似 protobuf,可指定封包的二進位形狀,但在傳輸方面沒有任何角色。這會讓 Overnet 容易受到 FIDL 中線路格式變更的影響,而 FIDL 團隊無法管理這種情況,因為 FIDL 使用者可透過一般傳輸機制運作。
  • Overnet 的程式碼過於複雜,且使用非同步模式,這種模式已證實極難進行偵錯。
  • Overnet 是設計為點對點的網狀網路,這會增加額外的複雜度,但在現今的用途中並不需要。

相關人員

協助人員:

hjfreyer@

審查者:

abarth@ cpu@ ianloic@ mgnb@ mkember@ slgrady@ wilkinsonclay@

社會化:

我們在 Fuchsia 工具團隊內討論了 FDomain 的設計,並向核心和 FIDL 團隊展示基本提案,兩者都提供了意見回饋。

需求條件

FDomain 應允許主機連線至 Fuchsia 目標,並在設定後,透過 FIDL 與服務通訊,這與目標上的元件類似。與 Overnet 一樣,這項功能將透過支援與較低層級核心原始碼 (具體來說是管道) 的通訊來實作,之後再在該通訊上實作 Fidl 繫結。Overnet 目前支援代理管道、Socket、事件和事件組合,因此可用於與任何不傳遞這些類型以外句柄的 FIDL 通訊協定進行通訊。此處會將 FDomain 指定為支援相同的句柄類型集合,但必須有明確的路徑可延伸至支援最多的 Fuchsia 句柄類型集合,希望能支援所有句柄類型。VMOs 對多個應用程式特別有用,因此很可能會成為未來的當務之急。請注意,並非所有句柄類型的所有作業都適合在遠端情境中顯示 (例如,遠端對 VMOs 進行對應和取消對應並無實用性),但 FDomain 會盡可能針對每個句柄類型提供最多能力。

與上述情況相關,FDomain 應透過網路呈現抽象化,讓類似功能可直接使用核心介面,但不會違反這些介面設計時的假設。Overnet 的許多限制都是因為其轉移和 Proxy 模式,也就是將句柄呈現給主機的模式,與原始支援集合以外的句柄類型不相容。

FDomain 的設計會假設在目標和主機之間交換可靠且有序的資料包。在 TCP 或 SSH 連線上加上額外的通訊協定層,或在 USB 大量端點上實作這項功能,應該不難。這些傳輸方式的詳細資訊不在核心通訊協定的範圍內。

與 Overnet 不同,FDomain 不會提供任何設施來自動探索或建立與目標的連線。FFX 已不再依賴 Overnet 對這些函式的實作方式。與 Overnet 不同,FDomain 純粹是端對端通訊協定,而非網狀網路通訊協定。

FDomain 應能偵測通訊協定版本不相容的問題,讓工具能輕鬆找出這類問題。我們很難預測未來可能會出現哪些相容性問題,但一般來說,FDomain 應盡可能與不相容的版本互通。如果 FDomain 直接使用 FIDL,則必須使用 FIDL 團隊可自行維護的設施,確保線路格式相容性,並在可用時回退。

使用者 API 相關規定

雖然 FDomain 通訊協定和直接公開的語意對於正確設計至關重要,但直接向使用者顯示的 API 也有重要限制。這些檔案會以主機端 Rust 箱的形式提供函式,讓您從主機連線至 FDomain。

雖然 FDomain 刻意不提供 Zircon 的確切模擬功能,但也必須支援不會中斷 Zircon 本身「程式設計模型」的繫結。也就是說,從主機使用 FDomain 中的句柄,與使用 Zircon 程式碼中的句柄,應該不會有明顯或令人意外的差異。

如要確定 Zircon 和 FDomain 主機端 API 之間的差異是否安全,則屬於 API 審查的範疇,超出本 RFC 的範圍。不過,我們會指出 FDomain 為了達成目標,必須做出的幾項預期差異:

  • 句柄的 FDomain 動作通常需要 IO,因此我們無法實作 Zircon API 的非阻斷語意。Zircon 句柄的 Rust 繫結包含每個句柄的較高層級「異步」版本,這些版本會使用 Rust 的未來系統處理非同步作業。我們很可能會在該層實作 FDomain 主機端程式庫,但不會提供可直接存取非阻斷作業的較低層級句柄。這會導致物件建立作業出現一些語意上的差異。此外,通常只使用原始句柄而非非同步版本的作業 (例如在 FIDL API 中建立端點),無法透過 FDomain 做出此區別。
  • FDomain 控點必須是遠端連線 FDomain 的一部分。在 API 中代表這些的 RAII 物件將由連線物件產生,而非在原地建構,且可能具有行為或型別屬性,反映其生命週期與連線生命週期相關聯的事實。(由於設計的這部分曾引起一些爭論,我們在此強調,本 RFC 並未提出任何具體建議)。
  • FDomain 作業會傳回不同的錯誤類型,反映出遠端通訊協定可能發生的更複雜失敗模式 (以及我們在遠端情境中提供的更豐富錯誤回報)。Rust 的錯誤處理程式設計慣例,應可讓使用者輕鬆瀏覽這項變更。

設計

從概念上來說,FDomain 是一組句柄,可透過一組作業進行遠端操控。FDomain 通訊協定會透過 FIDL 通訊協定呈現這些作業。

FDomain 會連線至主機並由主機操控,而 FDomain 和主機之間的連線就是 FDomain 連線。FDomain 最多只有一個已連線的主機,也就是說,主機不應觀察到其所控 FDomain 中的句柄遭其他執行者關閉、寫入或讀取。雖然工作階段恢復功能可能是未來值得探討的領域,但我們在本文件中假設主機一律會連線至新的 FDomain,也就是預先填入的句柄集合,且當主機中斷連線時,FDomain 會遭到銷毀,其中的句柄也會關閉。

核心通訊協定

由於 FDomain 的目的是在不支援核心句柄轉移的連線 (通常是給不支援的電腦主機) 中呈現 FIDL,因此 FDomain FIDL 本身無法在方法參數或傳回值的任何位置使用句柄。具體來說,resource 關鍵字絕對不得出現在 FDomain 的 FIDL 規格中。

由於 FDomain 的通訊協定訊息是設計用於透過管道以外的媒體傳輸,因此我們假設沒有訊息大小限制,因此不會限制通訊協定中的向量和其他結構體大小。

必須啟用錯誤回報功能

所有方法都必須能夠傳回錯誤。由於錯誤類型可擴充,因此我們日後可以為所有方法新增錯誤條件。

此外,這會強制所有方法為雙向方法,也就是說,在 (RFC-0138) RFC-0138 中指定的不明互動處理方式,一律會傳回不明序號的錯誤訊息給主機。這樣一來,我們就能更輕鬆地實作回溯相容性情節,因為根據「聰明的傳送端,昏暗的接收端」原則,所有相容性錯誤都會顯示在主機上。

句柄 ID 分配

主機會透過 ID 參照 FDomain 中的每個句柄。這些 ID 會在通訊協定中以 32 位元未簽署整數編碼,但不應是直接向主機公開的實際核心句柄編號。

為減少在通訊協定中執行作業所需的往返次數,我們會盡可能使用主機端 ID 分配。也就是說,如果某項作業會導致 FDomain 產生新的句柄,例如主機要求建立新管道或 Socket 時,主機會提供新管道將使用的 ID 號碼。這可讓通訊協定中的管道線程更完善,因為主機可以提交要求,要求在同一個交易中建立句柄,並對該句柄執行第一個作業,而不必等待每個作業的回覆。

這項政策的例外狀況是從管道讀取資料,這可能會產生任意數量的句柄。要求主機為可能透過讀取管道產生的句柄分配 ID,會導致提交讀取要求的過程變得難以處理,而且需要指定複雜的封鎖語意,才能與串流讀取相容。因為讀取管道產生的此類帳號代碼會由 FDomain 本身指派 ID。

啟動

主機連線至 FDomain 時,系統會假設從內容填入一些起始句柄。這些句柄類似於會提供給新啟動元件的句柄。FDomain 可讓主機以支援該比喻的方式擷取這些句柄,保留使用者直覺,即 FDomain 是元件拓撲中的節點。

處理作業

FDomain 會支援在 FDomain 中建立新的 Socket、管道、事件組合和事件。它也支援關閉和複製句柄,以及將句柄替換為具有不同權限的新版本,這與 zx_handle_replace 類似。

對於可能需要重試的讀取、寫入和其他作業 (也就是可能會因 ZX_ERR_SHOULD_WAIT 而失敗),FDomain 通訊協定會實作掛起的 get 作業,並在目標端執行必要的通訊埠等待作業。這樣一來,就能避免要求主機手動設定通訊埠時,發生笨拙且大量往返的情況。

穩定的線路編碼

FIDL 的交易格式並非用於在 Fuchsia 管道外傳輸。當 FIDL 需要在其他情境中序列化時,建議使用持久化標頭,如 RFC-0120 所述。

永久性標頭的常見用途是用於將儲存在休眠狀態的 FIDL 資料。雖然 FDomain 訊息不會像一般情況一樣透過管道傳送,但仍應以一般要求/回應的方式傳送和接收。

持久標頭和交易標頭都包含魔術數字和一系列相容性標記。交易標頭也會新增「動態旗標」欄位,用於新的彈性方法通訊協定,以及交易 ID 和方法序號,這兩者都是 FDomain 訊息所需的資訊。

最初的計畫是使用持續性標頭,並手動新增交易 ID 和方法序號,建立新的組合式標頭格式。FDomain 的所有方法都很靈活,因此動態標記欄位的值可以是隱含的。

在審查過程中,FIDL 團隊發現,合併的標頭與交易標頭非常相似,因此他們建議只使用一般 FIDL 交易標頭。本 RFC 將採用該提案,並瞭解 FIDL 維護者接受任何可能會改變交易標頭格式演進壓力的做法。

實作

您可以在單一 Rust crate 中提供目標端 FDomain 支援,而第一個整合器可能會是遠端控制服務。

您可以先提供裝置的傳輸機制,例如透過舊版 Overnet 通訊協定分發的 Socket,或是在上述通訊協定的較低層級中新增電路。從這裡開始,我們可以選擇透過 SSH 實作直接傳輸,或是直接實作專屬傳輸。

目標端需要進行更多工作。目前透過 Overnet 的 host 端 FIDL 通訊,會依賴 Fuchsia 的核心原始碼模擬,該模擬碼位於 fuchsia-async 程式庫中。FDomain 是專為非 Fuchsia 目標而設計,不應依賴此模擬功能。為此,我們需要為 FDomain 使用者提供一種新的 Rust FIDL 繫結,讓他們能夠在 FDomain 的基礎類型集合上運作。這些原始元素會代表 FDomain 中的個別句柄,但不會嘗試完美模擬 Fuchsia 核心介面。事實上,建議這些句柄明確指出它們是遠端物件的句柄,因為這些 API 可公開與該情境相關的獨特失敗情況錯誤。與 Overnet 相比,如果傳輸或內部通訊協定導入作業發生任何錯誤,使用者就會收到未經區分的 ZX_ERR_PEER_CLOSED

遷移

FDomain 目標端實作項目會依賴 Zircon API 來操作句柄。由於 Overnet 提供這些 API,因此應該可以在 Overnet 節點上執行 FDomain。

這是我們在 FFX 中遷移的初始策略。我們會在 FFX 守護程序中代管 FDomain,從舊版 Overnet 連線取得 RCS 代理程式,並在其命名空間中公開。主機可以使用魔法啟動子,要求新的 Socket 連線成為一般 Overnet 連線或 FDomain 連線 (目前 Overnet 連線會以魔法字串「CIRCUIT\0」開頭,因此我們會在這個位置尋找魔法字串)。

從這裡開始,FFX 工具架構將能夠為工具提供 Overnet 或 FDomain 代理程式,如果建立兩個連線,則可能會同時提供這兩種代理程式。這樣一來,我們就能輕鬆將個別工具遷移至 FDomain 主機端程式碼。在守護程本身中代管的服務不便透過 FDomain 使用,但其他架構壓力已促使這類服務消失或重新思考。

由於 FFX 外掛程式通常只會使用 FIDL Proxy,因此我們應該可以遷移至目標直接代管的 FDomain 連線,而無須進一步變更。我們應該可以同時支援 Overnet 和直接 FDomain 連線,就像我們在電路切換 Overnet 遷移作業中所做的那樣。

成效

FDomain 現有的應用程式都是目前由 Overnet 處理的開發人員工具。由於開發人員工具通常沒有嚴格的延遲或頻寬需求,因此從未認真評估 Overnet 效能。

不過,Overnet 的效能問題已經開始浮現,因此至少應設立效能預期值。因此,您應該測試下列項目:

  • 與 Socket 通訊的傳輸量、封鎖和串流。
  • 直接對簡單介面發出 FIDL 呼叫時的延遲時間。
  • 重複發出 FIDL 呼叫時的延遲時間,其中每個呼叫都是在回覆先前呼叫的管道上發出。這會建立如何在效能敏感應用程式中顯示處理建立作業。

回溯相容性

FDomain 代表與 Overnet 通訊協定完全切割。在完全拆除 Overnet 之前,我們會保留這兩種通訊協定一段時間,以便維持回溯相容性。

安全性考量

FDomain 核心通訊協定不會直接處理驗證,且設計目的是允許任意存取其控管的句柄集合。應以可安全地與存取工具共用句柄,且在可安全提供此類存取權的環境中,部署這類存取權。這些考量已納入 Overnet 的考量,因此接管其應用程式應可確保這些考量。

在權限提升問題日益嚴重的未來應用程式中 (Overnet 及其替代方案本質上都是權限最高的應用程式),我們可以想像一種攻擊類型,即攻擊者利用 FDomain 操控句柄,但該句柄實際上並未位於 FDomain 中。按照本文件中的指南,在 FDomain 通訊協定中不要使用實際的核心句柄號碼做為句柄 ID,應可減輕這類攻擊的所有明顯途徑。由於 Rust 的語言擁有強大的擁有權語義,因此在 Rust 中實作 FDomain 也能更輕鬆地防禦這些攻擊。

隱私權注意事項

FDomain 適用於開發中的裝置。不應有任何隱私權疑慮。即使在意料之外的應用程式中,也不太可能會出現隱私權疑慮,除非是來自安全性疑慮。

測試

我們已成功使用 Overnet 在單一程序中執行整合測試,其中通訊協定狀態的多個例項彼此相連,只要將一個例項的位元組輸出內容提供給另一個例項的輸入內容即可。這應該可讓我們測試各種設定和拓樸。

涉及實際網路元件的整合測試較為困難,但可能不會帶來太多額外價值,且可能會引入複雜實體測試設定中固有的不穩定風險。我們會在基礎架構可立即使用的設施中進行這類測試,並隨著可用性變更擴大測試範圍。

說明文件

除了以常規方式記錄實作可交付項目外,樹狀結構中也應包含通訊協定規格的實際形式。這項作業一開始將主要包含本 RFC 設計部分的文字,但可視需要納入通訊協定擴充功能。

缺點、替代方案和未知事項

與 Overnet 相比,FDomain 可大幅降低複雜度。也必須從頭開始實作,且與 Overnet 不相容。

遠端工具通訊協定的替代設計空間非常廣泛,因此假設需要新通訊協定,列舉替代方案的工作會非常耗時且效率不彰。我們可以選擇使用自訂二進位格式,而非 FIDL,藉此進一步掌控相容性。或者,我們可以提供 IPC 機制,不代表任何核心物件,而是為工具提供特定的自訂介面。

如果 Fuchsia 的用途擴大,FDomain 可能會出現其他用途,因為我們希望減少重複工作並提供較少的通訊協定。我們可以想像在資料中心中,Fuchsia 伺服器使用 FDomain 取代 SSH 做為遠端管理傳輸工具,而非單純的診斷工具。這裡所述的 FDomain 通訊協定應該可用於這類情況,但無法確定這類工作會如何擴展。

既有技術與參考資料

如要進一步瞭解該通訊協定,請參閱 Fuchsia 樹狀結構中的 Overnet 說明文件