linuxカーネルのIPフラグメント処理(1)
IPフラグメント処理
第一フラグメント便乗攻撃を調べるためlinuxのIPフラグメント処理を調べてみた。
長くなりそうなのでとりあえずinet_fragmentのメモ
ソースコード
inet_fragmentはプロトコルに依存しない処理を実装して、 ip_fragmentはipプロトコルに関わる処理を実装している
inet_fragment
ネットワーク層のプロトコルに依存しないフラグメント処理をしている。 主にやっていることは下記の3つ
- フラグメント可されたパケットの管理
- タイムアウト処理
- リミット処理
procによるパラメータの設定
ipv4の場合、タイムアウト処理とリミット処理の閾値は下記proc経由で設定可能
- /proc/sys/net/ipv4/ipfrag_time (フラグメント可されたパケットを最初に受信してから有効な秒数)
- /proc/sys/net/ipv4/ipfrag_high_thresh (メモリ使用量がこの値を超えるとフラグメント可されたパケットを受信できなくなる)
- /proc/sys/net/ipv4/ipfrag_low_thresh (メモリ使用量がこの値を超えると値を下回るまで削除が始まる)
カーネル内部でパラメータの格納はnetns_fragsで行われる
struct netns_frags { struct percpu_counter mem; // 現在使用しているメモリ量 int timeout; // フラグメントのタイムアウト int high_thresh; // 削除を開始するメモリ上限 int low_thresh; // 処理を中止するメモリ上限 }
この値はネームスペース毎に保持してあり、プロセスからアクセスするには
task_struct->nsproxy->net_ns->ipv4->frags
となる
フラグメント可されたパケットの管理
フラグメント可されたパケットはinet_fragsのハッシュで管理されており、値はinet_frag_queueとなる プロトコルに関わるところは抽象化されている 主なパラメータは下記の通り
struct inet_frag_queue { struct timer_list timer; // タイムアウトの設定 struct hlist_node list; // ハッシュリスト struct sk_buff *fragments; // 同一パケット内のフラグメント可されたパケットのリスト struct sk_buff *fragments_tail; // fragmentsの最後の要素 int len; // 現在格納されているパケットの最終位置 int meat; // 現在格納されてるパケットのバイト数 (最後のパケットが格納されていてlen == meatになると全て格納されたことになる) __u8 flags; // 下で説明 struct netns_frags *net; // 対象名前空間の総計と制限値へのポインタ }; struct inet_frags { struct inet_frag_bucket hash[INETFRAGS_HASHSZ]; unsigned int (*hashfn)(const struct inet_frag_queue *); // プロトコル毎のハッシュ値の計算 (必須) bool (*match)(const struct inet_frag_queue *q, const void *arg); // プロトコル毎の値比較(必須) void (*destructor)(struct inet_frag_queue *); // プロトコル固有の情報削除用フック(任意) void (*skb_free)(struct sk_buff *); // sk_buff削除時のフック(任意) void (*frag_expire)(unsigned long data); // タイムアウトまたはメモリ上限が超えて削除される時のフック(必須) struct kmem_cache *frags_cachep; // inet_frag_initで設定される名前がfrags_cache_nameでサイズがqsizeのスラブキャッシュ const char *frags_cache_name; // スラブキャッシュに使用される名前(必須) int qsize; // プロトコル固有データとinet_frag_queueデータの合計サイズ(必須) }
構造体自体は使用する側で確保して、必須となっているパラメータの設定を行い、
inet_frag_init
を呼ぶことにより使用可能となる。 inet_frag_init内ではhashの初期化とfrag_cache_name、qsizeからスラブハッシュを構築する
タイムアウト処理
IPフラグメントは各セグメント毎にタイムアウトが設定されるため、 inet_frag_queueにタイマーを持っている。
作成と同時にタイマーが設定され、 タイムアウト時の呼び出しはinet_fragsに設定されたfrag_expire関数が呼ばれる
inet_frag_create inet_frag_alloc setup_timer(&q->timer, f->frag_expire, (unsigned long)q); inet_flag_intern mod_timer(&qp->timer, jiffies + nf->timeout)
リミット処理
制限値を超えた場合は、カーネルのワークキューを使用してメモリの解放処理が行われている。
トリガーとなるのはソフトリミットを超えた場合で、ハードリミットを超えると処理を中断する。 ワーカースレッドが実行する関数は
inet_frag_worker
で、ソフトリミットを超えなくなるまでハッシュの要素を削除する。
expireが呼ばれる時のタイムアウトとリミット処理判断
expireはタイムアウトとリミット処理の両方で、リミットを超えて削除される場合は
INET_FRAG_EVICTED
フラグが設定される。 呼び出し元で判断を行いたい場合はこのフラグを参照する。