BPFについて

BPFについて

以下を使用していくつかのパケットをキャプチャする必要がある場合tcpdump

tcpdump -i eth0 "dst host 192.168.1.0"

いつも思うターゲットホスト 192.168.1.0その一部をBPF(Berkeley Packet Filter)といいます。私にとって、これはネットワークパケットをフィルタリングするための簡単な言語です。しかし、今日私のルームメイトは、BPFを使用してパフォーマンス情報をキャプチャできると述べました。彼の説明によると、これはperfmonWindowsのツールと同じです。これは本当ですか?質問の冒頭で述べたBPFと同じですか?

答え1

BPFとは何ですか?

BPF(またはより一般的には拡張バージョン、電子BPF) は、もともとパケットフィルタリング用に特別に設計された言語ですが、それ以上の機能を実行します。 Linuxでは、以下を含むさまざまな目的に使用できます。システムコールフィルタ安全上の理由から、終了するプロセスを選択してください。指摘したように、システムメモリが不足している場合や複雑なパフォーマンス監視が可能です。 Windowsはそうですが、eBPFサポートの追加perfmon、Windowsユーティリティでは使用されません。 Windowsには、eBPFのオペレーティングシステムのサポートに依存するWindows以外のユーティリティの互換性サポートのみが追加されました。

eBPF プログラムはユーザー空間では実行されません。代わりに、アプリケーションはeBPFプログラムを作成してカーネルに送信してから実行します。これは実際にはカーネルでインタプリタとして実装されている仮想プロセッサの機械語コードですが、使用することもできます。タイムリーなコンパイルパフォーマンスを劇的に向上させます。このプログラムは、パフォーマンスとネットワーキングに関連するインターフェースを含む、カーネルのいくつかの基本インターフェースにアクセスできます。その後、eBPFプログラムはカーネルと通信して、計算結果(例えば、ドロップされたパケット)を提供します。

eBPFプロジェクトの制限

サービス拒否攻撃や予期しない衝突を防ぐために、カーネルが最初に確認するコンパイルする前のコード。コードは実行する前にいくつかの重要なチェックを受けなければなりません。

  • 計画には以下が含まれます。4096権限のないユーザーのための一般的なガイドラインです。

  • 以下を除き、後方にジャンプすることはできません。境界ループそして関数呼び出し。

  • 決して到達できない指示はありません。

その結果、検証者はeBPFプログラムが停止したことを証明できるはずです。まだ解決策が見つかりません質問を停止もちろん、これがまさに独自のプログラムのみを許可する理由です。知る止まるだろうこの目的のために、プログラムは次のように表されます。方向性非循環グラフ。これに加えて、実際の作業をブロックして情報の漏洩や範囲外のメモリへのアクセスを避けたいと思います。制限された操作を許可しながらポインタが漏れるのを防ぎます。

  • ポインターは、確認可能な値で比較、保存、または返すことはできません。

  • ポインタ操作は、スカラー(ポインタから派生していない値)でのみ実行できます。

  • ポインタ操作を実行すると、指定されたメモリマップの外側を指す結果は発生しません。

検証者はかなり複雑ですそれ自体ですが、それ以上を行います。深刻な 安全 昆虫、少なくともいつbpf(2)システムコールは権限のないユーザーには無効

コードを見る

このコマンドのコンポーネントはdst host 192.168.1.0BPFではありません。それは単に使用された構文ですtcpdump。ただし、ユーザーが提供するコマンドは、BPFプログラムを生成してカーネルに送信するために使用されます。この例では、eBPFは使用されず、古いcBPFが使用されます。持ついくつかの重要な違いその間、どこかにあります(カーネルは内部的にcBPFをeBPFに変換しますが)。この-dフラグは、カーネルに送信されるcBPFコードを表示するために使用できます。

# tcpdump -i eth0 "dst host 192.168.1.0" -d
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 4
(002) ld       [30]
(003) jeq      #0xc0a80100      jt 8    jf 9
(004) jeq      #0x806           jt 6    jf 5
(005) jeq      #0x8035          jt 6    jf 9
(006) ld       [38]
(007) jeq      #0xc0a80100      jt 8    jf 9
(008) ret      #262144
(009) ret      #0

フィルタが複雑になるほど、バイトコードも複雑になります。-dカーネルにロードされるバイトコードを確認するには、マンページと追加フラグのいくつかの例を試してください。逆アセンブリコードの読み方については、以下を確認してください。BPFフィルタ文書。 eBPFプログラムを読んでいる場合は、一度見てください。eBPF命令セット仮想CPUの場合。

コードについて

簡単にするために、ターゲットIP 192.168.1.0の代わりに192.168.1.1を指定し、IPv4のみを一致させようとしているとします。これにより、IPv6を処理する必要がなくなり、コードが大幅に削減されます。

# tcpdump -i eth0 "dst host 192.168.1.1 and ip" -d
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 5
(002) ld       [30]
(003) jeq      #0xc0a80101      jt 4    jf 5
(004) ret      #262144
(005) ret      #0

上記のバイトコードが実際に何であるかを見てみましょう。する。 BPFバイトコードは、指定されたインターフェイスでパケットが受信されるたびに実行されます。パケットの内容(該当する場合はイーサネットヘッダを含む)は、BPFコードにアクセスできるバッファに配置されます。このコードは、パケットがフィルタと一致する場合はキャプチャバッファのサイズ(デフォルトは262144バイト)を返し、それ以外の場合は0を返します。

このフィルタを実行していて、192.168.1.142から192.168.1.1まで、空のペイロードを含むICMPメッセージを送信するパケットを受信するとします。ソースMACはaa:aa:aa:aa:aa:aa、ターゲットMACはbb:bb:bb:bb:bb:bbです。イーサネットフレームの内容(16進数)は次のとおりです。

aa aa aa aa aa aa bb bb bb bb bb bb 08 00 45 00
00 1c 77 71 40 00 40 01 3f 92 c0 a8 01 8e c0 a8
01 01 08 00 c1 c0 36 0e 00 01

最初の命令はですldh [12]。これは、パケットのオフセット12バイトにあるハーフワード(2バイト)をAレジスタにロードします。これは0x0800の値です(ネットワークデータは常にビッグエンディアンであることを覚えておいてください)。第2の命令は、jeq #0x800即ち値をAレジスタの値と比較することである。同じ場合は命令2にジャンプし、そうでなければ命令5にジャンプする。イーサネットフレームのこのオフセットでは、値0x800はIPv4プロトコルを指定します。比較が真なので、コードは命令2にジャンプします。ペイロードがIPv4でない場合は、コマンド5にジャンプします。

指針2(第3条)は ですld [30]。これにより、オフセット30の4バイトワード全体がAレジスタにロードされます。イーサネットフレームでは0xc0a80101です。次の命令は、jeq #0xc0a80101即値をAレジスタの内容と比較し、真の場合は4にジャンプし、それ以外の場合は5にジャンプします。値は宛先アドレスです(0xc0a80101は192.168.1.1のビッグエンディアン表現です)。値が一致するため、プログラムカウンタは4に設定されます。

命令4はret #262144BPFプログラムを終了し、呼び出しプログラムに整数262144を返す。この場合、コールプログラムはパケットtcpdumpがフィルタによってキャプチャされたことを知らせるため、カーネルからパケットの内容を要求し、より徹底的にデコードした後、その情報を端末に書き込みます。宛先アドレスがフィルタが探しているアドレスと一致しない場合、またはプロトコルタイプがIPv4でない場合、コードはそのアドレスが見つかるコマンド5にジャンプしますret #0。一致せずに終了します。

これは、パケットのオフセット12のハーフワードが0x800で、オフセット30の単語が0xc0a80101の場合は262144を返す方法です。そうでない場合は0が返されます。これはすべてカーネルで行われるため(オプションでJITエンジンによってネイティブマシンコードに変換された後)、高価なコンテキスト切り替えやカーネルスペースとユーザースペースの間にバッファを渡す必要がないため、フィルタはyesです。早く

高度な例

BPFコードは使用に限定されませんtcpdump。他の多くのユーティリティでこれを使用できます。このxt_bpfモジュールを使用して、BPFフィルタでiptablesルールを作成することもできます。ただし、バイトコードを生成するときは、tcpdump -dddiptablesがサポートしていないレイヤ2ヘッダが必要になるので注意してください。互換性を持たせるには、オフセットを調整する必要があります。

また、パケット長、ペイロード開始オフセット、パケットを受信するCPU、NetFilterフラグなど、生のパケットの内容を読み取って得られない情報を提供するさまざまな補助機能も提供されています。フィルター文書:

Linuxカーネルには、kパラメータを負のオフセット+特定の拡張オフセットに「オーバーロード」することによって、ロードコマンドクラスと連携するいくつかのBPF拡張もあります。このBPF拡張の結果はAにロードされます。

サポートされる BPF 拡張は次のとおりです。

拡大する 説明する
ロンドン skb->レン
元気 skb->プロトコル
タイプ skb->pkt_type
パフ ペイロード開始オフセット
イピディックス skb->dev->ifindex
エンラ タイプXとオフセットAのNetlink属性
エンラン X型のネストされたNetlinkプロパティ、オフセットA
表示 skb->マーク
待ち行列 skb->キューマッピング
ハードタイプ skb->開発者->タイプ
ハッシュ値を受信 skb->ハッシュ
CPU raw_smp_processor_id()
vlan_tci skb_vlan_tag_get(skb)
vlan_avail skb_vlan_tag_present(skb)
vlan_tpid skb->vlan_proto
ランド ランダム_u32()

たとえば、CPU 3で受信したすべてのパケットを一致させるには、次のようにします。

    ld #cpu
    jneq #3, drop
    ret #262144
drop:
    ret #0

これは、次の互換性のあるBPFアセンブリ構文を使用していることに注意してください。bpf_asmここで、他のアセンブリのリストはtcpdump構文を使用します。主な違いは、電子構文は名前付きラベルを使用し、後者のBPF構文は行番号を使用して各命令を表示することです。このアセンブリは、次のバイトコード(コンマ区切りの命令)に変換されます。

4,32 0 0 4294963236,21 0 1 1,6 0 0 262144,6 0 0 0,

その後、iptablesモジュールと一緒に使用できますxt_bpf

iptables -A INPUT -m bpf --bytecode "4,32 0 0 4294963236,21 0 1 1,6 0 0 262144,6 0 0 0," -j CPU3

これは、CPU3そのCPUから受信したすべてのパケットの宛先チェーンに移動します。

これが強力に見える場合は、これがすべてcBPFであることを覚えておいてください。 cBPFは内部的にeBPFに変換されますが、これはすべてです。何もない元のeBPFの機能と比較してみてください!

より多くの情報を知りたい場合

必ずお読みください。この記事cBPFの使い方を学びますtcpdump

読んでから必ず読んでくださいこの説明式をバイトコードに変換する方法tcpdump

それについて他のすべてを知りたい場合は、いつでも確認できます。ソースコード

答え2

eBPF プログラムはユーザー空間では実行されません。代わりに、アプリケーションはeBPFプログラムを作成してカーネルに送信してから実行します。

@forestの良い答えを補うために、これらの手順がどのように実行されるかを詳しく説明できます。

tcpdump が使用する cBPF にはフックはほとんどありません。ソケットに接続、のためのパケットが到着すると実行(これはtcpdumpが実行するものです。ソケットから受信したパケットをフィルタリングし、必要なパケットのみをユーザースペースに転送します。)seccomp フック、システムコール、およびそのパラメータに対していくつかのフィルタリングを実行します。

の重要な特徴の一つ電子BPFはい、添付可能ですより広いフックを選択カーネルでは(seccompを実行しませんが)、ネットワークの場合は次のものがあります。ソケットしかし、またTC(交通制御)フック、XDP(高速ネットワーキング用のドライバレベルフック)またはその他。あなたの質問について:プログラムは以下に添付することもできます。トラッキングポイントカーネル(システムコールやカーネルの「重要」関数などの一部の特定の関数に対して事前定義されたフック)またはカーネルプローブ(kプローブ)、これを通してすべての機能を追跡カーネルで(コンパイル時にインライン化されていない場合)、その他のタイプたとえば、セキュリティーユースケース用のLSMがあります。

一般的に依存トラッキングポイントまたはkprobeeBPFプログラムを関数に接続し、カーネルから関数が呼び出されるたびに実行します。プログラムは、関数のパラメーターまたは(終了時に添付されている場合)戻り値にアクセスできます。使用して地図、配列やハッシュマップなどの特殊カーネルメモリ領域で、データ共有eBPFプログラムと/またはユーザースペースの間で、プログラムは指標を収集または共有できます。状態連続実行間。

例えば、モニターオンopen()from BCCはシステムコールの開始および終了トレースポイントに追加されますopenat()。入力時に開こうとしているファイルのパスとファイルを開いたプロセスのPIDを収集し、ハッシュマップに保存します。システムコールが終了すると、2番目のプローブは戻り値を収集し、PIDに基づいてハッシュマップ内の関連項目を更新します。その後、ユーザースペースは、ハッシュマップ内のすべての項目を収集してダンプして、どのプロセスがどのファイルを開いたか、戻り値が何であるかを表示できます。

https://ebpf.io/eBPFを使い始めるのに最適な場所です。

関連情報