30分鐘掌握tcpdump
這是一篇關于tcpdump的文章,分為使用tcpdump、解讀輸出兩部分。其中“解讀輸出”部分中關于分片的解釋是個人認為最有價值的,當然如果你肯花30分鐘自己動手抓個包嘗試用本文介紹的方式還原IP、TCP頭部肯定會對你調試網絡程序有幫助(成為老司機專治各種網絡疑難雜癥想想是不是挺激動?)。
使用tcpdump
格式:tcpdump [選項] [表達式]
我常用的選項
- -n 禁用域名解析,tcpdump會對每次收到的數據包嘗試域名解析這會導致有些時候“遲遲看不到輸出”。加上這個選項讓tcpdump直接輸出IP地址。
- -X 按16進制輸出完成的數據包,不加這個選項tcpdump會只輸出ip,tcp/udp頭部信息。加上這個選項會讓tcpdump把整個數據包都打印出來。
- -i 指定網卡,不指定網卡的情況tcpdump會使用***個網卡。
- -vvvv 是兩個選項的組合-v會輸出稍微詳細一點的信息包括校驗和ttl之類的;-vvv會嘗試解析應用層協議,輸出詳細的信息。二者組合就能拿到完整的詳細信息
表達式用來過濾數據包,特別是加上X選項(輸出數據包的詳細內容會讓你的屏幕快速飛過~~),如果不指定表達式基本上是刷屏的速度。我一般會限制協議類型、主機地址或者端口。比如抓取HTTP數據包,我會tcpdump -X -i eth0 -vvvv -n port 80,指定抓取80端口的數據(對端80和本機80)
解讀tcpdump輸出
提到網絡就不能不提ISO分層模型、TCP/IP分層模型,這兩部分的內容教材的太多了我就不絮叨了。從現實意義上說其實網絡只有三層。物理層和數據鏈路層就是指網卡所以我們無需關心;IP層、TCP/UDP、應用層這三部分是我們最常接觸到的,其實它們是指一個東西。比如看一個HTTP GET請求。
這是用tcpdump抓到的一個GET請求,
***行,12:53:07.156463 IP (tos 0x2,ECT(0), ttl 64, id 22767, offset 0, flags [DF], proto TCP (6), length 577)
輸出了當前時間(本機)、IP頭部信息;
第二行,192.168.200.1.59222 > 192.168.200.10.80: Flags [P.], cksum 0x98a6 (correct), seq 966819399:966819924, ack 3158680476, win 4096, options [nop,nop,TS val 341138922 ecr 204637], length 525
說明了這個數據包是從 192.168.200.1的59222端口發送到 192.168.200.10的80端口,后面是TCP頭部信息。
第三行可不是“實際內容”,它是整個數據包的16進制輸出和對應的ASCII(包括IP頭部和TCP頭部)。
所以網絡分層其實就是指一個數據包擁有不同的頭部。這種設計是一種封裝,通過劃分“層”讓應用更容易開發。我們來剖析一下IP頭和TCP頭,掌握了這部分內容就算得上“精通網絡”了(我沒說錯,是精通~~)。
IP頭部
盜用一下wiki的圖(遍地都是這種圖我就不自己畫了)
最小的IP頭部是20個字節(不包含選項),上圖中每一行4個字節(32個bit),一共6行(***一行大小不固定,最多40個字節。)。
結合我們的例子,其實tcpdump已經幫我們把關鍵的IP信息輸出了。
我們直接看16進制的數據(驗證tcpdump輸出的IP信息和16進制表示的數據包是否吻合),每16bit一組。
- 4502 0241 58ef 4000 4006 ce68 c0a8 c801
- ***個4是表示是IPv4(占4bit);5表示頭部長度為5個32bit(這個IP頭部一共20個字節,說明這個數據包沒有包含“選項”);后面這個字段需要特別注意,有些資料直接把它解釋成TOS,不是完全正確的。它有兩部分含義DSCP就是傳統意義上的TOS,這里02是一個字節(8個二進制位)前6bit用來表示TOS,這里是0(很多設備是忽略TOS字段的);后面2bit顯式擁塞通告(ECN)是一種新增的特性(甚至Windows7還不支持)用來顯示告知對方自己是否支持擁塞控制(如果對端發生了擁塞會自動通過這兩個字段告知你——我忙不過來了,兄弟你慢一點~~)。二進制10(十進制2)表示支持ECN特性。(tcpdump ip頭部輸出tos 0x2,ECT(0)表示支持ECT,不支持會顯示成Not-ECT)
- 0241是數據的總長度,這里是16進制的它的10進制值是577(和tcpdump IP頭部輸出的lenght 577吻合)
- 58ef是數據包的唯一標識,一般約定雙方在TCP三次握手的時候隨機生成一個ID,每發送一個數據包彼此自增一下。它的主要用途是為了重傳,但是其實IP協議從來沒有實現過“重傳”(后面介紹)。(和tcpdump IP頭部輸出的id 22767吻合)
- 4是分片標志,基本上所有的IP數據包它都為4。因為IP數據包不會進行分片和重傳。
下面這段推翻了網絡上很多傳言(好吧,好像包括Wiki)
每個以太網卡都有MTU(***傳輸單元)的限制,這個表示網卡每次可以接收一個數據包的大小。所以即便IP數據包可以達到2^32=65535字節也不能一次發送這么大的數據包。每個IP數據包的大小被限制在1500個字節(最常見的MTU大小),除去最小的IP頭部(20)、最小TCP頭部(20)還剩下1460字節,這就是一個IP數據包可以攜帶的準確數據。如果超過這個設定就必須“分片”。
如果你是UDP協議基本上是自己考慮分片,UDP協議本身不會重傳,重組。所以一般用UDP協議都是“一槍頭買賣”——在一個數據包里包含所有信息不進行分片。(如果你用UDP協議一般傳送數據不要操過1380否則程序就必須考慮處理分片——也就是MTU-***IP頭60字節-***TCP頭60字節)
如果是TCP就很幸運了,TCP會幫你做分片和重組。(后面會講)
因為TCP已經考慮過分片了所以IP數據包無需再次分片,“標志”字段基本上都是——4(不分片)。(大家可以抓一下163之類的網站看看HTML被分段傳送的時候***行有沒有4000)
- 因為不考慮分片后面的偏移也沒有任何用途了——000
- 4006,存活時間40就是10進制的64,這是一個常用的值,一個數據包如果經過64次路由就可以被丟棄了(和Tcpdump輸出一致);06表示這是TCP協議(如果是01表示是ICMP、UDP是0x11)
- ce68校驗和,不管它
- c0a8 c801源IP地址,c0a8 c80a目標地址
用一段代碼表示如何自己構造IP數據包
對于同一個TCP會話來說IP頭是非常固定的。
TCP頭部
最小的TCP頭部是20個字節(不包含選項),上圖中每一行4個字節(32個bit),一共6行(***一行大小不固定,最多40個字節。)。
結合我們的例子,其實tcpdump已經幫我們把關鍵的IP信息輸出了。
注意這里的length是指數據的大小不包括IP頭和TCP頭
我們直接看16進制的數據,每16bit一組。
e756 0050 39a0 7e47 bc45 a39c
- e756來源端口5922,0050是目標端口80
- 39a07 e47=966819399,是seq號(序號)
- bc45 a39c=3158680476,是ack號
TCP約定通訊雙方三次握手的時候發送端會隨機生成一個seq號通過***個SYN協議發送給對端,對端也會隨機生成一個seq號同時設置ack+1響應對端。發送端則回復seq=自己seq+1,ack=對端 seq+1。通俗的說法,seq是自己的序號,ack是期望對端發送的數據,所以如果你拿到一個數據包seq=1024,ack=522;那么只要回復一條seq=522,ack=1025就可以實現“TCP會話劫持”(是不是很可怕?相當于不但可以探測到你的通話內容還可以“無縫介入”到你的談話,***冒充對方而不會產生任何違和感)。
第三行8018 1000 98a6 0000 0101 080a 1455
- 8表示8個32bit(這個TCP頭大小是8*4=32個字節)
- 0是保留
- 18是標志位,對應tcpdump中的flag。
FIN=0x01,表示數據已經發送完了可以釋放連接了,用于關閉TCP連接
SYN=0x02,這個太喜聞樂見了,用于TCP握手
RST=0x04,直接關閉TCP連接(不經過FIN)
PUSH=0x08,官方的解釋“盡快交給應用不等待緩沖區裝滿”,其實這個標志位會一直存在。只要你傳送數據一定會有這個標志位。
ACK=0x10,確認數據
tcpdump會分別用一個大寫字母輸出在Flags選項,比如例子中是“P”表示設置了push位,又因為有ack 3158680476所以一定有ACK標志位。不難算出PUSH+ACK=8+10=18
- 1000這個是數據窗口大小(10進制4096),TCP流量控制關鍵的技術手段就是這個。包括最近很火爆的“Google BBR”其實就是找到一種算法可以盡可能***的調整這個窗口的大小。(其實ECN更牛B,根本不用“計算”直接通告給你。只是很多操作系統和硬件都不支持。)
- 98a6 校驗和,不管他
- 0000這個通常和“標志位”配合使用,標志位設置URG (0x20)表示開啟“緊急數據”,這個字段就是“緊急數據的序號”。比如你在看一段視頻,已經還差1個數據包就可以播放5分鐘了但是現在網絡都在傳送后面的數據,就可以通過這個標志位“緊急”獲取關鍵的數據包。但是這個特性基本上沒啥用,設備會無視這些東西的。
這個TCP包是32個字節,上面已經介紹了20個字節所以還有12個字節(6組),它們用來表示TCP的選項。
- 0101 080a 1455 5dea 0003 1f5d,選項的格式格式是TLV結構(類型、長度、值)。簡單來說就是***個字節表示數據類型,不同的類型后面會有不同大小的長度和內容。
TCP常見的選項包括
0 (1字節)什么都沒有
1(1字節) 什么都沒有,經常用于“填充”
上面的兩個其實是同一個意思。
2 ***報文段長度(4字節)一般出現在三次握手的SYN包中,用于說明自己可以接收到的***報文長度(一般是MTU-40,就是以太網***傳輸單元-最小IP-最小TCP。這也印證了前面的說法——IP永遠不分片,TCP負責分片。)
3 窗口擴大因子(4字節),取值0-14。滑動窗口***值是2^16(65535),如果在一個“高延時高帶寬”(也叫長肥管道 )網絡中這個值會顯的特別小(所有的廣域網或者乃至整個互聯網都是這種網絡)。所以新增擴大因子用來擴大窗口的大小,它表示TCP窗口左移的位數(實際窗口大小=窗口*2^窗口多擴大因子)。
8:時間戳(10字節)對端的時間戳(4字節)自己的時間戳(4字節)。這個選項主要用來測量回路時間(RTT),也是TCP做為流量控制的一個關鍵手段。
例子中0101沒有意義,08表示這是時間戳選項(類型),后面0a是值所占字節(選項的整個長度,包括表示類型的1個字節表示長度的一個字節),1455 5dea(341138922) 四個字節表示發送端的時間戳,0003 1f5d(204637) 四個字節表示回顯的時間戳。(對應tcpdump輸出中的options [nop,nop,TS……])
用一段代碼表示如何構造TCP包

對于一個TCP會話來說response、control、seq、ack是變化的,滑動窗口大小則會隨著是否網絡擁塞產生變化。
后面就是實際的HTTP協議的內容了,4745 5420,0x47是G的ASCII、0x45是E的ASCII、0x54是T的ASCII、0x20是空格。。。。。后面就自己讀吧
總結
《TCP/IP協議詳解》中很大部分章節是在介紹tcpdump的輸出、IP頭部、TCP頭部(甚至可以說是三卷書都是在說這個),掌握tcpdump對于我們做網絡分析或者系統調試是非常有幫助的。大家玩的愉快。
高能預警
哥講tcpdump是為了下一篇——《手把手教你做ARP病毒》。史上最難殺死的ARP病毒橫行數十年依然如此無敵,作為一名老司機我要手把手教你做病毒,分分鐘讓你的網絡陷入癱瘓(還可以讓你“精準”打擊網絡內任何一個人——讓她上不了網然后偽裝成老司機修電腦)。
【本文是51CTO專欄作者邢森的原創文章,轉載請聯系作者本人獲取授權】
































