KVM で PXE ブートしようとしたら難儀した

KVM の場合,virt-install コマンドに --pxe オプションをつければ PXE ブートさせることができる。Xen と違って完全仮想化だから。ばんざい。

# virt-install --name=hogehoge --accelerate --os-variant=rhel5.4 \
 --ram=384 --vcpus=1            \
 --file=hogehoge.img            \
 --network=bridge:br0 --pxe     \
 --vnc --noautoconsole

あるいは qemu-kvm コマンドを直接叩いて network option rom を指定するという方法もあるようだ (参考, qemu-kvm の直接起動は Stray Penguin - Linux Memo (KVM-2) 参照)。

これで普通に DHCP サーバと TFTP サーバを構成していればうまく PXE ブートできるはずなのだが,手元の環境では以前 Proxy DHCP サーバで PXE ブート - daily dayflower で書いたように Proxy DHCP を使っている。

この Proxy DHCP 環境下で KVMPXE ブートさせようとしたらなかなかうまくいかなかった。


結論からいうと,KVM (Qemu) で利用しているネットワークコントローラ用の ROM (Etherboot) にバグがあった。

ちなみに Etherboot 由来のネットワークブート ROM は Oracle VirtualBox でも使っている。ので同様に Proxy DHCP 環境ではうまく PXE できない (かった)。

原因 1: Proxy DHCPOFFER の NBP file name に対応していない

Proxy DHCP サーバで PXE ブート - daily dayflower でも書いたように,PXE_DISCOVERY_CONTROL Option の bit 3 を立てると DHCPOFFER の段階でブートファイル名を返すことができる。

ところが,Etherboot 由来の PXE BIOS はこのオプションを無視する。そして UDP 4011 宛に DHCPREQUEST を投げてくる。

しかたないので pxe-pdhcp を改造して 68 番と 4011 番両ポートで待ち受けするように変更した。

原因 2: Etherboot の ProxyDHCP 時の DHCPREQUEST パケットがおかしい

これで一件落着かと思ったら,DHCPREQUEST のベンダオプション項がうまくパースできない。仕様の読み漏れかと思って Etherboot のサイトからソースを落として読んだら,バグがあった。ベンダオプション項の最後に End of Options (FFh) をつけ忘れている。

Etherboot 4.4.2 でのパッチは以下のとおり。

diff -r e53c2fc7ea6a src/core/nic.c
--- a/src/core/nic.c    Wed Feb 16 12:19:14 2011 +0900
+++ b/src/core/nic.c    Wed Feb 16 14:07:59 2011 +0900
@@ -1166,6 +1166,7 @@
                                /* Construct the ProxyDHCPREQUEST packet */
                                memcpy(ip.bp.bp_vend, rfc1533_cookie, sizeof rfc1533_cookie);
                                memcpy(ip.bp.bp_vend + sizeof rfc1533_cookie, proxydhcprequest, sizeof proxydhcprequest);
+                               ip.bp.bp_vend[sizeof rfc1533_cookie + sizeof proxydhcprequest] = RFC1533_END;
                                for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) {
                                        printf ( "\nSending ProxyDHCP request to %@...", arptable[ARP_PROXYDHCP].ipaddr.s_addr);
                                        udp_transmit(arptable[ARP_PROXYDHCP].ipaddr.s_addr, BOOTP_CLIENT, PROXYDHCP_SERVER,

報告しようと思ったらバグトラッカも落ちてるしレポジトリもみつからない。どうもサイトが故障していたようだ。一部サービス (ソース提供や rom-o-matic での NIC ROM 自動生成) はすでに復旧している。

一応上記のパッチを適用してビルドした ROM ではうまく PXE boot できた((NIC ROM の置き換えは,/usr/share/kvm 以下の pxe-virtio.bin などを置き換える。もともとは /usr/share/qemu-pxe-roms/ 以下の ROM ファイルへのシンボリックリンク。))。だが,前述したように,VirtualBox でも問題のある Etherboot 由来の NIC ROM を使っており,こちらをどのように差し替えるかがわからない。なので,pxe-pdhcp 側で workaround をおこなった。

ほんとは workaround のソースを示せればいいんだけど,諸般の事情*1でまだだせない。あくまで Etherboot のバグ対策だけだが,

  • DHCP Options の Class Identifier が 32 バイト続いたあとで,(本来 FFh があるべきだが) 「:UNDI:〜」とゴミが続いてしまっている
  • よって pdhcp.ccheck_dhcp_packet() において DHCP Option が 3Ah, 55h の並びになった場合,そこを終端とみなすようにする

という対策をおこなったところ,CentOS 5.5 の KVM guest および VirtualBox 4.0.2 において PXE ブートでできるようになった。

余談

Etherboot プロジェクトは実質 gPXE プロジェクトが後継となっているようだ。こちらのソースは一から書きおこしている部分が多く,上記のようなバグはない (なさそうだった)。

だが gPXE の NIC ROM を生成して KVM で使ってみたところ,なぜか主 DHCP サーバ宛に TFTP 要求をだしていた。ソース上は ProxyDHCP 用のコードもあるようだがまだきちんとインプリメントされていないのかもしれない。

なお,Etherboot では上記のパッチを適用して自分で ROM をビルドしたが,gPXE の NIC ROM は http://rom-o-matic.net/gpxe/gpxe-1.0.1/contrib/rom-o-matic/NIC ROM を生成した。いろいろ条件を指定して NIC ROM を生成できるのでおもしろいサイトである。


ちなみに,Etherboot (と gPXE) では,条件を指定することで TFTP だけでなく,HTTP 経由でイメージをとってこれる NIC ROM も作成できるようだ。また,gPXE のほうは iSCSI による SAN Boot にも対応しているらしい (id:adsaria さんによる実践例)。

*1:諸般の事情と書くとおおげさだけど,単に面倒だから。id:viver さんが pxe-pdhcp のソースを github にうつしてくれればいいなぁ。