NanoBSDで WANエミュレータ

最初はライブカメラを作るつもりで準備をしていたのだが、 ひょんな事からちょっと脱線して WANエミュレータを作ってみることにした。
下調べの結果、仕込むべきネタは dummynet + ipfw + bridgeで、 ipfwが dummynetのさまざまなパラメータを渡すための UIになっているみたい。
今回は帯域(bandwidth)と遅延(delay)を制御できればとりあえず OKなんだけど、 パケットロスの確率(plr)とかも制御できたりするらしい。

例によってググった結果、 とてもわかりやすい解説 (感謝!!) が見つかったので、それを元に NanoBSD特有の事情を組み合わせて作ってみた。

実は自分にとっての今回のポイントは、 ↑のページのおかげで、ブリッジの作成そのものよりも、 ブリッジ用にカスタマイズした NanoBSDのイメージを、 動作中の NanoBSD boxにインストールする環境を作るところだった。

以下、備忘録としてその手順を。

まずは母艦にて、カスタムイメージを作成。
通常のカーネル再構成と同様、 /usr/src/sys/i386/conf/GENERIC をベースに自分用のカーネルの構成をする。
今回は GENERICを NT7001という名前でコピーし、 Bridge/dummynet/Firewall作成用に以下のオプションを追加した上で、 カーネルの軽量化も狙って、 NT7001で使われる rl以外の各種の NICや、 SCSI/RAID関連などの不要なデバイスをざくざくコメントアウトしていく。

options    IPFIREWALL
options    IPFIREWALL_DEFAULT_TO_ACCEPT
options    DUMMYNET
options    BRIDGE
options    HZ=1000

次に、この構成を使って NanoBSDカーネルを作るため、 nanobsd.sh用のカスタムコンフィギュレーションファイルを以下の内容で作成。

NANO_MEDIASIZEに渡している値は、以前 NanoBSDで調べた手持ちの CF用の値。

NANO_MEDIASIZE=`expr 1002258432 / 512`
NANO_NAME=nt7001
NANO_KERNEL=NT7001
NANO_SRC=/usr/src

CONF_BUILD='\
NO_KLDLOAD=YES\
NO_PAM=YES\
'

COMF_INSTALL='\
NO_ACPI=YES\
NO_BLUETOOTH=YES\
NO_CVS=YES\
NO_FORTRAN=YES\
NO_HTML=YES\
NO_LPR=YES\
NO_MAN=YES\
NO_SENDMAIL=YES\
NO_SHAREDOCS=YES\
NO_EXAMPLES=YES\
NO_INSTALLLIB=YES\
NO_CALENDAR=YES\
NO_MISC=YES\
NO_SHARE=YES\
'

CONF_WORLD='\
NO_BIND=YES\
NO_MODULES=YES\
NO_KERBEROS=YES\
NO_GAMES=YES\
NO_RESCUE=YES\
NO_LOCALES=YES\
NO_SYSCONS=YES\
NO_INFO=YES\
'

cust_allow_ssh_root
### cust_install_files

そして、初回の作成時と同様、

# cd /usr/src/tools/tools/nanobsd
# sh nanobsd.sh -c myconf.nano

でイメージ作成。実際のイメージは、 /usr/obj/nanobsd.nt7001/ の下に作成される。

問題は、作成したイメージをどうやって CFにインストールするかだった。
もちろん、初回に作成した時のように、"_.disk.full" を丸ごと ddで CFに書き込んでもいいのだが、 いちいちケースを開けて CFを取り出したりするのは面倒だったし、 何よりその方法だと、すでに動作している環境を丸ごと消してしまうことになる。
すでにユーザを作成したり細々とした設定を入れてあって、 それらを消したくなかったので、 今回は設定を残したままネットワーク越しにアップデートする("_.disk.image"を使う) 方法にチャレンジしてみた。

ふつうに考えると、 すでに OSが動作しているパーティションに別のディスクイメージを重ねて 書くというのは相当無謀な所業だが、実は NanoBSDは、CFを

/dev/ad0s1a
/dev/ad0s2a
/dev/ad3s

という 3つのパーティションに分けて使っていて、 ad0s1aと、ad0s2aは、まったく同じ大きさのパーティションで、 実際には使うほうだけが read onlyでマウントされる。 (使われない側はマウントもされない。 逆に言えば、CFの半分近い容量は常時遊んでいることになる。)
そして、この、使われていない方のパーティションにイメージを書き込み、 そちらから bootできるようにしてくれるスクリプトが用意されている。 (/usr/src/tools/tools/nanobsd/Files/root/updatep[12])

ただ、これらのスクリプトは、 何も指定せずに nanobsd.shを実行した場合には、インストールされない。
実は動作中の環境はまさにそういう状況だったので、 手動でコピーしてやる必要があった。
また、アップデートの際には、"_.disk.image"を転送しつつ、 これらのスクリプトを母艦側から NanoBSD側の root権限で実行する必要があり、 そのために、SSHで root loginを許可する必要があった。

それらの条件がクリアされていれば、あとは母艦側から、

# cd /usr/obj/nanobsd.nt7001
# cat _.disk.image | ssh nanobsd "sh updatep2"

を実行した後、NanoBSD boxを rebootして、無事にアップデートは完了した。

なお、ad3sは、実際に動作する環境の /etc にコピーされる、 各種の設定の実体が置かれるパーティションで、 こちらも通常はマウントされない。 (ただ、fstabには /cfg に rwでマウントできるような記述がある。)
稼動開始後の設定は、この /cfg/ の下にあるファイルに対して行ってから reboot、というのが一番単純な方法となる。
# 面倒なときは / を rwでマウントし直して、 直接 /cfg/ 配下から /etc/ 配下にコピーしてしまったりもアリかも。