JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

可程式化資料平面與 P4

OpenFlow 讓控制器去填規則,但交換器依然只認得硬體出生時就會的那些標頭。這一篇再往下鑽一層:可程式化資料平面與 P4 語言讓你告訴晶片「封包到底是什麼」、以及如何剖析與改寫它——把「彈性」與「固定」之間的那條界線,一路下移到矽晶片裡。

OpenFlow 撞上的天花板

在第 2 篇你看到 OpenFlow 把交換器變成一位有禮貌的櫃員:控制器比對—動作(match-action)規則交下來,交換器比對封包欄位、再套用動作。這是一大勝利——但仔細看交換器「被允許」拿來比對的東西。硬體在出廠時就已經只認得一份固定的欄位選單:乙太網路位址、VLAN 標籤、IPv4 與 IPv6 位址、TCP 與 UDP 連接埠。OpenFlow 讓控制器去填這些格子裡的值;它「不」讓你去發明新的格子。櫃員拿的是一張預先印好的表格,而你只能在別人畫好的那些格線裡寫字。

在你需要一個晶片從沒聽過的標頭之前,這都還好。假設你的資料中心用一種新的封裝把封包包起來、或你發明了一個用來攜帶流 ID 的迷你自訂標頭、又或者明年有個新通訊協定要上線。在固定功能(fixed-function)的交換器上你就卡住了:你只能等——有時要等好幾年——等某家廠商去設計、製造、販售一顆認得你那個欄位的新晶片。OpenFlow 的版本甚至越長越臃腫,就為了追每一個新協定——每次改版都再栓上更多比對欄位,而硬體仍舊得照著被造出來才行。彈性,停在了矽晶片的邊緣。

可程式化資料平面問了一個更大膽的問題。與其讓晶片來規定它懂哪些協定,不如讓那些協定本身變成你載入晶片的軟體?這就是從「填規則的控制器」躍進到「同時也定義規則手冊詞彙的控制器」。它的口號是:應該是網路工程師告訴交換器「該怎麼處理封包」,而不是反過來。

可程式化資料平面到底是什麼

回想第 1 篇裡的資料平面:它是交換器中以線速碰觸每一個封包、在奈秒內決定每個封包往哪去的那一部分。可程式化資料平面保留那股驚人的速度,卻讓它的行為能由軟體重新設定。最主流的設計是一條由多個階段組成的管線,常被稱為 PISA(協定無關的交換器架構)。一個封包以三幕的形式流過它。

  1. 剖析(Parse)。一個可程式化的剖析器像一台小小的狀態機,走過進來的位元組,剝下你所定義的標頭——先乙太網路、再 IP、若你說有自訂標頭就再剝它。它不假設任何固定的版面;那張地圖是你寫的。
  2. 比對—動作(Match-action)。剖析出來的欄位流過一連串比對—動作表,概念跟 OpenFlow 一模一樣——但現在是你來選每一張表要比對哪些欄位、能跑哪些動作,包括對欄位做算術、以及讀寫晶片上一小塊稱為暫存器(register)的記憶體。
  3. 反剖析(Deparse)。反剖析器在封包離開前,把(可能已被修改的)標頭重新組裝回封包上,讓線上的位元反映出你做過的每一次改寫。

最關鍵的字眼是「協定無關」。硬體出廠時並不特別認得 IP 或 TCP;它提供的是通用的剖析、通用的表、與通用的動作。是你的程式告訴它:這幾個位元組是一個 IP 標頭、這個特定欄位是目的位址。同一顆晶片星期一可以是路由器、星期二是負載平衡器、星期三是一個數封包的遙測探針——不換新矽晶片,只換一支新程式。

P4:寫給管線的語言

P4 正是為這份工作打造的程式語言。它的名字是一個拗口的縮寫——Programming Protocol-independent Packet Processors(為協定無關的封包處理器而寫的程式設計)——但概念很簡單:P4 就是你用來寫上面那三幕的方式。你宣告自己的標頭格式(每個格子的形狀)、把剖析器寫成一台狀態機、再定義比對—動作表以及它們可套用的動作。接著一個 P4 編譯器把你的程式映射到一個特定的目標(target)上:一顆可程式化交換晶片、一台白盒交換器、伺服器裡的一張 SmartNIC、甚至是用來測試的軟體交換器。

// P4-flavored sketch (not exact syntax) of a custom header + table

header my_tag_t {        // a new header the chip never knew
    bit<16>  flow_id;    // 16 bits: a flow identifier we invented
    bit<8>   priority;   // 8 bits: our own priority field
}

parser MyParser {        // ACT 1: how to read the bytes
    state start      -> parse_ethernet
    state parse_ethernet -> parse_ipv4
    state parse_ipv4     -> parse_my_tag   // peel off our custom tag
    state parse_my_tag   -> accept
}

table forward {          // ACT 2: a match-action table WE define
    key    = { hdr.my_tag.flow_id : exact }   // match OUR field
    actions = { set_egress_port; drop }
}

// ACT 3 (deparser): re-emit ethernet + ipv4 + my_tag onto the wire
一段帶 P4 風味的草圖:我們發明一個帶有 flow_id 的 my_tag 標頭——硬體本來不認得它——剖析它、再在我們自己的表裡比對它。在固定功能的交換器上,這個欄位根本無從比對。

資料平面歸你之後,你能做什麼

第一份回報是跟得上變化。一個新協定、一種新隧道格式、某個既有欄位該如何處理的臭蟲——全都變成「重新編譯、重新載入」,而不是一輪買硬體的循環。你在 SDN 與 NFV 認識到的彈性,如今觸及到網路最快、最深的那一層——那層過去是凍結在矽晶片裡的。

第二份回報是可視性(visibility),這也是大家最興奮的一點。因為資料平面現在能以線速做算術、並在封包上蓋上自訂欄位,所以每一台交換器都能量測自己。有了帶內網路遙測(In-band Network Telemetry),交換器會在一個經過的封包上附上一筆小小的紀錄——它待在哪個佇列、等了多久、走了哪個連接埠——下一台交換器再附上它自己的那筆。封包抵達時,便攜帶著一本逐跳記錄它確切旅程的日記。這是任何抽樣式、事後才看的計數器都給不了你的遙測:逐封包的真相,由資料平面自己蒐集。

除了觀察,資料平面還能「動手」。一條可程式化管線可以在伺服器池之間做負載平衡、跑壅塞訊號機制的一部分、快取熱門的鍵—值查詢、或在最初的那幾奈秒就擋下一種阻斷服務攻擊的樣式——這一切都不必讓封包離開交換器、去問一個較慢的大腦該怎麼辦。過去住在軟體中介盒(第 3 篇的 VNF)裡的工作,有時可以直接落進線路本身。

白盒,以及誠實的取捨

可程式化資料平面與白盒交換器天生是一對:那是不附帶被綁死的廠商作業系統、裸賣的交換硬體,等於是網路界的一台通用 PC——你裝自己的軟體上去。買下機器、載入你的網路作業系統、載入你的 P4 程式——你擁有的是整個堆疊,而不是租一台封閉的設備。這也是為什麼超大規模資料中心——它們跑的交換器多到值得投入這份工——是最早的大規模採用者。

現在說那個「但是」,而且不只一個。力量伴隨著責任:一支 P4 程式是貨真價實、以線速運行的系統軟體,它裡頭的一個臭蟲可以用任何圖形介面打勾框都辦不到的方式,悄悄地損壞或丟棄封包。你繼承了晶片的硬性限制——用光了管線階段或表記憶體,某個功能就是塞不進去。相關技能比傳統設備稀缺、工具鏈也更年輕。而且可程式化並非魔法般的速度:它讓你決定資料平面「做什麼」,但晶片的時脈仍然決定「多快」,你每個封包能花的工,永遠不能超過線速預算所允許的。