LinuxのBPFで何ができるのか? BPFの「プログラムタイプ」とは:Berkeley Packet Filter(BPF)入門(4)(2/2 ページ)
Linuxにおける利用が急速に増えている「Berkeley Packet Filter(BPF)」について、基礎から応用まで幅広く紹介する連載。今回は、BPFでできること、BPFのプログラムタイプについて。
ネットワーク関連
ネットワークはBPFが最も多く利用されています。ネットワークスタック内のどこでBPFプログラムが実行されるか、簡単に示すと下図のようになります。
以降、具体的に見ていきます。
ソケット操作
BPFプログラムを利用して、ソケットに対して下記のような操作が可能です。
- パケットのフィルタリング
- ソケットの属性の変更
- リダイレクト
「BPF_PROG_TYPE_SOCKET_FILTER」がいわゆる昔から利用されてきたパケットフィルターです。カーネル内のネットワークスタックを通過したソケットのデータに対して、BPFプログラムを利用してフィルタリングすることが可能です。BPFプログラムの戻り値が有効なパケット長を意味します(0ならドロップ)。
「BPF_PROG_TYPE_SOCK_OPS」というプログラムタイプを利用すると、ソケットのコネクション確立やタイムアウトのときにBPFプログラムを呼び出せます。これと、「bpf_setsockopt()」という、「setsockopt()」を実行するヘルパー関数を組み合わせることで、接続状況に応じてソケットの属性を変更できます。
「BPF_PROG_TYPE_SK_SKB」では、「sockmap」という特別なBPFマップを利用してパケットを他のソケットにリダイレクトすることができます。これにより、ポート番号やパケットのデータに基づいたパケットのロードバランシングが行えます。
トンネリング
LinuxのL3ネットワークスタック内には「lightweight tunneling」というトンネリングを実施するための機能があり、これにBPFプログラムを利用することができます。
BPFプログラム内でパケット(skb)のカプセル化や脱カプセル化が行えます。これには「BPF_PROG_TYPE_LWT_IN」「BPF_PROG_TYPE_LWT_OUT」といったBPFプログラムタイプが関連します。また、最近ではIPv6のSegment Routing(SRv6)の実現にも利用されるようになっています(参考)。
帯域制御
Linuxには「tc」と呼ばれる帯域制御機構があります。この処理はL3スタック実行前に実施されます。この部分にもBPFプログラムをアタッチでき、パケットのドロップやリダイレクトをBPFプログラムで制御可能です。「BPF_PROG_TYPE_SCHED_CLS」「BPF_PROG_TYPE_SCHED_ACT」といったプログラムタイプが関連します。
XDP
XDPは最もNICに近い段階でパケットデータに対してBPFプログラムを実行します。デバイスドライバに強く結び付いており、カーネル内部のパケット管理データ構造(skb)を割り当てるよりも前にBPFプログラムを実行することで、高速なパケットのフィルタリングやフォワーディングを実現します。
このため、XDPの利用にはデバイスドライバの対応が必要になります。デバイスに非依存な「Generic XDP」と呼ばれる機能も存在しますが、ネイティブな対応よりも性能は劣ります。
代表的なユースケースとしてはロードバランシング(例:Facebookの「kataran」)やDDoS攻撃対策(CloudFlareでの事例)があります。
トレーシング関連
「kprobe」「tracepoint」「perf」といった、Linuxの従来のトレーシング機能が、BPFを利用することで、より柔軟に扱えるようになっています。「BPF_PROG_TYPE_PERF_EVENT」「BPF_PROG_TYPE_KPROBE」「BPF_PROG_TYPE_TRACEPOINT」のプログラムタイプが関連します。
これらを利用すると、下記のようなことが可能になります。
- 特定のパフォーマンスカウンタのイベントに応じてBPFプログラムを実行する(perf)
- 特定のカーネル関数呼び出し時にBPFプログラムを実行する(kprobe)
- 特定のカーネル関数内のイベント発生時にBPFプログラムを実行する(tracepoint)
BPFによるトレーシングの概要図を下に示します。
Linuxの「perf_event」という機能でパフォーマンスカウンタやkprobe、tracepointなどのイベントが抽象化されており、そのイベントにBPFプログラムをひも付けることが可能です。BPFプログラムはBPFマップにデータを記録できる他、ユーザー空間と共有のリングバッファーを利用することも可能です。
なお、他のBPFプログラムタイプであっても、BPFマップにはアクセス可能です。これを利用してトレーシングを実施できます。例えば、「BPF_PROG_TYPE_SOCKET_FILTER」でパケットフィルタリングをするBPFプログラムであっても、そのプログラム内でBPFマップにパケットの統計情報を記録することが可能です。
その他:赤外線デコード
Linux 4.18から、「LIRC」という赤外線を送受信するための機構において、プロトコルのデコードにBPFプログラムが利用できるようになっています。これには「BPF_PROG_TYPE_LIRC_MODE2」というプログラムタイプが利用されます。
このように、ネットワークやトレーシング以外でもBPFが利用されるようになっています。
seccompのBPFプログラムタイプがない理由
勘の良い方はseccompに関するプログラムタイプがないことに気付いたかもしれません。seccompはBPFを利用したシステムコールフィルタリング機能です。
seccompのBPFプログラムタイプがない理由は、seccompは歴史的いきさつから「eBPF」ではなく、「cBPF」を利用しているからです。seccompにおいて、BPFマップを利用してシステムコールの統計を取るといったことはできません。
seccompのBPFプログラムのロードは専用のシステムコール(seccomp(2))を利用します(なお、内部でcBPFプログラムはeBPFプログラムに変換され実行されています)。
システムコールの統計を取りたいのであれば、kprobeやtracepointでシステムコール関数をフックすることができます。「seccompでeBPFを利用するようにしよう」という話は前からあるので、将来的に変わる可能性はあります。
まとめ
本稿ではLinuxで利用されるBPFについて、主にBPFプログラムタイプの観点から説明しました。
次回はネットワークに関して、BPFプログラムを作成してさまざまな機能を試します。
参考文献
筆者紹介
味曽野 雅史(みその まさのり)
東京大学 大学院 情報理工学系研究科 博士課程
オペレーティングシステムや仮想化技術の研究に従事。
- メール:misono(at)os.ecc.u-tokyo.ac.jp
- ブログ:http://mmi.hatenablog.com/
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 2018年上半期に話題になったSpectreとその変異、Linuxカーネルでの対応まとめ
連載「OSS脆弱性ウォッチ」では、さまざまなオープンソースソフトウェアの脆弱性に関する情報を取り上げ、解説していく。2018年の上半期は、「Meltdown」「Spectre」とその変異(Variant)の脆弱性に悩まされた。今回はいつもとは異なり、上半期のまとめも兼ねて、Meltdown/Spectreの各変異をバージョンを追いかけながら整理する。 - 2017年版Linuxカーネル開発レポート公開――支援している企業トップ10とは?
The Linux Foundationは2017年版Linuxカーネル開発レポートを公開した。Linuxカーネル4.8から4.13までの開発に焦点を当て、カーネル開発に携わった開発者や変更数などについて言及した。 - Linuxカーネルのソースコードを読んで、システムコールを探る
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。前回まで、printf()内の中身をさまざまな方法で探り、write()やint $0x80の呼び出しまでたどり着いた。今回は、さらにその先にあるLinuxカーネル側のシステムコールを見ていく。