PACKET_RESERVE なる setsockopt(2) オプションを知った

tcpdump(1) および libpcap(3) のソースをちょこちょこ読み込んでいる.LinuxTCP/IPには色々知らないオプションがあるから,用例として勉強しているのだ.諸事情により仕事のやる気はすっかり減衰したが,それでも仕事をきっかけにTCP/IPで学ぶことはたくさんある.
今日も strace tcpdump -i eth0 -nne tcp など眺めていたら以下の2行が目を引いた:

getsockopt(3, SOL_PACKET, PACKET_RESERVE, [4], [4]) = 0
setsockopt(3, SOL_PACKET, PACKET_RX_RING, {block_size=131072, block_nr=31, frame_size=65616, frame_nr=31}, 16) = 0

2行目は "Linux zero copy networking" などでぐぐるとヒットするアレで,IPソケットの裏にあるカーネル空間のリングバッファ(NICがDMA書き込みするターゲットそのもの)をユーザ空間へと mmap(2) して直接に読み込みアクセスするためのオプションだが,1行目は聞いたことなかった.

どうやらリングバッファへDMAする毎に隙間を開けるものらしい.「後からVLANタグを作ったりするのに役立つ」とあるが,つまり PACKET_RX_RING を利用して mmap(2) してきた領域はユーザ空間から書き込みしてもオッケーってことなんだね.まぁそりゃそうか.ふむむ,これは夢が広がるぞ.
TCPはストリーム指向だからアプリ層での「1メッセージ」が複数のIPデータグラムに泣き別れしている可能性があり,recv(2) で普通にユーザ空間にコピーする限りそれは隠蔽されるが,そのオーバヘッドを嫌ってzero copyに手を出すと泣き別れした「1メッセージ」の再構築は自分でやらねばならない.そのときに泣き別れの後半のEtherヘッダ,IPヘッダ,TCPヘッダを読み終えた後ならそこらに直接 泣き別れの前半を上書きしてやれば効率よさげ.ただ流石に前半を収めるのに十分な隙間が空いてないとまずいから(リングバッファの0 byte目より前に書き込んでしまうかもしれない),この PACKET_RESERVE で予約しておけばよかろう.よし今度 実験しよう.(と言うかまだ PACKET_RX_RING / PACKET_TX_RING で具体的にどういう(物理的に連続な)メモリが割り当てられるのか理解してないから,先は長いが.)
はぁーしかし本当にユーザ空間TCPに手を出すのかしら.趣味なら興味を惹かれるネタだが,仕事でそこまで手を出したいかと言うと保守性など色々あるから避けて通りたい.