Open vSwitch 資料路徑開發者文件

Open vSwitch 核心模組允許對選定的網路裝置上的流級別資料包處理進行靈活的使用者空間控制。 它可以用於實現普通的乙太網交換機、網路裝置繫結、VLAN 處理、網路訪問控制、基於流的網路控制等等。

核心模組實現多個“資料路徑”(類似於網橋),每個資料路徑可以有多個“vports”(類似於網橋中的埠)。 每個資料路徑還關聯著一個“流表”,使用者空間使用“流”填充該表,這些“流”將基於資料包頭和元資料的鍵對映到一組操作。 最常見的操作是將資料包轉發到另一個 vport; 也實現了其他操作。

當資料包到達 vport 時,核心模組透過提取其流鍵並在流表中查詢它來處理它。 如果存在匹配的流,它將執行關聯的操作。 如果沒有匹配,它會將資料包排隊到使用者空間進行處理(作為處理的一部分,使用者空間可能會設定一個流以完全在核心中處理同一型別的更多資料包)。

流鍵相容性

網路協議隨著時間的推移而發展。 新的協議變得重要,而現有的協議失去了它們的突出地位。 為了使 Open vSwitch 核心模組保持相關性,更新的版本必須能夠解析其他協議作為流鍵的一部分。 有朝一日,甚至可能希望放棄對已過時的協議的解析支援。 因此,Open vSwitch 的 Netlink 介面旨在允許經過仔細編寫的使用者空間應用程式與任何版本的流鍵(無論是過去還是將來)一起工作。

為了支援這種向前和向後相容性,每當核心模組將資料包傳遞到使用者空間時,它還會傳遞從資料包中解析的流鍵。 然後,使用者空間從資料包中提取其自己的流鍵概念,並將其與核心提供的版本進行比較。

  • 如果使用者空間的資料包流鍵概念與核心的概念匹配,則無需進行任何特殊處理。

  • 如果核心的流鍵包含比使用者空間版本的流鍵更多的欄位,例如,如果核心解碼了 IPv6 標頭,但使用者空間停在乙太網型別(因為它不理解 IPv6),那麼同樣無需進行任何特殊處理。 只要使用者空間使用核心提供的流鍵,它仍然可以像往常一樣設定流。

  • 如果使用者空間的流鍵包含比核心的更多的欄位,例如,如果使用者空間解碼了一個 IPv6 標頭,但核心停在乙太網型別,那麼使用者空間可以手動轉發資料包,而無需在核心中設定流。 這種情況對效能不利,因為核心認為屬於該流的每個資料包都必須進入使用者空間,但轉發行為是正確的。 (如果使用者空間可以確定額外欄位的值不會影響轉發行為,那麼它可以設定一個流。)

隨著時間的推移,流鍵如何演變對於使其工作至關重要,因此以下各節將詳細介紹。

流鍵格式

流鍵作為 Netlink 屬性的序列透過 Netlink 套接字傳遞。 一些屬性表示資料包元資料,定義為無法從資料包本身提取的有關資料包的任何資訊,例如,接收資料包的 vport。 然而,大多數屬性是從資料包內的標頭中提取的,例如,來自乙太網、IP 或 TCP 標頭的源地址和目標地址。

<linux/openvswitch.h> 標頭檔案定義了流鍵屬性的確切格式。 為了在此處進行非正式的解釋性說明,我們將它們編寫為逗號分隔的字串,用括號表示引數和巢狀。 例如,以下內容可以表示與在 vport 1 上收到的 TCP 資料包相對應的流鍵

in_port(1), eth(src=e0:91:f5:21:d0:b2, dst=00:02:e3:0f:80:a4),
eth_type(0x0800), ipv4(src=172.16.0.20, dst=172.18.0.52, proto=17, tos=0,
frag=no), tcp(src=49163, dst=80)

通常,我們會省略對討論不重要的引數,例如

in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...)

萬用字元流鍵格式

萬用字元流使用透過 Netlink 套接字傳遞的兩個 Netlink 屬性序列來描述。 一個流鍵,完全如上所述,以及一個可選的相應流掩碼。

萬用字元流可以表示一組完全匹配流。 掩碼中的每個“1”位指定與流鍵中相應位的完全匹配。 “0”位指定一個不在乎的位,它將匹配傳入資料包的“1”位或“0”位。 透過減少使用者空間程式需要處理的新流的數量,使用萬用字元流可以提高流設定速率。

核心和使用者空間程式都可以選擇支援掩碼 Netlink 屬性。 核心可以忽略掩碼屬性,安裝完全匹配流,或者減少核心中不在乎的位的數量,使其小於使用者空間程式指定的數量。 在這種情況下,核心未實現的位中的變化只會導致額外的流設定。 核心模組還將與既不支援也不提供流掩碼屬性的使用者空間程式一起工作。

由於核心可能會忽略或修改萬用字元位,因此使用者空間程式可能難以確切知道安裝了哪些匹配項。 有兩種可能的方法:被動地安裝錯過核心流表 (因此根本不嘗試確定萬用字元更改) 的流,或者使用核心的響應訊息來確定已安裝的萬用字元。

在與使用者空間互動時,核心應完全按照原始安裝的方式維護金鑰的匹配部分。 這將提供一個控制代碼來識別所有未來操作的流。 但是,在報告已安裝流的掩碼時,掩碼應包括核心施加的任何限制。

使用重疊萬用字元流時的行為未定義。 使用者空間程式有責任確保任何傳入的資料包最多可以匹配一個流,無論是萬用字元流還是非萬用字元流。 當前的實現會盡最大努力檢測重疊的萬用字元流,並且可能會拒絕其中一些但不是全部。 但是,此行為可能會在未來版本中更改。

唯一的流識別符號

使用金鑰的原始匹配部分作為流標識控制代碼的替代方法是唯一的流識別符號,或 “UFID”。 UFID 對於核心和使用者空間程式都是可選的。

支援 UFID 的使用者空間程式應在流設定期間提供它以及流,然後在所有未來操作中使用 UFID 引用該流。 如果指定了 UFID,則核心不需要按原始流鍵對流建立索引。

演化流鍵的基本規則

對於遵循上面“流鍵相容性”下列出的規則的應用程式,需要一些注意才能真正保持向前和向後相容性。

基本規則是顯而易見的

==================================================================
New network protocol support must only supplement existing flow
key attributes.  It must not change the meaning of already defined
flow key attributes.
==================================================================

這條規則確實有一些不太明顯的後果,因此值得透過一些示例進行研究。 例如,假設核心模組尚未實現 VLAN 解析。 相反,它只是將 802.1Q TPID (0x8100) 解釋為 Ethertype,然後停止解析資料包。 忽略元資料,任何帶有 802.1Q 標頭的資料包的流鍵基本上都像這樣

eth(...), eth_type(0x8100)

天真地,為了新增 VLAN 支援,新增一個新的 “vlan” 流鍵屬性來包含 VLAN 標記,然後使用現有的欄位定義繼續解碼 VLAN 標記之外的封裝標頭是有意義的。 透過此更改,VLAN 10 中的 TCP 資料包將具有類似於以下的流鍵

eth(...), vlan(vid=10, pcp=0), eth_type(0x0800), ip(proto=6, ...), tcp(...)

但是,此更改將對尚未更新為理解新的 “vlan” 流鍵屬性的使用者空間應用程式產生負面影響。 該應用程式可以按照上面的流相容性規則,忽略它不理解的 “vlan” 屬性,因此假設該流包含 IP 資料包。 這是一個不好的假設(只有在解析並跳過 802.1Q 標頭時,該流才包含 IP 資料包),並且即使它遵循相容性規則,也可能導致應用程式的行為在核心版本之間發生變化。

解決方案是使用一組巢狀屬性。 例如,這就是 802.1Q 支援使用巢狀屬性的原因。 VLAN 10 中的 TCP 資料包實際上表示為

eth(...), eth_type(0x8100), vlan(vid=10, pcp=0), encap(eth_type(0x0800),
ip(proto=6, ...), tcp(...)))

請注意 “eth_type”、“ip” 和 “tcp” 流鍵屬性是如何巢狀在 “encap” 屬性中的。 因此,不理解 “vlan” 金鑰的應用程式將看不到這些屬性中的任何一個,因此不會錯誤地解釋它們。 (此外,外部 eth_type 仍然是 0x8100,沒有更改為 0x0800。)

處理格式錯誤的資料包

不要因為格式錯誤的協議頭、錯誤的校驗和等而在核心中丟棄資料包。 這將阻止使用者空間實現轉發每個資料包的簡單乙太網交換機。

相反,在這種情況下,包括一個具有“空”內容的屬性。 只要這些值在實踐中很少見,空內容是否可能是有效的協議值都無關緊要,因為使用者空間始終可以將具有這些值的所有資料包轉發到使用者空間並單獨處理它們。

例如,考慮一個包含 IP 標頭的資料包,該標頭指示協議 6 用於 TCP,但在 IP 標頭之後被截斷,因此缺少 TCP 標頭。 此資料包的流鍵將包括一個 src 和 dst 均為全零的 tcp 屬性,如下所示

eth(...), eth_type(0x0800), ip(proto=6, ...), tcp(src=0, dst=0)

作為另一個示例,考慮一個乙太網型別為 0x8100 的資料包,指示 VLAN TCI 應跟隨,但在乙太網型別之後被截斷。 此資料包的流鍵將包括一個全零位 vlan 和一個空的 encap 屬性,如下所示

eth(...), eth_type(0x8100), vlan(0), encap()

與源埠和目標埠為 0 的 TCP 資料包不同,全零位 VLAN TCI 並不常見,因此通常在 vlan 屬性中設定 CFI 位(也稱為核心中的 VLAN_TAG_PRESENT)以明確允許區分這種情況。 因此,此第二個示例中的流鍵明確指示缺少或格式錯誤的 VLAN TCI。

其他規則

流鍵的其他規則就不太微妙了

  • 不允許在給定的巢狀級別上使用重複的屬性。

  • 屬性的排序並不重要。

  • 當核心向用戶空間傳送給定的流鍵時,它總是以相同的方式組成它。 這允許使用者空間雜湊和比較它可能無法完全解釋的整個流鍵。