eBPF技術實踐:高性能ACL

本文是由字節跳動系統部 STE 團隊出品的文章。
對於 Linux 而言,iptables / nftables 是主流的網絡 ACL(Access Control List)解決方案。近些年隨着 eBPF 技術的快速發展,bpfilter 也被提上了日程,有望取代 iptables/nftables,成爲下一代網絡 ACL 的解決方案。
本文追隨 bpfilter 的腳步,利用 XDP+eBPF 技術解決 iptables / nftables 性能瓶頸,提供一種高性能網絡 ACL 的技術解決方案。

iptables / nftables 性能瓶頸

由於 iptables 和 nftables 的技術相似,但 iptables 相較簡單,所以我們用 iptables 舉例分析:

O(N)匹配

iptables 的規則樣式:

# iptables -A INPUT -m set --set black_list src -j DROP

# ......省略N條規則

# iptables -A INPUT -p tcp -m multiport --dports 53,80 -j ACCEPT

# ......省略N條規則

# iptables -A INPUT -p udp --dport 53 -j ACCEPT

如上所示,若匹配 DNS 的請求報文(目的端口 53 的 udp 報文),需要依次遍歷所有規則,才能匹配中。其中,ipset/multiport 等 match 項,只能減少規則數量,無法改變 O(N)的匹配方式。

協議棧丟包

iptables 常常和 ipset 結合使用,設置一些 IP 地址黑名單,防禦 DDOS(distributed denial-of-service)網絡攻擊。對於 DDOS 這樣的網絡攻擊,更早地丟包,就能更好地緩解 CPU 的損耗。但是用 iptables 作爲防 DDOS 攻擊的手段,效果往往很差。是因爲 iptables 基於 netfilter 框架實現,即便是攻擊報文在 netfilter 框架 PREROUTING 的 hook 點(收包路徑的最早 hook 點)丟棄,也已經走過了很多 Linux 網絡協議棧的處理流程。網上有比較數據,利用 XDP 技術的丟包速率要比 iptables 高 4 倍左右:

  • 預置條件:單條 udp 流、單個 CPU 處理
  • CPU: i7-6700K CPU @ 4.00GHz
  • NIC: 50Gbit/s Mellanox-CX4
  • iptables 規則:iptables -t raw -I PREROUTING -m set --set black_list src -j DROP
  • iptables 丟包速率:4,748,646 pps
  • XDP:PERCPU_HASH 類型的 eBPF map,存儲 IP 黑名單
  • XDP 丟包速率:16,939,941 pps

XDP

XDP(eXpress Data Path)是基於 eBPF 實現的高性能、可編程的數據平面技術。基本的軟件架構如下圖所示:

XDP 位於網卡驅動層,當數據包經過 DMA 存放到 ring buffer 之後,分配 skb 之前,即可被 XDP 處理。數據包經過 XDP 之後,會有 4 種去向:

  • XDP_DROP:丟包
  • XDP_PASS:上送協議棧
  • XDP_TX:從當前網卡發送出去
  • XDP_REDIRECT:從其他網卡發送出去

由於 XDP 位於整個 Linux 內核網絡軟件棧的底部,能夠非常早地識別並丟棄攻擊報文,具有很高的性能。這爲我們改善 iptables/nftables 協議棧丟包的性能瓶頸,提供了非常棒的解決方案。

eBPF

BPF(Berkeley Packet Filter)是 Linux 內核提供的基於 BPF 字節碼的動態注入技術(常應用於 tcpdump、raw socket 過濾等)。eBPF(extended Berkeley Packet Filter)是針對於 BPF 的擴展增強,豐富了 BPF 指令集,提供了 Map 的 KV 存儲結構。我們可以利用 bpf()系統調用,初始化 eBPF 的 Program 和 Map,利用 netlink 消息或者 setsockopt()系統調用,將 eBPF 字節碼注入到特定的內核處理流程中(如 XDP、socket filter 等)。如下圖所示:

至此,我們高性能 ACL 的技術方向已經明確,即利用 XDP 技術在軟件棧的最底層做報文的過濾。

整體架構

如下圖所示,ACL 控制平面負責創建 eBPF 的 Program、Map,注入 XDP 處理流程中。其中 eBPF 的 Program 存放報文匹配、丟包等處理邏輯,eBPF 的 Map 存放 ACL 規則。

匹配算法

爲了提升匹配效率,我們將所有的 ACL 規則做了預處理,將鏈式的規則拆分存儲。規則匹配時,我們參考內核的 O(1)調度算法,在多個匹配的規則中,快速選取高優先級的規則。

規則預處理

我們以之前的 iptables 規則舉例,看如何將其拆分存儲。首先,將所有規則根據優先級編號。比如,例子中的規則分別編號爲:1(0x1)、16(0x10)、256(0x100)。其次,將所有規則的匹配項歸類拆分。比如,例子中的匹配項可以歸類爲:源地址、目的端口、協議。最後,將規則編號、具體匹配項分類存儲到 eBPF 的 Map 中。

# ipset create black_list hash:net
# ipset add black_list 192.168.3.0/24

# iptables -A INPUT -m set --set black_list src -j DROP

# ...... 省略15條規則

# iptables -A INPUT -p tcp -m multiport --dports 53,80 -j ACCEPT

# ...... 省略240條規則

# iptables -A INPUT -p udp --dport 53 -j ACCEPT

舉例說明:

規則 1 只有源地址匹配項,我們用源地址 192.168.3.0/24 作爲 key,規則編號 0x1 作爲 value,存儲到 src Map 中。

規則 16 有目的端口、協議 2 個匹配項,我們依次將 53、80 作爲 key,規則編號 0x10 作爲 value,存儲到 dport Map 中;將 tcp 協議號 6 作爲 key,規則編號 0x10 作爲 value,存儲到 proto Map 中。

規則 256 有目的端口、協議 2 個匹配項,我們將 53 作爲 key,規則編號 0x100 作爲 value,存儲到 dport Map 中;將 udp 協議號 17 作爲 key,規則編號 0x100 作爲 value,存儲到 proto Map 中。

我們依次將規則 1、16、256 的規則編號作爲 key,動作作爲 value,存儲到 action Map 中。

交集

需要注意的是,規則 16、256 均有目的端口爲 53 的匹配項,我們應該將 16、256 的規則編號進行按位或操作,然後進行存儲,即 0x10 | 0x100 = 0x110。

通配

另外,規則 1 的目的端口、協議均爲通配項,我們應該將規則 1 的編號按位或追加到現有的匹配項中(圖中下劃線的 value 值:0x111、0x11 等)。同理,將規則 16、256 的規則編號按位或追加到現有的源地址匹配項中(圖中下劃線的 value 值:0x111、0x110)。至此,我們的規則預處理完成,將所有規則的匹配項、動作拆分存儲到 6 個 eBPF Map 中,如上圖所示。

類 O(1)匹配

報文匹配時,我們報文的 5 元組(源、目的地址,源、目的端口、協議)依次作爲 key,分別查找對應的 eBPF Map,得到 5 個 value。我們將這 5 個 value 進行按位與操作,得到一個 bitmap。這個 bitmap 的每個 bit,就表示了對應的一條規則;被置位爲 1 的 bit,表示對應的規則匹配成功。

舉例說明:

當用報文(192.168.4.1:10000 -> 192.168.4.100:53, udp)的 5 元組作爲 key,查找 eBPF Map 後,得到的 value 分別爲:src_value = 0x110、dst_value = NULL、sport_value = NULL、dport_value = 0x111、proto_value = 0x101。將非 NULL 的 value 進行按位與操作,得到 bitmap = 0x100(0x110 & 0x111 & 0x101)。由於 bitmap 只有一位被置位 1(只有一條規則匹配成功,即規則 256),利用該 bitmap 作爲 key,查找 action Map,得到 value 爲 ACCEPT。與 iptables 的規則匹配結果一致。

同理,當報文(192.168.4.1:1000 -> 192.168.4.100:53, tcp)的 5 元組作爲 key,查找 eBPF Map 後,處理的流程和上面的一致,最終匹配到規則 16。

同樣,當報文(192.168.3.1:1000 -> 192.168.4.100:53, udp)的 5 元組作爲 key,查找 eBPF Map 後,處理的流程和上面的一致。不同的是,得到的 bitmap = 0x101,由於 bitmap 有兩位被置位 1(規則 1、256),我們應該取優先級最高的規則編號作爲 key,查找 action Map。這裏借鑑了內核 O(1)調度算法的思想,做如下操作:

bitmap &= -bitmap

即可取到優先級最高的 bit,如 0x101 &= -(0x101)最終等於 0x1。我們用 0x1 作爲 key,查找 action Map,得到 value 爲 DROP。與 iptables 的規則匹配結果一致。

總結

本文基於 Linux 內核的 XDP 機制,提出了一種改善 iptables / nftables 性能的 ACL 方案。目前 eBPF 的技術在開源社區非常流行,特性非常豐富,我們可以利用這項技術做很多有意思的事情。感興趣的朋友可以加入我們,一起討論交流。

參考

本文轉載自公衆號字節跳動技術團隊(ID:toutiaotechblog)。

原文鏈接

https://mp.weixin.qq.com/s/25mhUrNhF3HW8H6-ES7waA

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章