FreeBSD 4.7 を利用して、 1枚のフロッピーに全て収めた FreeBSD 1FD PPPoE Router を作った記録です。
この記録は、個人的な備忘録のつもりです。 これらの情報が原因で何らかの被害等を被っても、 私はなんら責任を負えません。 情報を利用するのは自己責任でお願いします。
なぜこんなものを作るのでしょうか。
1FD に収めるにはかなりの機能を削がなければなりません。 そのために、まず、要求を決めます。私の場合は以下のような要求です。
以下のような情報がヒットしますが、 現在では、あまり有効ではありません。
FD | (ufs フォーマット) | |
/ | (ルートディレクトリ) | |
/kernel.gz | gzipped カーネル | |
/mfsroot.gz | gzipped MFS ルートディレクトリ ディスクイメージ | |
/boot | (起動関係) | |
/boot/loader | kgzipped カーネルローダ loader(8) | |
/boot/loader.rc | loader 用設定ファイル |
あと、作業のために適当にディレクトリを作っておいてください。 ここの例では、ホームディレクトリの下に fdbsd というディレクトリを作ってます。
# cd; mkdir fdbsd
# cd /sys/i386/confkernel 設定ファイルの標準の GENERIC を適当に名前を付けてコピーします。
# cp GENERIC miniコピーしたファイルを編集します。 これは単なるカーネル再構築とやっていることはかわりません。 必要な機能のみを有効にしてください。
# vi mini (編集は省略)あとは普通に kernel の構築を行います。 但し、make install してはダメです。 あくまでフロッピーから起動するためのカーネルですので(^^;
正常に kernel のコンパイルが終了したら、 作業ディレクトリにコピーしておいてください。
そこで、実行バイナリを dynamic link では無く、static link にします。 static link とは、 プログラムにとって必要なライブラリ(の中にある必要なモジュールパーツ) を全て実行バイナリの中に、 コンパイル時に静的にリンクすることです。 こうすれば実行するときに別にライブラリは必要ありません。
しかし、複数あるプログラムを全て static link したら、必ずデータの重複部分が出てきます。 これは容量の無駄になります。
そこで登場するのが、crunchgen です。
crunchgen は必要となるプログラムをまとめ、 1つの実行バイナリにして、 静的にリンクするデータを一元化し無駄を無くすプログラムです。
crunchgen 自体は、各プログラムの内容を把握し、 変数がかち合ったりしないように配慮しつつ、 Makefile を作成することです。 正常な Makefile が作成されれば、make を実行するだけで、 指定したプログラムをたった1つの実行バイナリファイルにすることが出来ます。 これで出来たプログラムは呼び出される名前によって、 自分自身の動作を変化させます。
先に作成した作業ディレクトリに移ってください。
# cd ~/fdbsdcrunchgen はカレントディレクトリを作業ディレクトリとする Makefile を作成しますので、 さらにもう1つディレクトリを作ってください。そこで作業します。
# mkdir crunch; cd crunch次のようなファイルを作ります。 (内容は各自の要求に合うように変更しなければなりません。)
fdbsd.conf:
# crunchgen configuration file for 1FD router using ppp srcdirs /usr/src/bin srcdirs /usr/src/sbin srcdirs /usr/src/usr.bin srcdirs /usr/src/usr.sbin libs -lutil -ll libs -lm libs -lnetgraph libs -ledit -lcrypt libs -lcurses -ltermcap -lgnuregex libs -lkvm -lz -lmd libs -lipsec -lwrap -lalias -lcrypto # /bin progs kill ls mkdir cat hostname progs ln pwd rm sh ps test progs stty cp # /usr/bin progs netstat progs ln pwd rm sh ps test progs stty cp # /usr/bin progs netstat # /sbin progs dmesg init mount umount reboot progs ifconfig route ping progs newfs sysctl # /usr/sbin progs ppp syslogd arp dev_mkdb ln test [ ln sh -sh ln sh -u ln reboot halt ln newfs mount_mfs special ifconfig buildopts -DRELEASE_CRUNCH special ppp buildopts -DNOINET6 -DNOKLDLOAD -DNORADIUS -DNOI4B -DNO_OPENSSL
それほど難しい内容ではありませんので、 多分理解できると思います。 設定ファイルの詳しい書式についての説明は、 FreeBSD の日本語マニュアル crunchgen をご覧ください。(日本語マニュアルありがたい。FreeBSD jpman プロジェクト万歳!!)。
要約すると、
設定ファイルを作ったら、早速 crunchgen します。
# vi fdbsd.conf少し作業に時間がかかりますが、 最近のマシンで実行するなら10秒もかかりません。 特にエラーが出力されなければ成功です。
(編集は省略)
# crunchgen fdbsd.conf
Run "make -f fdbsd.mk" to build crunched binary.では、make を実行しましょう。
#
# make -f fdbsd.mkmake がエラーにならなければ正常終了です。 作成されたバイナリをテストしてみましょう。 作成されたバイナリに、 元のプログラム名を指定すればそのプログラムが実行されます。 本当にクランチバイナリが動作しているのか疑心暗鬼な方は、 chroot して実行してみるのも面白いかもしれません。
(途中長いのは全部省略)
cc -static -o fdbsd fdbsd.o kill.lo ls.lo mkdir.lo cat.lo hostname.lo ln.lo pwd. lo rm.lo sh.lo ps.lo test.lo stty.lo cp.lo netstat.lo dmesg.lo init.lo mount.lo umount.lo reboot.lo ifconfig.lo route.lo ping.lo newfs.lo sysctl.lo ppp.lo syslo gd.lo arp.lo dev_mkdb.lo -lutil -ll -lm -lnetgraph -ledit -lcrypt -lcurses -lter mcap -lgnuregex -lkvm -lz -lmd -lipsec -lwrap -lalias -lcrypto
strip fdbsd
#
# ./fdbsd ps PID TT STAT TIME COMMAND 201 p0 S 0:00.04 sh 554 p0 R+ 0:00.00 ps 182 v0 Is+ 0:00.00 /usr/libexec/getty Pc ttyv0 (省略) #
プログラムの選別が気に入らない、追加したいのが有る、 make に失敗した、などと言う場合は conf ファイルを編集し直して、 再び crunchgen します。そのときにはオプションが必要です。
# crunchgen -f fdbsd.conf
ここで注意すべき事があります。
crunchgen で作成した makefile では、 make world 等の時と同じディレクトリ (標準では /usr/obj/...) があればそちらを、無ければ、 各ソースコードがあるディレクトリで make を実行したときにオブジェクトが生成されるディレクトリ (通常はソースコードと同じ場所) に中間バイナリを出力します。 crunchgen -f や make -f fdbsd.mk clean をしても基本的に作成されたその中間バイナリは 削除・更新されません!! (Makefile を書き換えても make をやり直してくれないのと同じ)。 何か変だと思ったら、 プログラムのソースディレクトリで make clean してから、 やり直してください。
crunchgen で作成する実行ファイル名は設定ファイルの .conf サフィックスを取り除いたものになります。
なお、うまくいかない場合は、 FreeBSD インストールフロッピーディスクの kern.flp を vnconfig して mount して既にあるファイルを消すだけでも、 とりあえず同様のファイルができます。
必要と思われるディレクトリを作成します。
# cd /mntkernel は init プログラムとして以下のプログラムを呼び出そうとしますので、 /sbin または /stand ディレクトリは必須です。 (シンボリックリンクでも構いません。)
# mkdir -p etc sbin dev var mnt mnt2
# ln -s sbin bin
/sbin/init:/sbin/oinit:/sbin/init.bak:/stand/sysinstall
必要となるデバイスファイルを作成します。
# cd /mnt/dev # sh /dev/MAKEDEV std fd0 ttyd0 tun0 # mknod ttyv0 c 12 0 (仮想コンソール等を利用しないなら不要) # mknod ttyp0 c 5 0 (telnetd,sshd等を利用しないなら不要) # mknod ptyp0 c 6 0 (telnetd,sshd等を利用しないなら不要)
注意しなければならないのは、 デバイスファイルは「ファイル」の1つに過ぎないので、 1つのデバイスファイルに付き1つ inode を消費します。 フロッピーディスクでは、利用可能領域を増やそうとすると 逆に利用可能な inode の個数が減ります。 使いもしないデバイスファイルを大量に作るとそれだけで大量に inode を消費しますので、注意が必要です。 inode の残り個数は以下のコマンドで確認できます。
# df -i
先ほど作成した crunch バイナリを配置します。
# cp ~/fdbsd/crunch/fdbsd .クランチバイナリは呼び出された名前によって動作が変わりますので、 名前を変えてリンクを張ります。
なお自分はこの作業が面倒だったので perl の超簡易スクリプトを作りました。 (第1引数にリンク元ファイル名、 標準入力に crunchgen の設定ファイルを与えてください。 自動でリンクを張ります。)# ln fdbsd ls # ln fdbsd ln # ln fdbsd mkdir # ln fdbsd sh (fdbsd.conf の中で設定した progs の分だけ ln) #
もし FreeBSD 標準の init(8) を利用するなら、/etc/rc は必須です。 その他の rc.* は、/etc/rc に全てまとめてしまえば必要ありません。 出来るなら、/etc/rc でファイルシステムのマウント、 ネットワークやファイアーウォール設定、 デーモンの起動などを行うように自分で記述すれば、 ずっとすっきりしてサイズも小さくてすみます。
シリアルや telnetd からログインをしたいなら /etc/gettytab や /etc/ttys が必要です。自分の場合はコンソールからのログイン(というか login 自体ありません。起動後はすぐに sh のプロンプトです(汗) のみなのでこれらはありません。
/etc/master.passwd や、/etc/spwd.db, /etc/pwd.db は login など利用する場合は必要ですが、 サイズがでかいので起動後に MFS をマウントした後、pwd_mkdb で作成したりします。
ネットワーク系のコマンドを利用する場合は /etc/services, /etc/protocols, /etc/host.conf, /etc/resolv.conf などは必須といえます。/etc/services などはサイズがでかいので、 必要な分のみにして残りは削除したほうが良いでしょう。
後は動かすサービスによります。 inetd を使いたい場合は /etc/inetd.conf, syslogd を使いたい場合は /etc/syslogd.conf が必要です。 また、libwrap (tcpwrapper) をリンクしてあるプログラムは /etc/hosts.allow が必要になります。 どのライブラリがリンクされているかは、ldd コマンドでわかります。
/var を使いたい場合は、mfs の root 領域以外にさらに 起動後に /var も mfs にします。
# dd if=/dev/zero of=aa
# rm aa
# cd; umount /mnt
フォーマット、disklabel 書き込み、newfs、マウントを行います。
# fdformat /dev/fd0.1440 # disklabel -B -r -w fd0 fd1440 # newfs -c 1 -i 131072 -o space -m 0 /dev/rfd0.1440 fd1440 # mount /dev/fd0c /mnt
ディレクトリを作り、ローダ loader(8) を kgzip(8) してインストールします。
# mkdir /mnt/boot # kgzip -o /mnt/boot/loader /boot/loader
ローダ設定ファイル boot/loader.rc を以下のような内容で作成します。
これは、/kernel のほかに、 mfs_root というタイプにタグ付けされたモジュールを読み込みます。 mfs_root というタグ付けにより、カーネルは mfsroot(.gz) が MemoryDisk のイメージであると判断します。load /kernel load -t mfs_root /mfsroot autoboot 10
あとは作成したカーネルと MFS root filesystem ディスクイメージをコピーするだけです。 カーネルもrootファイルシステムイメージも gzip で圧縮してください。 /boot/loader が gzip されたイメージも正常に読み込んでくれます。
# cd ~/fdbsd # gzip -9c kernel > /mnt/kernel.gz # gzip -9c rootfs.img > /mnt/mfsroot.gz
サイズが大きすぎる場合は、 カーネル構築オプションから不要なオプションを削除したり、 クランチバイナリから要らないバイナリを取り除いてみたりしてください。
中には情報が多少古いものもありますが、 1FD で起動する FreeBSD 環境を作りたいときには十分参考になるものなので、 是非読んでみることをお奨めします。
これの利点は、DOS から BSDBOOT.COM を利用してブートするとき等に発揮されます。 BSDBOOT.COM ではタイプタグ付きでファイルのロードをすることが出来ないので、 MD_ROOT_SIZE オプション付きで構築した kernel の中にルートファイルシステムのイメージを埋め込んでおきます。 こうすれば kernel.gz しか読み込むことの出来ないローダでも、 正常に起動し、ルートファイルシステムをマウントすることが出来ます。
これを利用する場合の注意ですが、 起動後にルートパーティション以外をさらに mfs で作成しようとする場合、 今までは FD から起動でしたので mount_mfs -s 720 /dev/rfd0c /var のように FD の BSDディスクラベル情報を元に mfs を構築できましたが、 今度はそれが無理になったので mount_mfs -s 720 -T minimum /dev/null /var 等として /etc/disktab からの情報を元に mfs を作成しなければなりません。 (他にも方法はあります。)
FD にカーネルを書き込む場合は strip してから使いましょう。
しかし /boot/loader では bzip2 で圧縮したイメージを解凍する際にあまりメモリを確保できず、 普通に bzip -4 で圧縮したデータも展開することが出来ません。 実質 bzip2 -1 や bzip2 -2 位で圧縮したデータしか展開できないようです。
それでも bzip2 -1 で圧縮すると gzip -9 で圧縮したものより結果が良いことが殆どですので、 ディスクスペースが足りない方は挑戦してみる価値はあります。 (50KB 前後は利用可能領域が増えます。)
ついでですが、/boot/loader はコンパイル時のオプションで Forth 言語サポートを無効にするとサイズが 50KB ほど縮みますので、 こちらもあわせて利用するとかなりのサイズを追加確保することができます。
# make NOFORTH=1 とか # make -DNOFORTH のようにします。コンパイル&リンクには btx と libstand、libi386 が必要です。 コンパイル時にエラーになる場合はこちらを先にコンパイルすると良いです。 (/usr/obj/usr/src/... が無い場合はエラーになると思います。)
- NOFORTH
- Forth 言語を無効にする。 これで作成した loader でも /boot/loader.rc を読み込んで load 命令や autoboot 命令を実行することは出来ますので、 特に利用する必要が無ければ定義すると良いと思います。
- LOADER_BZIP2_SUPPORT
- bzip2 されたデータのロードをサポートします。標準では無効になってます。
- LOADER_NO_GZIP_SUPPORT
- gzip されたデータのロードのサポートを無効にします。