netimpのコード
昨日の続きで、netimpのコードを眺めている。まずは上下層とのインタフェースからざっくり全体像をつかんでみる。
netimp(以下、1822と呼ぶ)はネットワーク層プロトコルなので、下位層はデータリンク層であるネットワークインタフェース(NIC)になる。データリンク層とネットワーク層でパケットを授受するために、ネットワーク層プロトコルごとにインタフェースキューが用意されている。インタフェースキューの実装はstruct ifqueueで、IF_ENQUEUE、IF_DEQUEUEなどの操作マクロが定義されている。ここで関係するインタフェースキューは、NICの送信キューであるstruct ifnetのif_sndメンバ変数とimpintrq変数の二つである。
一方、上位層だが、netinet(TCP/IP)のように階層が多段の場合とは違って一階層なので、pr_input、pr_output関数をつかってやりとりすることはない。実際はrawソケットインタフェースで読み書きするか、IPパケットをカプセル化していた場合はIP層にパケットをキューイングし直すことになる*1。
ではoutput側のルーチンを見てみよう。上位層からのエントリポイントはimpoutput関数である。impoutput関数ではリーダ(ヘッダ)を初期化し、impsnd関数を呼ぶ。impsndではインタフェースの送信キューにパケットを挿入している。ポイントを抜粋する。
if (IF_QFULL(&ifp->if_snd)) {
IF_DROP(&ifp->if_snd);
m_freem(m);
}
IF_ENQUEUE(&inp->if_snd, m);input側はもう少し複雑だ。下位層からのエントリポイントはimpinput関数である。impinput関数ではコントロールメッセージであればすぐ処理する。通常メッセージの場合、それがIPデータグラムであれば、ソフトウェア割込みNETISR_IPをスケジュールしてパケットをインタフェースキューipintrqに追加する。1822メッセージであればソフトウェア割込みNETISR_IMPをスケジュールしてインタフェースキューimpintrqに追加する。ポイントを抜粋する。
switch (ip->il_mtype) {
case IMPTYPE_DATA:
switch (ip->il_link) {
case IMPLINK_IP:
schednetisr(NETISR_IP);
inq = &ipintrq;
}
}
if (inq == &impintrq)
schednetisr(NETISR_IMP);
if (IF_QFULL(inq)) {
IF_DROP(inq);
m_freem(m);
}
IF_ENQUEUE(inq, m);ソフトウェア割込みのエントリポイントはimpintr関数である。そして本関数ではキューimpintrqからメッセージを取り出してraw_input関数を呼ぶ。ポイントを抜粋する。
for (;;) {
s = splimp();
IF_DEQUEUEIF(&impintrq, m, ifp);
splx(s);
if (m == 0)
return;
raw_intput(...);ここまではあまりプロトコルに依存してないし、一般的な話だと思う。最近のBSDでも基本はあまり違わないのではないだろうか。