# 111-2 專題研究筆記Week 5-6 https://satishdotpatel.github.io/maximizing-packet-capture-performance-with-tcpdump/ ## Packet Parsing 比較在user space、kernel、driver level的packet parsing速度。 ### User Space-TUN/TAP or Libpcap 由user space處理raw packet的辦法有兩種:TUN/TAP 或是 Libpcap。前者是利用虛擬layer2 NIC一頭接物理NIC,一頭接user space的application,如下圖: ![](https://i.imgur.com/PtoluvR.png) 而Libpcap則是利用eBPF協定讓packet能"跳過"linux network stack的parsing,將完整packet給到userspace的app手上。 ![](https://i.imgur.com/N3lJ2So.png) 這裡使用libpcap來實驗簡單的packet sniffer: https://www.tcpdump.org/pcap.html 實驗環境為host上的ubuntu 18.04 VM,使用bridge模式,但很奇怪的是都只有收到來自、傳給host的封包,後來加上設定只show出source或dist有VM IP的封包就正常了。 ![](https://i.imgur.com/xJ6Yy4R.png) 250為host,51為VM,此時正在用host ping VM。 將抓取封包的數量固定在一個,則他會等到接收到dist或source是自己的封包的時候將header的資訊parsing並print出來。 等待接收封包: ![](https://i.imgur.com/axo8cyj.png) Host ping他: ![](https://i.imgur.com/1ruF6qR.png) 目前的構想是以一個server的VM寫一個簡單的socket程式,ping他的時候顯示系統時間,而sniffer這邊在抓完封包時顯示系統時間,取其中的時間差當作比較值。 我們統一使用這個function來秀出現在的時間(ms)。 ``` long long current_timestamp() { struct timeval te; gettimeofday(&te, NULL); // get current time long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds // printf("milliseconds: %lld\n", milliseconds); return milliseconds; } ``` ![](https://i.imgur.com/7l5OY4n.png) ![](https://i.imgur.com/wl9kOHw.png) 1647930428053-1647930445922 = ??? 這裡發現接收packet的竟然時間還比較早,比對了一下發現兩個的系統時間不一致: ![](https://i.imgur.com/eoaeQPm.png) 這裡嘗試使用ntpdate來校正時間: ``` $ ntpdate ntp.ubuntu.com ``` ![](https://i.imgur.com/r7FJiKE.png) 可以看到還是有差距,之後將ntp server換成台灣的也大概還有一秒的差距,因此索性將server那端架成一個ntp server,讓cliet與他直接sync時間,但結果還是有一秒的落差,因此問題可能是出在其他地方,比如ssh顯示、指令延遲。 ![](https://i.imgur.com/1pFW8Gn.png) 這時候來看一下時間差,傳送時間為:08177,收到時間為:08432,時間約為432-177=255ms。 ### Driver Level-XDP 而在driver level,我們使用xdpdump作為packet parsing的工具,他可以讓我們將eBPF註冊進入NIC driver上,並在driver level就能提前將封包拆解。 https://github.com/Netronome/bpf-samples/tree/master/xdpdump 將原始碼裡面的timestamp格式改成與server端相同的: ``` long long current_timestamp() { struct timeval te; gettimeofday(&te, NULL); // get current time long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds // printf("milliseconds: %lld\n", milliseconds); return milliseconds; } ``` 並再次同步時間。 ![](https://i.imgur.com/lyL9YdH.png) ![](https://i.imgur.com/avouN2p.png) 這裡又發生了收到時間快於傳送時間的情況,但跳回去用libpcap寫的dumper試試看 (就是user space的實驗)還是一樣的結果(200~400ms),因此我們在解決時間同步的問題前先以相對結果來看: - user space:傳出去到parsing完約200~400ms - driver level:約-44ms 先以相對的速度來說,在driver level處理packet可以比在user space快上0.3~0.4秒,可以說是很顯著的提升。 ### Kernel Space-IPtable 至於kernel space,我們使用iptables來當作parsing的工具。為了要讓我們能看到解完封包的時間,我們開啟對於192.168.87.145的LOG功能: ``` $ sudo iptables -A INPUT -s 192.168.87.145/32 -j LOG --log-level 7 ``` 那這裡因為系統log無法像前兩個lab一樣弄成那樣的format,因此我們改寫我們ping端紀錄時間的方式: ``` struct timeval time_now; struct tm *time_str_tm; gettimeofday(&time_now, NULL); time_str_tm = gmtime(&time_now.tv_sec); printf("\n%02i:%02i:%02i:%06i\n", time_str_tm->tm_hour, time_str_tm->tm_min, time_str_tm->tm_sec, time_now.tv_usec); ``` 以data的方式記錄到ms的精度。 ping端 ![](https://i.imgur.com/STc9JFK.png) 接收端 ![](https://i.imgur.com/5iwUM9N.png) 這裡可以看到小時的部分沒對起來,但無傷大雅,從傳送到解包完成大概用時413-393=20ms。 與前述的兩者做總結: - user space:255ms - kernel space:20ms - driver level:-44ms 可以說是符合預期。