實戰!我用 Wireshark 讓你“看得見“ TCP

来源:https://www.cnblogs.com/xiaolincoding/archive/2020/05/20/12922927.html
-Advertisement-
Play Games

每日一句英語學習,每天進步一點點: 前言 為了讓大家更容易「看得見」 TCP,我搭建不少測試環境,並且數據包抓很多次,花費了不少時間,才抓到比較容易分析的數據包。 接下來丟包、亂序、超時重傳、快速重傳、選擇性確認、流量控制等等 TCP 的特性,都能「一覽無雲」。 沒錯,我把 TCP 的"衣服扒光"了 ...


每日一句英語學習,每天進步一點點:

前言

為了讓大家更容易「看得見」 TCP,我搭建不少測試環境,並且數據包抓很多次,花費了不少時間,才抓到比較容易分析的數據包。

接下來丟包、亂序、超時重傳、快速重傳、選擇性確認、流量控制等等 TCP 的特性,都能「一覽無雲」。

沒錯,我把 TCP 的"衣服扒光"了,就為了給大家看的清楚,嘻嘻。

提綱提綱

正文

顯形“不可見”的網路包

網路世界中的數據包交互我們肉眼是看不見的,它們就好像隱形了一樣,我們對著課本學習電腦網路的時候就會覺得非常的抽象,加大了學習的難度。

還別說,我自己在大學的時候,也是如此。

直到工作後,認識了兩大分析網路的利器:tcpdump 和 Wireshark,這兩大利器把我們“看不見”的數據包,呈現在我們眼前,一目瞭然。

唉,當初大學學習計網的時候,要是能知道這兩個工具,就不會學的一臉懵逼。

tcpdump 和 Wireshark 有什麼區別?

tcpdump 和 Wireshark 就是最常用的網路抓包和分析工具,更是分析網路性能必不可少的利器。

  • tcpdump 僅支持命令行格式使用,常用在 Linux 伺服器中抓取和分析網路包。
  • Wireshark 除了可以抓包外,還提供了可視化分析網路包的圖形頁面。

所以,這兩者實際上是搭配使用的,先用 tcpdump 命令在 Linux 伺服器上抓包,接著把抓包的文件拖出到 Windows 電腦後,用 Wireshark 可視化分析。

當然,如果你是在 Windows 上抓包,只需要用 Wireshark 工具就可以。

tcpdump 在 Linux 下如何抓包?

tcpdump 提供了大量的選項以及各式各樣的過濾表達式,來幫助你抓取指定的數據包,不過不要擔心,只需要掌握一些常用選項和過濾表達式,就可以滿足大部分場景的需要了。

假設我們要抓取下麵的 ping 的數據包:

要抓取上面的 ping 命令數據包,首先我們要知道 ping 的數據包是 icmp 協議,接著在使用 tcpdump 抓包的時候,就可以指定只抓 icmp 協議的數據包:

那麼當 tcpdump 抓取到 icmp 數據包後, 輸出格式如下:

從 tcpdump 抓取的 icmp 數據包,我們很清楚的看到 icmp echo 的交互過程了,首先發送方發起了 ICMP echo request 請求報文,接收方收到後回了一個 ICMP echo reply 響應報文,之後 seq 是遞增的。

我在這裡也幫你整理了一些最常見的用法,並且繪製成了表格,你可以參考使用。

首先,先來看看常用的選項類,在上面的 ping 例子中,我們用過 -i 選項指定網口,用過 -nn 選項不對 IP 地址和埠名稱解析。其他常用的選項,如下表格:

tcpdump 常用選項類tcpdump 常用選項類

接下來,我們再來看看常用的過濾表用法,在上面的 ping 例子中,我們用過的是 icmp and host 183.232.231.174,表示抓取 icmp 協議的數據包,以及源地址或目標地址為 183.232.231.174 的包。其他常用的過濾選項,我也整理成了下麵這個表格。

tcpdump 常用過濾表達式類tcpdump 常用過濾表達式類

說了這麼多,你應該也發現了,tcpdump 雖然功能強大,但是輸出的格式並不直觀。

所以,在工作中 tcpdump 只是用來抓取數據包,不用來分析數據包,而是把 tcpdump 抓取的數據包保存成 pcap 尾碼的文件,接著用 Wireshark 工具進行數據包分析。

Wireshark 工具如何分析數據包?

Wireshark 除了可以抓包外,還提供了可視化分析網路包的圖形頁面,同時,還內置了一系列的彙總分析工具。

比如,拿上面的 ping 例子來說,我們可以使用下麵的命令,把抓取的數據包保存到 ping.pcap 文件

接著把 ping.pcap 文件拖到電腦,再用 Wireshark 打開它。打開後,你就可以看到下麵這個界面:

是吧?在 Wireshark 的頁面里,可以更加直觀的分析數據包,不僅展示各個網路包的頭部信息,還會用不同的顏色來區分不同的協議,由於這次抓包只有 ICMP 協議,所以只有紫色的條目。

接著,在網路包列表中選擇某一個網路包後,在其下麵的網路包詳情中,可以更清楚的看到,這個網路包在協議棧各層的詳細信息。比如,以編號 1 的網路包為例子:

ping 網路包ping 網路包
  • 可以在數據鏈路層,看到 MAC 包頭信息,如源 MAC 地址和目標 MAC 地址等欄位;
  • 可以在 IP 層,看到 IP 包頭信息,如源 IP 地址和目標 IP 地址、TTL、IP 包長度、協議等 IP 協議各個欄位的數值和含義;
  • 可以在 ICMP 層,看到 ICMP 包頭信息,比如 Type、Code 等 ICMP 協議各個欄位的數值和含義;

Wireshark 用了分層的方式,展示了各個層的包頭信息,把“不可見”的數據包,清清楚楚的展示了給我們,還有理由學不好電腦網路嗎?是不是相見恨晚

從 ping 的例子中,我們可以看到網路分層就像有序的分工,每一層都有自己的責任範圍和信息,上層協議完成工作後就交給下一層,最終形成一個完整的網路包。


解密 TCP 三次握手和四次揮手

既然學會了 tcpdump 和 Wireshark 兩大網路分析利器,那我們快馬加鞭,接下用它倆抓取和分析 HTTP 協議網路包,並理解 TCP 三次握手和四次揮手的工作原理。

本次例子,我們將要訪問的 http://192.168.3.200 服務端。在終端一用 tcpdump 命令抓取數據包:

接著,在終端二執行下麵的 curl 命令:

最後,回到終端一,按下 Ctrl+C 停止 tcpdump,並把得到的 http.pcap 取出到電腦。

使用 Wireshark 打開 http.pcap 後,你就可以在 Wireshark 中,看到如下的界面:

HTTP 網路包HTTP 網路包

我們都知道 HTTP 是基於 TCP 協議進行傳輸的,那麼:

  • 最開始的 3 個包就是 TCP 三次握手建立連接的包
  • 中間是 HTTP 請求和響應的包
  • 而最後的 3 個包則是 TCP 斷開連接的揮手包

Wireshark 可以用時序圖的方式顯示數據包交互的過程,從菜單欄中,點擊 統計 (Statistics) -> 流量圖 (Flow Graph),然後,在彈出的界面中的「流量類型」選擇 「TCP Flows」,你可以更清晰的看到,整個過程中 TCP 流的執行過程:

TCP 流量圖TCP 流量圖

你可能會好奇,為什麼三次握手連接過程的 Seq 是 0 ?

實際上是因為 Wireshark 工具幫我們做了優化,它預設顯示的是序列號 seq 是相對值,而不是真實值。

如果你想看到實際的序列號的值,可以右鍵菜單, 然後找到「協議首選項」,接著找到「Relative Seq」後,把它給取消,操作如下:

取消序列號相對值顯示取消序列號相對值顯示

取消後,Seq 顯示的就是真實值了:

TCP 流量圖TCP 流量圖

可見,客戶端和服務端的序列號實際上是不同的,序列號是一個隨機值。

這其實跟我們書上看到的 TCP 三次握手和四次揮手很類似,作為對比,你通常看到的 TCP 三次握手和四次揮手的流程,基本是這樣的:

TCP 三次握手和四次揮手的流程TCP 三次握手和四次揮手的流程

為什麼抓到的 TCP 揮手是三次,而不是書上說的四次?

因為伺服器端收到客戶端的 FIN 後,伺服器端同時也要關閉連接,這樣就可以把 ACKFIN 合併到一起發送,節省了一個包,變成了“三次揮手”。

而通常情況下,伺服器端收到客戶端的 FIN 後,很可能還沒發送完數據,所以就會先回覆客戶端一個 ACK 包,稍等一會兒,完成所有數據包的發送後,才會發送 FIN 包,這也就是四次揮手了。

如下圖,就是四次揮手的過程:

四次揮手四次揮手

TCP 三次握手異常情況實戰分析

TCP 三次握手的過程相信大家都背的滾瓜爛熟,那麼你有沒有想過這三個異常情況:

  • TCP 第一次握手的 SYN 丟包了,會發生了什麼?
  • TCP 第二次握手的 SYN、ACK 丟包了,會發生什麼?
  • TCP 第三次握手的 ACK 包丟了,會發生什麼?

有的小伙伴可能說:“很簡單呀,包丟了就會重傳嘛。”

那我在繼續問你:

  • 那會重傳幾次?
  • 超時重傳的時間 RTO 會如何變化?
  • 在 Linux 下如何設置重傳次數?
  • ….

是不是啞口無言,無法回答?

不知道沒關係,接下里我用三個實驗案例,帶大家一起探究探究這三種異常。

實驗場景

本次實驗用了兩台虛擬機,一臺作為服務端,一臺作為客戶端,它們的關係如下:

實驗環境實驗環境
  • 客戶端和服務端都是 CentOs 6.5 Linux,Linux 內核版本 2.6.32
  • 服務端 192.168.12.36,apache web 服務
  • 客戶端 192.168.12.37

實驗一:TCP 第一次握手 SYN 丟包

為了模擬 TCP 第一次握手 SYN 丟包的情況,我是在拔掉伺服器的網線後,立刻在客戶端執行 curl 命令:

其間 tcpdump 抓包的命令如下:

過了一會, curl 返回了超時連接的錯誤:

date 返回的時間,可以發現在超時接近 1 分鐘的時間後,curl 返回了錯誤。

接著,把 tcp_sys_timeout.pcap 文件用 Wireshark 打開分析,顯示如下圖:

SYN 超時重傳五次SYN 超時重傳五次

從上圖可以發現, 客戶端發起了 SYN 包後,一直沒有收到服務端的 ACK ,所以一直超時重傳了 5 次,並且每次 RTO 超時時間是不同的:

  • 第一次是在 1 秒超時重傳
  • 第二次是在 3 秒超時重傳
  • 第三次是在 7 秒超時重傳
  • 第四次是在 15 秒超時重傳
  • 第五次是在 31 秒超時重傳

可以發現,每次超時時間 RTO 是指數(翻倍)上漲的,當超過最大重傳次數後,客戶端不再發送 SYN 包。

在 Linux 中,第一次握手的 SYN 超時重傳次數,是如下內核參數指定的:

$ cat /proc/sys/net/ipv4/tcp_syn_retries
5

tcp_syn_retries 預設值為 5,也就是 SYN 最大重傳次數是 5 次。

接下來,我們繼續做實驗,把 tcp_syn_retries 設置為 2 次:

echo 2 > /proc/sys/net/ipv4/tcp_syn_retries

重傳抓包後,用 Wireshark 打開分析,顯示如下圖:

SYN 超時重傳兩次SYN 超時重傳兩次

實驗一的實驗小結

通過實驗一的實驗結果,我們可以得知,當客戶端發起的 TCP 第一次握手 SYN 包,在超時時間內沒收到服務端的 ACK,就會在超時重傳 SYN 數據包,每次超時重傳的 RTO 是翻倍上漲的,直到 SYN 包的重傳次數到達 tcp_syn_retries 值後,客戶端不再發送 SYN 包。

SYN 超時重傳SYN 超時重傳

實驗二:TCP 第二次握手 SYN、ACK 丟包

為了模擬客戶端收不到服務端第二次握手 SYN、ACK 包,我的做法是在客戶端加上防火牆限制,直接粗暴的把來自服務端的數據都丟棄,防火牆的配置如下:

接著,在客戶端執行 curl 命令:

date 返回的時間前後,可以算出大概 1 分鐘後,curl 報錯退出了。

客戶端在這其間抓取的數據包,用 Wireshark 打開分析,顯示的時序圖如下:

從圖中可以發現:

  • 客戶端發起 SYN 後,由於防火牆屏蔽了服務端的所有數據包,所以 curl 是無法收到服務端的 SYN、ACK 包,當發生超時後,就會重傳 SYN 包
  • 服務端收到客戶的 SYN 包後,就會回 SYN、ACK 包,但是客戶端一直沒有回 ACK,服務端在超時後,重傳了 SYN、ACK 包,接著一會,客戶端超時重傳的 SYN 包又抵達了服務端,服務端收到後,超時定時器就重新計時,然後回了 SYN、ACK 包,所以相當於服務端的超時定時器只觸發了一次,又被重置了。
  • 最後,客戶端 SYN 超時重傳次數達到了 5 次(tcp_syn_retries 預設值 5 次),就不再繼續發送 SYN 包了。

所以,我們可以發現,當第二次握手的 SYN、ACK 丟包時,客戶端會超時重發 SYN 包,服務端也會超時重傳 SYN、ACK 包。

咦?客戶端設置了防火牆,屏蔽了服務端的網路包,為什麼 tcpdump 還能抓到服務端的網路包?

添加 iptables 限制後, tcpdump 是否能抓到包 ,這要看添加的 iptables 限制條件:

  • 如果添加的是 INPUT 規則,則可以抓得到包
  • 如果添加的是 OUTPUT 規則,則抓不到包

網路包進入主機後的順序如下:

  • 進來的順序 Wire -> NIC -> tcpdump -> netfilter/iptables
  • 出去的順序 iptables -> tcpdump -> NIC -> Wire

tcp_syn_retries 是限制 SYN 重傳次數,那第二次握手 SYN、ACK 限制最大重傳次數是多少?

TCP 第二次握手 SYN、ACK 包的最大重傳次數是通過 tcp_synack_retries 內核參數限制的,其預設值如下:

$ cat /proc/sys/net/ipv4/tcp_synack_retries
5

是的,TCP 第二次握手 SYN、ACK 包的最大重傳次數預設值是 5 次。

為了驗證 SYN、ACK 包最大重傳次數是 5 次,我們繼續做下實驗,我們先把客戶端的 tcp_syn_retries 設置為 1,表示客戶端 SYN 最大超時次數是 1 次,目的是為了防止多次重傳 SYN,把服務端 SYN、ACK 超時定時器重置。

接著,還是如上面的步驟:

  1. 客戶端配置防火牆屏蔽服務端的數據包
  2. 客戶端 tcpdump 抓取 curl 執行時的數據包

把抓取的數據包,用 Wireshark 打開分析,顯示的時序圖如下:

從上圖,我們可以分析出:

  • 客戶端的 SYN 只超時重傳了 1 次,因為 tcp_syn_retries 值為 1
  • 服務端應答了客戶端超時重傳的 SYN 包後,由於一直收不到客戶端的 ACK 包,所以服務端一直在超時重傳 SYN、ACK 包,每次的 RTO 也是指數上漲的,一共超時重傳了 5 次,因為 tcp_synack_retries 值為 5

接著,我把 tcp_synack_retries 設置為 2tcp_syn_retries 依然設置為 1:

echo 2 > /proc/sys/net/ipv4/tcp_synack_retries
echo 1 > /proc/sys/net/ipv4/tcp_syn_retries

依然保持一樣的實驗步驟進行操作,接著把抓取的數據包,用 Wireshark 打開分析,顯示的時序圖如下:

可見:

  • 客戶端的 SYN 包只超時重傳了 1 次,符合 tcp_syn_retries 設置的值;
  • 服務端的 SYN、ACK 超時重傳了 2 次,符合 tcp_synack_retries 設置的值

實驗二的實驗小結

通過實驗二的實驗結果,我們可以得知,當 TCP 第二次握手 SYN、ACK 包丟了後,客戶端 SYN 包會發生超時重傳,服務端 SYN、ACK 也會發生超時重傳。

客戶端 SYN 包超時重傳的最大次數,是由 tcp_syn_retries 決定的,預設值是 5 次;服務端 SYN、ACK 包時重傳的最大次數,是由 tcp_synack_retries 決定的,預設值是 5 次。

實驗三:TCP 第三次握手 ACK 丟包

為了模擬 TCP 第三次握手 ACK 包丟,我的實驗方法是在服務端配置防火牆,屏蔽客戶端 TCP 報文中標誌位是 ACK 的包,也就是當服務端收到客戶端的 TCP ACK 的報文時就會丟棄,iptables 配置命令如下:

接著,在客戶端執行如下 tcpdump 命令:

然後,客戶端向服務端發起 telnet,因為 telnet 命令是會發起 TCP 連接,所以用此命令做測試:

此時,由於服務端收不到第三次握手的 ACK 包,所以一直處於 SYN_RECV 狀態:

而客戶端是已完成 TCP 連接建立,處於 ESTABLISHED 狀態:

過了 1 分鐘後,觀察發現服務端的 TCP 連接不見了:

過了 30 分別,客戶端依然還是處於 ESTABLISHED 狀態:

接著,在剛纔客戶端建立的 telnet 會話,輸入 123456 字元,進行發送:

持續「好長」一段時間,客戶端的 telnet 才斷開連接:

以上就是本次的實現三的現象,這裡存在兩個疑點:

  • 為什麼服務端原本處於 SYN_RECV 狀態的連接,過 1 分鐘後就消失了?
  • 為什麼客戶端 telnet 輸入 123456 字元後,過了好長一段時間,telnet 才斷開連接?

不著急,我們把剛抓的數據包,用 Wireshark 打

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ASP.NET Core允許我們指定註冊服務的生存期.服務實例將根據指定的生存時間自動處理.因此,我們無需擔心清理此依賴關係,他將由ASP.NET Core框架處理.有如下三種類型的生命周期. 關於依賴註入通俗易懂的內容大家可以看一下我上一篇文章 ".NET IoC模式依賴反轉(DIP)、控制反轉( ...
  • 0.前言 《EF Core》實際上已經可以告一段落了,但是感覺還有一點點意猶未盡。所以決定分享一下,個人在實際開發中使用EF Core的一些經驗和使用的擴展包。 1. EF Core的非同步操作 正如這小節題目所言,EF Core是支持非同步操作的,但實際可用集中在SaveChanges和非同步查詢這兩個 ...
  • C#實現獲取指定文件目錄下的某種格式的文件集,並移動到Bak 1.獲取文件的路徑和移動到文件夾信息 string fileName = ""; string sourceFile = @"F:\Test文件夾\CSV"; string bakFilePath = @"F:\Test文件夾\CSV\b ...
  • 最近,團隊的小伙伴們在做項目時,需要用到JWT認證。遂根據自己的經驗,整理成了這篇文章,用來幫助理清JWT認證的原理和代碼編寫操作。 第一部分:Dotnet core使用JWT認證授權最佳實踐(一) (接上文) 測試運行 % dotnet run 等程式運行起來後,在瀏覽器輸入:http://loc ...
  • [TOC] 1.查看當前版本 2.下線升級的節點 3.解壓,改名,創建軟鏈接 4.拷貝conf文件和cert文件和sh腳本 5.重啟服務並檢查 ...
  • 工作中如果你是Linux運維,或者程式員,一定經常需要一個Linux的環境來讓你折騰。這個時候使用虛擬機對我們來說是一個不錯的選擇。 虛擬化技術目前主要有兩種:一、原生架構,這種虛擬機產品直接安裝在電腦硬體上,不需要操作系統的支持,這類虛擬機擁有強大的性能,主要用於生產環境,比如vSphere、C ...
  • 大家好,我是良許。 硬碟是電腦非常重要的一個部件,不管是代碼,還是 UI 、聲音、文檔,抑或是沒人時偷偷看的小視頻,都需要保存在硬碟里。 對於很多 Linux 伺服器,會進行很多的編譯操作。而編譯操作在很多情況下都會產生大量的中間文件,這些文件會占用很多的磁碟空間,導致磁碟空間一直吃緊。 別問為啥 ...
  • 一、準備環境 1)獲取crash工具。註意區分版本(arm/arm64/x86_64)。 2)獲取對應軟體版本的符號表文件(如vmlinux),可以將該文件放置 crash工具同一目錄下。 3)獲取sysdump文件,並把所有sysdump文件追加到一個文件sysdump.core中: 4)使用cr ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...