Netfilter/iptablesのいくつかの新しい進展

5887 ワード

Netfilterの最新の進展に注目して、新しいものは本当に多いですね.でも一番ドキドキするのは2つ.
一.新しいbpfモジュール
Linux kernel 3.9バージョンベースのpatchはxt_bpfのサポート、対応するiptablesモジュールはlibxt_bpf、これはiptables-1.4.19バージョンでサポートされています.その名の通り、bpfはバークレーパケットフィルタリングの略で、その説明については『BPF(BSD Packet Filter)--応用と理念の拡張』を参照してください.名称上、BPFはiptablesパッケージフィルタリングの第一選択技術であるはずだが、なぜかxt_tablesは常に独自のデータ構造を維持してruleのmatchとtargetを保存しており、現在BPFのサポートがあり、カーネルプロトコルスタックはiptablesルールを処理する際の効率が高くなると思います.従来の一連の複数のmatchは、bytecodeパラメータで示すことができ、コンパイルされたバイトコードであり、カーネルが直接実行するmatchにカプセル化されています.スピードが非常に速い.従来のmatchマッチングは基本的に遍歴であり、現在はbpfベースの遍歴ではなく、bytecodeを「実行」する!tcpdumpツールは従来BPFベースであるが、そのmatchは文法的にiptablesと極めて類似しているが、その整合効率はiptalesよりずっと高く、-i eth 2 tcp port 1234 and host 1.2.3.4のような整合はiptablesで4つのentryを確立する必要があるが、BPFを使用すると、以下の順序で実行するコードにコンパイルすることができる:1.devフィールド2をロードする.devフィールドを判断し、eth 2でない場合はx 3にジャンプする.プロトコルフィールド4をロードする.プロトコルフィールドを判断し、tcpでなければx...x.このコードはアセンブリコードに類似して返され、カーネル解釈によって実行される.しかし、私はkernel 2.6.32でコンパイルに成功しませんでした.このバージョンは古いので、多くのインタフェースと新しいカーネルが互換性がなく、長い間変更してやっとやっと実行できましたが、複雑なbytecodeを挿入することはできません.そうしないとpanicになります.いずれにしても、このBPFのアーキテクチャを採用すると、カーネル空間のコード実行効率が大幅に向上し、ipt_のようなコード量も大幅に減少します.do_tableというビッグマック関数も痩せるようになりました.
二.最新のnftablesプロジェクト
iptablesカーネルコードのダイエットといえば、Netfilterのウェブサイトには、既存のiptables/ebtables/arptablesおよび対応するv 6バージョンを完全に置き換えるためのnftablesプロジェクトという別の道が開かれています.nftablesの最も主要な革新は2つの点にあり、1つはコマンド構文の完全な変更であり、2つ目はカーネルコードの最適化である.BPFのようなフィルタリング方式を採用しており、matchesのマッチングプロセスはステータスマシン変換のプロセスであり、最終的な終了ノードはtargetである.コードレベルでは、matchストレージに対するiptablesの混乱した場面を完全に変更しました.以下は、一致するコアコードです.
//         , iptables              

struct nft_expr {

    const struct nft_expr_ops    *ops;

    unsigned char            data[];

};

struct nft_rule {

    struct list_head        list;

    struct list_head        dirty_list;

    struct rcu_head            rcu_head;

    u64                handle:46,

                    genmask:2,

                    dlen:16;

    unsigned char            data[]

        __attribute__((aligned(__alignof__(struct nft_expr))));

};



//net/netfilter/nf_tables_core.c



unsigned int

nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops)

{

    const struct nft_chain *chain = ops->priv;

    const struct nft_rule *rule;

    const struct nft_expr *expr, *last;

    struct nft_data data[NFT_REG_MAX + 1];

    unsigned int stackptr = 0;

    struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE];

    int rulenum = 0;

    /*

     * Cache cursor to avoid problems in case that the cursor is updated

     * while traversing the ruleset.

     */

    unsigned int gencursor = chain->net->nft.gencursor;



do_chain:

    rule = list_entry(&chain->rules, struct nft_rule, list);

next_rule:

    data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;

    list_for_each_entry_continue_rcu(rule, &chain->rules, list) {



        /* This rule is not active, skip. */

        if (unlikely(rule->genmask & (1 << gencursor)))

            continue;



        rulenum++;

               

        nft_rule_for_each_expr(expr, last, rule) {

            if (expr->ops == &nft_cmp_fast_ops)

                nft_cmp_fast_eval(expr, data);

            else if (expr->ops != &nft_payload_fast_ops ||

                 !nft_payload_fast_eval(expr, data, pkt))

                expr->ops->eval(expr, data, pkt);



            if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE)

                break;

        }



        switch (data[NFT_REG_VERDICT].verdict) {

        case NFT_BREAK:

            data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;

            /* fall through */

        case NFT_CONTINUE:

            continue;

        }

        break;

    }



    switch (data[NFT_REG_VERDICT].verdict) {

       //    

        case NF_ACCEPT:

    case NF_DROP:

    case NF_QUEUE:

        nft_chain_stats(chain, pkt, jumpstack, stackptr);



        if (unlikely(pkt->skb->nf_trace))

            nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);



        return data[NFT_REG_VERDICT].verdict;

    case NFT_JUMP:

        //stack        rule

                if (unlikely(pkt->skb->nf_trace))

            nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE);



        BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE);

        jumpstack[stackptr].chain = chain;

        jumpstack[stackptr].rule  = rule;

        jumpstack[stackptr].rulenum = rulenum;

        stackptr++;

        /* fall through */

    case NFT_GOTO:

        chain = data[NFT_REG_VERDICT].chain;

        goto do_chain;

    case NFT_RETURN:

        if (unlikely(pkt->skb->nf_trace))

            nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN);



        /* fall through */

    case NFT_CONTINUE:

        break;

    default:

        WARN_ON(1);

    }



    if (stackptr > 0) {

        if (unlikely(pkt->skb->nf_trace))

            nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN);



        stackptr--;

        chain = jumpstack[stackptr].chain;

        rule  = jumpstack[stackptr].rule;

        rulenum = jumpstack[stackptr].rulenum;

        goto next_rule;

    }

    nft_chain_stats(chain, pkt, jumpstack, stackptr);



    if (unlikely(pkt->skb->nf_trace))

        nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY);



    return nft_base_chain(chain)->policy;

}

新しいnftablesダイエットの原因は、コールバック関数を大量に採用し、判定ロジックを独立させ、コアのdo_tablesは単純なステータスマシンになりました!この抽出動作がもたらす効果はruleがより柔軟になったことであり,BPFの考え方のようにパケットは一歩一歩の結果に応じて異なるruleまたは異なるmatchの間で任意にジャンプすることができる.iptablesのマッチング過程での大量の判断に比べて、結果はハードコーディングで、nftablesは確かに質的な飛躍があり、nftablesが早く発表されることを期待しています.