SORACOM JunctionとeBPFでパケットキャプチャしてISPのようにトラフィック分析しよう(立志編)
今日はeBPFについて書いてみようと思います。
いくつかのDisclaimar
- これはSORACOM Adevent Calendar 2023の記事です
- わたしはこの記事の執筆現在、株式会社ソラコムで働いていますが、このブログ記事は会社を代表する意見やコメントではありません。いちユーザーとしてこんな感じのことをSORACOM使ってやってみようぜという記事です。
この記事はSORACOM JunctionとeBPF使うと、まるでISPのように、自分が管理するSIMトラフィック解析ができるよ!とドヤ顔で話すための試行錯誤とその失敗(いまのところ)の軌跡です。
eBPFってなに?
さて本題に戻ります。みなさま、eBPFってご存知ですか?eBPFとはカーネルの様々なイベント(例えばSyscall、カーネル内の関数が呼び出されたとき、パケットがNICに到達したとき)にフックして、任意のバイトコードを実行できるサンドボックスです。Kernelを直接変更したり、Kernelモジュールを書いたりすることなく、安全にKernelを拡張することができることが特徴です。代表的な事例としてはトレーシングやロードバランサー、DDoS Mitigationなどがあげられます。
例えばCloudflareはエッジサーバー内において、DDoS対策のためにルーターから届く毎秒2000万のパケットから意味のある10万のパケットのみをフィルタしてアプリケーションに届ける、というパイプラインを構築しています。毎秒2000万ですよ?すごくないですか?
なんでこんなに速度が出るかというと、カーネル内で任意のコードを(安全に)動かせるため、ユーザーランドへのメモリコピーやコンテキストスイッチングを避けることができます。また、例えばXDPというフックポイントを利用するとOSのネットワークスタックより前に処理を差し込めたり、NIC内でその処理を実行したりすることもできてしまいます。
下の画像はXDPとOSのネットワークスタックについての関係性について説明している図で、XDPのフックポイント(XDP eBPF)が左下のパケット到着直後に存在しているのが見て取れると思います。例えばここでeBPFプログラム内でXDP_DROPという結果を返すと、このタイミングでパケットを捨てることができてしまうのです。すごくないですか?
画像出典: Express Data Path - Wikipedia
Cloudflareがそれをどうやって活用しているかについては下記のスライドとブログを読んでいただくとどんなことをやっているのか、またそれをどうやって実装しているのかというのがイメージ湧くと思います。とてもおもしろいのでぜひ。
わたしとeBPF
わたしがeBPFに興味を持ったのは、SORACOMでのトラフィック分析、より具体的に言えばゼロレーティングのためのトラフィック分析に利用できないか?というのがきっかけです。
ゼロレーティングとは、例えば「このSIMは月額980円でxxGB利用可能です!しかもLINEでの通信利用分は無料!」というアレです。アレを実現するためには、自分が管理するトラフィックについて、どことどのくらいの量の通信をしたのか、ということを把握できる必要があります。
SORACOMには、SORACOM Junctionというサービスがあり、自分のアカウント内のすべてのSIMの通信をミラーして任意の宛先に送信することができます。それを受け取って解析、一番シンプルには宛先ドメインごとに通信量集計をすればゼロレーティングが実現できそうですよね!
画像出典 - SORACOM Junction - SORACOM
ゼロレーティングとかDPI(Deep Packet Inspection)と呼ばれるような技術分野は、少なくとも私が知る限りはまだまだプロプライエタリな製品を使う以外の選択肢がなく、自分で実装したりするような事例というのは少ないのかなと思っています。そういう背景もあって、普段から「ゼロレーティング、自分で実装できたらかっこいいよな。ひとつのチャレンジテーマとして持っておこう」と思っていました。
そんななか、たまたまRustの勉強をしているときに下記の本を読んでいる中で出会った「Rustでパケットキャプチャ」という章において、Rustを使えばSocket APIよりも低レイヤなIPパケットが簡単に扱えることを知りました(すいません、ネットワークプロバイダのSORACOMのSAとしてそんなことも知らなかったのかよ、というツッコミは甘んじて受けます)。この本の第2章はpnetというクレートを使って、データリンク層から取り出したフレームを扱うという演習になっています。
そして、Rustのpnetで遊んでいるうちに「よりパフォーマンスを出すパケットキャプチャプログラムを書くにはどうすればいいだろう?」としらべているなかで出会ったのがeBPFだったというわけです。当時Rustを触っていたということもあり、AYAというRust向けeBPFライブラリを使ってeBPFでパケットキャプチャをやってみたという話をSORACOM UGで発表したりしました。(ほんとうちょっと触っただけ)
やってみた - SORACOM Junction + eBPF + BCCでパケットキャプチャ
今回あらためて、BCCというツールセットと、PythonとCを使って以下のようなデモプログラムを実装してみました。パケットはもちろんSORACOM Junctionでミラーリングした実際の自分のSIMの通信を利用します。全体像としてはこんなかんじ。
パケットを受け取るEC2の中はこんな感じになりました。
前回SORACOM UGで発表した時点ではAYAを使っていましたが、より情報の多いBCCのほうが書きやすかったとうこともあり、今回はこちらを使いました。
BCCとは、eBPFプログラムを実装、実行するためのツール群でこれを使うとPythonやluaでフロントエンド(ユーザーランドプログラム)を書けるようになります。例えば、eth0で受け取ったパケットをXDPのところですべて破棄するプログラムは以下のように書けます。これなら「おれも書けるな」という気持ちになりませんか?
現状と今後
ということで書き始めるところまではたどり着いたんですが、このブログを書いている朝まで動いていたでもプログラムが「何もしていないのに壊れてしまった」のです・・・本当はInfluxdbやAmazon Timestreamに入れたところのデモ画面くらいまでたどり着きたかったのですが・・・XDPにプログラムをアタッチするところがどうやらうまくいかなくなってしまって、いまはlibbpfやBCCそのもののコードをちょっと調べているところです。より低レイヤな部分を覗くせっかくの機会だと思って。。笑
ということで実際の動いているデモは冬休みの宿題としてまた新たにブログ記事にまとめられたらと思います。
SORACOM JunctionとeBPF使うと、まるでISPのようにトラフィック解析ができるよ!とドヤ顔で言えるようになる日までもう少しがんばります。