linux報文高速捕獲技術對比--napi/libpcap/afpacket/pfring/dpdk/xdp

1. 傳統linux網絡協議棧流程和性能分析

Linux網絡協議棧是處理網絡數據包的典型系統,它包含了從物理層直到應用層的全過程。
在這裏插入圖片描述html

  1. 數據包到達網卡設備。
  2. 網卡設備依據配置進行DMA操做。(第1次拷貝:網卡寄存器->內核爲網卡分配的緩衝區ring buffer)
  3. 網卡發送中斷,喚醒處理器。
  4. 驅動軟件從ring buffer中讀取,填充內核skbuff結構(第2次拷貝:內核網卡緩衝區ring buffer->內核專用數據結構skbuff)
  5. 數據報文達到內核協議棧,進行高層處理。
  6. socket系統調用將數據從內核搬移到用戶態。(第3次拷貝:內核空間->用戶空間)

研究者們發現,Linux內核協議棧在數據包的收發過程當中,內存拷貝操做的時間開銷佔了整個處理過程時間開銷的65%,此外層間傳遞的系統調用時間也佔據了8%~10%。linux

協議棧的主要問題:shell

  1. 針對單個數據包級別的資源分配和釋放
    每當一個數據包到達網卡,系統就會分配一個分組描述符用於存儲數據包的信息和頭部,直到分組傳送到用戶態空間,其描述符才被釋放。此外,sk_buff龐大的數據結構中的大部分信息對於大多數網絡任務而言都是無用的.編程

  2. 流量的串行訪問
    現代網卡包括多個硬件的接收端擴展(receiver-side scaling, RSS)隊列能夠將分組按照五元組散列函數分配到不一樣的接收隊列。使用這種技術,分組的捕獲過程能夠被並行化,由於每一個RSS隊列能夠映射到一個特定的CPU核,而且能夠對應相應的NAPI線程。這樣整個捕獲過程就能夠作到並行化。
    可是問題出如今之上的層次,Linux中的協議棧在網絡層和傳輸層須要分析合併的全部數據包
    ①全部流量在一個單一模塊中被處理,產生性能瓶頸;
    ②用戶進程不可以從一個單一的RSS隊列接收消息.
    這就形成了上層應用沒法利用現代硬件的並行化處理能力,這種在用戶態分配流量前後序列的過程下降了系統的性能,丟失了驅動層面所得到的加速.
    此外,從不一樣隊列合併的流量可能會產生額外的亂序分組緩存

  3. 從驅動到用戶態的數據拷貝
    從網卡收到數據包到應用取走數據的過程當中,存在至少2次數據包的複製安全

  4. 內核到用戶空間的上下文切換
    從應用程序的視角來看,它須要執行系統調用來接收每一個分組.每一個系統調用包含一次從用戶態到內核態的上下文切換,隨之而來的是大量的CPU時間消耗.在每一個數據包上執行系統調用時產生的上下文切換可能消耗近1 000個CPU週期.網絡

  5. 跨內存訪問
    例如,當接收一個64 B分組時,cache未命中形成了額外13.8%的CPU週期的消耗.另外,在一個基於NUMA的系統中,內存訪問的時間取決於訪問的存儲節點.所以,cache未命中在跨內存塊訪問環境下會產生更大的內存訪問延遲,從而致使性能降低.數據結構

2. 提升捕獲效率的技術

目前高性能報文捕獲引擎中經常使用的提升捕獲效率的技術,這些技術可以克服以前架構的性能限制.架構

  1. 預分配和重用內存資源
    這種技術包括:
    開始分組接收以前,預先分配好將要到達的數據包所需的內存空間用來存儲數據和元數據(分組描述符).尤爲體如今,在加載網卡驅動程序時就分配好 N 個描述符隊列(每一個硬件隊列和設備一個).負載均衡

    一樣,當一個數據包被傳送到用戶空間,其對應的描述符也不會被釋放,而是從新用於存儲新到達的分組.得益於這一策略,在每一個數據包分配/釋放所產生的性能瓶頸獲得了消除.此外,也能夠經過簡化sk_buff的數據結構來減小內存開銷.

  2. 數據包採用並行直接通道傳遞.
    爲了解決序列化的訪問流量,須要創建從RSS隊列到應用之間的直接並行數據通道.這種技術經過特定的RSS隊列、特定的CPU核和應用三者的綁定來實現性能的提高.

    這種技術也存在一些缺點:
    ①數據包可能會亂序地到達用戶態,從而影響某些應用的性能;
    ②RSS使用Hash函數在每一個接收隊列間分配流量.當不一樣核的數據包間沒有相互關聯時,它們能夠被獨立地分析,但若是同一條流的往返數據包被分配到不一樣的CPU核上時,就會形成低效的跨核訪問.

  3. 內存映射.
    使用這種方法,應用程序的內存區域能夠映射到內核態的內存區域,應用可以在沒有中間副本的狀況下讀寫這片內存區域.
    用這種方式咱們可使應用直接訪問網卡的DMA內存區域,這種技術被稱爲零拷貝.但零拷貝也存在潛在的安全問題,嚮應用暴露出網卡環形隊列和寄存器會影響系統的安全性和穩定性 .

  4. 數據包的批處理.
    爲了不對每一個數據包的重複操做的開銷,可使用對數據包的批量處理.

    這個策略將數據包劃分爲組,按組分配緩衝區,將它們一塊兒複製到內核/用戶內存.運用這種技術減小了系統調用以及隨之而來的上下文切換的次數;同時也減小了拷貝的次數,從而減小了平攤處處理和複製每一個數據包的開銷.
    但因爲分組必須等到一個批次已滿或定時器期滿纔會遞交給上層,批處理技術的主要問題是延遲抖動以及接收報文時間戳偏差的增長.

  5. 親和性與預取.
    因爲程序運行的局部性原理,爲進程分配的內存必須與正在執行它的處理器操做的內存塊一致,這種技術被稱爲內存的親和性.
    CPU親和性是一種技術,它容許進程或線程在指定的處理器核心上運行.
    在內核與驅動層面,軟件和硬件中斷能夠用一樣的方法指定具體的CPU核或處理器來處理,稱爲中斷親和力.每當一個線程但願訪問所接收的數據,若是先前這些數據已被分配到相同CPU核的中斷處理程序接收,則它們在本地cache可以更容易被訪問到.

3. 典型收包引擎

3.1 libpcap

參考:libpcap實現機制及接口函數

libpcap的包捕獲機制是在數據鏈路層增長一個旁路處理,不干擾系統自身的網路協議棧的處理,對發送和接收的數據包經過Linux內核作過濾和緩衝處理,最後直接傳遞給上層應用程序。
libpcap 流程

  1. 數據包到達網卡設備。
  2. 網卡設備依據配置進行DMA操做。(第1次拷貝:網卡寄存器->內核爲網卡分配的緩衝區ring buffer)
  3. 網卡發送中斷,喚醒處理器。
  4. 驅動軟件從ring buffer中讀取,填充內核skbuff結構(第2次拷貝:內核網卡緩衝區ring buffer->內核專用數據結構skbuff)
  5. 接着調用netif_receive_skb函數:
    5.1 若是有抓包程序,由網絡分接口進入BPF過濾器,將規則匹配的報文拷貝到系統內核緩存 (第3次拷貝)。BPF爲每個要求服務的抓包程序關聯一個filter和兩個buffer。BPF分配buffer 且一般狀況下它的額度是4KB the store buffer 被使用來接收來自適配器的數據; the hold buffer被使用來拷貝包到應用程序。

5.2 處理數據鏈路層的橋接功能;
5.3 根據skb->protocol字段肯定上層協議並提交給網絡層處理,進入網絡協議棧,進行高層處理。
6. libpcap繞過了Linux內核收包流程中協議棧部分的處理,使得用戶空間API能夠直接調用套接字PF_PACKET從鏈路層驅動程序中得到數據報文的拷貝,將其從內核緩衝區拷貝至用戶空間緩衝區第4次拷貝

3.2 libpcap-mmap

libpcap-mmap是對舊的libpcap實現的改進,新版本的libpcap基本都採用packet_mmap機制。PACKET_MMAP經過mmap,減小一次內存拷貝(第4次拷貝沒有了),減小了頻繁的系統調用,大大提升了報文捕獲的效率。

3.3 PF_RING

參考:PF_RING學習筆記
咱們看到以前libpcap有4次內存拷貝。
libpcap_mmap有3次內存拷貝。
PF_RING提出的核心解決方案即是減小報文在傳輸過程當中的拷貝次數。
pfring
咱們能夠看到,相對與libpcap_mmap來講,pfring容許用戶空間內存直接和rx_buffer作mmap。
這又減小了一次拷貝(libpcap_mmap的第2次拷貝:rx_buffer->skb

PF-RING ZC實現了DNA(Direct NIC Access 直接網卡訪問)技術,將用戶內存空間映射到rx_buffer。經過這樣的方式,減小了一次拷貝(libpcap的第3次拷貝,每一個BPF過濾器有一個拷貝)。這就是零拷貝。

其缺點是,只有一個應用能夠在某個時間打開DMA ring(請注意,如今的網卡能夠具備多個RX / TX隊列,從而就能夠在每一個隊列上同時一個應用程序),換而言之,用戶態的多個應用須要彼此溝通才能分發數據包。

3.5 DPDK

參考:DPDK解析-----DPDK,PF_RING對比

pf-ring zc和dpdk都可以實現數據包的零拷貝,二者均旁路了內核,可是實現原理略有不一樣。pf-ring zc經過zc驅動(也在應用層)接管數據包,dpdk基於UIO實現。

1、UIO+mmap 實現零拷貝(zero copy)

UIO(Userspace I/O)是運行在用戶空間的I/O技術。Linux系統中通常的驅動設備都是運行在內核空間,而在用戶空間用應用程序調用便可,而UIO則是將驅動的不多一部分運行在內核空間,而在用戶空間實現驅動的絕大多數功能。
採用Linux提供UIO機制,能夠旁路Kernel,將全部報文處理的工做在用戶空間完成。
dpdk
2、UIO+PMD 減小中斷和CPU上下文切換

DPDK的UIO驅動屏蔽了硬件發出中斷,而後在用戶態採用主動輪詢的方式,這種模式被稱爲PMD(Poll Mode Driver)。

與DPDK相比,pf-ring(no zc)使用的是NAPI polling和應用層polling,而pf-ring zc與DPDK相似,僅使用應用層polling。

3、HugePages 減小TLB miss

在操做系統引入MMU(Memory Management Unit)後,CPU讀取內存的數據須要兩次訪問內存。第一次要查詢頁表將邏輯地址轉換爲物理地址,而後訪問該物理地址讀取數據或指令。

爲了減小頁數過多,頁表過大而致使的查詢時間過長的問題,便引入了TLB(Translation Lookaside Buffer),可翻譯爲地址轉換緩衝器。TLB是一個內存管理單元,通常存儲在寄存器中,裏面存儲了當前最可能被訪問到的一小部分頁表項。

引入TLB後,CPU會首先去TLB中尋址,因爲TLB存放在寄存器中,且其只包含一小部分頁表項,所以查詢速度很是快。若TLB中尋址成功(TLB hit),則無需再去RAM中查詢頁表;若TLB中尋址失敗(TLB miss),則須要去RAM中查詢頁表,查詢到後,會將該頁更新至TLB中。

而DPDK採用HugePages ,在x86-64下支持2MB、1GB的頁大小,大大下降了總頁個數和頁表的大小,從而大大下降TLB miss的概率,提高CPU尋址性能。

4、其它優化

SNA(Shared-nothing Architecture),軟件架構去中心化,儘可能避免全局共享,帶來全局競爭,失去橫向擴展的能力。NUMA體系下不跨Node遠程使用內存。
SIMD(Single Instruction Multiple Data),從最先的mmx/sse到最新的avx2,SIMD的能力一直在加強。DPDK採用批量同時處理多個包,再用向量編程,一個週期內對全部包進行處理。好比,memcpy就使用SIMD來提升速度。
cpu affinity

3.6 XDP

參考:DPDK and XDP

xdp表明eXpress數據路徑,使用ebpf 作包過濾,相對於dpdk將數據包直接送到用戶態,用用戶態當作快速數據處理平面,xdp是在驅動層建立了一個數據快速平面。
在數據被網卡硬件dma到內存,分配skb以前,對數據包進行處理。
在這裏插入圖片描述
請注意,XDP並無對數據包作Kernel bypass,它只是提早作了一點預檢而已。

相對於DPDK,XDP具備如下優勢:

  • 無需第三方代碼庫和許可
  • 同時支持輪詢式和中斷式網絡
  • 無需分配大頁
  • 無需專用的CPU
  • 無需定義新的安全網絡模型

XDP的使用場景包括:

  • DDoS防護
  • 防火牆
  • 基於XDP_TX的負載均衡
  • 網絡統計
  • 複雜網絡採樣
  • 高速交易平臺

4. 無鎖隊列技術

在報文捕獲的流程中,無鎖隊列是一個很重要的數據結構。生產者(網卡)寫數據和消費者(用戶態程序)讀數據,不加鎖,能極大提高效率。

無鎖隊列實現主要依賴的技術有:

  1. CAS原子指令操做
    CAS(Compare and Swap,比較並替換)原子指令,用來保障數據的一致性。
    指令有三個參數,當前內存值 V、舊的預期值 A、更新的值 B,當且僅當預期值 A和內存值 V相同時,將內存值修改成 B並返回true,不然什麼都不作,並返回false。

  2. 內存屏障
    執行運算的時候,每一個CPU核心從內存讀到各自的緩存中,結束後再從緩存更新到內存,這會引發線程間數據的不一樣步,故須要內存屏障強制把寫緩衝區或高速緩存中的數據等寫回主內存。
    主要分爲讀屏障和寫屏障:讀屏障可讓 cache中的數據失效,強制從新從主內存加載數據;
    寫屏障能使cache 中的數據更新寫入主內存。
    在實現 valotitle關鍵字中就用到了內存屏障,從而保證線程A對此變量的修改,其餘線程獲取的值爲最新的值。

5. 基於pfring/dpdk的應用

按照傳統的觀念,中間網絡節點只能按照協議棧的層次一層一層地解析數據包,所謂路由器是三層設備,交換機是二層設備,防火牆分爲二層防火牆和三層防火牆。

使用PF_RING/DPDK的設備,它能夠將數據包直接從網卡的芯片DMA到你機器上的內存,而後你經過一個應用程序而不是內核協議棧來處理數據包。

至於說你的應用程序怎麼處置數據包,我來列舉幾個:
1.深度解析數據包,按照各類你能夠想到的粒度來解析會話,而後記錄審計信息;

2.提供高性能的入侵檢測功能;

3.轉發數據包,按照路由器的方式。可是再也不僅僅經過查詢路由表的方式進行IP路由,而是能夠經過各類各樣的方式,轉發表徹底由你本身定義,好比實現一個通用的SDN流表;

4.根據上面第2點的含義,你能夠決定哪些包被丟棄,這就是一個高性能的防火牆。

相比內核協議棧的串行解決方案,使用PF_RING/DPDK是一個更加高效的方案,不但高效,並且靈活。若是你擁有多核心的處理器,你甚至能夠在用戶態並行處理數據包的各個層信息。

參考:

http://crad.ict.ac.cn/fileup/HTML/2017-6-1300.shtml https://coolshell.cn/articles/8239.html https://cloud.tencent.com/developer/article/1521276 https://blog.csdn.net/dandelionj/article/details/16980571 https://my.oschina.net/moooofly/blog/898798 https://blog.csdn.net/dog250/article/details/77993218

相關文章
相關標籤/搜索