みんな大好き eBPF。 eBPF 自体は C で書くが、その Map を他の言語から操作することもよくあると思う。いわゆる Data plane / Control Plane 的な役割分担ってやつ。
Go だと https://github.com/cilium/ebpf を使えばいいが、 Python だとどうするか?
BCC は嫌
Python で eBPF を利用する、となると BCC の情報がたくさんでてくる。 BCC は素晴らしいプロジェクトで nfsdist などツールは使ったこともあるのだが、 eBPF を開発するには使いたくない。
というのも、 BCC は eBPF プログラムの管理を含めて行うことを前提としている。全部乗っかりたいならいいが、 bpftool の使い方知ってるし、コンパイルだって Makefile でやりたいし、他の言語からだって使うし、そこをフレームワークで管理したくない。 BCC の作法でプログラムを書くのも嫌。
上記 Issue にもあるように、program は別途 ebpf で管理して Map だけ触りたい、といったケースでは BCC の Map は利用できないらしい(簡素なプログラムを書けばいいらしい?が... https://github.com/iovisor/bcc/issues/3517#issuecomment-1146853533)。
仮にそれが解決されたとしても pip で入れられなくて apt で入れる必要があり、その結果 dist-packages に入るから brew とか別途入れた Python ではパスに追加しないと使えない *1 とか、 virutalenv では system-site-packages しないと使えなくて不便とかもある。 面倒すぎ。
System call という API
現時点ではあまり有力なライブラリもない *2 ので、ctypes を使って System call を直接打つようにする。 eBPF の System call は割といろんなことができて、 Map の操作も一通りできる。
System call 自体は普通に ctypes の作法で打てば動く。Map の軽い操作くらいであればコードもシンプルで済むが、一つ注意点がある。それは、操作に使う Map の fd は BPF_OBJ_GET を利用すること。
こうせずに普通に open した fd を渡してしまうと、 error 22 (EINVAL) が返ってくる。
ということで、今 Python から pinned な Map を触る楽な方法は、 BPF system call の binding を ctypes で書いて適宜 call する ことだと思う。