任意にsk_を拡張する方法buffカーネルを再コンパイルしない


Linuxネットワークを游ぶ同好はきっと任意にネットワーク処理ロジックの行为を定义することができることを望んで、プロトコル処理のハイエンドのカスタマイズと言えて、最も明らかな方法は构造体の中で1つのフィールドをプラスして、事実上Linuxの1つの入口のフローコントロールのパッチIMQはこのようにして、それは简単にLinuxカーネルのsk_を修正しましたbuff構造体の定義、フィールドの追加、IMQで使用されるフィールドの追加、カーネルの再コンパイル...      カーネルを再コンパイルすることで、いつもニーズを満たすことができますが、悪夢自体はカーネルを再コンパイルすることです!私は特にカーネルを再コンパイルするのが嫌いです.貴重で限られた時間を浪費しているからです.また、このようなやり方はあまりにも挿入されず、場面が大きすぎます.Linuxカーネルコードはいつオブジェクト向け言語のように任意に拡張できますか?はい、LKMは可能ですが、ネットワークプロトコルスタックはコア機能としてLKMによって実現されるわけではありませんので、再コンパイルするか再コンパイルするかを除きます.      最近1冊の《JAVAプログラミング思想》を買って、ずっとこの本がいったいどんな思想を体現しているのかに悩んで、たくさん読んで、少し考えがあって、対象、対象の思想はこの本の核心で、注意して、多態ではありませんて、対象自身です.オブジェクトの利点は、拡張性、完全な拡張性にあります.だからこの考えに従って、私は一労永逸の仕事をして、sk_buffは拡張可能です.      Linuxカーネルは構造体の拡張を常に考慮しているため、多くの構造体にはprivate_というものが含まれています.dataのフィールドは、privateやprivなどとも呼ばれます.実際、ほとんどのC言語で書かれたフレームワークは、file構造体のような類似の思想を採用しています.
struct file {
....
    /* needed for tty driver, and maybe others */
    void            *private_data;
....
};
より一般的には、このフィールドは構造体の最後のフィールドとして存在し、OOの考えに近いが、privateフィールドは構造体内部の拡張、すなわちオブジェクトの属性拡張としてタイプ自体の拡張ではなく、オブジェクトの属性拡張として機能するため、永遠に達成できない.何と言いますか.オブジェクトを作成した場合:
struct file *f = ....;
f->private_data = ...;

私が誰に与えた値を見てみましょうか.fに与えられた値です.fはタイプではなく、オブジェクトです.この意味では、タイプは何ですか.struct fileです.file構造体の拡張が必要です.OOの考えによれば、私はそうすべきです.
struct txtfile_extends_file {
       struct file f;
       void *private;
};
そうしてfileにprivateを直接追加します.dataよりも、OOの思想を体現しています.file構造体にprivateフィールドを直接追加すると、fileインスタンスを取得すれば誰でも置換および解釈できますが、struct fileタイプが拡張されている場合は、タイプの定義者とクラス定義を理解している人だけが操作できます.例として次のようになります.file構造体にprivateを直接追加すると、次の値が合法的になります.
f1->private = a;
...
f1->private = b;
は、頻繁にロックされない限り、fileの拡張が不安定であり、任意の場所で再定義することができる.逆にstruct fileの定義を拡張すると、
struct txtfile1_extends_file {
       struct file f;
       void *private1;
};
および
struct txtfile2_extends_file {
       struct file f;
       void *private2;
};
は2つのタイプで、いつでもtxtfile 1のオブジェクトからprivate 2フィールドを参照するのは違法です.もちろん、上記の場合は強制タイプ変換で目的を達成することができますが、C言語自体はタイプチェックに厳しくありません.メモリ操作によって人に告げられない目的を達成することもできますが、コンピュータの世界の何事もメモリ操作によって行うことができます.もしあなたが技術を誇示するためにそんなに原始的な操作を採用しなければならないなら、それは大丈夫です.
      考えを理解してから、私たちはコードの修正に着手することができて、とても簡単で、もっと通用するために、私たちはただ1つのポインタを拡張することを望んで、ポインタは本当にC言語の1つの芸術で、それは1次元のメモリを多次元に拡張することができて、それはいかなるものを指すことができます.しかし、skbを割り当てるとき、そのポインタがどこを指しているのか、具体的なタイプも分からないので、メモリを予め割り当てるだけでいいので、メモリが価値がない今日、複数のバイトがいい(多くの弊害は資源が乏しい段階から受け継がれている.例えば、倹約など).
void __init skb_init(void)
{
    skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
                          sizeof(struct sk_buff) + sizeof(char *),
                          0,
                          SLAB_HWCACHE_ALIGN|SLAB_PANIC,
                          NULL);
    skbuff_fclone_cache = kmem_cache_create("skbuff_fclone_cache",
                        (2*(sizeof(struct sk_buff) + sizeof(char *))) +
                        sizeof(atomic_t),
                        0,
                        SLAB_HWCACHE_ALIGN|SLAB_PANIC,
                        NULL);
}
これにより、skbを拡張することができます.たとえば、次の構造体を定義します.
struct data_extends_skb {
       struct sk_buff skb;
       char *info;
};

PREROUTINGで呼び出す:
struct data_extends_skb *des = (struct data_extends_skb *)skb;
strcpy(des->info, "abcdefg", 6);
は、その後、infoを任意の後続の場所で取り出すことができる.しかし、2番目の実行スレッドが発生した場合、構造体data_が分からなくてもinfoの付与値を完全に変更できるという問題があります.extends_skbの定義は、infoフィールドの具体的な名前も知らない.
memcpy(skb + 1, 0, sizeof(char *));
もコード作成者の本意をキャンセルします.もちろん、これはC言語では仕方がありません.
Linuxプロトコルスタック伝達skbの不合理性
Linuxは完全に合理的ではなく、OOの思想でプロトコルスタックを再構築して実現したい場合は、既存のフレームワークが適切ではありません.
1.skbは割当て後に再定義できない
skbは、パケットがプロトコルスタックに入った後に一度だけ割り当てられ、その後プロトコルスタックから離れるまで、そのデータポインタを移動するだけで今どのレイヤに着いたかを示し、どのレイヤの処理関数でもskbの構造体自体は変更できない.この考え方は実際には最初に効率のために導入されたもので、『TCP/IP詳細解(第2巻実現)』を見たことがあると、以前のUNIX mbufはまったく使われていなかったことがわかります.実際には、mbufメカニズムは各層で再定義されており、効率は低いように見えますが、今日は本物に戻ると、OOの考えを体現しています.
      Linuxのskbが一度だけ割り当てられることは、skbデータの指示に基づいてskbをルーティングするか、skb内部のフィールドの値を変更するしかないことを意味しますが、skb自体を変更することはできません.つまり、現在のskbを解放してから、新しいskbを割り当てて代わりにするか、古いskbの内容を新しいskbにコピーして、新しいskbに新しいものを追加することはできません.
2.LinuxのNetfilterフレームワークはLinuxプロトコルスタックの処理方式を完全に継承している
NF_HOOK戻り値はプロトコルスタックの戻り値、すなわちint型の値であり、成功または失敗を表す.でもどうして?
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)           \
({int __ret;                                       \
if ((__ret=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) == 1)\
    __ret = (okfn)(skb);                               \
__ret;})
は、以下のように完全に定義できます.
#define NF_HOOK_THRESH(pf, hook, skb, indev, outdev, okfn, thresh)           \
({struct sk_buff  *__skb2, __ret;                                       \
if ((__skb2=nf_hook_thresh(pf, hook, (skb), indev, outdev, okfn, thresh, 1)) != NULL)\
    __ret = (okfn)(__skb2);                               \
__ret;})
....// nf_hook_thresh   
そうすれば、Netfilterでskbの再定義を実現することができます(私はまだプロトコルスタック全体にメスを入れる勇気がありません.変更する場所が多すぎます.やはりNetfilterだけにメスを入れましょう).これは非常に意味があり、OO思想の向上に迎合しています.つまり、どの特殊なクラスのオブジェクトもより一般的なベースクラスのオブジェクトに転換することができます.操作しているように見えるのはsk_buffオブジェクトは、実際にはベースクラスオブジェクトにすぎません.Netfilterのhookでは、次のことができます.
struct sub_extends_skb {
       struct sb_buff skb;
       type1 v1;
       type2 v2;
       type3 v3;
       ...
};

struct sub_extends_skb *sub_construct (struct sb_buff *skb)
{
       struct sub_extends_skb = _k_malloc_and_skb_copy(sizeof(sub_extends_skb), skb);
       ses->v1 = ...;
       ...
       return ses;
}
struct sub_extends_skb *ss;
if (...) {
       ss = construct(skb);
} else {
       ss = skb;
}
... // process
return ss;
skbのポインタを変更できる限り、skbのタイプを再定義できることを意味し、skbを拡張することが可能になることを意味します.      注意しなければならないのは、skbがいったいどんなタイプなのかを確定する必要があるとき、どうすればいいのかということです.OO言語に内蔵されているタイプチェック、反射などのメカニズム、例えばJAVAのinstance ofマクロなどは、Linuxカーネルという下位Cで書かれたコードの中で、どうすればいいのでしょうか.C言語には高度なOO言語の様々な内蔵特性がありません.これはプログラマー自身がこの問題を解決する必要があります.例えば、元のskbにv 2の値を取ることはできません.これは保証できません.以下のコードを書くことができます.
char *p = (char *)0;
*p = 'a';
...
高度な言語ほど、言語やコンピュータのメカニズム自体に関心を持たず、自分のビジネスロジックに専念していますが、コンピュータを本業とするシステムプログラマー、またはサイバーセキュリティシステムプログラマーは、高度な言語を使わないほうがいいです.あなたのビジネスロジックは、コンピュータやネットワークが示すカラフルで複雑なものを解決することです.スーツを着た人たちと戦うのではなく...これも一つの考えです!