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 メソッドのみをフィルターする別の方法を探しています。

解決策を探してみましたが、フィルタリングされたトラフィックをすべてpcapファイルに直接出力したいので、grepの使用が含まれているようですが、これは私のニーズには適していません。

答え1

tcpdumpバージョン 4.99.x 以降の IPv6の制限

IPv4の場合、パケット内のペイロードオフセットを取得するには、次のようにしてペイロードオフセットを計算すればよい。IPv4 の IHL+ 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

ip6 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特定のIPv6拡張ヘッダーの4つをループしてスキップし、 IPSec AHを処理し(19)ます(31)。他の場所でこのようなコードを生成する方法はわかりません。tcpダンプ言語(直接的なビーピーエフコード) を使用すると、ip6 protochain 6 and ip6 and tcp dst port 80取得した TCP ヘッダーを活用せずに、最初からやり直すか、さらに悪いことに、問題が発生します。

同様の問題は、Linux のさまざまなネットワーク機能やツールでも説明されています。例:tc u32伝える:

icmp_code VAL_MASK_8

次のヘッダー プロトコルが icmp または ipv6-icmp であると想定し、Type または Code フィールドの値を一致させます。これは危険です。コードでは、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'

これは次のように文書化できます (ただし、TCP ヘッダーが常に位置 40 から始まる 0 拡張ヘッダーの場合のみです)。

  • 次のヘッダーはTCP

    ip6[6] == 6
    
  • IPv6ペイロードの長さ:

    ip6[4:2]
    
  • TCPデータ オフセット (IPv6 のサイズは 40 + 12 = 52 に固定) (およびバイトを取得するための適切な調整)

    (ip6[52] & 0xf0) >> 2
    
  • TCPペイロード長 = IPv6ペイロード長 - TCPデータオフセット

  • 少なくとも「GET」の長さのTCPペイロードが少なくとも4バイトあることをテストします。

    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ダンプそしてBPF、nftables減算ができないため、サイズを正しく確認する方法がありません。

nftables以下の例は、OPの正確な例を模倣しようとしていますが、何らかの調整なしでは細部まで常に模倣できるわけではありません。方向もポートも指定せず、NAT前の受信パケットとNAT後の送信パケットをキャプチャします(そのため、フックと優先順位が選択されます)。選択されたパケットは最終的にに送信nflog施設:

2番目の呼び出し形式(nflog_グループが指定されている場合、Linux カーネルはパケットを nfnetlink_log に渡し、ログを netlink ソケット経由で指定されたグループに送信します。1 つのユーザー空間プロセスがグループにサブスクライブしてログを受信できます [...]

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では、nflog機能は、(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拡張ヘッダーを持つケースは、TCPではなくUDPとフラグメントパケット(フラグメントヘッダー) を使用するのはsocat(起動を防ぐために別のネットワーク名前空間でnf_defrag_ipv6)、TCP よりも UDP で断片化されたパケットを生成する方がはるかに簡単であり、拡張ヘッダーをどこかに配置する他の方法が見つからなかったためです。

関連情報