2006.09.12 ng_atmllc(4)で見るnetgraph(4)の使い方 netgraph(4) モジュールがたくさんあること, その品質は玉石混合なの は前回話した通りで, 実際のところ何をするのかさっぱりなものもあれ ば, マニュアルを見ても例がなくて途方にくれるものが多い。そうこう していくうちに見捨てられて…というようなある種の悪循環がなきにし もあらずではあるが…。 今回は ng_atmllc(4) に夢を見た私が, 七転八倒の末, 理解に至った経 緯についてメモっておく。 マニュアルが当てにならないならソース。というのがこの業界の原則な わけですが, netgraph も御多分に洩れず, /usr/src/sys/netgraph/ 以 下を参照することになります。 そこで ng_atmllc(4) が登場するわけですが, なぜ ng_atmllc(4) かと いうと a. Ethernet over ATM だとは知らなかった. b. IEEE802.3 with SNAP を実装してると思った. (少なくとも LLC は サポートしてる, 802.『1a』 SNAP というキーワードにだまされた:-) c. あまりでかいコードでない. むしろシンプル. d. netgraph も ng_atmllc も理解してなかった というのが理由となります(爆)。 さて, netgraph(4) モジュール全般で言えることですが, ng_atmllc(4) を含め, この手のマニュアルには HOOK について, 明記されています。 なんの説明はなくとも HOOK と netgraph(4) の原理(?)がわかれば動か せるという話なのですが, あまりにもしろーとには近寄りがたいものが あります。HOOK がわかれば netgraph(4) は万全なのでしょうが, まぁ 実際そうだとは思うようになりましたが…。まずは 1 から見て行くと しましょう。 ng_atmllc(4) を見るとどうやらフィルタ型のモジュールのように見え る(translates frams のくだり)。実際, 下記のように ether フックと atm フックがあって, 下から上がって来たパケットを ether なパケット に変換して上位層に渡すし, 上位層から降りて来たパケットを atm な パケットに変換してネットワークに流すようです(たぶん)。 ether upper | | +-----+-----+ +-----+-----+ | ng_atmllc | | ng_ether | +-----+-----+ +-----+-----+ | | atm lower つまり ether と atm にそれぞれに期待した流れでパケットが流れるよ う定義すればいいわけです。さて。それが問題。どうすればいいでしょ う。ここで ng_ether(4) モジュールをもってきます(あっさり言って るけど, ng_iface(4), ng_eiface(4)等試行錯誤の上の最後の結論:-)。 http://people.allbsd.org/~hrs/FreeBSD/fig.ng_ether.bmp によれば upper に接続したら, 接続した先に流れる(もちろん戻っても くる)し, lower に接続したら接続した先から流れてくるというどっか で(直上?:-)聞いたことあるような流れが実現できています。つまり upper に ether を lower に atm を接続してやればいいことに気がつ きます(下記図参照)。 上位層 | ether +-------------------+ upper | +-----+-----+ +-----------+ | ng_atmllc | | ng_ether | +-----+-----+ +-----------+ | atm +-------------------+ lower | 下位層 具体的には? 安直には ng_atmllc なノードを作成(ngctl mknode) し て接続(ngctl connect)してやればいいのですが, 先の説明でしたよう に ngctl mknode という命令はないので, ノード作成と接続を同時に 行います。 +------------+ | | # ngctl mkpeer rl0: atmllc upper ether | | +------------+ 読み方的には rl0:upper を atmllc:ether に…となりますが, これの 実行結果は, 名無しのノードが作成されて rl0:upper の先に ether が接続されます。 > rl0:upper.ether == 名無しのノード (これは atmllc:ether という意味ではない) この時点で下記のような接続状態となります。この状態では atm の先 に何もありませんので通信できなくなります(下に行かない, 下から戻 ってこない)。 上位層 | ether +-------------------+ upper | +-----+-----+ +-----------+ | ng_atmllc | | ng_ether | +-----+-----+ +-----+-----+ | | atm lower | 下位層 また, 名無しだと色々と面倒なので名前をつけてやります。 # ngctl name rl0:upper atmllc0 上位がつながったので, 下位の接続を行います。 +-----------+ | | # ngctl connect atmllc0: rl0: atm lower | | +---------+ これで接続完了。下記のように当初目論んでいた通りの接続を得ることが できます。 上位層 | ether +-------------------+ upper | +-----+-----+ +-----------+ | ng_atmllc | | ng_ether | +-----+-----+ +-----------+ | atm +-------------------+ lower | 下位層 netgraph(4) 的(ngctl dot 的)には下記のような図となります。 えぇい。わかりにくい。 orz 慣れてないというか。。。 +----------------+ / atmllc0 \ +----------+---------+ \ | / +--------+-------+ / \ +----+ +------------+ +-------+ |atm | / rl0 \ | ether | +----+ +--------+-------+ +------+ \ \ | / / \ +--+---+--+--+ / \ / \ / +-----+ +-----+ |lower| |upper| +-----+ +-----+ もしかしたら気がついたかも知れないけれど(私は今書いてる途中で 気がついた), 逆から接続しても問題ないことに気がつく。つまり, # ngctl mkpeer rl0: atmllc lower atm # ngctl name rl0:lower atmllc0 # ngctl connect atmllc0: rl0: ether upper ngctl dot で見ると図がえらくかわってるように見えるが, 実際には 左右対象になってるだけなのに気がつくだろう。 じゃあ atmllc:ether (表現的に ng なのに注意)から rl0:upper へ 接続する。というのは… # ngctl mkpeer atmllc: atmllc ether upper 残念ながら atmllc なノードがなくてできないし, そもそもこの upper ってどこの upper よ。ということで, このあたり自然に制限できてたり するので, うまいなぁと思う今日このごろである。 今回たまたまデータの流れる方向とフックを引っかける向きが(コマンド ライン上)同じだったので, 直観に反しないで設定できたけど, これが 逆向きの流れだと, 違和感ありまくりなのが注意。先の rl0:lower.atm な例がそれである。下から上に上がってくるはずだが, ngctl mkpeer での指定は上から下となる:-)。というわけで実際は正しいのだ。と肝に 命じて作業することとなる。 さて(「さて」が多いなぁ…), 最初に振った伏線はどこにいったのだろ か, そろそろ風呂敷を閉じるとしよう。上記のことに気がつくまで紆余 曲折があったわけで, あっさり気がついたわけではない。ng_atmllc.[ch] を読んで, printf(9) デバッグを駆使して流れを追ってはじめて理解で きたわけである。 ソースによれば ng_atm_newhook 関数で hook の登録を行っていること がわかる。priv に色々と登録してるようだが, この priv はノードに つき 1 個存在することがなんとなくわかる(ng_atm_constructor と いう名前なあたり, ひじょーにオブジェクティブ)。また, priv->HOGE がなかったら…というチェックを行ってるので, 二重登録はありえな いことになる(rl0:upper, rl0:lower ともに ether へつなぐとか)。 実際 printf(9) デバッグで追うと, ngctl mkpeer と ngctl connect 時に呼ばれていることが確認できる。それぞれ 1 個 priv に対して priv->ether と priv->atm に登録していたわけで…。 !! まさに!!である。パケットの流れは, 実際は ng_atmllc_rcvdata で制御してるわけだが, こちらも printf(9) デバッグで追うと, 例えば ngctl mkpeer rl0: atmllc upper ether 状態で ping を 打つと, ここにひっかかるのがわかる。 } else if (hook == priv->ether) { /* Add the LLC header */ M_PREPEND(m, NG_ATMLLC_HEADER_LEN + 2, M_DONTWAIT); : outhook = priv->atm; } そして, このブロックの最後で outhook = priv->atm; してると。 そこでまさに!!である。この関数の最後の方では, まさに outhook があれば…な処理をしている。 if (outhook == NULL) { NG_FREE_ITEM(item); return (0); } つまり ether のフックが成立して, atm のフックがあれば, そちら にパケットがフローすることを意味することに気がつくだろう。こ の瞬間に ngctl mkpeer rl0: atmllc upper ether ともう一つ必要 なのは ngctl connect atmllc0: rl0: atm lower であることに気が ついた。というのが実際。 逆の視点から見ると, パケットが上がってくると if (hook == priv->atm) { /* Ditch the psuedoheader. */ hdr = mtod(m, struct atmllc *); : if (ATM_LLC_TYPE(hdr) == NG_ATMLLC_TYPE_ETHERNET_NOFCS) { m->m_flags &= ~M_HASFCS; outhook = priv->ether; padding = 2; : } フレームタイプに応じて, 出口を決定しているのがわかる。lower から atm に入って, パケットがイーサフレームなら ether に抜ける。という 流れがここで確認できたわけである。 この流れを確認して, 設定してやって, tcpdump 取ってみたら…。思い っきり慄えました。次の瞬間泣きました:-)。 # ngctl mkpeer rl0: atmllc upper ether # ngctl name rl0:upper atmllc0 # ngctl connect atmllc0: rl0: atm lower # ping 255.255.255.255 & # tcpdump -nirl0 なんせ先頭に LLC ヘッダーが…。 orz この瞬間期待していたものと全然違うということに気がついたのさ。 ちゃんちゃん。 今回調査して, netgraph(4)のすごさ, というのを実感したわけですが 同時に期待した通りのものがあるとは限らないと気がつかされたわけ ですが, 自分で作ってみようかな。という程度には簡単に作れそうな 気がします。 というわけで, 次回は ng_ieee8023snap(4) モジュールの作成だ。 Don't miss it! (謎) Written by 重村法克