對於 tcpdump 的使用,大部分管理員會分成兩類。有一類管理員,他們熟知 tcpdump 和其中的所有標記;另一類管理員,他們僅瞭解基本的使用方法,剩下事情都要藉助參考手冊才能完成。出現這種情況的原因在於, tcpdump 是一個相當高階的命令,使用的時候需要對網路的工作機制有相當深入的瞭解。
在今天的文章中,我想提供一個快速但相當實用的 tcpdump 參考。我會談到基本的和一些高階的使用方法。我敢肯定我會忽略一些相當酷的命令,歡迎你補充在評論部分。
在我們深入瞭解以前,最重要的是瞭解 tcpdump 是用來做什麼的。 tcpdump 命令用來儲存和記錄網路流量。你可以用它來觀察網路上發生了什麼,並可用來解決各種各樣的問題,包括和網路通訊無關的問題。除了網路問題,我經常用 tcpdump 解決應用程式的問題。如果你發現兩個應用程式之間無法很好工作,可以用 tcpdump 觀察出了什麼問題。 tcpdump 可以用來抓取和讀取資料包,特別是當通訊沒有被加密的時候。
基礎知識
瞭解 tcpdump ,首先要知道 tcpdump中使用的標記(flag)。在這個章節中,我會涵蓋到很多基本的標記,這些標記在很多場合下會被用到。
不轉換主機名、埠號等
1 |
# tcpdump -n |
通常情況下, tcpdump 會嘗試查詢和轉換主機名和埠號。
1 2 3 4 |
# tcpdump tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 16:15:05.051896 IP blog.ssh > 10.0.3.1.32855: Flags [P.], seq 2546456553:2546456749, ack 1824683693, win 355, options [nop,nop,TS val 620879437 ecr 620879348], length 196 |
你可以通過 -n 標記關閉這個功能。我個人總是使用這個標記,因為我喜歡使用 IP 地址而不是主機名,主機名和埠號的轉換經常會帶來困擾。但是,知道利用 tcpdump 轉換或者不轉換的功能還是相當有用的,特別是有些時候,知道源流量(source traffic)來自哪個伺服器是相當重要的。
1 2 3 4 |
# tcpdump -n tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 16:23:47.934665 IP 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], seq 2546457621:2546457817, ack 1824684201, win 355, options [nop,nop,TS val 621010158 ecr 621010055], length 196 |
增加詳細資訊
1 |
# tcpdump -v |
增加一個簡單 -v 標記,輸出會包含更多資訊,例如一個 IP 包的生存時間(ttl, time to live)、長度和其他的選項。
1 2 3 4 |
# tcpdump tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 16:15:05.051896 IP blog.ssh > 10.0.3.1.32855: Flags [P.], seq 2546456553:2546456749, ack 1824683693, win 355, options [nop,nop,TS val 620879437 ecr 620879348], length 196 |
tcpdump 的詳細資訊有三個等級,你可以通過在命令列增加 v 標記的個數來獲取更多的資訊。通常我在使用 tcpmdump 的時候,總是使用最高等級的詳細資訊,因為我希望看到所有資訊,以免後面會用到。
1 2 3 4 |
# tcpdump -vvv -c 1 tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 16:36:13.873456 IP (tos 0x10, ttl 64, id 121, offset 0, flags [DF], proto TCP (6), length 184) blog.ssh > 10.0.3.1.32855: Flags [P.], cksum 0x1ba1 (incorrect -> 0x0dfd), seq 2546458841:2546458973, ack 1824684869, win 355, options [nop,nop,TS val 621196643 ecr 621196379], length 132 |
指定網路介面
1 |
# tcpdump -i eth0 |
通常情況下,如果不指定網路介面, tcpdump 在執行時會選擇編號最低的網路介面,一般情況下是 eth0,不過因系統不同可能會有所差異。
1 2 3 4 |
# tcpdump tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 16:15:05.051896 IP blog.ssh > 10.0.3.1.32855: Flags [P.], seq 2546456553:2546456749, ack 1824683693, win 355, options [nop,nop,TS val 620879437 ecr 620879348], length 196 |
你可以用 -i 標記來指定網路介面。在大多數 Linux 系統上,any 這一特定的網路介面名用來讓 tcpdump 監聽所有的介面。我發現這在排查伺服器(擁有多個網路介面)的問題特別有用,尤其是牽扯到路由的時候。
1 2 3 4 |
# tcpdump -i any tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 16:45:59.312046 IP blog.ssh > 10.0.3.1.32855: Flags [P.], seq 2547763641:2547763837, ack 1824693949, win 355, options [nop,nop,TS val 621343002 ecr 621342962], length 196 |
寫入檔案
1 |
# tcpdump -w /path/to/file |
tcpdump 執行結果會輸出在螢幕上。
1 2 3 4 |
# tcpdump tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 16:15:05.051896 IP blog.ssh > 10.0.3.1.32855: Flags [P.], seq 2546456553:2546456749, ack 1824683693, win 355, options [nop,nop,TS val 620879437 ecr 620879348], length 196 |
但很多時候,你希望把 tcpdump 的輸出結果儲存在檔案中,最簡單的方法就是利用 -w 標記。如果你後續還會檢查這些網路資料,這樣做就特別有用。將這些資料存成一個檔案的好處,就是你可以多次讀取這個儲存下來的檔案,並且可以在這個網路流量的快照上使用其它標記或者過濾器(我們後面會討論到)。
1 2 3 4 5 |
# tcpdump -w /var/tmp/tcpdata.pcap tcpdump : listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes 1 packet captured 2 packets received by filter 0 packets dropped by kernel |
通常這些資料被快取而不會被寫入檔案,直到你用 CTRL+C 結束 tcpdump 命令的時候。
讀取檔案
1 |
# tcpdump -r /path/to/file |
一旦你將輸出存成檔案,就必然需要讀取這個檔案。要做到這點,你只需要在 -r 標記後指定這個檔案的存放路徑。
1 2 3 |
# tcpdump -r /var/tmp/tcpdata.pcap reading from file /var/tmp/tcpdata.pcap, link-type EN10MB (Ethernet) 16:56:01.610473 IP blog.ssh > 10.0.3.1.32855: Flags [P.], seq 2547766673:2547766805, ack 1824696181, win 355, options [nop,nop,TS val 621493577 ecr 621493478], length 132 |
一個小提醒,如果你熟悉 wireshark 這類網路診斷工具,也可以利用它們來讀取 tcpdump 儲存的檔案。
指定抓包大小
1 |
# tcpdump -s 100 |
較新版本的 tcpdump 通常可以截獲 65535 位元組,但某些情況下你不需要截獲預設大小的資料包。執行 tcpdump 時,你可以通過 -s 標記來指定快照長度。
指定抓包數量
1 |
# tcpdump -c 10 |
tcpdump 會一直執行,直至你用 CTRL+C 讓它退出。
1 2 3 4 5 6 7 |
# tcpdump host google.com tcpdump : verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes ^C 0 packets captured 4 packets received by filter 0 packets dropped by kernel |
你也可以通過 -c 標記後面加上抓包的數量,讓 tcpdump 在抓到一定數量的資料包後停止操作。當你不希望看到 tcpdump 的輸出大量出現在螢幕上,以至於你無法閱讀的時候,就會希望使用這個標記。當然,通常更好的方法是藉助過濾器來截獲特定的流量。
基礎知識彙總
1 |
# tcpdump -nvvv -i any -c 100 -s 100 |
你可以將以上這些基礎的標記組合起來使用,來讓 tcpdump 提供你所需要的資訊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# tcpdump -w /var/tmp/tcpdata.pcap -i any -c 10 -vvv tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 10 packets captured 10 packets received by filter 0 packets dropped by kernel # tcpdump -r /var/tmp/tcpdata.pcap -nvvv -c 5 reading from file /var/tmp/tcpdata.pcap, link-type LINUX_SLL (Linux cooked) 17:35:14.465902 IP (tos 0x10, ttl 64, id 5436, offset 0, flags [DF], proto TCP (6), length 104) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1b51 (incorrect -> 0x72bc), seq 2547781277:2547781329, ack 1824703573, win 355, options [nop,nop,TS val 622081791 ecr 622081775], length 52 17:35:14.466007 IP (tos 0x10, ttl 64, id 52193, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.32855 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x4950), seq 1, ack 52, win 541, options [nop,nop,TS val 622081791 ecr 622081791], length 0 17:35:14.470239 IP (tos 0x10, ttl 64, id 5437, offset 0, flags [DF], proto TCP (6), length 168) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1b91 (incorrect -> 0x98c3), seq 52:168, ack 1, win 355, options [nop,nop,TS val 622081792 ecr 622081791], length 116 17:35:14.470370 IP (tos 0x10, ttl 64, id 52194, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.32855 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x48da), seq 1, ack 168, win 541, options [nop,nop,TS val 622081792 ecr 622081792], length 0 17:35:15.464575 IP (tos 0x10, ttl 64, id 5438, offset 0, flags [DF], proto TCP (6), length 104) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1b51 (incorrect -> 0xc3ba), seq 168:220, ack 1, win 355, options [nop,nop,TS val 622082040 ecr 622081792], length 52 |
過濾器
介紹完基礎的標記後,我們該介紹過濾器了。 tcpdump 可以通過各式各樣的表示式,來過濾所擷取或者輸出的資料。我在這篇文章裡會給出一些簡單的例子,以便讓你們瞭解語法規則。你們可以查詢 tcpdump 幫助中的 pcap-filter 章節,瞭解更為詳細的資訊。
查詢特定主機的流量
1 |
# tcpdump -nvvv -i any -c 3 host 10.0.3.1 |
執行上述命令, tcpdump 會像前面一樣把結果輸出到螢幕上,不過只會顯示源 IP 或者目的 IP 地址是 10.0.3.1 的資料包。通過增加主機 10.0.3.1 引數,我們可以讓 tcpdump 過濾掉源和目的地址不是 10.0.3.1 的資料包。
1 2 3 4 5 6 7 8 |
# tcpdump -nvvv -i any -c 3 host 10.0.3.1 tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 17:54:15.067496 IP (tos 0x10, ttl 64, id 5502, offset 0, flags [DF], proto TCP (6), length 184) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1ba1 (incorrect -> 0x9f75), seq 2547785621:2547785753, ack 1824705637, win 355, options [nop,nop,TS val 622366941 ecr 622366923], length 132 17:54:15.067613 IP (tos 0x10, ttl 64, id 52315, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.32855 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x7c34), seq 1, ack 132, win 540, options [nop,nop,TS val 622366941 ecr 622366941], length 0 17:54:15.075230 IP (tos 0x10, ttl 64, id 5503, offset 0, flags [DF], proto TCP (6), length 648) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1d71 (incorrect -> 0x3443), seq 132:728, ack 1, win 355, options [nop,nop,TS val 622366943 ecr 622366941], length 596 |
只顯示源地址為特定主機的流量
1 |
# tcpdump -nvvv -i any -c 3 src host 10.0.3.1 |
前面的例子顯示了源和目的地址是 10.0.3.1 的流量,而上面的命令只顯示資料包源地址是 10.0.3.1 的流量。這是通過在 host 前面增加 src 引數來實現的。這個額外的過濾器告訴 tcpdump 查詢特定的源地址。 反過來通過 dst 過濾器,可以指定目的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# tcpdump -nvvv -i any -c 3 src host 10.0.3.1 tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 17:57:12.194902 IP (tos 0x10, ttl 64, id 52357, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.32855 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x1707), seq 1824706545, ack 2547787717, win 540, options [nop,nop,TS val 622411223 ecr 622411223], length 0 17:57:12.196288 IP (tos 0x10, ttl 64, id 52358, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.32855 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x15c5), seq 0, ack 325, win 538, options [nop,nop,TS val 622411223 ecr 622411223], length 0 17:57:12.197677 IP (tos 0x10, ttl 64, id 52359, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.32855 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x1491), seq 0, ack 633, win 536, options [nop,nop,TS val 622411224 ecr 622411224], length 0 # tcpdump -nvvv -i any -c 3 dst host 10.0.3.1 tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 17:59:37.266838 IP (tos 0x10, ttl 64, id 5552, offset 0, flags [DF], proto TCP (6), length 184) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1ba1 (incorrect -> 0x586d), seq 2547789725:2547789857, ack 1824707577, win 355, options [nop,nop,TS val 622447491 ecr 622447471], length 132 17:59:37.267850 IP (tos 0x10, ttl 64, id 5553, offset 0, flags [DF], proto TCP (6), length 392) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1c71 (incorrect -> 0x462e), seq 132:472, ack 1, win 355, options [nop,nop,TS val 622447491 ecr 622447491], length 340 17:59:37.268606 IP (tos 0x10, ttl 64, id 5554, offset 0, flags [DF], proto TCP (6), length 360) 10.0.3.246.22 > 10.0.3.1.32855: Flags [P.], cksum 0x1c51 (incorrect -> 0xf469), seq 472:780, ack 1, win 355, options [nop,nop,TS val 622447491 ecr 622447491], length 308 |
過濾源和目的埠
1 |
# tcpdump -nvvv -i any -c 3 port 22 and port 60738 |
通過類似 and 操作符,你可以在 tcpdump 上使用更為複雜的過濾器描述。這個就類似 if 語句,你就這麼想吧。這個例子中,我們使用 and 操作符告訴 tcpdump 只輸出埠號是 22 和 60738 的資料包。這點在分析網路問題的時候很有用,因為可以通過這個方法來關注某一個特定會話(session)的資料包。
1 2 3 4 5 6 7 8 |
# tcpdump -nvvv -i any -c 3 port 22 and port 60738 tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 18:05:54.069403 IP (tos 0x10, ttl 64, id 64401, offset 0, flags [DF], proto TCP (6), length 104) 10.0.3.1.60738 > 10.0.3.246.22: Flags [P.], cksum 0x1b51 (incorrect -> 0x5b3c), seq 917414532:917414584, ack 1550997318, win 353, options [nop,nop,TS val 622541691 ecr 622538903], length 52 18:05:54.072963 IP (tos 0x10, ttl 64, id 13601, offset 0, flags [DF], proto TCP (6), length 184) 10.0.3.246.22 > 10.0.3.1.60738: Flags [P.], cksum 0x1ba1 (incorrect -> 0xb0b1), seq 1:133, ack 52, win 355, options [nop,nop,TS val 622541692 ecr 622541691], length 132 18:05:54.073080 IP (tos 0x10, ttl 64, id 64402, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.60738 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0x1e3b), seq 52, ack 133, win 353, options [nop,nop,TS val 622541692 ecr 622541692], length 0 |
你可以用兩種方式來表示 and 操作符,and 或者 && 都可以。我個人傾向於兩個都使用,特別要記住在使用 && 的時候,要用單引號或者雙引號包住表示式。在 BASH 中,你可以使用 && 執行一個命令,該命令成功後再執行後面的命令。通常,最好將表示式用引號包起來,這樣會避免不預期的結果,特別當過濾器中有一些特殊字元的時候。
1 2 3 4 5 6 7 8 |
# tcpdump -nvvv -i any -c 3 'port 22 && port 60738' tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 18:06:16.062818 IP (tos 0x10, ttl 64, id 64405, offset 0, flags [DF], proto TCP (6), length 88) 10.0.3.1.60738 > 10.0.3.246.22: Flags [P.], cksum 0x1b41 (incorrect -> 0x776c), seq 917414636:917414672, ack 1550997518, win 353, options [nop,nop,TS val 622547190 ecr 622541776], length 36 18:06:16.065567 IP (tos 0x10, ttl 64, id 13603, offset 0, flags [DF], proto TCP (6), length 120) 10.0.3.246.22 > 10.0.3.1.60738: Flags [P.], cksum 0x1b61 (incorrect -> 0xaf2d), seq 1:69, ack 36, win 355, options [nop,nop,TS val 622547191 ecr 622547190], length 68 18:06:16.065696 IP (tos 0x10, ttl 64, id 64406, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.60738 > 10.0.3.246.22: Flags [.], cksum 0x1b1d (incorrect -> 0xf264), seq 36, ack 69, win 353, options [nop,nop,TS val 622547191 ecr 622547191], length 0 |
查詢兩個埠號的流量
1 |
# tcpdump -nvvv -i any -c 20 'port 80 or port 443' |
你可以用 or 或者 || 操作符來過濾結果。在這個例子中,我們使用 or 操作符去截獲傳送和接收埠為 80 或 443 的資料流。這在 Web 伺服器上特別有用,因為伺服器通常有兩個開放的埠,埠號 80 表示 http 連線,443 表示 https。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# tcpdump -nvvv -i any -c 20 'port 80 or port 443' tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 18:24:28.817940 IP (tos 0x0, ttl 64, id 39930, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.1.50524 > 10.0.3.246.443: Flags [S], cksum 0x1b25 (incorrect -> 0x8611), seq 3836995553, win 29200, options [mss 1460,sackOK,TS val 622820379 ecr 0,nop,wscale 7], length 0 18:24:28.818052 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 10.0.3.246.443 > 10.0.3.1.50524: Flags [R.], cksum 0x012c (correct), seq 0, ack 3836995554, win 0, length 0 18:24:32.721330 IP (tos 0x0, ttl 64, id 48510, offset 0, flags [DF], proto TCP (6), length 475) 10.0.3.1.60374 > 10.0.3.246.80: Flags [P.], cksum 0x1cc4 (incorrect -> 0x3a4e), seq 580573019:580573442, ack 1982754038, win 237, options [nop,nop,TS val 622821354 ecr 622815632], length 423 18:24:32.721465 IP (tos 0x0, ttl 64, id 1266, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.246.80 > 10.0.3.1.60374: Flags [.], cksum 0x1b1d (incorrect -> 0x45d7), seq 1, ack 423, win 243, options [nop,nop,TS val 622821355 ecr 622821354], length 0 18:24:32.722098 IP (tos 0x0, ttl 64, id 1267, offset 0, flags [DF], proto TCP (6), length 241) 10.0.3.246.80 > 10.0.3.1.60374: Flags [P.], cksum 0x1bda (incorrect -> 0x855c), seq 1:190, ack 423, win 243, options [nop,nop,TS val 622821355 ecr 622821354], length 189 18:24:32.722232 IP (tos 0x0, ttl 64, id 48511, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.60374 > 10.0.3.246.80: Flags [.], cksum 0x1b1d (incorrect -> 0x4517), seq 423, ack 190, win 245, options [nop,nop,TS val 622821355 ecr 622821355], length 0 |
查詢兩個特定埠和來自特定主機的資料流
1 |
# tcpdump -nvvv -i any -c 20 '(port 80 or port 443) and host 10.0.3.169' |
前面的例子用來排查多埠的協議問題,是非常有效的。如果 Web 伺服器的資料流量相當大, tcpdump 的輸出可能有點混亂。我們可以通過增加 host 引數進一步限定輸出。在這種情況下,我們通過把 or 表示式放在括號中來保持 or 描述。
1 2 3 4 5 6 7 8 9 10 11 12 |
# tcpdump -nvvv -i any -c 20 '(port 80 or port 443) and host 10.0.3.169' tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 18:38:05.551194 IP (tos 0x0, ttl 64, id 63169, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.169.33786 > 10.0.3.246.443: Flags [S], cksum 0x1bcd (incorrect -> 0x0d96), seq 4173164403, win 29200, options [mss 1460,sackOK,TS val 623024562 ecr 0,nop,wscale 7], length 0 18:38:05.551310 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 40) 10.0.3.246.443 > 10.0.3.169.33786: Flags [R.], cksum 0xa64a (correct), seq 0, ack 4173164404, win 0, length 0 18:38:05.717130 IP (tos 0x0, ttl 64, id 51574, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.169.35629 > 10.0.3.246.80: Flags [S], cksum 0x1bcd (incorrect -> 0xdf7c), seq 1068257453, win 29200, options [mss 1460,sackOK,TS val 623024603 ecr 0,nop,wscale 7], length 0 18:38:05.717255 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.246.80 > 10.0.3.169.35629: Flags [S.], cksum 0x1bcd (incorrect -> 0xed80), seq 2992472447, ack 1068257454, win 28960, options [mss 1460,sackOK,TS val 623024603 ecr 623024603,nop,wscale 7], length 0 18:38:05.717474 IP (tos 0x0, ttl 64, id 51575, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.169.35629 > 10.0.3.246.80: Flags [.], cksum 0x1bc5 (incorrect -> 0x8c87), seq 1, ack 1, win 229, options [nop,nop,TS val 623024604 ecr 623024603], length 0 |
在一個過濾器中,你可以多次使用括號。在下面的例子中,下面命令可以限定截獲滿足如下條件的資料包:傳送或接收埠號為 80 或 443,主機來源於 10.0.3.169 或者 10.0.3.1,且目的地址是 10.0.3.246。
1 2 3 4 5 6 7 8 9 10 |
# tcpdump -nvvv -i any -c 20 '((port 80 or port 443) and (host 10.0.3.169 or host 10.0.3.1)) and dst host 10.0.3.246' tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 18:53:30.349306 IP (tos 0x0, ttl 64, id 52641, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.1.35407 > 10.0.3.246.80: Flags [S], cksum 0x1b25 (incorrect -> 0x4890), seq 3026316656, win 29200, options [mss 1460,sackOK,TS val 623255761 ecr 0,nop,wscale 7], length 0 18:53:30.349558 IP (tos 0x0, ttl 64, id 52642, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.35407 > 10.0.3.246.80: Flags [.], cksum 0x1b1d (incorrect -> 0x3454), seq 3026316657, ack 3657995297, win 229, options [nop,nop,TS val 623255762 ecr 623255762], length 0 18:53:30.354056 IP (tos 0x0, ttl 64, id 52643, offset 0, flags [DF], proto TCP (6), length 475) 10.0.3.1.35407 > 10.0.3.246.80: Flags [P.], cksum 0x1cc4 (incorrect -> 0x10c2), seq 0:423, ack 1, win 229, options [nop,nop,TS val 623255763 ecr 623255762], length 423 18:53:30.354682 IP (tos 0x0, ttl 64, id 52644, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.1.35407 > 10.0.3.246.80: Flags [.], cksum 0x1b1d (incorrect -> 0x31e6), seq 423, ack 190, win 237, options [nop,nop,TS val 623255763 ecr 623255763], length 0 |
理解輸出結果
開啟 tcpdump 的所有選項去截獲網路流量是相當困難的,但一旦你拿到這些資料你就要對它進行解讀。在這個章節,我們將涉及如何判斷源/目的 IP 地址,源/目的埠號,以及 TCP 協議型別的資料包。當然這些是相當基礎的,你從 tcpdump 裡面獲取的資訊也遠不止這些。不過這篇文章主要是粗略的介紹,我們會關注在這些基礎知識上。我建議你們可以通過幫助頁獲取更為詳細的資訊。
判斷源和目的地址
判斷源和目的地址和埠號相當簡單。
從上面的輸出,我們可以看到源 IP 地址是 10.0.3.246,源埠號是 56894, 目的 IP 地址是 192.168.0.92,埠號是 22。一旦你理解 tcpdump 格式後,這些資訊很容易判斷。如果你還沒有猜到格式,你可以按照 src-ip.src-port > dest-ip.dest-port: Flags[S] 格式來分析。源地址位於 > 前面,後面則是目的地址。你可以把 > 想象成一個指向目的地址的箭頭符號。
判斷資料包型別
1 |
10.0.3.246.56894 > 192.168.0.92.22: Flags [S], cksum 0xcf28 (incorrect -> 0x0388), seq 682725222, win 29200, options [mss 1460,sackOK,TS val 619989005 ecr 0,nop,wscale 7], length 0 |
從上面的例子,我們可以判斷這個資料包是一個 SYN 資料包。我們是通過 tcpdump 輸出中的 [S] 標記欄位得出這個結論,不同型別的資料包有不同型別的標記。不需要深入瞭解 TCP 協議中的資料包型別,你就可以通過下面的速查表來加以判斷。
- [S] – SYN (開始連線)
- [.] – 沒有標記
- [P] – PSH (資料推送)
- [F] – FIN (結束連線)
- [R] – RST (重啟連線)
在這個版本的 tcpdump 輸出中,[S.] 標記代表這個資料包是 SYN-ACK 資料包。
不好的例子
1 2 3 4 5 6 |
15:15:43.323412 IP (tos 0x0, ttl 64, id 51051, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.246.56894 > 192.168.0.92.22: Flags [S], cksum 0xcf28 (incorrect -> 0x0388), seq 682725222, win 29200, options [mss 1460,sackOK,TS val 619989005 ecr 0,nop,wscale 7], length 0 15:15:44.321444 IP (tos 0x0, ttl 64, id 51052, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.246.56894 > 192.168.0.92.22: Flags [S], cksum 0xcf28 (incorrect -> 0x028e), seq 682725222, win 29200, options [mss 1460,sackOK,TS val 619989255 ecr 0,nop,wscale 7], length 0 15:15:46.321610 IP (tos 0x0, ttl 64, id 51053, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.246.56894 > 192.168.0.92.22: Flags [S], cksum 0xcf28 (incorrect -> 0x009a), seq 682725222, win 29200, options [mss 1460,sackOK,TS val 619989755 ecr 0,nop,wscale 7], length 0 |
上面顯示了一個不好的通訊例子,在這個例子中“不好”,代表通訊沒有建立起來。我們可以看到 10.0.3.246 發出一個 SYN 資料包給 主機 192.168.0.92,但是主機並沒有應答。
好的例子
1 2 3 4 5 6 |
15:18:25.716453 IP (tos 0x10, ttl 64, id 53344, offset 0, flags [DF], proto TCP (6), length 60) 10.0.3.246.34908 > 192.168.0.110.22: Flags [S], cksum 0xcf3a (incorrect -> 0xc838), seq 1943877315, win 29200, options [mss 1460,sackOK,TS val 620029603 ecr 0,nop,wscale 7], length 0 15:18:25.716777 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 60) 192.168.0.110.22 > 10.0.3.246.34908: Flags [S.], cksum 0x594a (correct), seq 4001145915, ack 1943877316, win 5792, options [mss 1460,sackOK,TS val 18495104 ecr 620029603,nop,wscale 2], length 0 15:18:25.716899 IP (tos 0x10, ttl 64, id 53345, offset 0, flags [DF], proto TCP (6), length 52) 10.0.3.246.34908 > 192.168.0.110.22: Flags [.], cksum 0xcf32 (incorrect -> 0x9dcc), ack 1, win 229, options [nop,nop,TS val 620029603 ecr 18495104], length 0 |
好的例子應該向上面這樣,我們看到典型的 TCP 3次握手。第一資料包是 SYN 包,從主機 10.0.3.246 傳送給 主機192.168.0.110,第二個包是 SYN-ACK 包,主機192.168.0.110 回應 SYN 包。最後一個包是一個 ACK 或者 SYN – ACK – ACK 包,是主機 10.0.3.246 回應收到了 SYN – ACK 包。從上面看到一個 TCP/IP 連線成功建立。
資料包檢查
用十六進位制和 ASCII 碼列印資料包
1 |
# tcpdump -nvvv -i any -c 1 -XX 'port 80 and host 10.0.3.1' |
排查應用程式網路問題的通常做法,就是用 tcpdump 的 -XX 標記列印出 16 進位制和 ASCII 碼格式的資料包。這是一個相當有用的命令,它可以讓你看到源地址,目的地址,資料包型別以及資料包本身。但我不是這個命令輸出的粉絲,我認為它太難讀了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# tcpdump -nvvv -i any -c 1 -XX 'port 80 and host 10.0.3.1' tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 19:51:15.697640 IP (tos 0x0, ttl 64, id 54313, offset 0, flags [DF], proto TCP (6), length 483) 10.0.3.1.45732 > 10.0.3.246.80: Flags [P.], cksum 0x1ccc (incorrect -> 0x2ce8), seq 3920159713:3920160144, ack 969855140, win 245, options [nop,nop,TS val 624122099 ecr 624117334], length 431 0x0000: 0000 0001 0006 fe0a e2d1 8785 0000 0800 ................ 0x0010: 4500 01e3 d429 4000 4006 49f5 0a00 0301 E....)@.@.I..... 0x0020: 0a00 03f6 b2a4 0050 e9a8 e3e1 39ce d0a4 .......P....9... 0x0030: 8018 00f5 1ccc 0000 0101 080a 2533 58f3 ............%3X. 0x0040: 2533 4656 4745 5420 2f73 6f6d 6570 6167 %3FVGET./somepag 0x0050: 6520 4854 5450 2f31 2e31 0d0a 486f 7374 e.HTTP/1.1..Host 0x0060: 3a20 3130 2e30 2e33 2e32 3436 0d0a 436f :.10.0.3.246..Co 0x0070: 6e6e 6563 7469 6f6e 3a20 6b65 6570 2d61 nnection:.keep-a 0x0080: 6c69 7665 0d0a 4361 6368 652d 436f 6e74 live..Cache-Cont 0x0090: 726f 6c3a 206d 6178 2d61 6765 3d30 0d0a rol:.max-age=0.. 0x00a0: 4163 6365 7074 3a20 7465 7874 2f68 746d Accept:.text/htm 0x00b0: 6c2c 6170 706c 6963 6174 696f 6e2f 7868 l,application/xh 0x00c0: 746d 6c2b 786d 6c2c 6170 706c 6963 6174 tml+xml,applicat 0x00d0: 696f 6e2f 786d 6c3b 713d 302e 392c 696d ion/xml;q=0.9,im 0x00e0: 6167 652f 7765 6270 2c2a 2f2a 3b71 3d30 age/webp,*/*;q=0 0x00f0: 2e38 0d0a 5573 6572 2d41 6765 6e74 3a20 .8..User-Agent:. 0x0100: 4d6f 7a69 6c6c 612f 352e 3020 284d 6163 Mozilla/5.0.(Mac 0x0110: 696e 746f 7368 3b20 496e 7465 6c20 4d61 intosh;.Intel.Ma 0x0120: 6320 4f53 2058 2031 305f 395f 3529 2041 c.OS.X.10_9_5).A 0x0130: 7070 6c65 5765 624b 6974 2f35 3337 2e33 ppleWebKit/537.3 0x0140: 3620 284b 4854 4d4c 2c20 6c69 6b65 2047 6.(KHTML,.like.G 0x0150: 6563 6b6f 2920 4368 726f 6d65 2f33 382e ecko).Chrome/38. 0x0160: 302e 3231 3235 2e31 3031 2053 6166 6172 0.2125.101.Safar 0x0170: 692f 3533 372e 3336 0d0a 4163 6365 7074 i/537.36..Accept 0x0180: 2d45 6e63 6f64 696e 673a 2067 7a69 702c -Encoding:.gzip, 0x0190: 6465 666c 6174 652c 7364 6368 0d0a 4163 deflate,sdch..Ac 0x01a0: 6365 7074 2d4c 616e 6775 6167 653a 2065 cept-Language:.e 0x01b0: 6e2d 5553 2c65 6e3b 713d 302e 380d 0a49 n-US,en;q=0.8..I 0x01c0: 662d 4d6f 6469 6669 6564 2d53 696e 6365 f-Modified-Since 0x01d0: 3a20 5375 6e2c 2031 3220 4f63 7420 3230 :.Sun,.12.Oct.20 0x01e0: 3134 2031 393a 3430 3a32 3020 474d 540d 14.19:40:20.GMT. 0x01f0: 0a0d 0a ... |
只列印 ASCII 碼格式的資料包
1 |
# tcpdump -nvvv -i any -c 1 -A 'port 80 and host 10.0.3.1' |
我傾向於只列印 ASCII 格式資料,這可以幫助我快速定位資料包中傳送了什麼,哪些是正確的,哪些是錯誤的。你可以通過 -A 標記來實現這一點。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# tcpdump -nvvv -i any -c 1 -A 'port 80 and host 10.0.3.1' tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 19:59:52.011337 IP (tos 0x0, ttl 64, id 53757, offset 0, flags [DF], proto TCP (6), length 406) 10.0.3.1.46172 > 10.0.3.246.80: Flags [P.], cksum 0x1c7f (incorrect -> 0xead1), seq 1552520173:1552520527, ack 428165415, win 237, options [nop,nop,TS val 624251177 ecr 624247749], length 354 E.....@.@.Ln ... ....\.P\.....I'........... %5Q)%5C.GET /newpage HTTP/1.1 Host: 10.0.3.246 Connection: keep-alive Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 |
從上面的輸出,你可以看到我們成功獲取了一個 http 的 GET 請求包。如果網路通訊沒有被加密,用人類可閱讀的格式打出包中資料,對於解決應用程式的問題是很有幫助。如果你排查一個網路通訊被加密的問題,列印包中資料就不是很有用。不過如果你有證書的話,你還是可以使用 ssldump 或者 wireshark。
非 TCP 資料流
雖然這篇文章主要採用 TCP 傳輸來講解 tcpdump ,但是 tcpdump 絕對不是隻能抓 TCP 資料包。它還可以用來獲取其他型別的資料包,例如 ICMP、 UDP 和 ARP 包。下面是一些簡單的例子,說明 tcpdump 可以截獲非 TCP 資料包。
ICMP 資料包
1 2 3 4 5 6 |
# tcpdump -nvvv -i any -c 2 icmp tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 20:11:24.627824 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84) 10.0.3.169 > 10.0.3.246: ICMP echo request, id 15683, seq 1, length 64 20:11:24.627926 IP (tos 0x0, ttl 64, id 31312, offset 0, flags [none], proto ICMP (1), length 84) 10.0.3.246 > 10.0.3.169: ICMP echo reply, id 15683, seq 1, length 64 |
UDP 資料包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# tcpdump -nvvv -i any -c 2 udp tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes 20:12:41.726355 IP (tos 0xc0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 76) 10.0.3.246.123 > 198.55.111.50.123: [bad udp cksum 0x43a9 -> 0x7043!] NTPv4, length 48 Client, Leap indicator: clock unsynchronized (192), Stratum 2 (secondary reference), poll 6 (64s), precision -22 Root Delay: 0.085678, Root dispersion: 57.141830, Reference-ID: 199.102.46.75 Reference Timestamp: 3622133515.811991035 (2014/10/12 20:11:55) Originator Timestamp: 3622133553.828614115 (2014/10/12 20:12:33) Receive Timestamp: 3622133496.748308420 (2014/10/12 20:11:36) Transmit Timestamp: 3622133561.726278364 (2014/10/12 20:12:41) Originator - Receive Timestamp: -57.080305658 Originator - Transmit Timestamp: +7.897664248 20:12:41.748948 IP (tos 0x0, ttl 54, id 9285, offset 0, flags [none], proto UDP (17), length 76) 198.55.111.50.123 > 10.0.3.246.123: [udp sum ok] NTPv4, length 48 Server, Leap indicator: (0), Stratum 3 (secondary reference), poll 6 (64s), precision -20 Root Delay: 0.054077, Root dispersion: 0.058944, Reference-ID: 216.229.0.50 Reference Timestamp: 3622132887.136984840 (2014/10/12 20:01:27) Originator Timestamp: 3622133561.726278364 (2014/10/12 20:12:41) Receive Timestamp: 3622133618.830113530 (2014/10/12 20:13:38) Transmit Timestamp: 3622133618.830129086 (2014/10/12 20:13:38) Originator - Receive Timestamp: +57.103835195 Originator - Transmit Timestamp: +57.103850722 |
如果你覺得有好例子進一步說明 tcpdump 命令,請在評論中補充。
打賞支援我翻譯更多好文章,謝謝!
打賞譯者
打賞支援我翻譯更多好文章,謝謝!
任選一種支付方式