DHCP Relay Agent+Filterで既存のDHCPサーバーがある環境でPXEを使う

DHCPサーバーが既にあるネットワークでPXEを使おうとすると、通常は既存のDHCPサーバーを一度落としてから、新たにPXE用のDHCPサーバーを立てないといけません。
これがVIVERでは非常に大きな問題で、1つのサブネット内で2つ目のDHCPを立てるときは、1つ目のDHCPサーバーの設定を知っていないと、ネットワークを混乱させてしまいます。VIVERでは「できるだけ」ネットワークを混乱させないようにPXE用のDHCPサーバーを自動的に構築するのですが、既存のDHCPサーバーのアドレスプールの範囲が分からないので、やっぱり混乱させてしまう可能性があります。


そこで!DHCP Relay Agentの実装をハックして、リレー時にDHCPパケットを改変する機能を追加してみました。既存のDHCPサーバーが発行するDHCPOFFERをフックして、それにPXE用のオプションを載せた上で(+ごにょごにょして)再度DHCPOFFERを発行すれば、クライアントは見事にだまされます。


というわけで、↓これがそのパッチです。
dhcprelay-0.3.2b-filter.patch
dhcprelay-0.3.2b.tar.gzに当ててください…と、これを当てるとautoconf/automakeのバージョンの関係で何やらコンパイルできない?ので、configure無しで直にmakeしました(Linux)。そのパッケージは→dhcprelay-0.3.2b-filter-sourceonly.tar.gz


※追記:PXEの仕様自体に既存のDHCPサーバーの設定を変更せずにネットワークブートする規格が存在したので、こちらの実装の方が確実です:Proxy DHCPを使ってネットワークブートサーバーを構築する


使い方は、

$ # ./dhcprelay-filter  <device to client> <device to server> -- <tftp server address> <boot file name>
$ sudo ./dhcprelay-filter  eth0 eth0 -- 192.168.0.2 "/pxelinux.0"

です。元々はDHCP Relay Agentなので中継もできるハズですが、むしろそちらは試していません。(DHCPDISCOVERを中継しないようにしているのでたぶん動きません)
67/udp(dhcps)と68/udp(dhcpc)を待ち受けるので、DHCPクライアントやDHCPサーバーが起動している場合は終了してださい。(DHCPクライアントも終了しないといけません)




さて、その仕組みを。以下が同じ場合(DHCPを中継しない場合)です。RFC2123Wikipediaと一緒にどうぞ。


クライアントがPXEでブートしようとすると、まずDHCPDISCOVERをブロードキャストしてきます。これを受信して、その「トランザクションID」(DHCPヘッダのxidフィールド)を記録しておきます。トランザクションIDは、DHCPクライアントがDHCPDISCOVERを送るときにランダムに作成する数値らしいです。
続いて、既存のDHCPサーバーがDHCPOFFERをブロードキャストしてくれます。これを受信します。ただし記録しておいたトランザクションIDとは別のトランザクションIDが付いていたら捨てます。そしてこのパケットを以下のように書き換えます。ここがキモです。

DHCPヘッダのnext boot server (siadddr)フィールドにTFTPサーバーのIPアドレスをセット
セット!
DHCPヘッダのClient boot file name (file)フィールドにブートローダのパスをセット
ブートローダのパスは、起動時の引数で渡されたです。TFTPサーバーの/からの相対パス
Server Identifier (option 54)を自分のアドレスに書き換える
クライアントからは./dhcprelay-filterが既存のDHCPサーバーとは別のDHCPサーバーに見えるようになります。
Server Identifier (option 54)以外のoptionを削除する
Option Overload (option 52)などが残っていたりするとうまく動いてくれません。
Address Lease Time (option 51)を1分にセット
ブートローダをダウンロードするためだけにしか使われないので、短くしておきます。どうせ後でまたすぐDHCPDISCOVERが飛ぶことになります。

この書き換えたパケットをブロードキャストします。そうすると、クライアントは見事にDHCPREQUESTを返してくるハズです(ブロードキャスト)。このDHCPREQUESTは、既存のDHCPサーバーが発行したIPアドレスを使うようにセットされているので(Requested IP Address, option 50)、既存のDHCPサーバーはDHCPACKを返してきます。…たぶん。ここのところは微妙で、Server Identifierなどが書き換わっているので、DHCPサーバーの実装によっては無視されるかもしれません。

このDHCPACKも、DHCPOFFERと同じように書き換えてブロードキャストします。…ここのところはDHCPREQUESTを受信した時点でDHCPACKを返せばいいのですが、単純に手抜きです(^_^;)




ちなみにPXEの仕様はここにありますが、仕様上は上記のオプションではブートしないハズなのです。DHCPOFFERにClass Identifier (option 60)をセットしないといけないように書かれているのですが、セットしなくてもブートします(逆にセットするとブートしないカードもあったような…3com?)。それからUDPの4011ポートも不要です。
仕様上の方法も少し試してみたところ、Client IdentifierにPXEClient + PXE Server (udp/4011)(実装はこれを利用)でもブートできました。カードはIntel/PRO 1000です。


…ということで仕様と現状の違いが良く分かりません。どなたか詳しい方、調べてしまった方、ぜひレポートを!