# DPDK 安裝 & 環境架設
DPDK 安裝流程、VM 部署、官方 sample 運行。
###### tags: `dpdk`
## 安裝、配置
### 檢查&設定 hugepage
```shell
# xxxx 視 kernel 版本而定 (按 tab 自動補齊)
grep -i huge /boot/config-.........
grep -i huge /proc/meminfo
```
* 有下列兩行表示支援目前的系統 hugepage
```
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
```
**配置 hugepage (暫時配置,每次重新開機後須重新設定)**
```shell
echo 1024 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# 檢查
cat /proc/meminfo | grep Huge
sudo mkdir /mnt/huge
sudo mount -t hugetlbfs nodev /mnt/huge
sudo sysctl -w vm.nr_hugepages=1024
```
**每次開機後自動掛載(未設定成功,原因不明,每次開機後仍然執行上方暫時配置的指令)**
```shell
vim /etc/fstab
# 加入
nodev /mnt/huge hugetlbfs defaults 0 0
```
### 安裝 & 編譯 DPDK
**若使用 vmware(等虛擬機),必須先使用修補檔**
- 先下載 [patch](http://patches.dpdk.org/patch/11622/)
- [patch 使用說明](https://blog.longwin.com.tw/2013/08/linux-diff-patch-learn-note-2013/),以 `patch -p0 < xxx.patch` 啟動並輸入要更新的檔案路徑。
- 路徑:
`/home/yen/dpdk-stable-18.11.2/kernel/linux/igb_uio/igb_uio.c`
**安裝相關套件**
```shell
sudo apt-get update
sudo apt-get install -y python libpcap-dev build-essential linux-headers-`uname -r` libnuma-dev pkg-config gcc libnuma-dev libpcre3 libpcre3-dev libssl-dev
```
**下載 & 編譯(這邊使用的是 dpdk-18.11.2 版本)**
```shell
### 下載
wget http://fast.dpdk.org/rel/dpdk-18.11.2.tar.xz
tar -xvf dpdk-18.11.2.tar.xz
### 環境變數設
export DPDK_DIR=$HOME/dpdk-stable-18.11.2
export DPDK_TARGET=x86_64-native-linuxapp-gcc
export DPDK_BUILD=$DPDK_DIR/$DPDK_TARGET
export LD_LIBRARY_PATH=$DPDK_DIR/x86_64-native-linuxapp-gcc/lib
export RTE_SDK=$HOME/dpdk-stable-18.11.2
export RTE_MACHINE="native"
export RTE_TARGET=x86_64-native-linuxapp-gcc
## 安裝
cd $DPDK_DIR && sudo make install T=$DPDK_TARGET DESTDIR=install
sudo sed -i 's/CONFIG_RTE_BUILD_SHARED_LIB=n/CONFIG_RTE_BUILD_SHARED_LIB=y/g' ${DPDK_DIR}/config/common_base
```
### 載入 kernel module & 綁定網卡
DPDK 提供的核心模組內包含了部分網路卡的驅動,利用此核心模組所提供的 API (DPDK library),可以使開發者直接控制網路卡的行為。
```shell
sudo modprobe uio
sudo modprobe vfio-pci
sudo insmod $HOME/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko
sudo insmod $HOME/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/rte_kni.ko kthread_mode=multiple carrier=on
# 使用 usertools 內的工具進行設定
cd $HOME/dpdk-stable-18.11.2/usertools
# 查看目前網路卡搭載的驅動狀態
sudo $HOME/dpdk-stable-18.11.2/usertools/dpdk-devbind.py --status
# 卸載指定網卡當前所搭載的驅動
sudo $HOME/dpdk-stable-18.11.2/usertools/dpdk-devbind.py -u 00:08.0
# 指定驅動與網卡做綁定
sudo $HOME/dpdk-stable-18.11.2/usertools/dpdk-devbind.py -b igb_uio 0000:00:08.0
# 最後檢查
sudo $HOME/dpdk-stable-18.11.2/usertools/dpdk-devbind.py --status
```
**卸載 module**
```shell
sudo rmmod /home/yen/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko
sudo rmmod /home/yen/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/rte_kni.ko
```
### 設定 shared file (library)
若執行 DPDK 程式出現 shared file error 等訊息,依照下列指令將編譯後的 dpdk 函式庫加入 shared file。
編輯 `/etc/ld.so.conf` ,加入 DPDK library。
```
$ sudo vim /etc/ld.so.conf
# 加入
/home/使用者名稱/dpdk-stable-18.11.2/install/lib
/home/使用者名稱/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/lib
$ sudo ldconfig
```
### 簡易操作腳本
- 5,6 做 Bind 的部分需要按照的裝置自行調整
```shell
while((1));do
echo "1. Set hugepage"
echo "2. Insert modules"
echo "3. Remove modules"
echo "4. Show NIC driver status"
echo "5. Bind NIC (ens38) with igb_uio"
echo "6. Bind NIC (ens38) with e1000"
echo "7. Set vEth0 up"
echo "U. Install dependencies"
echo "D. Remove dpdk directory and unzip again"
echo "R. Recompile DPDK package"
read -p "Action ?" n
case "$n" in
1)
echo 1024 | sudo tee /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
cat /proc/meminfo | grep Huge
sudo mount -t hugetlbfs nodev /mnt/huge
sudo sysctl -w vm.nr_hugepages=1024
;;
2)
sudo modprobe uio
sudo modprobe vfio-pci
sudo insmod /home/$(whoami)/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko
sudo insmod /home/$(whoami)/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/rte_kni.ko kthread_mode=multiple
carrier=on
;;
3)
sudo rmmod /home/$(whoami)/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko
sudo rmmod /home/$(whoami)/dpdk-stable-18.11.2/x86_64-native-linuxapp-gcc/kmod/rte_kni.ko
;;
4)
sudo /home/$(whoami)/dpdk-stable-18.11.2/usertools/dpdk-devbind.py --status
;;
5)
sudo ifconfig ens38 down
sudo /home/$(whoami)/dpdk-stable-18.11.2/usertools/dpdk-devbind.py -b igb_uio 0000:02:06.0
;;
6)
sudo /home/$(whoami)/dpdk-stable-18.11.2/usertools/dpdk-devbind.py -b e1000 0000:02:06.0
sudo ifconfig ens38 up
;;
7)
sudo ip addr add 192.168.0.254/24 dev vEth0
sudo ip -6 addr add 2001:0db8:0:f101::1/64 dev vEth0
sudo ip link set vEth0 up
;;
U)
sudo apt-get update
sudo apt-get install -y python libpcap-dev build-essential linux-headers-`uname -r` libnuma-dev pkg-config gcc libnuma-dev make
;;
D)
sudo rm -rf /home/$(whoami)/dpdk-stable-18.11.2
tar -xvf dpdk-18.11.2.tar.xz
;;
R)
export DPDK_DIR=/home/$(whoami)/dpdk-stable-18.11.2
export DPDK_TARGET=x86_64-native-linuxapp-gcc
export DPDK_BUILD=$DPDK_DIR/$DPDK_TARGET
export LD_LIBRARY_PATH=$DPDK_DIR/x86_64-native-linuxapp-gcc/lib
export RTE_SDK=$HOME/dpdk-stable-18.11.2
export RTE_MACHINE="native"
export RTE_TARGET=x86_64-native-linuxapp-gcc
cd $DPDK_DIR && sudo make -j4 install T=$DPDK_TARGET DESTDIR=install
sudo sed -i 's/CONFIG_RTE_BUILD_SHARED_LIB=n/CONFIG_RTE_BUILD_SHARED_LIB=y/g' ${DPDK_DIR}/config/common_base
esac
done
```
### lcore 隔離
`sudo vim /etc/default/grub`
加入 `isolcpus=2,3,4` 後重新開機。
---
## 範例程式練習
### 1. hello world
這個範例程式會根據執行時選定的 core mask 啟用對應的 core,並讓每個 core 執行 hello world。
```shell
#刪除所有 export 的環境變數
#$ unset GNUPLOT_DRIVER_DIR
cd examples/helloworld/
export RTE_SDK=$HOME/dpdk-stable-18.11.2
export RTE_TARGET=x86_64-native-linuxapp-gcc
make && sudo ./build/helloworld -c 3 -n 2
```
- `-c`: core mask,3 表示啟用 core 0,1(3 = 00000011)
- `-n`: memory channel 的數量
執行結果:

### 2. 以 KNI (虛擬網卡)進行封包轉發
KNI 模擬 Linux network interface 的功能,會和 kernel 做溝通,讓使用者可以操作 ethtools。
1. 編譯&執行
```shell
cd examples/kni/
export RTE_SDK=$HOME/dpdk-stable-18.11.2
export RTE_TARGET=x86_64-native-linuxapp-gcc
make && sudo ./build/kni -c 0xf0 -n 2 -- -P -p 0x1 --config="(0,4,5)"
```
- `-c`: core mask,0xf 表示使用 core 0,1,2,3 (4 = 00001111)
- `-n`: memory channel 的數量
- `-P`: 混雜模式
- `-p`: port mask,0x3 表示使用 2個 ports(兩張網卡)
- `--config=`"(port_ID,TX_coreID,RX_coreID)"
2. 設定 vEth 網卡的 IP 並啟動
```shell
sudo ip addr add 192.168.0.254/24 dev vEth0
sudo ip -6 addr add 2001:0db8:0:f101::1/64 dev vEth0
sudo ip link set vEth0 up
sudo ip addr add 192.168.1.254/24 dev vEth1
sudo ip link set vEth1 up
```
* 設定後 `ifcofig` 觀察目前網路介面,經由 KNI 建立的網路經面為: vEth0

* 使用 tcpdump 查看流經 vEth0 的封包(ping 測試)

### 3. nDPI + l3fwd
- 需使用2張網路卡(2個 port
1. 編譯,因為使用了 nDPI 和 DPDK 的 libraries ,所以必須 export 兩個 libraries 的位置
```shell
### 設置環境變數(找不到環境變數時就要設定一次)
export RTE_SDK=$HOME/dpdk-stable-18.11.2
export RTE_TARGET=x86_64-native-linuxapp-gcc
export nDPI_src=$HOME/nDPI
### 使用 --eth-dest 指定 port 連接目標的 MAC
make && sudo ./build/nDPIexe -c 0xf -n 4 -- -L -p 0x7 --config="(0,0,0),(1,0,1),(2,0,2)" --parse-ptyp --eth-dest=0,68:b5:99:b6:d3:5d --eth-dest=1,68:b5:99:b6:d3:75 --eth-dest=2,b4:96:91:65:62:ab -- -n2
make && sudo ./build/nDPIexe -c 0xf -n 4 -- -L -p 0x7 --config="(0,0,0),(1,0,1)" --parse-ptyp --eth-dest=0,68:b5:99:b6:d3:5d --eth-dest=1,68:b5:99:b6:d3:75 --eth-dest=2,b4:96:91:65:62:ab -- -n2
### tcpreplay
tcpprep -a bridge -i filtered/slowdownload.pcap -o processed/input.cache
tcprewrite --enet-dmac=B4:96:91:65:62:A8,B4:96:91:65:62:A9 --enet-smac=68:b5:99:b6:d3:5d,68:b5:99:b6:d3:75 --endpoints=192.168.1.100:192.168.2.100 -m1500 -i filtered/slowdownload.pcap -o processed/out.pcap -c processed/input.cache
sudo tcpreplay -l10 -t -c processed/input.cache -i enp5s0f1 -I enp4s0f1 processed/out.pcap
### tshark
tshark -n -r"packets_default.pcap" -Y "tcp.stream > 0 and tcp.stream < 100" -w tmp.pcap
### perf
ps aux | grep "nDPI" | awk 'NR==2{print $2}' | xargs sudo perf top -p
### kill process
ps aux | grep "nDPI" | awk 'NR==2{print $2}' | xargs sudo kill -9
```
目前連線狀況:

2. 設定 static arp 跟 route
因為目前的轉發程式內沒有支援 ARP reply 與 ARP cache 的功能,所以需要透過設定 static ARP。
- 設定路由 (netplan 設置、route -n 檢查)
- 設定 static ARP
**netplan 設置(路由設定參考)**
client-1
```
network:
ethernets:
ens33:
addresses: [192.168.44.130/24]
nameservers:
addresses: [8.8.8.8,8.8.4.4]
dhcp4: no
ens38:
dhcp4: no
addresses: [192.168.0.101/24]
routes:
- to: 192.168.1.0/24
via: 192.168.0.10
version: 2
```
**Static ARP 設置**
- 指令: `arp -s target_IP target_MAC_addr`
- 需確定與主機連線的 port 與 MAC addr
client_1
```shell
sudo arp -s 192.168.0.10 B4:96:91:65:62:A8
```
3. 使用 `tcpreplay` 發送封包進行測試
- `tcpprep` 將封包進行分流並存成 cache 檔
- `rewrite` 依據 cache 檔的分流紀錄改寫封包內容
- `tcpreplay` 將指定的 pcap 檔從指定的介面發送
## 函式介紹
```C
static uint16_t rte_eth_tx_burst( uint16_t port_id,
uint16_t queue_id,
struct rte_mbuf ** tx_pkts,
uint16_t nb_pkts
)
```
- port_id: 識別網路介面的編號
- queue_id: 識別各介面底下佇列的編號
- tx_pkts: 欲傳送封包所在的記憶體地址(可能多個封包,因此是一陣列用於紀錄多個記憶體地址)
- nb_pkts: 欲傳送的封包數量上限
[pcre 函式說明](http://man7.org/linux/man-pages/man3/pcre_compile.3.html)