Proxy DHCPを使ったPXEブート

このページは，Proxy DHCPを使ったPXEブートに関する情報をまとめています．

PXEブートに関する一般的な知識については，[参考文献](#参考文献)を参照してください．

# Raspberry Pi

[公式サイトのヘルプ](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/net.md)に書かれていますが，`PXE_BOOT_MENU`オプションの`description`が`Raspberry Pi Boot`（case-sensitive）を含んでいる場合のみ，TFTPリクエストが送信されます．

上記の`PXE_BOOT_MENU`オプションを含む応答を返したサーバーのIPアドレスに対して，`bootcode.bin`に対するTFTP Readリクエストを送信します．サーバーのIPアドレスは`recv_from()`で取得していると思われます．このことは，公式フォーラムでも話題になったようです．

* [network booting with TFTP server IP != DHCP server IP - Raspberry Pi Forums](https://www.raspberrypi.org/forums/viewtopic.php?t=160656#p1323132)

また，以下の情報などを指定しても無視されます．

* `siaddr`
* `sname`
* `file`
* `server identifier`オプション
* `PXE_BOOT_SERVERS`オプション

`open-iscsi`を使えば，iSCSIストレージ上のルートファイルシステムをPXEブート時にマウントできます．

* [Net boot (PXE + iSCSI) with a RaspberryPi 3 - darknao's stuff](https://stuff.drkn.ninja/post/2016/11/08/Net-boot-(PXE-iSCSI)-with-a-RaspberryPi-3)

手順が煩雑なので[スクリプト](https://gist.github.com/masnagam/6343a594e515f18314e09e2b61fdec83)を作成しました．

```console
curl -sSL https://git.io/make-iscsi-rootfs-v6 | sh -s -- \
  -b -p 192.168.0.9 -t iqn.2019-04.example.host:iscsi.target \
  -i iqn.2019-04.example.host:iscsi.initiator
```

オプションの意味はヘルプに記載してあります．ターゲットに接続出来ない場合，再起動後に再度実行してください．

`/etc/fstab`は更新しないので，スクリプト実行後に適切に書き換えてください．

## Raspbian BusterでPXEブートが機能しない

以下を`cmdline.txt`に追加すればPXEブートできるように．

```text:cmdline.txt
modprobe.blacklist=bcm2835_v4l2
```

ただし，カメラは機能しなくなります．カメラが必要な方は，暫くはRaspbian Stretchを使い続けましょう．

* https://www.raspberrypi.org/forums/viewtopic.php?t=245301
* https://github.com/raspberrypi/linux/issues/3067

Raspbian StretchでもKernelを更新するとPXEブートできなくので，kernel関係のアップデートは暫くやめておきましょう．

# ROCK64

Proxy DHCPは使えません．

以下のどちらかになりますが，素直にDHCPサーバーを使うことをお薦めします．

1. MACアドレスかベンダークラス識別子でフィルターして`siaddr`を付与できるDHCPサーバーを使う
2. SPIフラッシュに書き込むU-Bootを改造し，`serverip`を固定する

何らかの理由で，DHCPサーバーの設定を変更できない，ブロードキャストドメインの分割ができない場合は，後者以外の選択肢はありません．

## U-Boot (ayufan-rock64/linux-u-boot)

ROCK64でPXEブートを行う場合，SPIフラッシュに書き込んだU-Bootを使います．SPIフラッシュへの書き込み手順は以下を参照してください．

* [NOOB - PINE64](http://wiki.pine64.org/index.php/NOOB#Flashing_u-boot_to_SPI_Flash)

U-Bootの処理内容はスクリプトで記述されています．私が実際に抽出したわけではありませんが，[こちら](https://gist.github.com/tstellanova/e59a9066640ffc7860cf538ee4171924)に記述されているようになっているみたいです．少なくとも`dhcp`および`pxe (get|boot)`の処理順は，実際のDHCP/TFTPパケットの順序とも合致します．

U-Bootの厄介な点は

* Proxy DHCPが機能しません
    * `yiaddr`がないとDHCPリプライを破棄
        * https://github.com/ayufan-rock64/linux-u-boot/blob/rockchip-master/net/bootp.c#L1037
    * １つのDHCPリクエストに対して，有効なリプライは１つのみ
        * 最初に届いた`yiaddr`を含むリプライの`siaddr`が`pxe get`のサーバーIPとなります
        * つまり，Proxy DHCPサーバーから`yiaddr`を返せたとしても，DHCPサーバーとの反応速度の競争となります
* `pxe get`前の`dhcp`は，ベンダークラス識別子が`U-Boot.armv8`となっており，`dnsmasq`の`pxe-service`は機能しません
* `pxe get`前の`dhcp`で`siaddr`を取得できないと，`pxe get`で`pxelinux.cfg/*`へのTFTPリクエストが出ません
* ベンダー拡張をサポートしていないのでoption(43)は機能しません

## U-Bootのserveripの初期値を設定

TFTPサーバーのIPアドレスを含むU-Bootを作成する[スクリプト](https://raw.githubusercontent.com/masnagam/sbc-scripts/master/rock64-make-pxeloader)を作成しました．

```console
curl -sSL https://git.io/rock64-make-pxeloader | \
  sh -s -- -r 2017.09-rockchip-ayufan-1045-g9922d32c04 -s 10.1.2.3
```

`serverip`の初期値を`10.1.2.3`に設定したU-BootをDockerコンテナ上でビルドします．ビルド結果はカレントディレクトリの`out`ディレクトリにコピーされます．

改造後のU-Bootは，`dhcp`で`serverip`が設定されなかった場合，`pxe get`で`10.1.2.3`に対してTFTPリクエストを送信します．

`pxe get`の動作については以下を参照してください．

* https://github.com/ayufan-rock64/linux-u-boot/blob/rockchip-master/doc/README.pxe

PXELINUXについては以下を参照してください．

* https://wiki.syslinux.org/wiki/index.php?title=PXELINUX

以下のようなフォルダ構成にすることで，機器ごとに読み込むファイルを変更できます．

* tftp-root/
    * pxelinux.cfg/
        * 01-AA-BB-CC-DD-EE-FF（AA-BB-CC-DD-EE-FFはMACアドレス）
    * rock64/
        * aabbccddeeff/（aabbccddeeffはMACアドレス）
            * initrd.img-xxxファイルなど`/boot`以下のファイル・フォルダをコピー

```text:01-AA-BB-CC-DD-EE-FF
default linux

label linux
    kernel /rock64/aabbccddeeff/vmlinuz-xxx
    initrd /rock64/aabbccddeeff/initrd.img-xxx
    devicetreedir /rock64/aabbccddeeff/dtbs/xxx
    append ...
```

# 参考文献

* [Preboot Execution Environment (PXE) Specification Version 2.1](http://www.pix.net/software/pxeboot/archive/pxespec.pdf)
* [Dynamic Host Configuration Protocol - Wikipedia](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol)