はじめに
サーバーが老朽化してしまいました。ただの老朽化ではありません。なんせPentiumIIIで動いていたんです。入れ替えるの面倒なんで放置気味になっていき、最終的には動いているからいいやってなりました。その間にハードディスクが死んだり、CPUファンが死んだり、電源ファンが死んだりしましたけど、入れ替えるより面倒じゃないのでハードの方をチョコチョコ対応してしのいでいました。
でももう無理なようです。ついにマザーボードがおかしくなったようでbootに支障が出るようになりました。動作中は良いのですが、一度電源を切ると再起動できるかは運だめしです。
老朽化していくサーバーに対してデスクトップなどは適当に入れ替えていたため、性能の逆転現象が起きています。デスクトップの方が速いんです。ディスク容量もあるんです。
性能の良いハードを入れてVM化することも考えましたが、やっぱり逆転現象は起こるでしょう。そもそも手元にハードを置くこと自体が問題なのでしょう。VMにするなら、クラウドでもいんじゃないかという思考に落ち着くことになりました(使い方はほぼレンタルサーバーですが)。
日経コンピュータに『個人情報などをクラウドに置くことが問題になるような法的な根拠はない』とかいう理由でクラウド化を進める企業が紹介されていました。普通は流出とか技術面を心配するもんだから、法的な根拠があろうが無かろうが関係ないんじゃないかと思ったのですが、天下の日経コンピュータが言っているので間違いないですね。私、権威主義者ですから。
クラウドに置いてはいけないという法的根拠はないんです。だからクラウド化、進めましょう
クラウドにAzureを選んだのは深い理由はありません。手元に使えるクレジットがあったからです。ビデオ会議と一緒で最初の頃に苦労された方がいて、今はその結果が反映済の状態なので、どこも似たりよったりではないでしょうか。他人の苦労をかすめ取るのです。苦労された方、ありがとうございます。
Azureに初めてログインして、いろいろ試していると翌日には日本マイクロソフト株式会社の営業の方から電話がありました。そんなこと今まで無かったので凄くびっくりしました。何かお困りのことがありましたら、私宛にご連絡ください的な感じです。
その時に営業さんがAzureをアジュールと呼んでいました。Azureがまだメジャーではなかった頃。Azureという見慣れない単語を見て、なんて読むんだろうと思い、ググりました。その頃は今みたいに英単語サイトも無かったので、blogが何かでアジューやアジャ、アジャーみたいな感じになるというのを見つけ、それ以来、アジャーと呼んでいました。
だからMicrosoftな環境でミスすると『アジャーやっちゃった』です。でもこれが使えないことを知りました。アジャー。
DHCPサーバーのクラウド化
クラウド化するサーバー機能にはいろいろあるのですが、このドキュメントではDHCPサーバーを扱います。以下のような環境を作ることを目指します。
自組織とクラウドとの通信用に固定IPが最低一個は必要です。以降はRFC6890の例示用のIPアドレスから拝借して、203.0.113.1を固定IPとして使います。またAuzreのグローバルIPアドレスは198.51.100.1を使います。
Azureのサポートサーバーソフト
Azureではサーバーソフトの要件というのがあって、DHCPサーバーはサポートしないといことが、このドキュメントで明記されていました。以下に引用します。
The following roles are not supported on Microsoft Azure virtual machines:
Dynamic Host Configuration Protocol Server
Hyper-V (Hyper-V role is supported in Azure Ev3, and Dv3 series VMs only)Rights Management Services
Windows Deployment Services
でも、ここでいうDHCPサーバーのサポートは、Auzre上にある仮想ネットワーク内での話しです。
AzureにVMを作成する際、特に指定をしないと10.0.0.0/24ような仮想ネットワークに接続されます。このネットワークへのIPアドレスの払い出しはAzureが管理しているDHCPサーバーによって行われます。仮想ネットワークで独自DHCPサーバーを構築すると、この仕組みと干渉するためサポート外とされています。
今回行おうとしているのは、現在動かしている自分組織のプライベートネットワークに対するDHCPサーバーの構築です。幸いなことに、このプライベートネットワークは10.0.0.0/24ではないので、Azureの仮想ネットワークと干渉することはありません。
サーバーOSの環境
以降、具体的なコマンドなども登場しますので使用するサーバー環境についてまとめておきます。
- OS
- Debian 10で確認。Debian 11,12も可
2021年9月の追記
Debian 11がリリースされたので入れ替えました。ソースを確認したところ修正元ソースには変更が入っていないようなので、このドキュメントの記述通りで問題ないです。upgradeで/etc/defaultのisc-dhcp-relayが作られてしまうので、そこだけ注意してください。
2022年10月の追記
Debian 11でisc-dhcpのsecurity updateが行われました。再びソースを確認したところ修正元ソースには変更が入っていないようなので、このドキュメントの記述通りで問題ないです。upgradeで/etc/defaultのisc-dhcp-relayが作られるのも一緒です。
2023年5月の追記
Debian 11.7でisc-dhcpのproposed-updatesが行われました。再びソースを確認したところ修正元ソースには変更が入っていないようなので、このドキュメントの記述通りで問題ないです。upgradeで/etc/defaultのisc-dhcp-relayが作られるのも一緒です。
2023年7月の追記
Debian 12がリリースされました。このバージョンからisc-dhcpのベースバージョン4.4.1から4.4.3へバージョンアップされています。色々変わっていると嫌だなと思っていたのですが、このドキュメントで触っているソースはほとんど変わっていませんでした。行番号などを取り直したので、別な節を作ってますが、内容はこれまでと同じなので、まずは一通り目を通してからDebina 12の節に行ってください。
AzureでDHCPサーバーを動かす。
動かすには特別なことはほとんどありません。単純にapt installでDHCPサーバーをインストールするだけです。
root@1giantstar:~# apt insall isc-dhcp-server
インストール後、/etc/dhcp/dhcpd.confを編集し必要な情報を記載します。IPv6のことを考えなくて良くするため、/etc/default/isc-dhcp-serverの定義にINTERFACESv4の指定しましょう。INTERFACESv4を設定し、INTERFACESv6を空("")にしておけば、起動スクリプトで-4が追加され、IPv4のみになります。
その後、仮想ネットワークと干渉しないように、iptablesで固定IPからの要求のみに対応するようにしましょう。この設定で仮想ネットワークからのDHCPクライアント要求はサーバーに通知されなくなります。
root@giantstar:~# iptables -I INPUT -i eth0 -p udp ! -s 203.0.113.1 --dport 67 -j DROP
最近のlinuxカーネルはインターフェース名としてenpとかensとかが使われるのですが、Auzreはeth0じゃないとうまく動かないようです。まぁこの辺りはAzureでdebianを動かす話なので、本ドキュメントでは扱いません。
外部からAuzreに接続することになるので、ネットワークセキュリティグループの設定が必要です。固定IPからのdhcpポート(67)接続を通すように設定してください。もしロードバランサーを使っているなら、NATの設定も必要です。
DHCPリレーオプション
DHCPリレーの構成が以下のようになっている場合、192.168.100.0/24からのDHCPクライアント要求は、DHCPリレーエージェントに中継されます。リレーエージェントの中継先はDHCPサーバーである192.168.900.1です。リレー時、DHCPクライアント要求にはリレーエージェントのIPアドレスが、Gateway IP Address(giaddr)として追加されます。以下の例ではIPアドレスはDHCP要求を受け付けているens2s0のアドレスである192.168.100.1が使用されます。
DHCPサーバーでは要求パケットがリレーされている場合、自身のインタフェースアドレスではなく、giaddrを基準にIPアドレスの配布基準を決定します。例の場合は192.168.100.0/24のネットワークであると判断し、192.168.100.1へ割り当て結果を送信します。
クローズドなネットワークでは、192.168.900.1から192.168.100.1までのネットワークルートは経由するルーターに設定されているので問題ありません。しかし今回のケースにようにDHCPサーバーを外部に設置した場合、問題となります。
外部サーバーでは以下のような構成になりますが、Azure上に存在するDHCPサーバーにens2s0のアドレスである192.168.100.1を通知しても、Azureからの送信では192.168.100.1までのルーティングが存在しないため、結果として不到達となります。見え方としてリレーエージェントからDHCPサーバーへの要求は到達しますが、割り当て結果が届かないという状態なります。
この問題に対応するには、RFC3527で定義されているLink Selection sub-optionを使用する必要があります。
RFC3527はRFC3046で定義されたリレーオプションの追加サブオプションの定義です。RFC3527をサポートするということは、RFC3046のリレーオプションもサポートする必要があります。
RFC3046はDHCP要求パケットにリレー専用のオプションを定義しています。RFC3046で定義しているのは、Agent Circuit IDやAgent Remote IDといったサブオプションで、主にRAS(Remote Access Server)でDHCPリレーエージェントを実行する場合の追加情報です。例えば、RASに複数のモデムが存在する場合、モデム単位にCircuit IDを振って、どのCircuit IDからの要求なのかをDHCPサーバーで判断できるようにします。
RFC3527は、このリレーオプションのサブオプションとしてLink Selectionを追加します。Link Selectionはgiaddrを基準としてたIPアドレスの配布を別なIPアドレスに変更するためのサブオプションです。Link Selectionを使用することにより以下のようなIPアドレスをDHCPサーバーに渡すことが可能となり、不到達の問題が回避できます。
Debianでリレーエージェントを構成してみましょう。
root@dwarfstar:~# apt insall isc-dhcp-relay
でインストールします。
iscのDHCPリレーエージェントで、RFC3527を使用する場合、giaddrに設定したいインターフェースを-Uオプションに指定します。Link Selection側(DHCPクライアント要求を受ける側)は、引数未指定でインターフェース名をとして指定します。
ens2s0 -U ens3s0
このオプションを/etc/default/isc-dhcp-relayに指定すれば先の例のように動作します。
It's a trap!
これでAzureでDHCPサーバーが動くようになりました。めでたしめでたし。ではありませんね。DHCPリレーエージェントをDebianで動かしたので、自組織のネットワークとInternetの間のルーターがDebianである前提になっています。昔はこんな環境もありましたが、今はHGWや専用ルーターなどを使ってInternetに接続しますよね。私のところもそうです。以下のような機材を用意しました。
ルーターがYAMAHAなのは、これまでの実績からです。NVR700WはDHCPリレーエージェントをサポートしていますので、設定してしまいましょう....なんと。RFC3527サポートしてない。ダジゲデ。
一応、機器を手配する前にDHCPリレーエージェントが動くことは確認したんですけどね。キーワードとして、RFC3527が無いから嫌な予感はしてたんですよ。もっと高いルーター買わないとダメなんですかね。でもオーバースペックなんですよね。お高いし。
万策尽きたのでしょうか?。とりあえず老朽化したサーバーの中でで比較的新しいAtomサーバーにDebianをインストールしてプライベートネットワーク内に配置しました。こいつでDHCPリレーエージェントを動かせばなんとかなるんじゃないですか?
だめです。-Uオプションでens2s0を指定してもgiaddrには203.0.113.1が入りません。192.168.100.2が設定されます。まぁ仕組み上は当たり前ですよね。NATのアドレスを知っているのはNVR700Wだけなんですから
Use the Source, Luke. Let go, Luke.
本当に万策尽きたのでしょうか?。-Uにens2s0を指定すると、Link Selectionにはens2s0のアドレスである192.168.100.2が設定されるようです。あとは、giaddrにNATのアドレスが入ってくれれば....ソースいじちゃいましょうか?簡単そうだし。
root@dwarfstar:~# pwd
/root
root@dwarfstar:~# mkdir src
root@dwarfstar:~# cd src
root@dwarfstar:~/src# apt install dpkg-dev
root@dwarfstar:~/src# apt source isc-dhcp-relay
これでsrc配下にパッケージの元のとなるソースがダウンロードされてisc-dhcp-4.4.1ディレクトリが作成されます。このディレクトリはdebian用のbuildスクリプトが反映されたソースツリーです。
root@dwarfstar:~/src# apt build-dep isc-dhcp-relay
これでコンパイルに必要な依存ライブラリがインストールされます。それでは修正に入りましょう。
1374 /* RFC3527: Use the inbound packet's interface address in
1375 * the link selection suboption and set the outbound giaddr
1376 * to the uplink address. */
1377 if (adding_link_select) {
1378 *sp++ = RAI_LINK_SELECT;
1379 *sp++ = 4u;
1380 memcpy(sp, &giaddr.s_addr, 4);
1381 sp += 4;
1382 packet->giaddr = uplink->addresses[0];
1383 log_debug ("Adding link selection suboption"
1384 " with addr: %s", inet_ntoa(giaddr));
1385 }
1382行目が-Uで指定されたインターフェースのIPアドレスをgiaddrに設定しているコードです。以下の1383,1384行のようにしてしまいましょう。
1375 /* RFC3527: Use the inbound packet's interface address in
1376 * the link selection suboption and set the outbound giaddr
1377 * to the uplink address. */
1378 if (adding_link_select) {
1379 *sp++ = RAI_LINK_SELECT;
1380 *sp++ = 4u;
1381 memcpy(sp, &giaddr.s_addr, 4);
1382 sp += 4;
1383 //packet->giaddr = uplink->addresses[0];
1384 packet->giaddr.s_addr = inet_addr("203.0.113.1");
1385 log_debug ("Adding link selection suboption"
1386 " with addr: %s", inet_ntoa(giaddr));
1387 }
もう一つ、DHCPサーバーからの回答でgiaddrを判定している部分があります。以下です。
846 /* Find the interface that corresponds to the giaddr
847 in the packet. */
848 if (packet->giaddr.s_addr) {
849 for (out = interfaces; out; out = out->next) {
850 int i;
851
852 for (i = 0 ; i < out->address_count ; i++ ) {
853 if (out->addresses[i].s_addr ==
854 packet->giaddr.s_addr) {
855 i = -1;
856 break;
857 }
858 }
859
860 if (i == -1)
861 break;
862 }
863 } else {
864 out = NULL;
865 }
853行目が判定部分です。packet側には変更した203.0.113.1が入っているので元に戻します。
846 /* Find the interface that corresponds to the giaddr
847 in the packet. */
848 if (packet->giaddr.s_addr) {
849 for (out = interfaces; out; out = out->next) {
850 int i;
851
852 for (i = 0 ; i < out->address_count ; i++ ) {
853 packet->giaddr.s_addr = out->addresses[i].s_addr;
854 if (out->addresses[i].s_addr ==
855 packet->giaddr.s_addr) {
856 i = -1;
857 break;
858 }
859 }
860
861 if (i == -1)
862 break;
863 }
864 } else {
865 out = NULL;
866 }
853行目のように行を追加してください。
修正前の行番号と修正後の行番号に差があるのは、行追加を行っている関係です。修正前の方を参考にしてください。
この修正で動くはずです。コンパイルしてみましょう。
root@dwarfstar:~/src# cd isc-dhcp-4.4.1/
root@dwarfstar:~/src/isc-dhcp-4.4.1# pwd
/root/src/isc-dhcp-4.4.1
root@dwarfstar:~/src/isc-dhcp-4.4.1# dpkg-buildpackage -rfakeroot -uc -b
これでdebianのbuildスクリプトによってconfigureなど必要な設定が動き、.debパッケージが作成されます。作成場所は一つ上のディレクトリです。
試しに動かしてみましょう。まず最初にDHCPサーバーからの返事がこのマシンに届くようにしないといけません。先の例のようにDebianルーターになっているように見せるということです。これにはNVR700Wの静的マスカレードを使います。以下のコマンドです。
nat descriptor masquerade static 1 1 192.168.100.2 udp dhcps
NVR700Wでなくても普通のルーターなら同じような設定が可能でしょう。NATアドレスの67ポート(dhcps)をDebianサーバーに向ける設定にすれば良いだけです。
ルーターによっては、inputフィルターの設定が必要な場合があるでしょう。Azureのグローバルアドレス(この例では198.51.100.1)からの67ポートへの入力を許可してください。私はNVR700Wの動的フィルタを使用しているので不要でした。
ではいよいよ起動です。以下でDHCPリレーエージェントを起動してDHCPクライアントを適当に動かしましょう。
root@dwarfstar:~# /root/src/isc-dhcp-4.4.1/relay/dhcrelay -U ens2s0 198.51.100.1
確認できたでしょうか?万策は尽きてませんでした。実はここの修正と同じようなことやっているところがあります。Cumulus Networksとい会社の自社Linuxです。マニュアルを見ると-Uオプションに10.0.0.1%loというようなアドレスを指定することが可能のようです。ソース公開されていないので詳細は不明ですが、giaddrに設定するアドレスを引数として拡張されているのではないかと思います。私の目的には上で十分なのでそこまでやりません。
環境セットアップ
うまく動いたので環境を整備しましょう。コンパイルしたパッケージをインストールしてしまう方法もあるのですが、更新で上書きされてしまう可能性があります。changelogをいじって、コンパイルした方が新しいと認識させる方法もありますが、そういう小細工を行うと本当に更新が必要かどうかが分からなくなってしまいます。方針としてコンパイルしたものを別サービスとして動かすことにします。
まずは、パッケージの方を動かなくしましょう。systemctlでdisableにするとinit.dなどの修正が入るようなので、これもやめて以下のようにします。起動スクリプトでこのファイルが無い場合は起動しないようになっているためです。
root@dwarfstar:~# cd /etc/default
root@dwarfstar:/etc/default# mv isc-dhcp-relay isc-dhcp-relay.norun
コンパイルした分の起動スクリプトを作りましょう。元はパッケージのもので良いでしょう。
root@dwarfstar:/etc/default# cd /etc/init.d
root@dwarfstar:/etc/init.d# cp isc-dhcp-relay isc-dhcp-relay-powered-by-chokon
起動スクリプトの先頭のコメントはLSBとかいう/etc/rcX.dへのシンボリックを作るためのメタ情報です。でもただのコメントなので注意しましょう。説明以外の部分は触らないようにしましょう。systemctlコマンドはこのコメントを見て正しい起動順を認識します。ここの文法は私も良く分かっていません。覚えようかなと思っているうちにsysv initの時代が終わってsystemdの時代が始まってしまったので、結局覚えないままです。
#!/bin/sh
#
#
### BEGIN INIT INFO
# Provides: isc-dhcp-relay-powered-by-chokon
# Required-Start: $remote_fs $network
# Required-Stop: $remote_fs $network
# Should-Start: $local_fs
# Should-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: DHCP relay Powered By Chokon
# Description: Dynamic Host Configuration Protocol Relay Powered By Chokon
### END INIT INFO
# It is not safe to start if we don't have a default configuration...
# Source init functions
. /lib/lsb/init-functions
DHCRELAYPID=/var/run/dhcrelay.pid
OPTIONS="-4 -U ens2s0 198.51.100.1"
EXECBIN="/root/src/isc-dhcp-4.4.1/relay/dhcrelay"
case "$1" in
start)
start-stop-daemon --start --quiet --pidfile $DHCRELAYPID \
--exec $EXECBIN -- -q $OPTIONS
;;
stop)
start-stop-daemon --stop --quiet --pidfile $DHCRELAYPID
;;
status)
test -e $DHCRELAYPID
;;
restart | force-reload)
$0 stop
sleep 2
$0 start
;;
*)
echo "Usage: /etc/init.d/isc-dhcp-relay-powered-by-chokon {start|stop|restart|force-reload}"
exit 1
esac
exit 0
元のスクリプトには、/etc/deafult配下のチェック等ありましたが、不要なのでばっさりカットです。またbinaryの場所も起動にマウントされているのでコンパイルソースのものをそのまま使っています。必要なら修正してください。
root@dwarfstar:/etc/init.d# systemctl is-enabled isc-dhcp-relay-powerd-by-chokon
isc-dhcp-relay-powered-by-chokon.service is not a native service, redirecting to systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install is-enabled isc-dhcp-relay-powered-by-chokon
disabled
root@dwarfstar:/etc/init.d# systemctl enable isc-dhcp-relay-powered-by-chokon
isc-dhcp-relay-powered-by-chokon.service is not a native service, redirecting to systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install is-enabled isc-dhcp-relay-powered-by-chokon
enabled
これでサービスが有効になりました。startコマンドで起動できることを確認してください。
root@dwarfstar:/etc/init.d# systemctl start isc-dhcp-relay-powered-by-chokon
root@dwarfstar:/etc/init.d# systemctl status isc-dhcp-relay-powered-by-chokon
● isc-dhcp-relay-powered-by-chokon.service - LSB: DHCP relay Powered by chokon
Loaded: loaded (/etc/init.d/isc-dhcp-relay-powered-by-chokon; generated)
Active: active (running) ...
Docs: man:systemd-sysv-generator(8)
Tasks: 1 (limit: 1059)
Memory: 1.6M
CGroup: /system.slice/isc-dhcp-relay-powered-by-chokon.service
└─1023 /root/src/isc-dhcp-4.4.1/relay/dhcrelay -q -4 -U ens2s0 192.51.100
....
May the Source be with you.
ソースをいじるのはやりすぎでしょうか?仕様上問題のある修正ではありませんよね。実装が無いだけです。もしソースをいじらないのであれば、自分でコードを書いてしまうのが一番の解決方法だと思いますが、DHCPプロトコルを完全に網羅するコードを自力で書くのはなかなか大変だと思います。まぁ人様のソースですが、修正後のものはinhouseとして扱えば、自分で書いたものと運用上の差はあまり無いように思えます。
あとはセキュリティ更新ですね。元のソースにセキュリティfixが入るかもしれません。この対応は、セキュリティ問題が発生した段階で、新しいソースを入手して同じ手順を踏むしかありません。ただ今回修正したコード付近で問題が発生する可能性はそんなに大きくないように思えます。そのため修正箇所を再調査するといった手間はそんなに多くはないでしょう。そもそも自力でコードを書いていたら、人目に触れる機会が低くなるので、誰もセキュリティ問題を指摘してくれないです。そう考えると再コンパイルの手間も安定運用の駄賃と思った方が良さそうです。
どのように考えるにしても、ソースに手を入れるということは、管理も必要となるということです。その覚悟は必要でしょう。
Cloudへの道は半ば
当初の計画では組織内のサーバーを全部排除することでした。今回の対応はこの目的から逸脱してしまいました。
NVR700WにもDHCPサーバー機能は存在するので、そもそもAzureなんかで動かさなければ良いという考えもあるのですが、これまで使っていた設定ファイルをそのまま使いたいです。NVR700WのDHCPサーバー機能とisc-dhcpの一番の違いは、MACアドレスでIPアドレスを固定に払い出す場合の設定方法です。
isc-dhcpは、固定払い出しの設定とレンジによる払い出しの設定が独立しているので、レンジは192.168.100.100-192.168.100.200にして、固定で192.168.100.3を割り当てるという設定が可能です。しかしNVR700WのDHCPサーバーは固定で割り当てる場合もレンジ内である必要があります。スコープという機能を使えば同様のことはできるのですが、かなり無理矢理感のある設定方法です。そんなことする方が悪いんじゃんという意見もあると思いますが、今までやっていた設定が使えないのを目の当たりにすると、モヤモヤするのです。
今は余りのAtomサーバーでDebianを動かしていますが、そんなに負荷のかかる処理でも無いので、そのうち、RaspberryPiとかOpenWRTとかに移行してしまおうと思います。そうすれば専用機器みたいなものなのでサーバー排除計画が達成したことになるでしょう。
おまけ。どうせならRDNSS。
実はもう一つ困ったことがありました。クラウド化に伴ってネットワーク環境の見直しも行いました。IPv6 Readyです。このIPv6ですが、DNSのIPv6アドレス配布に問題があります。
ここに詳しい説明がありますが、Androidだけ鬼子になっています。
IPv6導入前から、この点は把握していたので、自組織へのIPv6アドレス配布はDHCPv6は使用せず、ルーター広告(RA)を使う方針としていました。DNSサーバーアドレスの配布はRDNSSで行うつもりです。でもNVR700WってRDNSSの広告できないんです。また調査不足か。
2021年9月の追記
NVR700Wのファームに修正が入り、RDNSSの広告ができるようになったようです。でもDHCPリレーの方は変わっていないで、私は試していません。もしRDNSS絡みでこのドキュメントを参照されているなら本体のマニュアルを確認された方が良いです。
今回、サーバーを残すことにしたので、こいつにRDNSSを広告するようにしてもらいましょう。以下でインストールです。
root@dwarfstar:~# apt install radvd
radvdは通常はルーターで動作し、IPv6アドレスの広告が主な機能です。RDNSSの広告は追加機能的な位置づけです。今回はルーターではなく、普通のサーバーで動かすので少し設定にコツが必要です。以下のように設定ファイルを作りましょう。IPv6アドレスは例示用のアドレスです。
interface ens2s0 {
# 定期的にルータ広告を送る
AdvSendAdvert on;
# デフォルトルーターでない
AdvDefaultLifetime 0;
AdvCurHopLimit 0;
AdvDefaultPreference low;
AdvSourceLLAddress off;
RDNSS 2001:db8::1 {
};
};
ルーター広告にはデフォルトルーターを通知するフィールドが存在します。このフィールドにはルーター広告を発行しているインターフェースのIPアドレスが設定されます。今回のケースではens2s0のIPv6アドレスです。しかしこのサーバーはルーターではありません。このためクライアントはルーターでないサーバーにパケットを送信して送信障害が発生してしまいます。
IPv4の場合、こういう場合は正しいデフォルトルーターを通知する仕組みがあったと思うのですが、IPv6はそういうのは無いようです。
このような状態にならないようにするには、デフォルトルーターの生存期間を0に設定する必要があります。先の設定例ではAdvDefaultLifetimeがこれに相当します。生存期間が0の場合、クライアントはデフォルトルーターとして採用しなくなります。RDNSSのようなオプションだけを広告したい場合は、この設定を行う必要があります。
あとはAndroidでの確認ですが、AndroidにDNSサーバーを設定を確認する標準GUIは無いようです。getpropという開発ツールを使えば確認できるようですが、私は、Termuxを使いました。このアプリにはgetpropを発行するパッケージがあるので別途インストールしDNSサーバーが上記の設定通りになることを確認できます。これでIPv6 Readyです。KAMEを踊らせましょう。
この中に何人か仲間外れがいる
出来上がった環境で運用を始めたのですが、しばらくするとAzureのDHCPサーバーのログに不思議なログが出始めました。
Nov 29 20:43:40 giantstar dhcpd[53950]: DHCPREQUEST for 192.168.100.24 from 00:00:5e:00:53:00 via eth0
Nov 29 20:43:40 giantstar dhcpd[53950]: DHCPACK on 192.168.100.24 to 00:00:5e:00:53:00 via eth0
このログが結構頻繁に出ています。おかしいなと思って調べると他のアドレスでも同様のログが出ています。一度でだすとリトライするようでかなり頻繁に出ます。
最初に見つけたログは、リコーの複合機のものなのですが、この子は中でNetBSDが動いています。なのでsyslogが参照できるので確認してみました。すると以下のようにDHCPサーバーへの接続エラーが大量に出力されていました。
#[ncsd(77)]20/11/30 06:50:13 DHCP server not found ERR:
#[ncsd(77)]20/11/30 06:52:45 DHCP server not found ERR:
#[ncsd(77)]20/11/30 06:55:18 DHCP server not found ERR:
#[ncsd(77)]20/11/30 06:57:48 DHCP server not found ERR:
...
でも、そもそもこのログを見るためにログインしてますのでIPアドレス自体はちゃんと取得できているようです。何が起きているのでしょうか。電源を切って再投入してログを観察しました。以下は電源投入時のdiscoverです。
Nov 30 15:39:52 giantstar dhcpd[53950]: DHCPDISCOVER from 00:00:5e:00:53:00 via 203.0.113.1
Nov 30 15:39:52 giantstar dhcpd[53950]: DHCPOFFER on 192.168.100.24 to 00:00:5e:00:53:00 via 203.0.113.1
Nov 30 15:39:52 giantstar dhcpd[53950]: DHCPREQUEST for 192.168.100.24 (192.51.100.1) from 00:00:5e:00:53:00 via 203.0.113.1
Nov 30 15:39:52 giantstar dhcpd[53950]: DHCPACK on 192.168.100.24 to 00:00:5e:00:53:00 via 203.0.113.1
リレーサーバー経由で払い出されています。このログと先のログを良く見比べると、viaの後が違います。リレー経由のログは固定IP(203.0.113.1)が表示されていますが、先のログではインターフェース名(eth0)となっています。
この部分がインターフェース名になるのは、リレー経由で接続していない場合です。どうもこの子達は直接DHCPサーバーへ接続しているようです。その結果、DHCPリレーオプションで説明した外部サーバーの構成と同様に、戻りアドレスがプライベートアドレスとなり、DHCPACKが受信できずリトライを繰り返しているようです。
他のクライアントはこのような動作にはならないので、なぜこのような動作になるのかは不明なのですが、このような動作をするクライアントはすべて組み込み系のDHCPクライアントです。HPのLaserjetとかPanasonicの電話機とかです。ちょっと気になる点として、この子達は、割り当て時のClient identifierを認識しないようでした。実装が古いというか限定的なようなのでリレーサーバーの認識も行われないのかもしれません。
なぜクライアントがAzure上のDHCPサーバーに気づくのでしょうか?最初のdiscover時はリレーサーバー経由になっているようです。その謎を探るためパケットを観察しました。その結果、最初のDHCPOFFER時にServer identifierオプションとしてDHCPサーバーのアドレス(192.51.100.1)が渡ってました。
Auzre側のDHCPサーバーの設定を調べると、/etc/dhcpd.confに以下のような記述がありました。これが原因のようです。
server-identifier giantstar;
ここをリレーサーバーのIPアドレスに買い替えると、エラーログはでなくなりました。
server-identifier 192.168.100.2;
iscのDHCPサーバーでは複数のサブネットへのIPアドレス配布をサポートしています。定義の際は、サブネット単位に{}で区切ってアドレスレンジなどを記述します。未確認なのですがserver-identifierは、サブネットの{}単位に定義可能なのだと思います。
Remember, the Source will be with you, always.
Debian 12では、isc-dhcpのベースバージョンが4.4.1から4.4.3へバージョンアップされました。でもこのドキュメントで変更しているソース(dhcrelay.c)は、ほとんど一緒です。行番号に多少変更があるので、変更前の行番号とソースを以下に書いておきます。
giaddrを設定しているコードは以下の行番号になりました。1249行と1250行をドキュメント通りに変更すれば良いでしょう。
1241 /* RFC3527: Use the inbound packet's interface address in
1242 * the link selection suboption and set the outbound giaddr
1243 * to the uplink address. */
1244 if (adding_link_select) {
1245 *sp++ = RAI_LINK_SELECT;
1246 *sp++ = 4u;
1247 memcpy(sp, &giaddr.s_addr, 4);
1248 sp += 4;
1249 packet->giaddr = uplink->addresses[0];
1250 log_debug ("Adding link selection suboption"
1251 " with addr: %s", inet_ntoa(giaddr));
1252 }
DHCPサーバーからの回答部分は以下です。
783 /* Find the interface that corresponds to the giaddr
784 in the packet. */
785 if (packet->giaddr.s_addr) {
786 for (out = interfaces; out; out = out->next) {
787 int i;
788 for (i = 0 ; i < out->address_count ; i++ ) {
789 if (out->addresses[i].s_addr ==
790 packet->giaddr.s_addr) {
791 i = -1;
792 break;
793 }
794 }
795 if (i == -1)
796 break;
797 }
798 } else {
799 out = NULL;
800 }
789行目を同様に変更すれば良いです。
upgradeで/etc/defaultのisc-dhcp-relayが作られてしまうのも同じです。mvしてオリジナルのisc-dhcp-relayが起動しないようにしてください。
あとがき
昔、新世紀エヴァンゲリオンの考察同人誌というのを読んだことがあって、”あとがき”みたいな部分に、”この本を書く上でいろいろなことを調べた(死海文書?とか)。異論や反論があるなら、最低限自分と同じレベルの調査を行ってからじゃないと聞く耳持たない。”というようなことが書いてあって同人誌っていうのは凄い世界だなぁと思いました。