# 111-2專題研究筆記Week7-8 ## Performance Implications of Packet Filtering https://www.net.in.tum.de/fileadmin/bibtex/publications/papers/ITC30-Packet-Filtering-eBPF-XDP.pdf 現代的網路架構在防火牆上有一些缺失,主要有以下四個: - 該被filter掉的packet太晚被filter掉導致效能出現不必要的overhead及隱藏的危險性 - 開發者對於送到自己的app的封包應該有最適的filtering規則,應該要能將自己的filtering policy與自己的app一起推出 - 就算管理者知道每個app的需求,還是需要應對數不清、不斷變動的網路相關configuration policy - 最後就是效能的問題,與上面所述三者也都有相關 而現代所用的防火牆技術大部分是透過hook在kernel的module來達成的,比如iptables、nftables,但當一個packet被filter掉的時候,一些不必要的traffic早就造成了overhead,如 memory copy。為了解決上述問題,有兩種與以往不同的filter技術被推出,那就是將filter的場域從kernel分別往上移或往下移---往上移至user space或是將packet parsing的工作直接交由外部的FPGA類型網卡解決(見 https://www.xilinx.com/publications/about/ANCS_final.pdf )。 我們首先透過XDP將進入network stack之前的packet就先作過濾的動作,可以過濾掉可疑的host、Denial-of-Service flooding attack等較為簡單的情況(如之前正仁學長做過的實驗)。 還有是透過eBPF將傳統的rule set給break,讓app來handle他自己的packet,如此就可以將這條data path的rule給最簡化:因為他只需要顧到一個application。要達成上述的成果,我們需要使用socket attached packet filting,將filter的場所放置到socket level也就是user level的位置。 ![](https://i.imgur.com/1W7XGWQ.png) ## 基準測試:iptables with Moongen ### moongen ![](https://i.imgur.com/4D32Ymo.png) https://github.com/emmericp/MoonGen 藉由lua script jit(Just-In-Time virtual machine)以及intel DPDK實現的packet flow generator,DPDK透過zero-copy技術將NIC收發的封包直接存入記憶體,app可以透過DPDK提供的API直接存取該段記憶體,徹底繞過了kernel,因此可以對傳出去的封包有著極大的操作空間,包括從單一位址發送多種host的packet,可以拿來自訂我們需要的測試流量,再加上定義封包使用的是lua script,其不需要經過編譯,因此速度上可大大加快。 照著github頁面裝之後還是遇到很多問題(在ubuntu18.04上),之後找到了一些還需安裝的部分: ``` libtbb-dev dpdk-igb-uio-dkms ``` ![](https://i.imgur.com/4m0Fd2P.png) 這是原先計劃的設計圖,但很快就遇到了許多問題......經過一兩個下午的推敲之後大概知道原因:Moongen使用的DPDK面向近代的高性能網路卡,且只有特定型號能支援,而具體的原因在travel過許多lua的bug後,推測為目前使用oracle vm模擬出來的虛擬網卡的型號與driver型號並不支援moongen專案的要求,舉例來說,一個sample script就會需要NIC上具備3個以上的TX及RX queue,而虛擬機提供的模擬NIC只有提供TX queue、RX queue各一個,因此使用moongen的辦法在現階段可能需要先行擱置。 ## 基準測試:iptables with Iperf ![](https://i.imgur.com/ayC2Gj2.png) 無法以論文的moongen方式來分析效能,於是我們採用了備用方案:測試流量的工具iperf。原先打算利用一台client與server的結構來先對number of rule與傳輸效率的關係做比較,但後來發現這樣傳輸的瓶頸不會在packet parsing上,且還有packet loss等問題,做出來的差距並不明顯,因此後來採用在本機端對自己做iperf,這樣瓶頸就會出現在packet parsing上了。以下的數據是ubuntu 18.04vm上進行,限制使用單核CPU且將效能壓在70%也就是約3 Ghz的頻率,並且將recieve buffer限制在1 kb避免其影響速率判斷,以不同的iptables rule數量來分析packet processing的效率,而最基準的數據也就是沒有開啟iptables時的數據為**1.98G per seconed**: ![](https://i.imgur.com/b7Sao7e.png) 可以看到,隨著iptables規則的提升,處理packet的速度也顯著地下降。 接下來我們將實驗改成在阻擋ICMP洪水攻擊時重複以上類似的實驗,這也是主要與之後的**XDP**、**socket eBPF**比較的數值,架構圖如下: ![](https://i.imgur.com/AaGoTIK.png) 這次的rule就設立一條,主要是DROP掉所有來自attacker的攻擊。 在持續由attacker進行洪水攻擊的同時,處理正常封包的速度由原先的**1.98G per second**降至**0.939G per second**,但若沒有開啟iptables來阻擋便會只剩下**0.489G per second**,如下表 | 未遭受攻擊 | 遭受攻擊| 遭受攻擊且有開iptables | | :-----:| :----: | :----: | | **1.98G per second** | **0.489G per second** | **0.939G per second** | ![](https://i.imgur.com/hZVjJHw.png) ## 基準測試:XDP with Iperf 透過在driver level提前利用簡單邏輯篩選掉封包,可以減少kernel的meta-data structure如sk_buff等的使用,有效減少系統over head。 這次為了方便起見,採用了之前正仁學長的專案,將bytecode load進去後一樣用與iptables相似的拓墣來實驗: ![](https://i.imgur.com/EzZ4jnN.png) 而實驗結果如下表: | 未遭受攻擊 | 遭受攻擊| 遭受攻擊且有開XDP | | :-----:| :----: | :----: | | **1.98G per second** | **0.51G per second** | **1.05G per second** | 就結果而言與iptables相比,在遭受相同的攻擊時處理正常封包的效率能有10%以上的提升。 接著我們透過這個專案來測試XDP在rule的數量多起來的時候的表現: https://github.com/gamemann/XDP-Firewall 這個專案可以將XDP規則寫在檔案裡,我們一樣測試32、64......之類數量的規則。 conf檔例子: ``` interface = "ens18"; updatetime = 15; filters = ( { enabled = true, action = 0, udp_enabled = true, udp_dport = 27015 }, { enabled = true, action = 1, tcp_enabled = true, tcp_syn = true, tcp_dport = 27015 }, { enabled = true, action = 0, icmp_enabled = true, icmp_code = 0 }, { enabled = true, action = 0, srcip = "10.50.0.4" } ); ``` 但這裡遇到了些問題,自己對自己做iperf測速來觀察packet process效率的方法無法使用,因為packet似乎在一個host內傳送時根本不會傳到那麼底層,也就是說吃不到XDP的data path,因此這部分暫時無法做明顯的比較。 之後想到了解決辦法,自身IP loopback是藉由lo這個linux自帶NIC所完成的,所以將XDP load進lo上面,就可以讓loop back的流量經過XDP。 **塞XDP的rule只有塞到128個,因為再多的話程式竟然會噴segmentation fault......推測是kernel效能的問題。** ![](https://i.imgur.com/RE7S5qV.png) 從圖表中看出來,塞無用rule的情況下XDP竟然效能來的比iptables差?這顯然不合預期。推測這樣判斷並不準確,問題應該是出由同個host不同網卡互傳的問題上,應該還是要將sender與reciever分開到兩個不同機器。 ### ***想到了有趣的問題,過去用iperf測試時都以頻寬作為測量標準,實際傳送出去的封包可能沒幾個,接下來會修正之前做的實驗,改以size小但數量大的封包flow來做測試。*** ### ***iperf實在是很看出接收端的packets per seconed,換作其他測試工具:ostinato*** ## Testing with ostinato ostinato packets generator可以完全自訂所要傳送的packet,包括source、payload及protocal等等。 ![](https://i.imgur.com/paqaLuZ.jpg) 這裡打算使用parser端建立一個udp socket program,循環將socket buffer中的封包讀出,並print出接收pps,並利用ostinato建立一個stream以每秒30000個封包的速度傳送udp封包給parser,每個封包大小為64byte。 ![](https://i.imgur.com/8kmKluS.jpg) 但在這裡又遇到了一些問題,原本是在parser端做一個socket program來取封包,結果不管加幾條rule都不會影響取封包的速度,摸索、嘗試很久後,了解到可能跟linux的udp、tcp bufer size有關。 因此透過 ``` echo 'net.core.rmem_max=20000000' >> /etc/sysctl.conf ``` 來將buffer size放大。 接下來開始測試增加rule對iptables處理封包的影響,一樣測試32、64.......個無用rule對封包處理速度的影響。但是很奇怪的是不論加多少個rule,都不會影響每秒處理的封包量。 ![](https://i.imgur.com/IpAqX46.png) 比對論文的結果之後推測應該是因為硬體限制: **對於kernel的限制,應該是到處理1Mpps(每秒百萬個封包)以上等級才會體現出來,但目前透過虛擬機,就算如何壓低封包數量也最多只能每秒傳送10萬個左右的封包。** **也有嘗試過將CPU 效能降低等來強制出現瓶頸的辦法,但也無法。推測應該使用論文裡說的以實體機器、實體線路對接,才有可能產生如此大量的封包流。** ### iptables with mixed packets 於是處理封包速率的實驗先暫時擱置,現在預計使用ostinato產生兩個封包流,其中一個的source ip被iptables drop掉,另一個可以通過,來觀察其表現。 ![](https://i.imgur.com/TfGYYBs.jpg) | 正常情況 | 30%為Drop封包| 80%為Drop封包 | | :-----:| :----: | :----: | | **70k packets per second** | **25k packets per second** | **11k packets per second** | ### XDP with mixed packets ![](https://i.imgur.com/5mmJCMo.jpg) 這裡一樣用上面用過的簡易XDP防火牆,Drop掉來自192.168.88.117的封包,如上圖: 實驗結果如下: | 正常情況 | 30%為Drop封包| 80%為Drop封包 | | :-----:| :----: | :----: | | **70k packets per second** | **45k packets per second** | **23k packets per second** | ## 待處理部分 1、接下來希望能夠用實體機器產生百萬級別的封包流,只有這樣才能真正反應parsing所帶來的效能瓶頸。 2、ostinato仍然有許多不方便的地方,如接收封包的部分,而moongen卻是能處理接收封包的邏輯,會嘗試在實體機器上嘗試。 3、Paper中還有講到**Socket eBPF**的部分還沒做,之後也要完成。