Linux-Kernel

轉發的 ipv4 數據包的不需要的碎片整理

  • June 20, 2021

我想處理使用者空間中的 ip 片段,並且我正在使用 iptables NF_QUEUE 將數據包定向到使用者空間。

問題是 IPv4 數據包總是重新組合併作為一個數據包而不是單個片段傳遞。對於 IPv6,分片按應有的方式傳遞。

我認為 conntracker 可能會導致它並在rawiptables 表中禁用它,但事實證明,當數據包到達原始表時,它已經重新組裝:

# iptables -t raw -nvL
Chain PREROUTING (policy ACCEPT 58 packets, 62981 bytes)
pkts bytes target     prot opt in     out     source               destination         
   1 30028 CT         all  --  *      *       0.0.0.0/0            10.0.0.0/24          NOTRACK

這是通過 IPv4 發送 30000 字節的 UDP 數據包時。IPv6的對應:

# ip6tables -t raw -nvL
Chain PREROUTING (policy ACCEPT 46 packets, 62304 bytes)
pkts bytes target     prot opt in     out     source               destination         
  21 31016 CT         all      *      *       ::/0                 1000::               NOTRACK

這是在帶有 virtio 網路設備的虛擬環境 kvm/qemu 中,mtu=1500。一些硬體解除安裝似乎不會導致這種情況,因為我可以看到所有帶有tcpdump -ni eth2 host 10.0.0.0.

raw/PREROUTING所以我的問題是Linux核心中的什麼可以強制IPv4數據包在netfilter鏈之前重新組裝?

我懷疑“ingress/qdisc”位於 AF_PACKET (tcpdump) 和 raw/PREROUTING 鏈之間,但我找不到問題所在。

數據包流:https ://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

每當使用conntrack時,主要用於:

  • 狀態防火牆 ( -m conntrack ...)
  • NAT ( -t nat ...)

核心模組nf_defrag_ipv4nf_defrag_ipv6. 該工具以 -400 的優先級連接到網路預路由:在iptables 的原始表之前,它以 -300 的優先級連接。在一個或多個數據包遍歷之後,nf_defrag_ipv[46]不存在任何片段:數據包被提前重新組裝。目標是 Netfilter 和 iptables 中的各種協議檢查器可以獲取所有數據包內容,包括例如 UDP 目標埠:此資訊將僅存在於第一個片段中。

因此,為了避免這種情況,原始表中的-j NOTRACK(被 廢棄-j CT --notrack)是不夠的。

一罐:

  • 永遠不要直接(有狀態規則)或間接(NAT)使用conntrack ,
  • 或創建一個新的網路命名空間

直接處理流量(很可能使用被盜的物理介面或macvlan介面,或者被橋接但不被主機路由)並確保在此命名空間中不發生有狀態規則。碎片整理工具不會掛在網路命名空間中,只要沒有任何東西強制它這樣做(並且可能還有足夠新的核心)

  • 或者有一個在優先級-400之前掛鉤的鏈條。這在最近足夠多的核心中實際上是可能的:

    # modinfo -p iptable_raw
    raw_before_defrag:Enable raw table before defrag (bool)
    

    刷新原始表,解除安裝模組並重新載入(並調整/etc/modprobe.d/):

    modprobe iptable_raw raw_before_defrag=1
    
    • 對於nftables

    只需創建優先級低於 -400 的鏈,例如:

    nft add table ip handlefrag
    nft add chain ip handlefrag predefrag '{ type filter hook prerouting priority -450; policy accept; }'
    nft add rule ip handlefrag predefrag ip 'frag-off & 0x3fff != 0' notrack 
    

    (只處理後面的片段,而不是第一個,替換0x3fff0x1fff

    對於 IPv6,方法不同,因為片段標頭可能不是下一個標頭。但是nft在其 man 中提供了一個簡單的表達方式:exthdr frag exists檢測作為片段的數據包。

    • iptables-nft API不存在任何東西(這是許多發行版(如 Debian)的預設設置):它不使用模組iptable_raw並且沒有選項來創建優先級為 -450 的實際nftables鏈。

    因此,如果您的命令輸出如下所示:

    # iptables -V
    iptables v1.8.7 (nf_tables)
    

    您不能在conntrack中單獨使用它。您必須恢復到iptables-legacy或切換到nft,或者…

    • 仍然可以做的是混合nftables (上面的規則)在數據包進行碎片整理之前將數據包標記為notrack ,然後繼續使用**iptables處理剩餘部分。同時使用nftablesiptables是沒有問題的,只要理解 OP 連結的 Netfilter 示意圖中的操作順序即可。

引用自:https://unix.stackexchange.com/questions/650790