---
# System prepended metadata

title: 111-2專題研究筆記Week7-8

---

# 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**的部分還沒做，之後也要完成。
