電脳世界のケーキ屋さん

考えの甘い甘党エンジニアがいろいろ書くブログ

Linux PXE 実習(DHCP設定ファイルを読み解く)

はじめに

以前の投稿で PXE用サーバを構築したが, その際 dhcpd の設定についてあまり理解していなかったので, これを機に理解していこうという次第.

調べたら思ったよりキツかった.
コンフィグについては改良の余地あり.
DHCPあなどるなかれ...

amaneku.hatenadiary.com

件のコンフィグ

まずは結論から

#~~~~~~~~~~~~~~~~~~~~~~~~
# /etc/dhcp/dhcpd.conf
#~~~~~~~~~~~~~~~~~~~~~~~~
# オプション93番について 'architecture-type' と定義, データは 16ビットの符号無整数とする
option architecture-type code 93 = unsigned integer 16;`

# オプション空間 'pxelinux' を定義
option space pxelinux;

# オプション208番を pxelinux オプション空間内で magic として定義, データは文字列型
option pxelinux.magic      code 208 = string;

# 以下同様に configfile, pathprefix, reboottime を定義する
option pxelinux.configfile code 209 = text;
option pxelinux.pathprefix code 210 = text;
option pxelinux.reboottime code 211 = unsigned integer 32;

# 自身(DHCPサーバ)のアドレスを定義
local-address 192.168.4.28;

# サブネット 192.168.4.0/24 のスコープに関する宣言
subnet 192.168.4.0 netmask 255.255.255.0 {
  allow booting;
  allow bootp;

  # オプション1番でサブネットマスクを渡すことを明示
  option subnet-mask 255.255.255.0;

  # BOOTPデータとして, TFTPサーバのアドレスを定義
  next-server 192.168.4.28;

  # このサブネットでリースするアドレスの範囲を定義
  range 192.168.4.33 192.168.4.64;

  # デフォルトのリース時間と, 最大リース時間の定義
  default-lease-time 3600;
  max-lease-time 21600;

  # オプション空間 "pxelinux" をサイトローカルオプションとして利用することを宣言
  site-option-space "pxelinux";

  # pxelinux.magic に値を設定(実はなくても良い)
  option pxelinux.magic f1:00:74:7e;

  # クライアントが dhcp-parameter-request-list を付加していた場合の対応
  if exists dhcp-parameter-request-list {
    # クライアントへの返信時に強制的にオプション d0,d1,d2,d3 を含める
    # ※d0, d1, d2, d3 はオプション番号を16進数表記したもの(208~211)
    option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list,d0,d1,d2,d3);
  }

  # architecture-type が 00:00 だった場合の対応(以下同様)
  if option architecture-type = 00:00 {
    # pxelinux.pathprefix に値を設定
    option pxelinux.pathprefix "/bios/";
    # filename に値を設定
    filename "bios/lpxelinux.0";
  } elsif option architecture-type = 00:09 {
    # EFIx64
    filename "efi64/syslinux.efi";
    option pxelinux.pathprefix "/efi64/";
  } elsif option architecture-type = 00:07 {
    # EFIx64
    filename "efi64/syslinux.efi";
    option pxelinux.pathprefix "/efi64/";
  }
}

前提知識

まず 1行目から6行目までの option で始まるディレクティブについて.
これについては man dhcp-options でマニュアルが読める.

この辺りを把握するには DHCP のオプションについて理解する必要がある(あった) ここがもの凄く話が長いので閲覧注意.

DHCPのオプション

DHCPは一般的にはネットワークに接続されたクライアントに対して, 利用可能なIPアドレスをリースするためのシステムではあるが, この際に オプション を用いて様々な追加の情報のやり取りができる.

PXENBPファイルを指定していたり, サブネット長を指定しているのがまさにそれである.
(クライアントのIPやNextサーバの指定はメッセージ本体に含まれる)

オプションはそれを識別するオプション番号と, オプションデータのサイズ, オプションデータ本体のセットで構成される.
例えばオプション番号 1 はサブネットマスクをクライアントに指定するためのオプションだが, RFC2132 を見ると以下のようなフォーマットであることが分かる.

The code for the subnet mask option is 1, and its length is 4 octets.

 Code   Len        Subnet Mask
+-----+-----+-----+-----+-----+-----+
|  1  |  4  |  m1 |  m2 |  m3 |  m4 |
+-----+-----+-----+-----+-----+-----+

Code というのがオプション番号(以後そのように)となっており, 1オクテットの範囲で 0 ~255 の値を取る.
Len がその後に続く実データのサイズ(オクテット数)を示している.

前半のオプション番号については、用途についてRFCで定められている.
RFC3942 ではオプション番号のうち, 224 から 254 までがサイトローカルオプション番号として予約されており, 要するにユーザが自由に定義して使って良いオプションとなっている.
(製品とかアプリケーションには組み込むんじゃねーぞと注記がある. ちゃんと守られていないのか若干愚痴っぽい)

具体的にどのオプション番号にどんな意味があるのかは, IANAが公開している情報を見るのが分かりやすい模様.
RFCだと点在していて追っていくのが辛い.

BOOTP and DHCP options - IANA

オプションのカスタマイズ(DHCPHello World!)

オプションについては既存で定義されているものが幾つかあることは先に述べた.
これについてサイトローカルで独自の意味に書き換えることができる.

例えば, オプション1番は本来サブネットマスクを指定するものだが, 以下のように設定することで, そこに任意の文字列を入れて遊ぶことができる.

option local-hello code 1 = text;

local-address 192.168.4.28;

subnet 192.168.4.0 netmask 255.255.255.0{
  range 192.168.4.33 192.168.4.64;
  option local-hello "Hello World!";
}

Offer をパケットキャプチャすると以下のようになっており, ちゃんとオプション1番に値でハローワールドできていることが確認できる.

Frame 2: 342 bytes on wire (2736 bits), 342 bytes captured (2736 bits)
Ethernet II, Src: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx), Dst: RealtekU_fb:17:a9 (52:54:00:xx:xx:xx)
Internet Protocol Version 4, Src: 192.168.4.28, Dst: 192.168.4.36
User Datagram Protocol, Src Port: 67, Dst Port: 68
Dynamic Host Configuration Protocol (Offer)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x39de4e39
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 192.168.4.36
    Next server IP address: 0.0.0.0
    Relay agent IP address: 0.0.0.0
    Client MAC address: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Offer)
    Option: (54) DHCP Server Identifier (192.168.4.28)
    Option: (51) IP Address Lease Time
    Option: (1) Subnet Mask                         ※サブネット用のオプションに...
        Length: 12
        Value: 48656c6c6f20576f726c6421             ※Hello World!(16進数ASCIIなので適宜翻訳してください)
        [Expert Info (Error/Protocol): length isn't 4]
    Option: (255) End
    Padding: 000000000000000000000000000000000000000000000000…

もちろん既存のオプションでこのようなことをしても余り幸せにはなれないが, ISC-DHCP がオプション名を定義していないものや, 前述のサイトローカルオプションを利用するためにはこの手法を使う必要がある.

冒頭に示したコンフィグの1行目はつまりオプション93番を arcitecture-type として宣言しているのである.
右辺はオプションの型を示すもので, この場合は符号無16ビット整数値.

option architecture-type code 93 = unsigned integer 16;`

なお, dhcpd.conf で整数値を参照する場合は16進数表記で8ビット毎にコロンで区切る必要がある.

if option architecture-type = 00:00 {  ※つまり 0 だったらという意味

この辺りは man dhcp-eval のマニュアルを見るのが良い.

オプション空間について

ISC-DHCP ではオプションをグループにまとめることができる.
これに用いるのが option space ディレクティブになる.
ここでオプション空間名, 言い換えればグループ名を定義する.

以降はその空間名をプレフィックスにしてオプションを定義すると, そのオプションをオプション空間に所属させることができる.

option space pxelinux;
option pxelinux.configfile code 209 = text;
option pxelinux.pathprefix code 210 = text;
option pxelinux.reboottime code 211 = unsigned integer 32;

一体これを何に使うのかというと, マニュアルを見ると2つの用法がある模様.

  • site-option-space によってサイトローカルオプションとして利用
  • vendor-option-space によってベンダー固有オプションとして利用

vendor-option-space については横道なので割愛.
site-option-space で, 定義したオプション空間を指定することで, そのスコープのサイトローカルオプションとして, 定義したオプション空間が利用される.
つまり, オプション空間に所属する各種オプションが設定された状態になる.

恐らく, このサブネット,クラスならこのオプション空間を...といった使い道をするのものだと思われる.
実は今回の dhcpd.conf についてはオプション空間は利用する必要がない.

あと site-option-space で指定したオプション空間に, 224より小さい番号のオプションがいると起動時に警告が出る.
今回の PXE で用いるオプションは224以下なので, 個別に名前を宣言してあげる方がベターなようである.

WARNING: site-local option codes less than 224 have been deprecated by RFC3942.
  You have options listed in site local space pxelinux that number as low as 209.
  Please investigate if these should be declared as regular options rather than site-local options, or migrated up past 224.

クライアントへのオプションの送信(request-listについて)

if exists dhcp-parameter-request-list ... という記載に関しての説明.

定義の通りにいけば, 以下のように記述すれば, PXEに関するオプションがクライアントに渡されると予想されるが, 実際はそうならない(ケースが多い) これは オプション55の dhcp-parameter-request-list(以後長いので request-list) というオプションが関わっている.

option space pxelinux;
option pxelinux.configfile code 209 = text;
option pxelinux.pathprefix code 210 = text;
option pxelinux.reboottime code 211 = unsigned integer 32;

local-address 192.168.4.28;

subnet 192.168.4.0 netmask 255.255.255.0{
  next-server 192.168.4.28;

  ※以下のオプションは想定通りにクライアントに渡されない場合がある
  site-option-space "pxelinux";
  option pxelinux.configfile = "/bios/pxelinux.cfg/default";
  option pxelinux.pathprefix = "/bios/";
  option pxelinux.reboottime = 00:00:00:10;
  range 192.168.4.33 192.168.4.64;
}

request-list はクライアントから, サーバに対して 「このオプションのデータを下さい」という要求を示すオプションである.
ISC-DHCPサーバは, 受信したパケットに request-list が含まれていると, マニュアル上は以下の動きをする.

  • request-list で要求されたオプションを返す
  • 該当スコープで有効なオプションがないものについては無視する

ここで重要なのが, request-list に含まれておらず, サーバ側で設定されているオプションはクライアントに送信されない点にある.
例えばサーバで PXEブートに必要な情報を用意していても, クライアントがリクエストしていなければ渡さない動作を取ることになる.

具体的な動作例を示す.


先のコンフィグについて, サーバクライアント間のやり取りについてパケットキャプチャを取ると以下のようになっている.

クライアント=>サーバの DISCOVERパケット
オプション列に 55 の request-list がない状態である.

Frame 1: 342 bytes on wire (2736 bits), 342 bytes captured (2736 bits)
Ethernet II, Src: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx), Dst: Broadcast (ff:ff:ff:ff:ff:ff)
Internet Protocol Version 4, Src: 0.0.0.0, Dst: 255.255.255.255
User Datagram Protocol, Src Port: 68, Dst Port: 67
Dynamic Host Configuration Protocol (Discover)
    Message type: Boot Request (1)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x6f5b3f25
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 0.0.0.0
    Next server IP address: 0.0.0.0
    Relay agent IP address: 0.0.0.0
    Client MAC address: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Discover)
    Option: (50) Requested IP Address (192.168.4.36)
    Option: (61) Client identifier
    Option: (255) End
    Padding: 000000000000000000000000000000000000000000000000…

これに対するサーバからの Offer は以下のような内容になる.

サーバ=>クライアントの OFFERパケット
209~211の設定で定義したオプションが含まれていることが確認できる.

Frame 2: 346 bytes on wire (2768 bits), 346 bytes captured (2768 bits)
Ethernet II, Src: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx), Dst: RealtekU_fb:17:a9 (52:54:00:xx:xx:xx)
Internet Protocol Version 4, Src: 192.168.4.28, Dst: 192.168.4.36
User Datagram Protocol, Src Port: 67, Dst Port: 68
Dynamic Host Configuration Protocol (Offer)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x6f5b3f25
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 192.168.4.36
    Next server IP address: 192.168.4.28
    Relay agent IP address: 0.0.0.0
    Client MAC address: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Offer)
    Option: (54) DHCP Server Identifier (192.168.4.28)
    Option: (51) IP Address Lease Time
    Option: (1) Subnet Mask (255.255.255.0)
    Option: (209) PXE Configuration file  ※
    Option: (210) PXE Path Prefix         ※
    Option: (211) Reboot Time             ※
    Option: (255) End

次にrequest-listが含まれている場合を確認する.

クライアント=>サーバの DISCOVERパケット(request-list有)
55番のオプションデータが付与されていることが確認できる.
中身は説明した通り, 「この番号のオプションを下さい」というものである.
このリストの中身は dhclient のデフォルトのリクエスト内容になる.

Frame 1: 342 bytes on wire (2736 bits), 342 bytes captured (2736 bits)
Ethernet II, Src: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx), Dst: Broadcast (ff:ff:ff:ff:ff:ff)
Internet Protocol Version 4, Src: 0.0.0.0, Dst: 255.255.255.255
User Datagram Protocol, Src Port: 68, Dst Port: 67
Dynamic Host Configuration Protocol (Discover)
    Message type: Boot Request (1)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x8600fa35
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 0.0.0.0
    Next server IP address: 0.0.0.0
    Relay agent IP address: 0.0.0.0
    Client MAC address: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Discover)
    Option: (50) Requested IP Address (192.168.4.36)
    Option: (55) Parameter Request List                         ※
        Length: 7
        Parameter Request List Item: (1) Subnet Mask            ※
        Parameter Request List Item: (28) Broadcast Address     ※
        Parameter Request List Item: (2) Time Offset            ※
        Parameter Request List Item: (3) Router                 ※
        Parameter Request List Item: (15) Domain Name           ※
        Parameter Request List Item: (6) Domain Name Server     ※
        Parameter Request List Item: (12) Host Name             ※
    Option: (61) Client identifier
    Option: (255) End
    Padding: 00000000000000000000000000000000

これ対するサーバからの Offer は以下のようなものになる.

サーバ=>クライアントの OFFERパケット(request-list有) 付加されると想定していたオプションがないことが見て取れる.
クライアントからは他にもドメイン名やゲートウェイのリクエストがあったが, 今回の設定ではスコープに情報が定義されていないのでサーバは無視した形となっている.

Frame 2: 342 bytes on wire (2736 bits), 342 bytes captured (2736 bits)
Ethernet II, Src: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx), Dst: RealtekU_fb:17:a9 (52:54:00:xx:xx:xx)
Internet Protocol Version 4, Src: 192.168.4.28, Dst: 192.168.4.36
User Datagram Protocol, Src Port: 67, Dst Port: 68
Dynamic Host Configuration Protocol (Offer)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0x8600fa35
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 192.168.4.36
    Next server IP address: 192.168.4.28
    Relay agent IP address: 0.0.0.0
    Client MAC address: RealtekU_xx:xx:xx (52:54:00:xx:xx:xx)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Offer)
    Option: (54) DHCP Server Identifier (192.168.4.28)
    Option: (51) IP Address Lease Time
    Option: (1) Subnet Mask (255.255.255.0)
    Option: (255) End
    Padding: 000000000000000000000000000000000000000000000000…

かくかくしかじかなので, クライアントが request-list を付けてきていた場合は, サーバ側が必要と判断するオプションは明示的に渡す必要がある.

ISC-DHCP では dhcp-parameter-request-list をスコープ内で定義した場合は, そのオプションを強制的に返す動作を取る. (man dhcp-optionsより) なので, dhcp-parameter-request-list に渡したいオプションを付加させてやれば良い.

それが以下の部分の記述の意味となる.

  if exists dhcp-parameter-request-list {
    # Always send the PXELINUX options (specified in hexadecimal)
    option dhcp-parameter-request-list = concat(option dhcp-parameter-request-list, d1, d2, d3);
  }

concat(option dhcp-parameter-request-list, d1, d2, d3) が実際にオプションを連結している箇所である.
d1, d2, d3 というのは 16進表記によるオプション番号を表わしており, それぞれ 209,210,211 になる.
これで各オプションの値を示すことになるらしい. へぇ~(マニュアル内にはどこを探しても見つからなかった)

ちなみに d1 = "/bios/pxelinux.cfg/default"; とかできるかなぁと思ったけどできなかった残念.

おまけ

vendor-option-space について

オプション番号43番の vendor specific の中にベンダーが独自のデータを挿入できる設定.
例えば以下のようなコンフィグを作ると, 43番オプションの中に, 1番オプションとして "Hello World" の値が入ったデータがクライアントに返される.
この時サブネットマスクのオプションは汚染されない.

オプションの中にオプションを入れ子にできるイメージ.

option space local;
option local.hello code 1 = text;

local-address 192.168.4.28;

subnet 192.168.4.0 netmask 255.255.255.0{
  next-server 192.168.4.28;

  vendor-option-space local;
  option local.hello = "Hello World";
  range 192.168.4.33 192.168.4.64;
}

ベンダーが製品とかで使う時はこれ使ってね!と記載があるが, どうもあまり守ってくれてないらしい.
どうも解釈の仕方がマチマチで, vendor-specific ではなく site-local を使っているケースが多い模様.

dhclient の使いかた

request-list を付加しないようにするには, 設定ファイルに空の request ; ディレクティブを記述しておく.

また, デフォルトだと2回目以降のIPの取得でアドレスの再利用のために DISCOVER を送らずにいきなり REQUEST を送る挙動を取る.
これを回避したい場合は事前に RELEASE を行う必要がある.
これは -r オプションで実施可能.

使っていたコマンドは以下の感じ.

DISCOVERYの実施

# dhclient -d -cf dhclient.conf

アドレスのリリース

# dhclient -d -cf dhclient.conf -r

dhclient.conf

interface "ens3" {
  # request-list を付加したくない時は以下のように何も付けていない request ディレクティブを記述する
  # request ;

  # request ディレクティブがない場合は以下のように記述したのと同義
  # request subnet-mask, broadcast-address, time-offset, routers, domain-name, domain-name-servers, host-name;
}

208オプションについて

オプション番号 208 は "PXELINUX Magic" として予約されている.
これは古い PXELINUX(3.55以前) が DHCPオプションを認識するために必要なオプションである.
現行の PXELINUX を使うのであれば不要.

SYSLINUX Wiki より

Option 208 pxelinux.magic
Earlier versions of PXELINUX required this option to be set to F1:00:74:7E (241.0.116.126) for PXELINUX to be able to recognize any special DHCP options whatsoever. As of PXELINUX 3.55, this option is deprecated and is no longer required.

参考文献

IANA bootp dhcp parameters
man dhcpd.conf
man dhcp-options
Debian wiki(man dhcp-options)日本語訳 man dhcp-eval
SYSLINUX WIKI > PXELINUX

おわりに

結局 PXE やりたいなら dnsmasq とかの方がいいんじゃないかと
しかし勉強のためにあえて茨の道を進むのもエンジニアとしてのロマンか(ドMか)