| 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 繫結可在主機上使用這些管道編譯,因此 FIDL 可用於從主機與目標通訊。
Overnet 的做法有幾項缺點:
- 主機上的核心控制代碼模擬作業必然不完美。控制代碼僅限於單一程序,如要在主機上的程序之間移動控制代碼,必須建立額外的 Overnet 連線。完全缺少某些機制,例如 zx_channel_call。
- 控制代碼的錯誤回報功能並非以複雜且容易出錯的網路堆疊為基礎設計。如果 Overnet 控制代碼發生任何服務中斷問題,系統最終會回報為 PEER_CLOSED,且不會提供進一步資訊。
- 核心控制代碼並非設計為透過網路進行 Proxy 處理。Overnet 的設計和實作過程未充分諮詢核心團隊。因此,目前無法透過現有 API 正確導入部分功能 (例如物件信號),而且未來可能也無法導入。
- 承諾要處理 Proxy 並讓這些 Proxy 在主機上遠端運作,與在目標上本機運作的方式大致相同,這使得支援特定物件 (例如 VMO) 幾乎完全不可能。
除了方法上的問題,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 目前支援 Proxy 管道、插槽、事件和事件配對,因此可用於與任何不將控制代碼傳輸至這些類型以外的 FIDL 通訊協定通訊。FDomain 會在此指定為支援同一組控制代碼類型,但必須有明確的擴充路徑,才能支援最大量的 Fuchsia 控制代碼類型,希望可以支援所有控制代碼類型。VMO 對於多項應用程式特別重要,而且很可能成為未來工作的首要領域。請注意,並非所有控制代碼類型上的所有作業,都適合在遠端環境中呈現 (例如,遠端對應和取消對應 VMO 並不實用),但 FDomain 會盡可能呈現與各控制代碼類型相關的能力。
與上述內容相關,FDomain 應提供線路上的抽象化,允許類似的功能直接使用核心介面,但不會破壞這些介面設計時的假設。Overnet 的許多限制,都是因為其將控制代碼呈現給主機的轉移和 Proxy 模型,與原始支援集以外的控制代碼類型不相容。
FDomain 的設計會假設目標和主機之間能夠交換可靠的排序資料包。在 TCP 或 SSH 連線的基礎上,實作這個功能應該很簡單,只要新增一小層通訊協定即可,也可以在 USB 大量端點的基礎上實作。這些傳輸的具體細節不在核心通訊協定的範圍內。
與 Overnet 不同,FDomain 不會提供任何設施,自動探索或建立與目標的連線。FFX 已經不依賴 Overnet 實作這些函式。與 Overnet 不同,FDomain 是純粹的端對端通訊協定,而非網狀網路通訊協定。
FDomain 應能偵測通訊協定版本不相容的問題,讓工具輕鬆找出相關問題。很難預測日後可能會出現哪些相容性問題,但一般來說,FDomain 應盡可能與不相容的版本互通。如果 FDomain 直接使用 FIDL,就必須使用 FIDL 團隊本身維護的設施,確保線路格式相容性,並在可用的情況下回溯。
使用者 API 的相關規定
FDomain 通訊協定和直接公開的語意對於正確設計而言非常重要,但直接向使用者顯示的 API 也存在重要限制。這些會以主機端 Rust Crate 的形式呈現,提供從主機連線至 FDomain 的函式。
雖然 FDomain 刻意不提供 Zircon 的精確模擬,但它也必須支援不會中斷 Zircon 本身「程式設計模型」的繫結。也就是說,在主機的 FDomain 中處理控制代碼,不應與在 Zircon 程式碼中處理控制代碼有細微或令人意外的差異。
判斷 Zircon 和 FDomain 主機端 API 之間允許的差異,是 API 審查的主題,超出本 RFC 的範圍。不過,我們將列出幾項預期差異,這些差異是 FDomain 達成目標的必要條件:
- 控制代碼上的 FDomain 動作通常需要 IO,因此我們無法實作 Zircon API 的非封鎖語意。Zircon 控點的 Rust 繫結包含每個控點的高階「非同步」版本,可使用 Rust 的 Future 系統處理非同步作業。我們可能會在該層級實作 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 分配
FDomain 中的每個控制代碼都會由主機透過 ID 參照。這些 ID 會在通訊協定中編碼為 32 位元不帶正負號的整數,但「不應」是直接向主機公開的實際核心控制代碼編號。
為減少在通訊協定中執行作業所需的往返次數,我們會盡可能使用主機端 ID 分配。也就是說,如果作業會在 FDomain 中產生新的控制代碼 (例如主機要求建立新管道或通訊端時),主機會提供新管道本身使用的 ID 編號。這樣一來,主機就能在同一個交易中,提交要建立控制代碼的要求,以及該控制代碼的第一個作業,不必等待每個作業的回覆,因此通訊協定中的管道作業可以更順暢。
但從管道讀取資料是這項政策的例外狀況,因為這可能會產生任意數量的控制代碼。要求主機為讀取管道可能產生的控制代碼分配 ID,會使提交讀取要求變得笨拙,且為了與串流讀取相容,必須指定複雜的封鎖語意。因此,讀取通道產生的控制代碼會由 FDomain 本身指派 ID。
啟動
主機連線至 FDomain 時,系統會從內容假設要填入幾個起始控制代碼。這些控制代碼類似於提供給新啟動元件的控制代碼。FDomain 可讓主機以支援該類比的方式擷取這些控制代碼,保留使用者直覺,其中 FDomain 是元件拓撲中的節點。
處理作業
FDomain 支援在 FDomain 內建立新的通訊端、管道、事件配對和事件。此外,這項功能也支援關閉及複製控制代碼,以及使用具有不同權限的新版本取代控制代碼,與 zx_handle_replace 類似。
如果是可能必須重試的讀取、寫入和其他作業 (也就是可能因 ZX_ERR_SHOULD_WAIT 而失敗),FDomain 通訊協定會實作暫止的 get 作業,並在目標端執行必要的通訊埠等待作業。這樣可避免主機手動設定通訊埠時,發生笨拙的動作和大量往返行程。
穩定線路編碼
FIDL 的交易格式並非設計用於在 Fuchsia 管道外傳輸。如需在其他環境中序列化 FIDL,建議使用 RFC-0120 中所述的持續性標頭。
永久性標頭通常用於 FIDL 資料,這些資料會儲存在靜態狀態。雖然 FDomain 訊息不會透過管道傳輸 (通常是這樣),但仍應以一般要求-回應方式傳送及接收。
持續性標頭和交易標頭都包含魔術數字和一系列相容性標記。交易標頭也新增了「動態標記」欄位,用於新的彈性方法通訊協定,以及交易 ID 和方法序數,這兩者都是 FDomain 訊息的必要欄位。
最初的計畫是使用持續性標頭,並手動新增交易 ID 和方法序數,建立新的合併標頭格式。FDomain 的所有方法都很彈性,因此動態旗標欄位的值可能是隱含的。
審查時,FIDL 團隊發現合併後的標頭與交易標頭非常相似,因此建議直接使用一般的 FIDL 交易標頭。這項 RFC 將採用該提案,並瞭解這可能會改變交易標頭格式的演進壓力,而 FIDL 維護人員接受這點。
實作
目標端 FDomain 支援功能可透過單一 Rust Crate 提供,第一個整合者可能是 Remote Control Service。
裝置的傳輸作業可能會先以透過舊版 Overnet 通訊協定分配的通訊端形式提供,或是以該通訊協定下層的額外電路形式提供。然後,我們可能會選擇透過 SSH 實作直接傳輸,或是直接實作自訂傳輸。
目標端需要進行更多作業。目前透過 Overnet 進行的 FIDL 主機端通訊,是依附於 fuchsia-async 程式庫中的 Fuchsia 核心基本項目模擬。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 Proxy,並在命名空間中公開。主機可以使用魔法啟動器,要求新的 Socket 連線成為一般 Overnet 連線或 FDomain 連線 (目前 Overnet 連線以魔法字串「CIRCUIT\0」開頭,因此我們已在這個位置尋找魔法)。
FFX 工具架構會在這裡提供 Overnet 或 FDomain Proxy 給工具,如果建立兩條連線,可能兩者都會提供。這樣我們就能輕鬆將個別工具遷移至 FDomain 主機端程式碼。從 FDomain 使用代管於精靈本身的服務並不方便,但其他架構壓力已促使這類服務消失或重新設計。
由於 FFX 外掛程式通常只會取用 FIDL Proxy,我們應該能夠遷移至由目標直接代管的 FDomain 連線,且不必對外掛程式進行任何變更。我們應該能同時支援 Overnet 頂端的連線和直接 FDomain 連線,就像我們在電路交換 Overnet 遷移作業中一樣。
效能
FDomain 現有的應用程式都是開發人員工具,目前由 Overnet 處理。從未有人認真評估 Overnet 效能,因為開發人員工具通常沒有嚴格的延遲或大頻寬需求。
不過,關於 Overnet 效能的問題已開始浮現,因此至少建立效能預期可能值得。因此,請務必測試下列項目:
- 透過通訊端、封鎖和串流傳輸的輸送量。
- 直接對簡單介面發出 FIDL 呼叫時的延遲時間。
- 重複發出 FIDL 呼叫時的延遲時間,每次呼叫都會在回覆前一次呼叫時傳送的管道上發出。這會決定在對效能要求嚴格的應用程式中,控制代碼建立作業的顯示方式。
回溯相容性
FDomain 將代表與 Overnet 通訊協定徹底劃清界線。為確保回溯相容性,我們會在完全停用 Overnet 前,保留這兩種通訊協定一段時間。
安全性考量
FDomain 核心通訊協定不會直接處理驗證,且設計目的是允許任意存取其控制的控制代碼集合。部署時應確保只有可安全地與存取者共用的控制代碼,以及可安全地授予這類存取權的環境,才能取得存取權。Overnet 已考量這些事項,因此接管其應用程式應可確保這些事項。
在日後權限提升問題更令人擔憂的應用程式中 (Overnet 本質上是權限最高的應用程式,因此無論取代它的應用程式為何,都是如此),可以想像攻擊者會操縱 FDomain 中不應存在的控制代碼,發動這類攻擊。遵循本文中的指南,不要在 FDomain 通訊協定中使用實際的 Kernel 控制代碼號碼做為控制代碼 ID,應可減輕這類攻擊的所有明顯向量。在 Rust 中實作 FDomain 也會因為該語言強大的擁有權語意,而更容易防禦這些攻擊。
隱私權注意事項
FDomain 適用於開發中的裝置。不應有獨特的隱私權疑慮。即使是意料之外的應用程式,除了安全性問題外,也不太可能有獨特的隱私權問題。
測試
我們已成功使用 Overnet 產生整合測試,這些測試會託管在單一程序中,而通訊協定狀態的多個執行個體會彼此連線,方法是將一個執行個體的位元組輸出內容提供給另一個執行個體做為輸入內容。這應該可讓我們測試各種設定和拓撲。
涉及實際網路元件的整合測試較為困難,但可能不會增加太多額外價值,且可能因複雜的實體測試設定而產生不穩定風險。我們會使用基礎架構完善的設施進行這類測試,並隨著供應情形擴大測試範圍。
說明文件
除了以一般方式記錄實作交付項目外,樹狀結構中也應存在通訊協定規格的動態表單。這項內容主要會包含本 RFC 的「設計」一節文字,但可視需要調整,納入通訊協定擴充功能。
缺點、替代方案和未知事項
與 Overnet 相比,FDomain 大幅降低了複雜度。此外,這項功能必須從頭開始實作,且無法與 Overnet 回溯相容。
遠端工具通訊協定的替代設計空間廣大,因此假設需要新通訊協定,列舉替代方案是一項漫長且沒有效率的工作。我們本來可以選擇自訂二進位格式,而非 FIDL,這樣或許就能進一步控管相容性。或者,我們也可以呈現完全不代表核心物件的 IPC 機制,而是為工具提供特定的自訂介面。
如果 Fuchsia 的用途擴大,很可能會有其他 FDomain 用途,因為大家希望減少重複作業,並提供較少的通訊協定。可以想像資料中心內的 Fuchsia 伺服器使用 FDomain 取代 SSH,做為遠端管理傳輸方式,而不只是診斷工具。這裡說明的 FDomain 通訊協定一般來說應該適用於這類工作, 但我們無法確定它是否能擴展至這類工作。
既有技術和參考資料
如要進一步瞭解該通訊協定,請參閱 Fuchsia 樹狀結構中的 Overnet 說明文件。