FreeBSD 4.7-STABLE で 1FD Router

 FreeBSD 4.7 を利用して、 1枚のフロッピーに全て収めた FreeBSD 1FD PPPoE Router を作った記録です。

 この記録は、個人的な備忘録のつもりです。 これらの情報が原因で何らかの被害等を被っても、 私はなんら責任を負えません。 情報を利用するのは自己責任でお願いします。


目次

文書内リンクです。

更新履歴

2002-11-16
本文を多少修正。補遺A, 補遺B, 補遺C を加筆。目次を作成。

はじめに

 なぜこんなものを作るのでしょうか。

 1FD に収めるにはかなりの機能を削がなければなりません。 そのために、まず、要求を決めます。私の場合は以下のような要求です。

今は昔 1FD FreeBSD

 本格的に作成する前に、まず先人の記録を参考にするため、 web を検索して調べます。 そうすると何件か有用な情報が検索にヒットします。

 以下のような情報がヒットしますが、 現在では、あまり有効ではありません。

概要

フロッピーディスク内容

次のような構成にします。

FD(ufs フォーマット)
/(ルートディレクトリ)
/kernel.gzgzipped カーネル
/mfsroot.gzgzipped MFS ルートディレクトリ ディスクイメージ
/boot(起動関係)
/boot/loaderkgzipped カーネルローダ loader(8)
/boot/loader.rcloader 用設定ファイル

作成

 主に以下のものを作成します。
  1. 機能限定 kernel
  2. crunch された実行バイナリ
  3. root filesystem の MD ディスクイメージ
FreeBSD のソースプログラムがないとダメなので、 (どれが必要か調べるのが面倒なので)全部 /usr/src 以下に展開してください。 FreeBSD をインストールする際に全部展開してある人はこの作業は必要ないです。

あと、作業のために適当にディレクトリを作っておいてください。 ここの例では、ホームディレクトリの下に fdbsd というディレクトリを作ってます。

# cd; mkdir fdbsd

機能限定カーネルの作成

/sys/i386/conf で作業します。
# cd /sys/i386/conf
kernel 設定ファイルの標準の GENERIC を適当に名前を付けてコピーします。
# cp GENERIC mini
 コピーしたファイルを編集します。 これは単なるカーネル再構築とやっていることはかわりません。 必要な機能のみを有効にしてください。
 options MD_ROOT を必ず有効にしてください。 MD_ROOT_SIZE有効にしないでください。
 あとあと使用する可能性の有るネットワークカード、仮想デバイス、コンソール関係、 フロッピー周り、場合によっては ata と atadisk、シリアルあたりを有効にしたら、 後は全てのデバイスを off にしないとフロッピーの空き領域が足りないでしょう。 gzip で圧縮して 650KB 位までに抑えたほうが良いです。 前述しましたが pseudo-device gzip は不要です。
# vi mini (編集は省略)
あとは普通に kernel の構築を行います。 但し、make install してはダメです。 あくまでフロッピーから起動するためのカーネルですので(^^;

 正常に kernel のコンパイルが終了したら、 作業ディレクトリにコピーしておいてください。

crunch されたバイナリの作成

crunchgen(1) とは

 フロッピーディスクの容量は今となっては大変少ないです。 そこに必要なプログラムを入れなければなりませんが、 今日日のプログラムは全て dynamic link されています。 dynamic link されているプログラムは、 リンク先のライブラリ (ELF 形式なら拡張子は so (shared object?)) が無ければ動作しません。しかし、ls -l /usr/lib/libc.so.4 としてみるとわかる通り、FreeBSD 標準Cライブラリだけでも 500KB ほどあります。 これではフロッピーに各種プログラムを入れる隙がありません。

 そこで、実行バイナリを dynamic link では無く、static link にします。 static link とは、 プログラムにとって必要なライブラリ(の中にある必要なモジュールパーツ) を全て実行バイナリの中に、 コンパイル時に静的にリンクすることです。 こうすれば実行するときに別にライブラリは必要ありません。

 しかし、複数あるプログラムを全て static link したら、必ずデータの重複部分が出てきます。 これは容量の無駄になります。

 そこで登場するのが、crunchgen です。

 crunchgen は必要となるプログラムをまとめ、 1つの実行バイナリにして、 静的にリンクするデータを一元化し無駄を無くすプログラムです。

 crunchgen 自体は、各プログラムの内容を把握し、 変数がかち合ったりしないように配慮しつつ、 Makefile を作成することです。 正常な Makefile が作成されれば、make を実行するだけで、 指定したプログラムをたった1つの実行バイナリファイルにすることが出来ます。 これで出来たプログラムは呼び出される名前によって、 自分自身の動作を変化させます。

クランチバイナリの作成

 前置きが長くなりましたが、 FD router に必要なプログラムを整理して、設定ファイルを作成します。

 先に作成した作業ディレクトリに移ってください。

# cd ~/fdbsd
crunchgen はカレントディレクトリを作業ディレクトリとする 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 プロジェクト万歳!!)。

要約すると、

srcdirs
ソースファイルのあるディレクトリを指定。 crunchgen がこの中からプログラムを探してくれる。
progs
クランチバイナリにするプログラムを指定する。
libs
リンカに渡すライブラリを指定。
ln
プログラム名のエイリアスを指定する。
special
各プログラム毎のオプションを指定する。
となります。

 設定ファイルを作ったら、早速 crunchgen します。

# vi fdbsd.conf
(編集は省略)
# crunchgen fdbsd.conf
 少し作業に時間がかかりますが、 最近のマシンで実行するなら10秒もかかりません。 特にエラーが出力されなければ成功です。
Run "make -f fdbsd.mk" to build crunched binary.
#
では、make を実行しましょう。
# make -f fdbsd.mk
(途中長いのは全部省略)
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
#
make がエラーにならなければ正常終了です。 作成されたバイナリをテストしてみましょう。 作成されたバイナリに、 元のプログラム名を指定すればそのプログラムが実行されます。 本当にクランチバイナリが動作しているのか疑心暗鬼な方は、 chroot して実行してみるのも面白いかもしれません。
# ./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

 ここで注意すべき事があります。

  1. buildopts などを変更して make する時
    crunchgen で作成した makefile では、 make world 等の時と同じディレクトリ (標準では /usr/obj/...) があればそちらを、無ければ、 各ソースコードがあるディレクトリで make を実行したときにオブジェクトが生成されるディレクトリ (通常はソースコードと同じ場所) に中間バイナリを出力します。 crunchgen -f や make -f fdbsd.mk clean をしても基本的に作成されたその中間バイナリは 削除・更新されません!! (Makefile を書き換えても make をやり直してくれないのと同じ)。 何か変だと思ったら、 プログラムのソースディレクトリで make clean してから、 やり直してください。
  2. 作成される実行バイナリファイル名
    crunchgen で作成する実行ファイル名は設定ファイルの .conf サフィックスを取り除いたものになります。

FD起動後の root filesystem を作成

 FD から起動後、init によって / (root ディレクトリ) としてマウントされるファイルシステムのディスクイメージを作成します。

ディスクイメージの作成

 いきなり手作業でディスクイメージを作成するのは無理です。 そこで FreeBSD に備わっている仮想ディスクデバイス化機能を利用します。

  1. 作業ディレクトリに戻る
    # cd ~/fdbsd
  2. 1.44MB FD のサイズの、中身が 0 なファイルを作る
    # dd if=/dev/zero of=rootfs.img count=1440 bs=1024
    ちなみに 1.44MB である必要は無いですが、 disktab ですとか disklabel の都合上、 既に存在するメディアの設定が使える分楽になります。
  3. 仮想デバイスファイルに結びつける
    # vnconfig -s labels -c /dev/rvn0 rootfs.img
  4. ディスクラベルを書き込む
    # disklabel -wBr vn0 fd1440
  5. ufs にフォーマット
    # newfs -i 4000 -o space -m 0 /dev/rvn0c
  6. 作成した仮想ディスクをマウント
    # mount /dev/vn0c /mnt
 ここまでで、正常に /mnt に書き込める仮想ディスクが出来ました。 /mnt で行った内容は、実際には全て rootfs.img に反映されます。 (ですので、マウントしたままファイルシステム rootfs.img を FD にコピーして利用しようとしても、SUPERBLOCK が CLEAN じゃないと叱られます。)

 なお、うまくいかない場合は、 FreeBSD インストールフロッピーディスクの kern.flp を vnconfig して mount して既にあるファイルを消すだけでも、 とりあえず同様のファイルができます。

ディレクトリ作成

必要と思われるディレクトリを作成します。

# cd /mnt
# mkdir -p etc sbin dev var mnt mnt2
# ln -s sbin bin
kernel は init プログラムとして以下のプログラムを呼び出そうとしますので、 /sbin または /stand ディレクトリは必須です。 (シンボリックリンクでも構いません。)
/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 .
 クランチバイナリは呼び出された名前によって動作が変わりますので、 名前を変えてリンクを張ります。
# ln fdbsd ls
# ln fdbsd ln
# ln fdbsd mkdir
# ln fdbsd sh
(fdbsd.conf の中で設定した progs の分だけ ln)
#
なお自分はこの作業が面倒だったので perl の超簡易スクリプトを作りました。 (第1引数にリンク元ファイル名、 標準入力に crunchgen の設定ファイルを与えてください。 自動でリンクを張ります。)

/etc の設定ファイル類

 もし 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 にします。

0 で埋める

 ディスクイメージを /mnt にマウントして編集していると、 ディスクイメージの中身に編集したファイルのゴミが残ります。 これらのファイルとして使われていない領域のゴミは、 後々このイメージを圧縮するときに圧縮率の妨げになりますので、 データ 0 (ゼロ) でクリアしておきます。
# dd if=/dev/zero of=aa
# rm aa

アンマウント

 マウントしたディスクイメージでの作業が終了したら必ず umount してください。 そうしないと root filesystem が NOT CLEAN という事で起動時に mount 出来なくなります。
# 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 を以下のような内容で作成します。

load /kernel
load -t mfs_root /mfsroot
autoboot 10
これは、/kernel のほかに、 mfs_root というタイプにタグ付けされたモジュールを読み込みます。 mfs_root というタグ付けにより、カーネルは mfsroot(.gz) が MemoryDisk のイメージであると判断します。

 あとは作成したカーネルと MFS root filesystem ディスクイメージをコピーするだけです。 カーネルもrootファイルシステムイメージも gzip で圧縮してください。 /boot/loader が gzip されたイメージも正常に読み込んでくれます。

# cd ~/fdbsd
# gzip -9c kernel > /mnt/kernel.gz
# gzip -9c rootfs.img > /mnt/mfsroot.gz

サイズが大きすぎる場合は、 カーネル構築オプションから不要なオプションを削除したり、 クランチバイナリから要らないバイナリを取り除いてみたりしてください。

実際に起動

 作成が終わったら実際に起動してみましょう。 正常に起動し、MFS をマウントし、init が読み込まれ rc が実行され、 ログインプロンプトが出てきたら(または sh が実行されたら)完成です。 うまくいかない場合は、微調整をするなりします。

参考URL

 下記にあげる URL を参考にさせていただきました。 有用な情報をありがとうございます。

 中には情報が多少古いものもありますが、 1FD で起動する FreeBSD 環境を作りたいときには十分参考になるものなので、 是非読んでみることをお奨めします。


[ 補遺 ]

補遺A. MD_ROOT_SIZE

 カーネル設定オプションに MD_ROOT_SIZE と言うものがあります。 これを設定して構築したカーネルは write_mfs_in_kernel (場所は /usr/src/release/write_mfs_in_kernel.c) でファイルシステムイメージをカーネルに内包することが出来ます。

 これの利点は、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 してから使いましょう。

補遺B. bzip2'd kernel と /boot/loader

 /boot/loader は make 時のオプションを変更すると bzip2 で圧縮されたデータ (カーネルやファイルシステムイメージ) を読み込むことが出来ます。

 しかし /boot/loader では bzip2 で圧縮したイメージを解凍する際にあまりメモリを確保できず、 普通に bzip -4 で圧縮したデータも展開することが出来ません。 実質 bzip2 -1 や bzip2 -2 位で圧縮したデータしか展開できないようです。

 それでも bzip2 -1 で圧縮すると gzip -9 で圧縮したものより結果が良いことが殆どですので、 ディスクスペースが足りない方は挑戦してみる価値はあります。 (50KB 前後は利用可能領域が増えます。)

 ついでですが、/boot/loader はコンパイル時のオプションで Forth 言語サポートを無効にするとサイズが 50KB ほど縮みますので、 こちらもあわせて利用するとかなりのサイズを追加確保することができます。

/boot/loader のコンパイル

 make に渡すオプションとして以下のものがあります。

# make NOFORTH=1 とか # make -DNOFORTH のようにします。
NOFORTH
Forth 言語を無効にする。 これで作成した loader でも /boot/loader.rc を読み込んで load 命令や autoboot 命令を実行することは出来ますので、 特に利用する必要が無ければ定義すると良いと思います。
LOADER_BZIP2_SUPPORT
bzip2 されたデータのロードをサポートします。標準では無効になってます。
LOADER_NO_GZIP_SUPPORT
gzip されたデータのロードのサポートを無効にします。
 コンパイル&リンクには btx と libstand、libi386 が必要です。 コンパイル時にエラーになる場合はこちらを先にコンパイルすると良いです。 (/usr/obj/usr/src/... が無い場合はエラーになると思います。)

補遺C. クランチバイナリのサイズ調整方法

 クランチバイナリは要求に合わせて何回か作り直すと思います。 サイズを切り詰めないとダメな場合、 make を実行したディレクトリにサフィックスが .lo というファイルが大量に出来ます。 これは設定ファイルに記述した各コマンドの中間オブジェクトが、 とりあえずリンクされた状態のバイナリファイルです。 これのサイズを目安にコマンドを選別すると選別しやすいです。

[ 戻 る ]


Copyright (C) 2002 by kattyo@abk as 合著.
E-Mail: kattyo@anet.ne.jp
リンクは確認不要です。
最終更新:2002.11.16 / Count 23254.