1 起因
前段時(shí)間,一直在調(diào)線上的一個(gè)問題:線上應(yīng)用接受POST請求,請求body中的參數(shù)獲取不全,存在丟失的狀況。這個(gè)問題是偶發(fā)性的,大概發(fā)生的幾率為5%-10%左右,這個(gè)概率已經(jīng)相當(dāng)高了。在排查問題的過程中使用到了tcpdump和Wireshark進(jìn)行抓包分析。感覺這兩個(gè)工具搭配起來干活,非常完美。所有的網(wǎng)絡(luò)傳輸在這兩個(gè)工具搭配下,都無處遁形。
為了更好、更順手地能夠用好這兩個(gè)工具,特整理本篇文章,希望也能給大家?guī)硎斋@。為大家之后排查問題,添一利器。
2 tcpdump與Wireshark介紹
在網(wǎng)絡(luò)問題的調(diào)試中,tcpdump應(yīng)該說是一個(gè)必不可少的工具,和大部分linux下優(yōu)秀工具一樣,它的特點(diǎn)就是簡單而強(qiáng)大。它是基于Unix系統(tǒng)的命令行式的數(shù)據(jù)包嗅探工具,可以抓取流動在網(wǎng)卡上的數(shù)據(jù)包。
默認(rèn)情況下,tcpdump不會抓取本機(jī)內(nèi)部通訊的報(bào)文。根據(jù)網(wǎng)絡(luò)協(xié)議棧的規(guī)定,對于報(bào)文,即使是目的地是本機(jī),也需要經(jīng)過本機(jī)的網(wǎng)絡(luò)協(xié)議層,所以本機(jī)通訊肯定是通過API進(jìn)入了內(nèi)核,并且完成了路由選擇?!颈热绫緳C(jī)的TCP通信,也必須要socket通信的基本要素:src ip port dst ip port】
如果要使用tcpdump抓取其他主機(jī)MAC地址的數(shù)據(jù)包,必須開啟網(wǎng)卡混雜模式,所謂混雜模式,用最簡單的語言就是讓網(wǎng)卡抓取任何經(jīng)過它的數(shù)據(jù)包,不管這個(gè)數(shù)據(jù)包是不是發(fā)給它或者是它發(fā)出的。一般而言,Unix不會讓普通用戶設(shè)置混雜模式,因?yàn)檫@樣可以看到別人的信息,比如telnet的用戶名和密碼,這樣會引起一些安全上的問題,所以只有root用戶可以開啟混雜模式,開啟混雜模式的命令是:ifconfig en0 promisc, en0是你要打開混雜模式的網(wǎng)卡。
Linux抓包原理:
Linux抓包是通過注冊一種虛擬的底層網(wǎng)絡(luò)協(xié)議來完成對網(wǎng)絡(luò)報(bào)文(準(zhǔn)確的說是網(wǎng)絡(luò)設(shè)備)消息的處理權(quán)。當(dāng)網(wǎng)卡接收到一個(gè)網(wǎng)絡(luò)報(bào)文之后,它會遍歷系統(tǒng)中所有已經(jīng)注冊的網(wǎng)絡(luò)協(xié)議,例如以太網(wǎng)協(xié)議、x25協(xié)議處理模塊來嘗試進(jìn)行報(bào)文的解析處理,這一點(diǎn)和一些文件系統(tǒng)的掛載相似,就是讓系統(tǒng)中所有的已經(jīng)注冊的文件系統(tǒng)來進(jìn)行嘗試掛載,如果哪一個(gè)認(rèn)為自己可以處理,那么就完成掛載。
當(dāng)抓包模塊把自己偽裝成一個(gè)網(wǎng)絡(luò)協(xié)議的時(shí)候,系統(tǒng)在收到報(bào)文的時(shí)候就會給這個(gè)偽協(xié)議一次機(jī)會,讓它來對網(wǎng)卡收到的報(bào)文進(jìn)行一次處理,此時(shí)該模塊就會趁機(jī)對報(bào)文進(jìn)行窺探,也就是把這個(gè)報(bào)文完完整整的復(fù)制一份,假裝是自己接收到的報(bào)文,匯報(bào)給抓包模塊。
Wireshark是一個(gè)網(wǎng)絡(luò)協(xié)議檢測工具,支持Windows平臺、Unix平臺、Mac平臺,一般只在圖形界面平臺下使用Wireshark,如果是Linux的話,直接使用tcpdump了,因?yàn)橐话愣訪inux都自帶的tcpdump,或者用tcpdump抓包以后用Wireshark打開分析。
在Mac平臺下,Wireshark通過WinPcap進(jìn)行抓包,封裝的很好,使用起來很方便,可以很容易的制定抓包過濾器或者顯示過濾器,具體簡單使用下面會介紹。Wireshark是一個(gè)免費(fèi)的工具,只要google一下就能很容易找到下載的地方。
所以,tcpdump是用來抓取數(shù)據(jù)非常方便,Wireshark則是用于分析抓取到的數(shù)據(jù)比較方便。
3 tcpdump使用
3.1 語法
類型的關(guān)鍵字
host(缺省類型): 指明一臺主機(jī),如:host 210.27.48.2
net: 指明一個(gè)網(wǎng)絡(luò)地址,如:net 202.0.0.0
port: 指明端口號,如:port 23
確定方向的關(guān)鍵字
src: src 210.27.48.2, IP包源地址是210.27.48.2
dst: dst net 202.0.0.0, 目標(biāo)網(wǎng)絡(luò)地址是202.0.0.0
dst or src(缺省值)
dst and src
協(xié)議的關(guān)鍵字:缺省值是監(jiān)聽所有協(xié)議的信息包
fddi
ip
arp
rarp
tcp
udp
其他關(guān)鍵字
gateway
broadcast
less
greater
常用表達(dá)式:多條件時(shí)可以用括號,但是要用轉(zhuǎn)義
非 : ! or “not” (去掉雙引號)
且 : && or “and”
或 : || or “or”
3.2 選項(xiàng)
3.3 命令實(shí)踐
1、直接啟動tcpdump,將抓取所有經(jīng)過第一個(gè)網(wǎng)絡(luò)接口上的數(shù)據(jù)包
2、抓取所有經(jīng)過指定網(wǎng)絡(luò)接口上的數(shù)據(jù)包
3、抓取所有經(jīng)過 en0,目的或源地址是 10.37.63.255 的網(wǎng)絡(luò)數(shù)據(jù):
4、抓取主機(jī)10.37.63.255和主機(jī)10.37.63.61或10.37.63.95的通信:
5、抓取主機(jī)192.168.13.210除了和主機(jī)10.37.63.61之外所有主機(jī)通信的數(shù)據(jù)包:
6、抓取主機(jī)10.37.63.255除了和主機(jī)10.37.63.61之外所有主機(jī)通信的ip包
7、抓取主機(jī)10.37.63.3發(fā)送的所有數(shù)據(jù):
8、抓取主機(jī)10.37.63.3接收的所有數(shù)據(jù):
9、抓取主機(jī)10.37.63.3所有在TCP 80端口的數(shù)據(jù)包:
10、抓取HTTP主機(jī)10.37.63.3在80端口接收到的數(shù)據(jù)包:
11、抓取所有經(jīng)過 en0,目的或源端口是 25 的網(wǎng)絡(luò)數(shù)據(jù)
12、抓取所有經(jīng)過 en0,網(wǎng)絡(luò)是 192.168上的數(shù)據(jù)包
13、協(xié)議過濾
14、抓取所有經(jīng)過 en0,目的地址是 192.168.1.254 或 192.168.1.200 端口是 80 的 TCP 數(shù)據(jù)
15、抓取所有經(jīng)過 en0,目標(biāo) MAC 地址是 00:01:02:03:04:05 的 ICMP 數(shù)據(jù)
16、抓取所有經(jīng)過 en0,目的網(wǎng)絡(luò)是 192.168,但目的主機(jī)不是 192.168.1.200 的 TCP 數(shù)據(jù)
17、只抓 SYN 包
18、抓 SYN, ACK
19、抓 SMTP 數(shù)據(jù),抓取數(shù)據(jù)區(qū)開始為”MAIL”的包,”MAIL”的十六進(jìn)制為 0x4d41494c
20、抓 HTTP GET 數(shù)據(jù),”GET “的十六進(jìn)制是 0x47455420
21、抓 SSH 返回,”SSH-“的十六進(jìn)制是 0x5353482D
22、高級包頭過濾如前兩個(gè)的包頭過濾,首先了解如何從包頭過濾信息:
23、抓 DNS 請求數(shù)據(jù)
24、其他-c 參數(shù)對于運(yùn)維人員來說也比較常用,因?yàn)榱髁勘容^大的服務(wù)器,靠人工 CTRL+C 還是抓的太多,于是可以用-c 參數(shù)指定抓多少個(gè)包。
3.4 抓個(gè)網(wǎng)站練練
想抓取訪問某個(gè)網(wǎng)站時(shí)的網(wǎng)絡(luò)數(shù)據(jù)。比如網(wǎng)站?http://www.baidu.com/?怎么做?
1、通過tcpdump截獲主機(jī)www.baidu.com發(fā)送與接收所有的數(shù)據(jù)包
2、訪問這個(gè)網(wǎng)站
3、想要看到詳細(xì)的http報(bào)文。怎么做?
4、分析抓取到的報(bào)文
4 tcpdump抓取TCP包分析
TCP傳輸控制協(xié)議是面向連接的可靠的傳輸層協(xié)議,在進(jìn)行數(shù)據(jù)傳輸之前,需要在傳輸數(shù)據(jù)的兩端(客戶端和服務(wù)器端)創(chuàng)建一個(gè)連接,這個(gè)連接由一對插口地址唯一標(biāo)識,即是在IP報(bào)文首部的源IP地址、目的IP地址,以及TCP數(shù)據(jù)報(bào)首部的源端口地址和目的端口地址。TCP首部結(jié)構(gòu)如下:
注意:通常情況下,一個(gè)正常的TCP連接,都會有三個(gè)階段:1、TCP三次握手;2、數(shù)據(jù)傳送;3、TCP四次揮手
其中在TCP連接和斷開連接過程中的關(guān)鍵部分如下:
源端口號:即發(fā)送方的端口號,在TCP連接過程中,對于客戶端,端口號往往由內(nèi)核分配,無需進(jìn)程指定;
目的端口號:即發(fā)送目的的端口號;
序號:即為發(fā)送的數(shù)據(jù)段首個(gè)字節(jié)的序號;
確認(rèn)序號:在收到對方發(fā)來的數(shù)據(jù)報(bào),發(fā)送確認(rèn)時(shí)期待對方下一次發(fā)送的數(shù)據(jù)序號;
SYN:同步序列編號,Synchronize Sequence Numbers;
ACK:確認(rèn)編號,Acknowledgement Number;
FIN:結(jié)束標(biāo)志,F(xiàn)INish;
4.1 TCP三次握手
三次握手的過程如下:
step1. 由客戶端向服務(wù)器端發(fā)起TCP連接請求。Client發(fā)送:同步序列編號SYN置為1,發(fā)送序號Seq為一個(gè)隨機(jī)數(shù),這里假設(shè)為X,確認(rèn)序號ACK置為0;
step2. 服務(wù)器端接收到連接請求。Server響應(yīng):同步序列編號SYN置為1,并將確認(rèn)序號ACK置為X+1,然后生成一個(gè)隨機(jī)數(shù)Y作為發(fā)送序號Seq(因?yàn)樗_認(rèn)的數(shù)據(jù)報(bào)的確認(rèn)序號未初始化);
step3. 客戶端對接收到的確認(rèn)進(jìn)行確認(rèn)。Client發(fā)送:將確認(rèn)序號ACK置為Y+1,然后將發(fā)送序號Seq置為X+1(即為接收到的數(shù)據(jù)報(bào)的確認(rèn)序號);
為什么是三次握手而不是兩次對于step3的作用,假設(shè)一種情況,客戶端A向服務(wù)器B發(fā)送一個(gè)連接請求數(shù)據(jù)報(bào),然后這個(gè)數(shù)據(jù)報(bào)在網(wǎng)絡(luò)中滯留導(dǎo)致其遲到了,雖然遲到了,但是服務(wù)器仍然會接收并發(fā)回一個(gè)確認(rèn)數(shù)據(jù)報(bào)。但是A卻因?yàn)榫镁檬詹坏紹的確認(rèn)而將發(fā)送的請求連接置為失效,等到一段時(shí)間后,接到B發(fā)送過來的確認(rèn),A認(rèn)為自己現(xiàn)在沒有發(fā)送連接,而B卻一直以為連接成功了,于是一直在等待A的動作,而A將不會有任何的動作了。這會導(dǎo)致服務(wù)器資源白白浪費(fèi)掉了,因此,兩次握手是不行的,因此需要再加上一次,對B發(fā)過來的確認(rèn)再進(jìn)行一次確認(rèn),即確認(rèn)這次連接是有效的,從而建立連接。
對于雙方,發(fā)送序號的初始化為何值有的系統(tǒng)中是顯式的初始化序號是0,但是這種已知的初始化值是非常危險(xiǎn)的,因?yàn)檫@會使得一些黑客鉆漏洞,發(fā)送一些數(shù)據(jù)報(bào)來破壞連接。因此,初始化序號因?yàn)槿‰S機(jī)數(shù)會更好一些,并且是越隨機(jī)越安全。
tcpdump抓TCP三次握手抓包分析:
sudotcpdump-n-S-ilo0host10.37.63.3andtcpport8080
# 接著再運(yùn)行:
curlhttp://10.37.63.3:8080/atbg/doc
控制臺輸出:
每一行中間都有這個(gè)包所攜帶的標(biāo)志:
S=SYN,發(fā)起連接標(biāo)志。
P=PUSH,傳送數(shù)據(jù)標(biāo)志。
F=FIN,關(guān)閉連接標(biāo)志。
ack,表示確認(rèn)包。
RST=RESET,異常關(guān)閉連接。
.,表示沒有任何標(biāo)志。
第1行:16:00:13.486776,從10.37.63.3(client)的臨時(shí)端口61725向10.37.63.3(server)的8080監(jiān)聽端口發(fā)起連接,client初始包序號seq為1944916150,滑動窗口大小為65535字節(jié)(滑動窗口即tcp接收緩沖區(qū)的大小,用于tcp擁塞控制),mss大小為16344(即可接收的最大包長度,通常為MTU減40字節(jié),IP頭和TCP頭各20字節(jié))?!緎eq=1944916150,ack=0,syn=1】
第2行:16:00:13.486850,server響應(yīng)連接,同時(shí)帶上第一個(gè)包的ack信息,為client端的初始包序號seq加1,即1944916151,即server端下次等待接受這個(gè)包序號的包,用于tcp字節(jié)流的順序控制。Server端的初始包序號seq為1119565918,mss也是16344?!緎eq=1119565918,ack=1944916151,syn=1】
第3行:15:46:13.084161,client再次發(fā)送確認(rèn)連接,tcp連接三次握手完成,等待傳輸數(shù)據(jù)包?!綼ck=1119565919,seq=1944916151】
4.2 TCP四次揮手
連接雙方在完成數(shù)據(jù)傳輸之后就需要斷開連接。由于TCP連接是屬于全雙工的,即連接雙方可以在一條TCP連接上互相傳輸數(shù)據(jù),因此在斷開時(shí)存在一個(gè)半關(guān)閉狀態(tài),即有有一方失去發(fā)送數(shù)據(jù)的能力,卻還能接收數(shù)據(jù)。因此,斷開連接需要分為四次。主要過程如下:
step1. 主機(jī)A向主機(jī)B發(fā)起斷開連接請求,之后主機(jī)A進(jìn)入FIN-WAIT-1狀態(tài);
step2. 主機(jī)B收到主機(jī)A的請求后,向主機(jī)A發(fā)回確認(rèn),然后進(jìn)入CLOSE-WAIT狀態(tài);
step3. 主機(jī)A收到B的確認(rèn)之后,進(jìn)入FIN-WAIT-2狀態(tài),此時(shí)便是半關(guān)閉狀態(tài),即主機(jī)A失去發(fā)送能力,但是主機(jī)B卻還能向A發(fā)送數(shù)據(jù),并且A可以接收數(shù)據(jù)。此時(shí)主機(jī)B占主導(dǎo)位置了,如果需要繼續(xù)關(guān)閉則需要主機(jī)B來操作了;
step4. 主機(jī)B向A發(fā)出斷開連接請求,然后進(jìn)入LAST-ACK狀態(tài);
step5. 主機(jī)A接收到請求后發(fā)送確認(rèn),進(jìn)入TIME-WAIT狀態(tài),等待2MSL之后進(jìn)入CLOSED狀態(tài),而主機(jī)B則在接受到確認(rèn)后進(jìn)入CLOSED狀態(tài);
為何主機(jī)A在發(fā)送了最后的確認(rèn)后沒有進(jìn)入CLOSED狀態(tài),反而進(jìn)入了一個(gè)等待2MSL的TIME-WAIT主要作用有兩個(gè):
第一,確保主機(jī)A最后發(fā)送的確認(rèn)能夠到達(dá)主機(jī)B。如果處于LAST-ACK狀態(tài)的主機(jī)B一直收不到來自主機(jī)A的確認(rèn),它會重傳斷開連接請求,然后主機(jī)A就可以有足夠的時(shí)間去再次發(fā)送確認(rèn)。但是這也只能盡最大力量來確保能夠正常斷開,如果主機(jī)A的確認(rèn)總是在網(wǎng)絡(luò)中滯留失效,從而超過了2MSL,最后也無法正常斷開;
第二,如果主機(jī)A在發(fā)送了確認(rèn)之后立即進(jìn)入CLOSED狀態(tài)。假設(shè)之后主機(jī)A再次向主機(jī)B發(fā)送一條連接請求,而這條連接請求比之前的確認(rèn)報(bào)文更早地到達(dá)主機(jī)B,則會使得主機(jī)B以為這條連接請求是在舊的連接中A發(fā)出的報(bào)文,并不看成是一條新的連接請求了,即使得這個(gè)連接請求失效了,增加2MSL的時(shí)間可以使得這個(gè)失效的連接請求報(bào)文作廢,這樣才不影響下次新的連接請求中出現(xiàn)失效的連接請求。
為什么斷開連接請求報(bào)文只有三個(gè),而不是四個(gè)因?yàn)樵赥CP連接過程中,確認(rèn)的發(fā)送有一個(gè)延時(shí)(即經(jīng)受延時(shí)的確認(rèn)),一端在發(fā)送確認(rèn)的時(shí)候?qū)⒌却欢螘r(shí)間,如果自己在這段事件內(nèi)也有數(shù)據(jù)要發(fā)送,就跟確認(rèn)一起發(fā)送,如果沒有,則確認(rèn)單獨(dú)發(fā)送。而我們的抓包實(shí)驗(yàn)中,由服務(wù)器端先斷開連接,之后客戶端在確認(rèn)的延遲時(shí)間內(nèi),也有請求斷開連接需要發(fā)送,于是就與上次確認(rèn)一起發(fā)送,因此就只有三個(gè)數(shù)據(jù)報(bào)了。
5 Wireshark分析tcpdump抓包結(jié)果
1、啟動8080端口,tcpdump抓包命令如下:
tcpdump-ilo0-s0-n-Shost10.37.63.3andport8080-w./Desktop/tcpdump_10.37.63.3_8080_20160525.cap
# 然后再執(zhí)行curl
curlhttp://10.37.63.3:8080/atbg/doc
2、使用Wireshark打開tcpdump_10.37.63.3_8080_20160525.cap文件
No. 1-4 行:TCP三次握手環(huán)節(jié);
No. 5-8 行:TCP傳輸數(shù)據(jù)環(huán)節(jié);
No. 9-13 行:TCP四次揮手環(huán)節(jié);
3、順便說一個(gè)查看 http 請求和響應(yīng)的方法:
彈窗如下圖所示,上面紅色部分為請求信息,下面藍(lán)色部分為響應(yīng)信息:
以上是Wireshark分析tcpdump的簡單使用,Wireshark更強(qiáng)大的是過濾器工具,大家可以自行去多研究學(xué)習(xí)Wireshark,用起來還是比較爽的。
推薦幾個(gè)關(guān)于Wireshark的文章:
Wireshark基本介紹和學(xué)習(xí)TCP三次握手
一站式學(xué)習(xí)Wireshark