
トラフィックをキャプチャするために「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 で断片化されたパケットを生成する方がはるかに簡単であり、拡張ヘッダーをどこかに配置する他の方法が見つからなかったためです。