vmlinux.hとは何か。なぜeBPFプログラムにとって重要なのか。 #aqua #セキュリティ #eBPF
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
本ブログは「Aqua Security」社の技術ブログで2021年3月30日に公開された「 What is vmlinux.h and Why is It Important for Your eBPF Programs? 」の日本語翻訳です。
vmlinux.hとは何か。なぜeBPFプログラムにとって重要なのか。
eBPF は、開発者が Linux カーネルの戦略的なポイントにカスタムコードを追加し、簡単な C 言語や Go 言語のプログラムを書くことでカーネルとのやりとりができる、強力かつ興味深い技術です。開発者が書いて実行した eBPF プログラムは、アタッチしたプロセスのメモリ内のデータを検査できます。しかし、そのためには eBPF プログラムがどのようなデータ構造を扱っているかを知る必要があります。そのためには、次の1行が必要です。それは、「#include "vmlinux.h"」です。このブログでは、vmlinux.h とは何か、なぜ eBPF のプログラムを書くときに vmlinux.h を使うべきなのかを説明します。
vmlinux.hの簡単な説明
vmlinux.h は生成されたコードです。これには、実行中の Linux カーネルが自身のソースコードで使用するすべての型定義が含まれています。Linux をビルドしたときの成果物の1つが、 vmlinux というファイルです。これは、主要なディストリビューションに普通は含まれているファイルで、コンパイルされた起動可能なカーネルを内部に含む ELF バイナリです。
Linux リポジトリの mainline には、bpftool という名前のツールがあります。このツールは、vmlinux オブジェクトファイルを読み込んで、vmlinux.h ファイルを生成する機能を持っています。vmlinux.h ファイルには、インストールされたカーネルが使用するすべての型定義が含まれているため、非常に大きなヘッダーファイルとなります。
実際のコマンドは以下となります。
The actual command is:
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
このヘッダファイルをインポートすると、bpf プログラムはメモリを読みとることができ、どのバイトが構造体のどのフィールドに対応しているかを知ることができます。
例えば、linux ではプロセスの概念を task_struct という型で表現しています。bpf プログラムから task_struct の値を検査したい場合、その定義を知る必要があります。
一度のコンパイルでどこでも実行可能
vmlinux.h ファイルは、インストールされているカーネルから生成されています。異なるバージョンのカーネルを実行している別マシンでは、再コンパイルせずに bpf プログラムを実行すると、プログラムが壊れる可能性があります。これは、linux のソースコードの中で、バージョンごとに内部構造体の定義が変わるためです。
しかし、libbpf を使うと、一度のコンパイルでどこでも実行できます。libbpf には BPF_CORE_READ などのマクロが定義されており、vmlinux.h で定義されている型の中のどのフィールドにアクセスしようとしているかを分析します。したがって、自分のカーネルから生成した vmlinux.h ファイルで bpf プログラムをコンパイルして、別のカーネルで実行しても問題ありません。
まとめ
すべての Linux カーネルタイプを含む独自の vmlinux.h ヘッダを生成することで、eBPF プログラムを書く際にカーネルヘッダへの依存をなくすことができます。次回の記事では、vmlinux.h の利便性、libbpf の柔軟性、Go 言語の安全性の両方を活用した bpf プログラムの書き方を紹介する予定です。