使用 tcpdump 過濾 IPv6 封包中的 HTTP 方法

使用 tcpdump 過濾 IPv6 封包中的 HTTP 方法

我正在使用“tcpdump”來捕獲流量,並且我想透過 HTTP 方法進行過濾。當我有 IPv4 封包時,我使用:tcpdump -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'過濾 HTTP GET 封包。

不幸的是,該tcp[]過濾器不適用於 IPv6 封包。根據文件

針對傳輸層標頭的算術表達式(例如tcp[0])不適用於 IPv6 封包。

因此,我正在尋找另一種方法來使用 tcpdump 僅過濾 IPv6 流量的 HTTP 方法。

我嘗試尋找解決方案,但似乎它們包含 grep 的用法,這不適合我的需求,因為我想將所有過濾後的流量直接輸出到 pcap 文件

答案1

tcpdump從版本 4.99.x 開始,IPv6的限制

對於 IPv4,要取得封包中的有效負載偏移量,只需使用以下方法或多或少計算有效負載偏移量:IPv4 的國際人道法+ TCP的資料偏移量也考慮 IPv4 選項和 TCP 選項。這很簡單,可以包含在tcp轉儲直接在產生 BPF 字節碼過濾器時直接產生(例如一些可用的 BPF 文件)那裡)。

相反,對於 IPv6,固定標頭和上報頭之間可以存在可變數量(可能在 0 到 9 之間)的擴展標頭。這表示可能包含10 種情況(0、1、2、... 9 擴充標頭)的程式碼來尋找封包中的有效負載偏移量,假設某些情況沒有其他特殊性,甚至嘗試針對格式錯誤的資料包提供保護(這可能導致誤報匹配):我只能假設這沒有實現,因為它很複雜並且不願意提供一個有缺陷的實現來忽略這些標頭。

即使是一個簡單的測試,例如ip6 and tcp dst port 80不考慮擴展標頭,如下面生成的 BPF 程式碼所示:

$ tcpdump --version 
tcpdump version 4.99.3
libpcap version 1.10.3 (with TPACKET_V3)
OpenSSL 3.0.9 30 May 2023
$ tcpdump -y EN10MB -d ip6 and tcp dst port 80
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 7
(002) ldb      [20]
(003) jeq      #0x6             jt 4    jf 7
(004) ldh      [56]
(005) jeq      #0x50            jt 6    jf 7
(006) ret      #262144
(007) ret      #0

所以可能會錯過交通。

從 tcpdump 4.99.x 開始,唯一對 IPv6 擴充標頭進行充分處理的地方記錄在手冊頁的末尾:

ip6 proto應該追逐標頭鏈,但目前它沒有。ip6 protochain是為此行為提供的。

事實上,目前從 tcpdump 4.99.x 起ip6 and tcp相當於ip6 proto 6(它們產生相同的字節碼):

$ tcpdump -y EN10MB -d ip6 and tcp
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 8
(002) ldb      [20]
(003) jeq      #0x6             jt 7    jf 4
(004) jeq      #0x2c            jt 5    jf 8
(005) ldb      [54]
(006) jeq      #0x6             jt 7    jf 8
(007) ret      #262144
(008) ret      #0

whileip6 protochain 6進行了更徹底的檢查,並且是涉及上層(最終傳輸標頭)(例如 TCP)的任何測試中所期望的:

$ tcpdump -y EN10MB -d ip6 protochain 6
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 35
(002) ldb      [20]
(003) ldx      #0x28
(004) jeq      #0x6             jt 32   jf 5
(005) jeq      #0x3b            jt 32   jf 6
(006) jeq      #0x0             jt 10   jf 7
(007) jeq      #0x3c            jt 10   jf 8
(008) jeq      #0x2b            jt 10   jf 9
(009) jeq      #0x2c            jt 10   jf 19
(010) ldb      [x + 14]
(011) st       M[0]
(012) ldb      [x + 15]
(013) add      #1
(014) mul      #8
(015) add      x
(016) tax      
(017) ld       M[0]
(018) ja       4
(019) jeq      #0x33            jt 20   jf 32
(020) txa      
(021) ldb      [x + 14]
(022) st       M[0]
(023) txa      
(024) add      #1
(025) tax      
(026) ldb      [x + 14]
(027) add      #2
(028) mul      #4
(029) tax      
(030) ld       M[0]
(031) ja       4
(032) add      #0
(033) jeq      #0x6             jt 34   jf 35
(034) ret      #262144
(035) ret      #0

上面的 4 個測試包括jt 10和 之間的行(10,並(018) ja 4循環 4 個特定的 IPv6 擴充標頭以跳過它們,同時(19)處理(31)IPSec AH。我不知道如何在其他地方產生這樣的程式碼tcp轉儲語言(而不是直接的帶通濾波器代碼)。使用ip6 protochain 6 and ip6 and tcp dst port 80只是從頭開始,或者更糟,而不是利用剛剛獲得的 TCP 標頭。

Linux 的各種網路設施和工具中都描述了同樣類型的問題。例子:tc u32告訴:

icmp_code VAL_MASK_8

假設下一個標頭協定為 icmp 或 ipv6-icmp 並符合類型或程式碼欄位值。正如代碼假設的那樣,這是危險的IPv4 的最小標頭大小和缺少 IPv6 的擴充標頭

所有這些甚至沒有考慮到專門針對 HTTP 的情況,當重複使用連接時,下一個 HTTP 查詢甚至可能不在封包邊界的開頭,或者如果此類資料嵌入為資料而不是查詢,則此類查詢可能會匹配出現在資料包邊界的開始處。

tcpdump使用版本 4.99.x測試 0 擴充標頭情況

這是使用 0 個擴充標頭的測試,即處於同一級別tcp轉儲目前是這樣,只需完成最少的驗證:

tcpdump -n -s 0 -A 'ip6[6] == 6 and ( ip6[4:2] - ((ip6[52] & 0xf0) >> 2) >= 4 ) and ip6[40 + ((ip6[52] & 0xf0) >> 2) :4] == 0x47455420'

可以這樣記錄(仍然僅適用於 0 擴展標頭情況,其中 TCP 標頭始終從位置 40 開始):

  • 下一個標頭是 TCP

    ip6[6] == 6
    
  • IPv6有效負載長度:

    ip6[4:2]
    
  • 傳輸控制協定資料偏移量(固定 IPv6 的大小為 40 + 12 = 52)(並進行適當調整以取得位元組)

    (ip6[52] & 0xf0) >> 2
    
  • TCP 負載長度 = IPv6 負載長度 - TCP 資料偏移量

  • 測試至少有 4 個位元組的 TCP 有效負載,至少具有“GET”的長度

    ip6[4:2] - ((ip6[52] & 0xf0) >> 2)  >= 4
    
  • 以 ASCII/UTF-8 編碼的 4 位元組字串「GET」可以用 4 位元組值 0x47455420 表示

  • TCP 負載偏移量為 IPv6 固定標頭長度 (40) + TCP 資料偏移量

    40 + ((ip6[52] & 0xf0) >> 2)
    
  • 測試從 IPv6 固定標頭長度 (40) + 資料偏移開始的第一個 4 位元組字是否等於字串「GET」的值

    ip6[40 + ((ip6[52] & 0xf0) >> 2) :4] = 0x47455420
    

nftablesLinux 核心 >= 5.15.54 +tcp轉儲

我提出了一種簡單的僅適用於 Linux 的替代方法,使用nftables並且需要足夠新的核心(Linux 核心版本>= 5.15.54向後移植自5.16) 也nftables>= 1.0.1支援@ih(內部標頭/有效負載)原始有效負載,而不僅僅是@th(傳輸頭),因為nftables@th它本身太有限,無法以有用的方式進行任意處理。

IPv6 資料封包已解析,包括任何 IPv6 擴充標頭,@th 和 @in 指標已可用於nftables:無需進一步處理即可取得所有案例。我依靠nftables如果封包太短,則檢查失敗,而無需計算實際的 TCP 有效負載大小(與tcp轉儲和帶通濾波器,nftables無法進行減法,因此無法正確檢查大小)。

nftables下面的例子試圖模仿OP的精確例子,但如果不進行一些調整,就不能總是在所有細節上。無方向或端口,捕獲 NAT 之前的傳入資料包和 NAT 之後的傳出資料包(因此選擇了掛鉤和優先權)。最終選定的資料包發送到網誌設施

在第二種調用形式中(如果nflog_組指定),Linux 核心會將資料包傳遞給 nfnetlink_log,後者將透過 netlink 套接字將日誌傳送到指定的群組。一個使用者空間進程可以訂閱該群組以接收日誌 [...]

table ip6 special_log
delete table ip6 special_log

table ip6 special_log {
        chain log_to_nflog {
                log group 4242
        }

        chain ih_filter {
                meta l4proto tcp @ih,0,32 0x47455420 counter jump log_to_nflog
        }

        chain c_ingress {
                type filter hook prerouting priority -150; policy accept;
                jump ih_filter
        }
        chain c_egress {
                type filter hook postrouting priority 150; policy accept;
                jump ih_filter
        }
}

在 Linux 上,網誌設施可作為(libpcap和)tcp轉儲

# tcpdump -D |grep nflog
12.nflog (Linux netfilter log (NFLOG) interface) [none]

所選的資料包nftables因此可以顯示(重複使用相同的 NFLOG 群組:4242):

tcpdump -n -s 0 -A -i nflog:4242

注意:比直接捕獲有更多的延遲。

注意:正確處理nftables具有擴展標頭的情況實際上是使用 UDP(而不是 TCP)和分段資料包(由片段頭)使用socat(在單獨的網路命名空間中以防止nf_defrag_ipv6啟動),因為使用 UDP 生成分段資料包比使用 TCP 更容易,而且我沒有找到其他方法在某處擁有擴展標頭。

相關內容