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・元のMAChops1、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を含むscope10.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と通信の記録
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
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'})]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'}cat arp.csvIP,MAC 10.20.0.50,02:00:00:00:00:0a 10.20.0.50,02:00:00:00:00:14
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
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)]演習完了