許可をうけて翻訳したものです。:::XDPに飛び込もう車輪を再発明する(しない)ベリファイアにひっかかるカスタムXDPローダーユーザー空間とのやり取りマップに関する注意事項この先はXDPに飛び込もうXDPに関するこのシリーズの最初のパートでは、XDPを紹介し、最も単純な例について説明しました。それでは、いくつかのより高度なeBPF機能(マップ)といくつかの一般的な落とし穴を探りながら、もうすこし高度なことをしてみましょう。8で利用可能で、今すぐダウンロードして実行することができます。車輪を再発明する(しない)サンプルにパケット解析を追加するところから始めます。このような作業を簡単にするために、XDPプログラムのincludeセクションに以下を追加して、一般的なネットワークプロトコルのカーネル定義を再利用します。<linux/ip.h>XDPコンテキストを介してパケットの内容にアクセスする必要があります。その定義を見てみましょう。*/};パケットの内容は、ctx->dataとctx->data_endの間にあります。それで、構文解析コードを追加して、パケットのアドレスを使ってみましょう。この例では、IPv4宛先アドレスがゼロのパケットをドロップします。XDP_PASS;}ベリファイアにひっかかる上記のコードは問題なくコンパイルできますが、iprouteを使用してロードしようとすると、驚くことになります。program/map!ベリファイアの検証を通過できません。このベリファイアのエラーメッセージは「パケットの最初の数バイトにアクセスしているために発生したかもしれない」と誤解を招くかもしれません。各イーサネットフレームは少なくとも64バイトの長さでなければならないことを私達は知っています、従って、私達は私達がパケットペイロードの中の有効なオフセットにアクセスしていることがわかっています。代わりに、ベリファイアは明示的なチェックのみに依存します。パケット内のオフセットにアクセスしたり操作したりする前に、そのオフセットがパケット本体の内側にあることを条件式でチェックする必要があります。この例では、各ヘッダーにアクセスする前に、次のようなパッチを追加して、ヘッダーの末尾がパケットの末尾より手前にあることを確認する必要があります。私たちはすでにパート1でマップについて話しました。実際にそれらを使用する方法を見てみましょう。XDPプログラムを拡張して、ユーザーが実行時にドロップするアドレスを指定でき、関連する統計を読み取ることができるようにします。最初のステップとして、iproute2ツールをカスタムローダープログラムに置き換える必要があります。これは、このツールではマップの操作ができないためです。XDPプログラムをロードするために使用されるコードは、次のようになります。0);我々はLinuxカーネルソースにバンドルされているlibbpfヘルパーライブラリを使用しています。bpf_prog_load_xattr()は、prog_load_attr引数で指定されたeBPFプログラムをロードします。指定されたオブジェクトのすべてのELFセクションを解析して、すべての関連情報を抽出し、それをobj状況データに入れます。見つかった各プログラム(textセクション)は、新しく割り当てられたファイル記述子(prog_fd)を介してカーネル内にロードされます。このファイル記述子は、後でbpf_set_link_xdp_fd()関数を介して、ロードされたプログラムを選択されたデバイスに接続するために使用されます。最後の引数では、既存のXDPプログラムがある場合はそれを置き換えるためのフラグ、ドライバレベルのXDPフックを使用するためのフラグなど、いくつかのフラグを指定できます。デフォルトでは:ドライバレベルのフックを使用しようとし、その後一般的なものにフォールバックします。指定したデバイスにXDPプログラムがすでにインストールされている場合は失敗します。最後に、プログラムの終了時に呼び出される最後のヘルパー関数は、XDPプログラムをNICから切り離し、関連するすべてのカーネルリソースを解放します。ユーザー空間とのやり取りそれでは、面白い部分、マップに移りましょう。ユーザー空間とeBPFプログラムの間で共有されるすべてのデータ構造は「マップ」と呼ばれますが、実際にはハッシュマップ、配列、キューなど、さまざまな種類があります。通常、「シンプル」と「CPUごと」の2種類があります。CPUごとのマップでは、各エントリはすべてのローカルで利用可能なCPUに対して複製されます。カーネル内部では、各CPUはそのプライベートコピーにのみアクセスします。CPUごとのマップは、あらゆる種類の競合関連の問題を回避します。eBPFプログラムがパケットごとにデータエントリを変更しなければならない場合は、これが推奨されます。マップデータは、ユーザー空間とeBPFプログラムの両方からアクセスされます。両側に含まれるヘッダーファイルにデータ型定義を追加すると便利です。この例では、マップを使用してフィルタリングする送信元アドレスを指定し、指定した各アドレスについてドロップされたバイト数とパケット数をカウントします。このようなマップをプログラムに追加するには、以下のような記述が必要です。fd");このマップは実際にはCPUごとのハッシュテーブルであり、定義にはキーと値のサイズだけしか含まれていません。カーネルには割り当てとルックアップの実行、およびエントリの更新を行うために必要なそれらの情報だけがあればよいためです。マップ定義には、そのようなマップ内で許可されている最大エントリ数も含まれています。ハッシュマップは最初は空で、上記の制限を越えて挿入しようとすると失敗します。配列は指定された制限に等しい固定サイズを持ちます。ユーザー空間は、指定されたファイル記述子を介してマップにアクセスできます。libbpfヘルパー関数を使用するのはここでは少し複雑すぎるように見えるかもしれませんが、eBPFプログラムが複数のマップを公開するときには本当に役に立ちます。これでユーザースペースとeBPFのインタラクションを追加する準備が整いました。entry.bytes);これで、eBPFプログラムは、送信元IPアドレスがdrop_mapハッシュテーブルで見つかった場合にのみパケットをドロップし、関連する統計情報を更新します。ユーザースペースプログラムは、そのようなマップをゼロ化された統計で埋め、(定期的に)そのようなエントリを調べ、eBPFプログラムによって報告された統計を出力します。簡潔にするために、ソースアドレス[list]をフェッチしたり、正常終了するための定型的なユーザースペースコードは省略しています。それができれば、私たちはビルドと実行の準備が整っています。現在のコードでは、ユーザースペースプログラムのランダムなクラッシュからeBPFフィルターに明らかに何も効果がないことに至るまで、期待外れでしょう。ユーザースペースプログラムが異常終了した場合、XDPプログラムはネットワークデバイスに接続されたままになり、後で実行開始時に失敗します。このような場合、ユーザーは手動でiprouteを使ってXDPプログラムをデタッチする必要があります。offいくつかの幸運な場合には、現在のコードはほとんど完璧に動作し、シャットダウン時にXDPプログラムを切り離すことに失敗することがあります。eBPFデバッグ機能を使用してプログラムのステータスをダンプしましょう。XDP_DROP;それから例をもう一度実行して、以下のファイルにメッセージが表示されるのを確認します。/sys/kernel/debug/tracing/traceeBPFヘルパー関数は各入力パケットに対して正しく呼び出されます。運が良ければ、ユーザースペースによって作成されたマップエントリに関連付けられた統計情報が破損しているように見えることがあります。エントリ作成後に最初のパケットを受信した場合でもかなりランダムな値が含まれます。可能な最大CPU個数分の値を読み取り、それぞれをカーネルマップ内の対応するCPUごとの値へコピーします。この例ではユーザー空間プログラムスタックにあるstats変数以外のデータにアクセスする場合にこれが発生します。ユーザースペースプロセスがマップからエントリを読み込もうとすると、カーネルは同じ量のデータを指定されたアドレスにコピーし、再びスタック上のデータにアクセスして、上記のランダムな動作を引き起こします。解決策は、次のような記述をおこない、マップエントリに十分なストレージを割り当てることです。entry\n");そして、マップを読むときは、すべての値を調べて集計します。}これで、私たちのIPフィルタアプリケーションは完成です。eBPFによって提供される機能のいくつかを取り上げましたが、もっと多くの機能があります。さまざまなタスクに使用する準備ができているeBPFヘルパー関数が他にもたくさんあります。変更後のパケットチェックサムの更新、パケットの転送などです。良い出発点は、Linuxカーネルソース内のこのヘッダです。これには、実装されたヘルパー関数の公式ドキュメントが含まれています。include/uapi/linux/bpf.hさらに、カーネルソース内のsamples/bpf/ディレクトリには、さらに複雑なXDPの例がいくつか含まれています。そこを見る前に関連する背景が必要になります。例の完全なソースコードは、次の場所にあります。Hatのさいとうです。各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。