BCC(BPF Compiler Collection)によるBPFプログラムの作成Berkeley Packet Filter(BPF)入門(6)(1/2 ページ)

Linuxにおける利用が急速に増えている「Berkeley Packet Filter(BPF)」について、基礎から応用まで幅広く紹介する連載。今回は、BCC(BPF Compiler Collection)によるBPFプログラムの作成について。

» 2019年12月17日 05時00分 公開
[味曽野雅史OSSセキュリティ技術の会]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

 Linuxにおける利用が急速に増えている「Berkeley Packet Filter(BPF)」について、基礎から応用まで幅広く紹介する連載「Berkeley Packet Filter(BPF)入門」。今回はBPFプログラム作成のための代表的なライブラリである「BCC(BPF Compiler Collection)」を紹介します。

 BCCを利用することで、簡単にトレーシングやネットワーク処理のBPFプログラムを作成することができます。さらに、BCCにはBPFを利用したさまざまなトレーシングツールが含まれているので、一般のツール利用者にとってもBCCは有用です。

BPF Compiler Collection(BCC)とは

 BCCはBPFによるプログラム作成を支援するためのライブラリおよび、それを利用したツール群です。

 「IOVisor」というLinux Foundationのプロジェクトにより開発されています。BPFプログラム作成のためのライブラリは複数存在しますが、その中でもBCCは代表的な存在です。

 BCCのコア部分はC++で記述されていますが、同じ開発リポジトリ上でPythonおよびLuaのバインディングが開発されています。また、Go言語Rustバインディングもあります。一般にBCCはPythonから利用されることが多く、BCCリポジトリに含まれるツールも多くはPythonで記述されています。

 本稿でもPythonでBCCを利用します。

BCCの機能

 ライブラリとしてのBCCは以下の機能を提供します。

  • BPFプログラムを簡単に記述するためのModified C(BPF C)
  • BPF Cのコンパイル機能
  • BPFプログラムローダー
  • BPFマップへアクセスするための関数

 BCCでは、「BPF C」と呼ばれる、Cの方言でBPFプログラムを作成します。BPF Cは基本的にはC言語そのものですが、BPFマップの定義などのBPF固有の処理が書きやすくなっています。BCCのプログラムローダーは、「LLVM/Clang」を用いてBPF CのAST (Abstract Syntax Tree)を解析、変更した上でBPFプログラムにコンパイルし、カーネルにロードします。

 BCCでは主に以下のBPFプログラムの作成に用いられています(ただし、この限りではありません)。

  • トレーシング(「BPF_PROG_TYPE_KPROBE」「BPF_PROG_TYPE_TRACEPOINT」「BPF_PROG_TYPE_PERF_EVENT」)
  • ソケットフィルター(「BPF_PROG_TYPE_SOCKET_FILTER」)
  • XDP(「BPF_PROG_TYPE_XDP」)

コラム C言語以外によるBPFプログラムの作成

 BCCにはさまざまな言語によるバインディングが存在しますが、あくまでBPFプログラム自体はC(BPF C)によって記述します。このことは残念に思われるかもしれませんが、BPFプログラムは基本的にLinuxカーネル内のデータ構造にアクセスするので、カーネルと同じC言語の方がいろいろと都合の良いことが多いです。

 幾つか、C言語以外からBPFプログラムを作成する試みがあります。特にBPFによるトレーシング用途として「bpftrace」「ply」といったDTraceライクなDSLが開発されています。内部的には、bpftraceはLLVMおよびBCCを利用し、plyはLLVMを利用せず、直接BPFコードを生成します。

 またPythonのバイトコードを直接BPFプログラムに変換する「py2bpf」といったものもあります。

BCCのインストール

 多くのパッケージマネジャーがデフォルトでBCCを取り扱っています。こちらにディストリビューションに応じたBCCのインストール方法が記載されています。

 以下にUbuntu 18.04.3に対するインストール方法を記します。

公式のリポジトリからのインストール

 BCCは公式のリポジトリからインストールすることが可能です。

sudo apt-get install bpfcc-tools python3-bpfcc linux-headers-$(uname -r)

 「/usr/sbin/」以下に「-bpfcc」というpostfixでBCCのツールがインストールされます。

IOVisorのリポジトリからのインストール

 IOVisorのリポジトリを利用すると、BCCのnightly-buildが導入できます。

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
echo "deb [trusted=yes] https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs)-nightly main" | sudo tee /etc/apt/sources.list.d/iovisor.list
sudo apt update
sudo apt install python3-bcc bcc-tools libbcc-examples

 「/usr/share/bcc/tools/」以下にBCCのツールがインストールされます。

 Ubuntuの公式リポジトリにあるものと、IOVisorが提供するもので名前が異なる点に注意してください。問題を避けるためにインストールするのはどちらか片方のものにするのがいいでしょう。

Dockerの利用

 Dockerを利用してBCCを試すこともできます。以下でBCCがインストール済みのコンテナを起動できます。

docker run -it --rm \
  --privileged \
  -v /lib/modules:/lib/modules:ro \
  -v /usr/src:/usr/src:ro \
  -v /etc/localtime:/etc/localtime:ro \
  --workdir /usr/share/bcc/tools \
  zlim/bcc

BCCインストールの確認

 以下のコマンドが成功すればBCCのpythonバインディングを利用することができます。

sudo python3 -c "import bcc"

BCCによるパケットトレースプログラムの作成

 ここからは、BCCを用いてBPFプログラムを作成します。

 BCCリポジトリの「examples」にBCCの使用例が、また「tools」にBCCを利用したトレーシングツールがあります。

 BCCのメインはカーネルトレーシングですが、ここでは前回に続いてソケットに対するBPFプログラムを作成してみます。カーネルトレーシングについては次回詳しく触れます。

 前回利用したパケットトレースプログラム(「sockex1_user.c」「sockex1_kern.c」)と同等のものを、BCCを用いて作成してみます。作成するBPFプログラムは、下図のようにソケットに対してアタッチされ、IPv4のプロトコルタイプ別に受信パケットサイズを記録します。

sockec1.pyプログラムの動作の概要

 以下にBCCによるプログラムを示します。BPF C固有の記述箇所に関しては★印を付けています。

  1. #!/usr/bin/env python
  2. # -*- cofing: utf-8 -*-
  3. from __future__ import print_function
  4. import ctypes as ct
  5. import os
  6. import subprocess
  7. import time
  8. from bcc import BPF
  9. prog = r"""
  10. // SPDX-License-Identifier: GPL-2.0+
  11. #define BPF_LICENSE GPL
  12. #include <uapi/linux/if_ether.h>
  13. #include <uapi/linux/if_packet.h>
  14. #include <uapi/linux/ip.h>
  15. #include <net/sock.h>
  16. #include <bcc/proto.h>
  17. // ★(1)BPFマップの定義
  18. BPF_ARRAY(my_map, long, 256);
  19. int bpf_prog(struct __sk_buff *skb)
  20. {
  21. int index;
  22. long *value;
  23. u8 *cursor = 0;
  24. if (skb->pkt_type != PACKET_OUTGOING)
  25. return 0;
  26. // ★(2)パケットデータへのアクセス
  27. struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
  28. if (!(ethernet->type == 0x0800)) {
  29. return 0;
  30. }
  31. struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
  32. index = ip->nextp;
  33. // ★(3)BPFマップのアクセス
  34. value = my_map.lookup(&index);
  35. if (value)
  36. lock_xadd(value, skb->len);
  37. return 0;
  38. }
  39. """
  40. PROTO = {
  41. "ICMP": 1,
  42. "TCP": 6,
  43. "UDP": 17,
  44. }
  45. def main(interface="lo", debug=0):
  46. # (4)BPFプログラムのロード
  47. bpf = BPF(text=prog, debug=debug)
  48. bpf_prog = bpf.load_func("bpf_prog", BPF.SOCKET_FILTER)
  49. # (5)BPFプログラムのアタッチ
  50. BPF.attach_raw_socket(bpf_prog, interface)
  51. my_map = bpf.get_table("my_map")
  52. devnull = open(os.devnull, "w")
  53. p = subprocess.Popen(
  54. ["/bin/ping", "-4", "-c5", "localhost"], stdout=devnull)
  55. for _ in range(5):
  56. # (6) BPFマップへのアクセス
  57. print("TCP {} UDP {} ICMP {} bytes".format(
  58. my_map[ct.c_int(PROTO["TCP"])].value,
  59. my_map[ct.c_int(PROTO["UDP"])].value,
  60. my_map[ct.c_int(PROTO["ICMP"])].value))
  61. time.sleep(1)
  62. p.wait()
  63. if __name__ == "__main__":
  64. import argparse
  65. parser = argparse.ArgumentParser()
  66. parser.add_argument("--interface", default="lo")
  67. parser.add_argument("--debug", default=0)
  68. args = parser.parse_args()
  69. main(args.interface, args.debug)

 このBPFプログラムは、受信したパケットについてIPヘッダのプロトコル番号別にパケットサイズを計数します。以下に実行結果を示します。

$ sudo python3 sockex.py
TCP 0 UDP 0 ICMP 0 bytes
TCP 0 UDP 0 ICMP 196 bytes
TCP 0 UDP 0 ICMP 392 bytes
TCP 0 UDP 0 ICMP 588 bytes
TCP 0 UDP 0 ICMP 784 bytes

 このように、Pythonバインディングを利用したBCCでは、BPF CでBPFプログラムを記述し、それ以外のBPFプログラムのロードやBPFマップのアクセスなどはPythonから行います。

 次ページで要点を説明します。

       1|2 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

スポンサーからのお知らせPR

Linux �ス�ス�ス�ス OSS 鬮ォ�ェ陋滂ソス�ス�コ闕オ譁溷クキ�ケ譎「�ス�ウ驛「�ァ�ス�ュ驛「譎「�ス�ウ驛「�ァ�ス�ー

髫エ蟷「�ス�ャ髫エ魃会スス�・髫エ蟶キ�」�ッ闖ォ�」

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。