一覧NW-14-2 · DHCPと時刻同期

DHCPリレーと配布オプション

割当て範囲と端末設定を照合し、重複とオプション誤配布を特定します。

説明

DHCPからアドレスを取得できても、予約と手動設定の重複や、ルーターの誤配布があると通信は正常に進みません。サーバー側の設定と端末が実際に受け取った値を合わせて確認します。

リレーのgiaddrから配布scopeを求め、予約と別端末の手動設定、ARP応答を照合します。次に配布されたルーターが端末の範囲外にあることを確かめ、遠隔DNSとの位置関係を整理します。

1リレーが示す端末側のネットワーク

relayed.binはリレーが転送するDiscoverのDHCP本体です。hopsとgiaddrを取り出します。

python3 -cリレーが更新した固定欄 'from pathlib import Path; import socket; b=Path("relayed.bin").read_bytes(); print(b[3],socket.inet_ntoa(b[24:28]),b[28:34].hex(":"))'hops・giaddr・元のMAC

hops1、giaddr10.20.0.1、元の端末MAC末尾0aです。サーバーが別ネットワークにあっても、リレーが端末側リンクの情報を伝えます。外側IPの送信元だけで割当て範囲を決めるとは限りません。

2giaddrに対応するscope

この例ではgiaddrを含むscopeを選ぶ設定です。config.jsonには二つの配布範囲があります。

python3 -cリレー情報から配布範囲を選択 'import json,ipaddress; c=json.load(open("config.json")); print([(network,options) for network,options in c["scopes"].items() if ipaddress.ip_address(c["relay"]) in ipaddress.ip_network(network)])'giaddrを含むscope

10.20.0.0/24が選ばれ、ルーターは10.20.0.1です。サーバーの接続先ネットワークと、端末に貸すアドレスのネットワークは同じとは限りません。実装によってはOption82等の情報も選択に使います。

3予約と手動設定が重なった場合

端末MAC末尾0aへの予約と、別端末の手動設定を照合します。

python3 -c予約と手動IPを比較 'import json; c=json.load(open("config.json")); print(c["reservation"]); print(c["static_hosts"]); print(set(c["reservation"].values())&set(c["static_hosts"].values()))'同じアドレスの使用

どちらも10.20.0.50で重複しています。予約はこのサーバーの割当て方針です。別端末による同じIPアドレスの手動設定を防ぐ機能ではないため、手動使用範囲・予約・動的プールを合わせて管理します。

4実際に観測した応答と照合する

同じリンクで観測したARP応答の対応を保存した記録です。

cat arp.csv一つのIPへの二つの応答

同じIPに二つのMACが応答しています。これだけなら冗長構成の切替等も候補ですが、前の設定記録では別端末の手動設定との重複が確認できます。この例では手動端末のIPを管理された別の値へ変更する必要があります。

5配布されたルーターが端末の範囲外

端末に適用されたleaseのルーターを、配布プレフィックスと比較します。

python3 -c端末の範囲とルーターを比較 'import json,ipaddress; c=json.load(open("config.json")); l=c["lease"]; network=ipaddress.ip_interface(l["ip"]+"/"+str(l["prefix"])).network; print(network,l["router"],ipaddress.ip_address(l["router"]) in network)'適用されたオプションの整合

端末は10.20.0.0/24ですが、配布ルーターは10.10.0.1で範囲外です。この構成は特別なオンリンク指定等を使わない通常のLANなので、scopeの意図と違うオプションが配られています。アドレス取得成功だけで外部通信まで正常とは判断できません。

6正しいルーターと遠隔DNS

この端末に意図したルーター10.20.0.1とDNS192.0.2.53の位置を確認します。

python3 -cルーターとDNSの配置 'import ipaddress; n=ipaddress.ip_network("10.20.0.0/24"); print([(a,ipaddress.ip_address(a) in n) for a in ("10.20.0.1","192.0.2.53")])'同じリンクである必要の違い

ルーターは同じリンク、DNSは別ネットワークです。DNSが範囲外であること自体は誤りではなく、ルーター経由で到達できれば利用できます。重複IPの解消と配布ルーターの訂正後、端末に設定が反映されたことと、DNSへの経路を再確認します。

まとめ

リレー情報に対応するscopeと、端末へ適用したオプションを照合します。

予約は手動IPの重複を防ぐ認証ではなく、端末状態やARP記録も確認します。

操作履歴と解説を振り返れます。

シェル / PCAPと通信の記録

1
python3 -c 'from pathlib import Path; import socket; b=Path("relayed.bin").read_bytes(); print(b[3],socket.inet_ntoa(b[24:28]),b[28:34].hex(":"))'
1 10.20.0.1 02:00:00:00:00:0a
2
python3 -c 'import json,ipaddress; c=json.load(open("config.json")); print([(network,options) for network,options in c["scopes"].items() if ipaddress.ip_address(c["relay"]) in ipaddress.ip_network(network)])'
[('10.20.0.0/24', {'router': '10.20.0.1', 'dns': '192.0.2.53'})]
3
python3 -c 'import json; c=json.load(open("config.json")); print(c["reservation"]); print(c["static_hosts"]); print(set(c["reservation"].values())&set(c["static_hosts"].values()))'
{'02:00:00:00:00:0a': '10.20.0.50'}
{'02:00:00:00:00:14': '10.20.0.50'}
{'10.20.0.50'}
4
cat arp.csv
IP,MAC
10.20.0.50,02:00:00:00:00:0a
10.20.0.50,02:00:00:00:00:14
5
python3 -c 'import json,ipaddress; c=json.load(open("config.json")); l=c["lease"]; network=ipaddress.ip_interface(l["ip"]+"/"+str(l["prefix"])).network; print(network,l["router"],ipaddress.ip_address(l["router"]) in network)'
10.20.0.0/24 10.10.0.1 False
6
python3 -c 'import ipaddress; n=ipaddress.ip_network("10.20.0.0/24"); print([(a,ipaddress.ip_address(a) in n) for a in ("10.20.0.1","192.0.2.53")])'
[('10.20.0.1', True), ('192.0.2.53', False)]

演習完了