めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

PF_PACKETの仕組み

Software Design 2012年6月号の『「生」ネットワークプログラミング』が妙にツボにハマって面白かったのですが、さすがにあのページ数でPF_PACKETを語ると、ついていけない読者も多くて残念かも。

ということで、参考までに概要図を書いてみました。

C言語のソケットプログラミングをなんとなく勉強した方は多いと思います。ソケットは、カーネル内部のネットワークレイヤーとユーザ空間を結びつけるインターフェース(API)なわけですが、カーネル内部では、通常、上図(右側)の流れで受信パケットが処理されていきます。

  • 物理NICが受け取ったパケットは、デバイスドライバがL2プロトコルの処理をして、さらにL3プロトコルの種類(IPv4/IPv6などの違い)の判別まで行います。
  • 判別した結果に基づいて、適切なL3プロトコルのハンドラーが呼び出されて、L3プロトコルの処理をして、さらにL4プロトコルの種類(TCP/UDP/ICMPなどの違い)の判別を行います。
  • 判別した結果に基づいて、適切なL4プロトコルのハンドラーが呼び出されて、最後に、PF_INETの口を通って、ソケット経由でユーザプロセスに受け渡されます。

一方、PF_PACKETは、デバイスドライバがL2処理をした直後のパケットをこっそりコピーして、横流しする裏口として用意されています。Socketをオープンする際のお作法(呪文?)は次のとおりで、通常は、第一引数に「PF_INET」(もしくはAF_INET)を指定します。

sock = socket(PF_INET, SOCK_STREAM, htons(ETH_P_IP));

ここに「PF_PACKET」を指定することで、受信直後の生パケットをユーザ空間のアプリケーションで受け取ることができるようになります。これは、パケットのコピーを受け取っているだけなので、このパケットを受け取るべき本来のアプリケーションにもちゃんと同じパケットは届きます。

ちなみに、Linuxカーネルのネットワークレイヤーを詳しく解説した書籍がほとんどなくて、困っています。私がよく参考にするのは、Understanding Linux Network Internalsですが、リファレンス本としてはいいのですが、頭から読んで理解するには、ちょっと苦しい。。。よい書籍があればご紹介ください。