作者:ARTHURCHIAO’S BLOG
來源:http://arthurchiao.art/blog/tcpdump-practice-zh/
本文將展示如何使用 tcpdump 抓包,以及如何用 tcpdump 和 wireshark 分析網絡流量。文中的例子比較簡單,適合作爲入門參考。
1. 基礎環境準備
爲方便大家跟着上手練習,本文將搭建一個容器環境。
1.1 Pull Docker 鏡像
$ sudo docker pull alpine:3.8
1.2 運行容器
$ sudo docker run -d --name ctn-1 alpine:3.8 sleep 3600d
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
233bc36bde4b alpine:3.8 "sleep 3600d" 1 minutes ago Up 14 minutes ctn-1
進入容器:
$ sudo docker exec -it ctn-1 sh
查看容器網絡信息:
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:09
inet addr:172.17.0.9 Bcast:0.0.0.0 Mask:255.255.0.0
1.3 安裝 tcpdump
/ # apk update
/ # apk add tcpdump
2 HTTP/TCP 抓包
接下來我們用 wget 獲取一個網站的首頁文件(index.html),同時 tcpdump 抓包,對抓 到的網絡流量進行分析。
2.1 HTTP 請求:下載測試頁面
example.com 是一個測試網站,wget 是一個 linux 命令行工 具,可以下載網絡文件。
如下命令可以下載一個 example.com 網站的首頁文件 index.html:
/ # wget http://example.com
Connecting to example.com (93.184.216.34:80)
index.html 100% |*****************************| 1270 0:00:00 ETA
雖然這看起來極其簡單,但背後卻涵蓋了很多複雜的過程,例如:
- **域名查找:**通過訪問 DNS 服務查找 example.com 服務器對應的 IP 地址
- **TCP 連接參數初始化:**臨時端口、初始序列號的選擇等等
- 客戶端(容器)通過 TCP 三次握手協議和服務器 IP 建立 TCP 連接
- 客戶端發起 HTTP GET 請求
- 服務器返回 HTTP 響應,包含頁面數據傳輸
- 如果頁面超過一個 MTU,會分爲多個 packet 進行傳輸(後面會看到,確實超過 MTU 了)
- TCP 斷開連接的四次揮手
2.2 抓包:打到標準輸出
用下面的 tcpdump 命令抓包,另一窗口執行 wget http://example.com,能看到如下類 似的輸出。爲了方便後面的討論,這裏將一些字段去掉了,並做了適當的對齊:
/ # tcpdump -n -S -i eth0 host example.com
1 02:52:44.513700 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [S] , seq 3310420140, length 0
2 02:52:44.692890 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [S.], seq 1353235534, ack 3310420141, length 0
3 02:52:44.692953 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353235535, length 0
4 02:52:44.693009 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [P.], seq 3310420141:3310420215, ack 1353235535, length 74: HTTP: GET / HTTP/1.1
5 02:52:44.872266 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , ack 3310420215, length 0
6 02:52:44.873342 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , seq 1353235535:1353236983, ack 3310420215, length 1448: HTTP: HTTP/1.1 200 OK
7 02:52:44.873405 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353236983, length 0
8 02:52:44.874533 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [P.], seq 1353236983:1353237162, ack 3310420215, length 179: HTTP
9 02:52:44.874560 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353237162, length 0
10 02:52:44.874705 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [F.], seq 3310420215, ack 1353237162, length 0
11 02:52:45.053732 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , ack 3310420216, length 0
12 02:52:45.607825 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [F.], seq 1353237162, ack 3310420216, length 0
13 02:52:45.607869 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353237163, length 0
參數說明:
- **-n:**打印 IP 而不是 hostname,打印端口號而不是協議(例如打印 80 而不是 http)
- **-S:**打印絕對時間戳
- **-i eth0:**指定從 eth0 網卡抓包
- **host example.com:**抓和 example.com 通信的包(雙向)
更多 tcpdump 的常用命令,可以參考tcpdump: An Incomplete Guide。
2.3 抓包:存文件
-w 命令可以將抓到的包寫到文件,注意這和用重定向方式將輸出寫到文件是不同的。 後者寫的只是標準輸出打印的 LOG,而 -w 寫的是原始包。
/ # tcpdump -i eth0 host example.com -w example.pcap
^C
13 packets captured
13 packets received by filter
0 packets dropped by kernel
生成的 pcap 文件可以用 tcpdump
或者 wireshark
之類的網絡流量分析工具打開。
3 流量分析: tcpdump
如果不指定輸出的話,tcpdump 會直接將信息打到標準輸出,就是我們上面看到的那樣。從 這些輸出裏,我們看到很多信息。
3.1 每列說明
第 1 列是爲了討論方便而加的行號,實際的 tcpdump 輸出並沒有這一列。接下來將用 #
號加數字表示第幾個包,例如 #3
表示第 3 個包。
接下來依次爲:
- packet 時間戳,例如
02:52:44.513700
表示抓到這個包的時間是** 02 時 52 分 44 秒 513 毫秒** - packet 類型,這裏是
IP
包 - 源 (SRC) IP 和端口,目的 (DST) IP 和端口
- packet TCP flags,其中
S
表示syn
包
.
表示ack
包
F
表示fin
包
P
表示push
包(發送正常數據) - 序列號(seq)
- 應答號(ack)
- 包的 payload 長度
- 包的部分內容(ASCII)
3.2 三次握手(1~3)
wget 是基於 HTTP 協議,因此它在下載文件之前,必定要和服務端建立一個連接。
而 TCP 建立連接的過程就是著名的三次握手 [4]:
- client -> server: SYN
- server -> client: SYN+ACK
- client -> server: ACK
我們可以看到,這剛好對應於前三個包:
1 02:52:44.513700 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [S] , seq 3310420140, length 0
2 02:52:44.692890 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [S.], seq 1353235534, ack 3310420141, length 0
3 02:52:44.692953 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353235535, length 0
第一次握手: SYN
#1
包含以下信息:
- 02:52:44.513700 時刻,客戶端主動向 server(93.184.216.34)發起一個 SYN 請求,請求建立連接
- 客戶端請求的服務端端口是 80(HTTP 服務默認 80 端口),客戶端使用的是臨時端口(大於 1024)41038
- #1 序列號是 3310420140,這是客戶端的初始序列號(客戶端和服務端分別維護自己的序列號,兩者沒有關係;另外,初始序列號是系統選擇的,一般不是 0)
- #1 length 爲 0,因爲 SYN 包不帶 TCP payload,所有信息都在 TCP header
第二次握手: SYN+ACK
#2 的 ack 是 3310420140,等於 #1 的 seq 加 1,這就說明,#2 是 #1 的應 答包。
這個應答包的特點:
- TCP flags 爲 S.,即 SYN+ACK
- length 也是 0,說明沒有 payload
- seq 爲 1353235534,這是服務端的初始序列號
- 到達 eth0 的時間爲 02:52:44.692890,說明時間過了 18ms
第三次握手: ACK
同理,#3 的 ack 等於 #2 的 seq 加 1,說明 #3 是 #2 的應答包。
這個包的特點:
- TCP flags 爲 .,即 ACK
- 長度爲 0,說明沒有 TCP payload
至此,三次握手完成。
3.3 正常數據傳輸
三次握手完成後,client 和 server 開始 HTTP 通信,客戶端通過 HTTP GET 方法下載 index.html。
4 02:52:44.693009 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [P.], seq 3310420141:3310420215, ack 1353235535, length 74: HTTP: GET / HTTP/1.1
5 02:52:44.872266 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , ack 3310420215, length 0
6 02:52:44.873342 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , seq 1353235535:1353236983, ack 3310420215, length 1448: HTTP: HTTP/1.1 200 OK
7 02:52:44.873405 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353236983, length 0
8 02:52:44.874533 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [P.], seq 1353236983:1353237162, ack 3310420215, length 179: HTTP
9 02:52:44.874560 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353237162, length 0
這裏可以看到:
- #4: client 向 server 發起 HTTP GET 請求,請求路徑爲根路徑(/),這個 packet 長度爲 74 字節
- #5: 發送了 ACK 包,對 #4 進行確認
- #6: 發送了 1448 字節的數據給 client
- #7: client 對 server 的 #6 進行應答
- #8: server 向 client 端繼續發送 179 字節數據
- #9: client 對 server 的 #8 進行應答
3.4 四次揮手
最後是四次揮手 [5]:
- client -> server: FIN (我們看到的是 FIN+ACK,這是因爲這個 FIN 包除了正常的關閉連接功能之外,還被用於應答 client 發過來的前一個包)
- server -> client: ACK
- client -> server: FIN+ACK
- server -> client: ACK
10 02:52:44.874705 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [F.], seq 3310420215, ack 1353237162, length 0
11 02:52:45.053732 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , ack 3310420216, length 0
12 02:52:45.607825 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [F.], seq 1353237162, ack 3310420216, length 0
13 02:52:45.607869 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] , ack 1353237163, length 0
4 流量分析: wireshark
tcpdump 可以指定 -r 讀取 pcap 文件,並以指定的格式輸出包的信息,最後輸出的內容 和上面看到的類似。我們上面的流量非常簡單,所以看 tcpdump 的輸出就夠了。
對於複雜的 pcap,例如,其中包含了上百個 IP 地址、上千個端口、上萬個連接的 pcap, 通過 tcpdump 看輸出可能就比較低效了。
這時,wireshark 這樣帶圖形用戶界面,且功能強大的網 絡流分析工具就派上了用場。
wireshark 支持強大的過濾功能,支持按 IP、端口、協議、連接、TCP flag 以及它們的各 種組合進行過濾,然後進行分析,大大節省網絡排障的時間。
wireshark 官方維護了一個 sample pcap列表 ,我們拿 iperf-mptcp-0-0.pcap 作爲例子來展示如何使用 wireshark。
4.1 追蹤 TCP 流
下載後雙擊就可以用 wireshark 打開。看到有重傳(TCP Retransmition)的包:
在重傳的包上,右鍵 -> Follow -> TCP Stream,會過濾出只屬於這個連接的包:
我們看到,這個連接只有 3 個包:
- #1 在 08:00:05.125 發送出去,請求建立連接
- 大約 1s 後,客戶端仍然沒有收到服務端的 ACK 包,觸發客戶端 TCP 超時重傳
- 又過了大約 2s,仍然沒有收到 ACK 包,再次觸發超時重傳
- 這裏其實還可以看出 TCP 重傳的機制:指數後退,比如第一次等待 1s,第二次等 待 2s,第三次等待 4s,第四次 8s
因此,從這個抓包文件看,這次連接沒有建立起來,而直接原因就是 client 沒有收到 server 的應答包。要跟進這個問題,就需要在 server 端一起抓包,看應答包是否有發出來 。本文不對此展開。
4.2 過濾流
上面的截圖我們看到 wireshark 裏有 tcp.stream eq 1,這其實就是其強大的過濾表達式。
我們可以直接手寫表達式,然後回車,符合條件的包就會顯示出來。而且,在編輯表達式的 時候,wireshark 有自動提示,還是比較方便的。這些表達式和 tcpdump 的 filter 表達 式很類似,如果熟悉 tcpdump,那這裏不會有太大困難。
下面舉一些例子:
- ip.addr == 192.168.1.1 過濾 SRC IP 或 DST IP 是 192.168.1.1 的包
- ip.src_host == 192.168.1.1 and ip.dst_host == 192.168.1.2 過濾 SRC IP 是 192.168.1.1,並且 DST IP 是 192.168.1.2 的包
- tcp.port == 80 源端口或目的端口是 80 的包
- tcp.flags.reset == 1 過濾 TCP RST 包。先找到 RST 包,然後右鍵 Follow -> TCP Stream 是常用的排障方式
- tcp.analysis.retransmission 過濾所有的重傳包
4.3 導出符合條件的包
有時 pcap 文件太大,導致 wireshark 非常慢,而大部分數據包可能是不需要的。在這種情況 下,可以先用過濾條件篩選出感興趣的包,然後 File -> Export Specified Packets … ,彈出的對話框裏,可以選擇當前顯示的包,或者某個指定區間的包另存爲新 pcap。
然後就可以關閉原來的 pcap,打開新的 pcap 進行分析。
5 總結
tcpdump 和 wireshark 功能非常強大,組合起來更是網絡排障的首選利器。這裏介紹的內 容只是九牛一毛,更多的時候,你需要 tcpdump+wireshark+google。
References
1、Man Page of tcpdump
2、Wireshark
3、Wireshark: Sample Pcaps
4、TCP 3-way Handshaking
5、TCP 4-times Close
6、tcpdump: An Incomplete Guide
今天的推薦不知道大家喜歡嗎?如果你喜歡,請在文章底部留言
和點贊
,以表示對我的支持,你們的留言
和點贊
是我持續更新的動力哦,感謝大家!
1、點個贊,讓更多的人看到這篇文章,順便激勵下我,嘻嘻。
2、關注我的原創微信公衆號「傑哥的IT之旅」
,專注於IT技術乾貨文章,以及不定期的分享學習資料,實用工具,面試經驗
等,當然了還有內推機會
哦,期待你的關注!