BPFを用いた錆の使用(第3報)


私は錆が好きです.私は生態系が好きです、私はパフォーマンスが好きです、それは私にとって完璧なブレンドです.安全性の特徴と相まって、私には賢い旧友がいて、そうでなければ私にいくつかの壮大な問題を引き起こす可能性のある地域を案内している.

ではなぜ錆?Cは罰金
あなたが最後の記事で見たように、BPFプログラムはCでプログラムされます、しかし、なぜ我々はさびを使うことができませんでしたか?それはCと同じくらい低いレベルにすることができます.
私の心の中で最大の勝利は、C BPFの生態系と比較して巨大な可能性を持っているクレート生態系と一緒に達成することができる(私はできれば、これらの次のいくつかの記事を横切って改善することができるだろう人間工学)です.それは、VerifierのためにBorrowチェッカーがmootであると主張されることができました、しかし、私は全く同じ光でそれを見ません.確かに、検証者は特定のチェックを実施し、借り手チェッカーよりもはるかに厳格である
私たちは、錆の借入制度に傾くことから何かを利用することはできません.
もちろん、私が錆でより生産的であるという主観的な利益があります、そして、私がさびているサポートアプリケーション/ライブラリを書いているならば、C .

RedBPF
として、それは錆でBPFのコードを書くために専用の箱の数が判明する!多くは完全性と活動の様々な状態にある.私が見つけた箱のセットは、最も完全で、有望です redbpf コレクション.それは完璧ではないが、それは偉大な約束を示し、開発者は超友好的で有用です.このポストでは、私はデザイン決定に飛び込むことを望みますredbpf クレート.
あなたがパート1にすべての方法を覚えているならば、我々が最初に解決しようとしていた問題はネットワーク1でした、そこで、我々はパケットを検証して、ペイロードの部分に応じて複数のイングルポートに単一のイングルポートを多重化する必要がありました.

箱の中のwhats
The redbpf リポジトリはいくつかの木枠に分けられます:
  • bpf-sys : 結合を提供するlibbpf とBCCの一部を使用してbindgen
  • cargo-bpf : エーcargo 錆のBPFプロジェクトの設定と構築のボイラープレートを扱うサブコマンド
  • また、追加のバインディングを生成するライブラリと、開発用ローダも含まれています
  • redbpf : BPFプログラムのロードと相互作用のためのユーザ空間ライブラリ
  • rebpf-probes : RISTSにKprobe、Uprobe、およびXDPまたはソケットBPFプログラムを書くためのライブラリ
  • また、bindings モジュールbindgen 結合を生成するlibbpf
  • rebpf-macros : 使われるprocマクロを含んでいる手続き的なマクロ枠
    BPF関数とコードをラップするにはredbpf-probes
  • rebpf-tools : プロジェクトの生成cargo-bpf 構造コードの作り方を示す
  • 面白いことにbpf-sys のみ使用されますcargo-bpf and redbpf , ではなくredbpf-probes or redbpf-macros . I asked about this on Github そして、両方の理由が歴史的で、安定性のためであると言われました、しかし、現在、物事は改善しましたbpf-sys with libbpf . コードを調査している間、それが初期の少しの混乱を引き起こしたので、これは大きいです.
    いくぶん無視するredbpf-tools それはほとんどの例のコードとして.私はそれがexamples/ しかし、それは私がそれを移動しようとして計画しないだけの主観的な好みです.最後にredbpf-macros 重さによって使われるredbpf-probes , 以降redbpf-probes このポストのための我々の主な焦点である、我々もある程度procマクロに触れます.
    別のツールがあります. bpf-linker RedBPFコレクションの一部ではありませんが、RedBPFの最初の作成者によって最近書かれました.私はまだそれが非常に新しいので、まだそれを使用し始めていないが、私はそれがこのスペースの鍵になることは間違いない.

    第1部問題への例
    まず最初にRedbpfを使って問題を解決します.これは、将来の反復で解決策を対比し、デザインの変更と改善を発見/議論できるようになります.
    RubyでBPFコードを書くには、使いやすいです
    cargo-bpf (一部)redbpf 「プロジェクト」を設定し、開発ローダとして機能することもできます.
    経由でインストールすることをお勧めしますcargo install からgit リポジトリですが、まずLLVM 11やカーネルヘッダなどの必要な開発ファイルをすべて確認しなければなりません
    すべての必要なパッケージがインストールされたらcargo-bpf
    $ cargo install cargo-bpf --git https://github.com/redsift/redbpf
    
    サンプルプロジェクトを作成しますmplex :
    $ cargo new mplex
    $ cd mplex/
    
    我々のプロジェクトは、将来的にカスタムローダと少なくとも一つのBPFオブジェクトを含むでしょう.mplex 私たちの“アウター”のユーザースペースアプリケーションとローダのための場所の所有者になります.使いましょうcargo-bpf BPFオブジェクトを追加するには
    プロジェクトは、スタンドアロンバイナリの形になります.
    cargo-bpf これはカーゴの[[bin]] そして、BPFコードをコンパイルするために通過される特定の貨物機能を必要とします.あなたが走るときcargo bpf build これは、プロジェクトを検索し、バイナリリストを検索し、適切な貨物機能を有効にしてビルドします.このようにして、BPFプログラムとユーザ空間アプリケーション/ローダによって必要とされる依存関係を分割できます.
    最初に我々は使用されますがcargo-bpf ローダとして、我々はこのポストの重要な部分に集中することができます.

    xdp , tc , socket ?
    を、我々は我々の最初の決定ポイントにしていますBPFプログラムにどこでフックするか?我々はネットワークフックが欲しいということを知っています、そして、ソケット層はあまりに高いです、我々は着信ポートに影響を及ぼしたいです.私たちはTCを使用することができました、しかし、我々はingress側を必要とするだけです、そして、我々がパケットの一部を書き直すので、それはパケットメモリへの直接のアクセスを持つのが良いでしょう.

    XDP
    XDPは私たちの箱のすべてをチェックして、パケットを観察するか、突然変異する最も早い可能なポイントです.
    XDPに対する引数は、おそらく、私たちがたぶんネットワークスタックの上にパケットを渡しているかもしれないということです(突然変異の後)、私たちは、TC層にソケットバッファを割り当てることによって、本当に何も節約しません.
    XDPは通常、パケットをドロップ/リダイレクトしようとしているときに最適です.おそらく後のポストで私は戻ってくるとTCのバリアントを表示するので、我々は2つのソリューションを対照することができます.実際に私の仕事をうまくすれば、2つの解決策はあまりにも我々はこれらの木箱を改善している時間によって異なります.
    そうすればcargo bpf BPF実行可能ファイルを作成します.mplex_xdp :
    $ cargo bpf add mplex_xdp
    
    私たちのプロジェクトの構造には2つのバイナリがあります.mplexsrc/main.rs ) は最終的なユーザ空間ローダであり、src/mplex_xdp/main.rs が追加されました.

    It's going to feel like we just jumped from level 0 to 100 skipping all the steps in-between. But the purpose of this post is to discuss the current state of RedBPF networking, vs future improvements we can make. Not the specifics of how
    we can implement a program for the problem in part 1.



    現在のREDBPF XDP例
    生成された例を削除するsrc/mplex_xdp/main.rs 以下の単純化されたコードを使用して、set - out - outタスクを実行します.
    #![no_std]
    #![no_main]
    use redbpf_probes::{
        bindings::tcphdr,
        net::Transport,
        xdp::prelude::*,
    };
    
    program!(0xFFFFFFFE, "GPL");
    
    #[xdp]
    pub fn mplex(ctx: XdpContext) -> XdpResult {
        // only match TCP
        if let Ok(Transport::TCP(tcp)) = ctx.transport() {
            unsafe {
                // Only match destination port 5000
                if u16::from_be((*tcp).dest) == 5000 {
                    let d = ctx.data()?; // get the payload
                    let ds = d.slice(d.len())?; // turn the payload into a byte slice
    
                    // "Validation" ensure payload length is within the narrow window
                    let payload_len = ds.len();
                    if payload_len < 290 || payload_len > 294 {
                        // Drop packet if not
                        return Ok(XdpAction::Drop);
                    }
    
                    // Multiplex based on entity tag which is 3 bytes, at an offset of 
                    // 20 bytes into the payload
                    //
                    // Double slice means "skip 20 bytes, then return the next 3"
                    match &ds[20..][..3] {
                        b"600" => {
                            // re-write destination port
                            (*(tcp as *mut tcphdr)).dest = u16::to_be(5001); 
                        }
                        b"601" => {
                            (*(tcp as *mut tcphdr)).dest = u16::to_be(5002);
                        }
                        b"602" => {
                            (*(tcp as *mut tcphdr)).dest = u16::to_be(5003);
                        }
                        _ => return Ok(XdpAction::Drop);, // no matching tag means invalid; drop
                    }
                }
            }
        }
    
        // Allow to pass up the stack
        Ok(XdpAction::Pass)
    }
    
    警告:上記のコードはBPF検証器を正しく失敗させます.なぜなら、適切な制限チェックをしていないからです.これは実際にREDBPFネットワークを改善するために私をリードすることの一つです.したがって、この例では、解決策は非常に重要ではありません.
    しかし一般的に、我々はこれが同等のCより人間工学と読みやすさ(IMO)に関して飛躍的であることをすでに見ることができます.
    我々が我々の入って来るインターフェース(これを検証者を落ち着かせた後)でこれを走らせることになっているならば、我々は第1部で説明されるすべての問題が魔法的になくなったという事実を見ます.サーバーアプリケーションは、複数のポートが利用されていると考えているので、それは幸せです、そして、彼らはあまりにも幸せであるように、外部のソースシステムは単一のポート間でデータを送信するだけです.一方、この小さなXDPシムは、パフォーマンスのヒットなしで、一緒に夢中です.

    我々はより良いか?
    しかし、我々はされていない!我々は、標準的なさびのイディオムを使用してVerifierを幸せにすることができます、我々は我々がすでにそれを行うために錆を伝えることによって、それらのことをしているように見えるときに、単におしゃぶりを幸せにするフープを介してジャンプする必要はありません.There
    また、あまりにも多くですunsafe 私にとって、それは少しもあまりにも、現在サポートされている4つのプロトコルに結合されているので抽象化は少し短くなります.
    作者と話すことの後redbpf-probes , 彼らはネットワーキング/XDPが彼らの主な焦点でなかったと言いました、その代わりに、彼らはネットワーキングAPIが固くないか、それほど多くの考えがそれに入れられたように、彼らがより頻繁に追跡面を利用します.それは単に概念の証明として書かれた最初のコードの一部でもありました.
    それはオープンソースですので、我々はそれを助けることができるかどうかを見てみましょう!

    I reached out to the original author, and we've been going back and forth about potential changes. He's on board, and we're both super excited to see where we can take this!



    包む
    このシリーズの一部では、我々は、現在の実装に注意深く、または変更のためにマークしたい領域に深くダイビングを行います.