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

DHCPによるアドレス割り当てとリース更新

メッセージの欄と時刻から、割当てと更新の状態を確認します。

説明

DHCPはIPアドレスと関連設定を一定期間貸し出します。初めて取得するときと、取得済みのアドレスを更新するときでは、同じメッセージ名でも送信先や欄の内容が違います。

四つの交換をPCAPで確認し、XID、Requested IP、Server Identifier、リース時間をバイト列で照合します。続いてT1・T2・期限と更新の交換を調べ、新しい期限を計算します。

1割当ての四つのメッセージ

端末MAC末尾0aが新たにIPv4アドレスを取得する交換を表示します。

tcpdump -n -tt -vvv種類・送受信先・オプション -r allocation.pcap割当ての交換

Discover、Offer、Request、ACKの順です。端末はアドレス未設定のため0.0.0.0から送信し、この例ではbroadcastフラグに合わせて応答もブロードキャストされています。すべての場面で応答が必ずブロードキャストになるわけではありません。

2同じ取引を識別するXID

要求とACKの固定部分からXID、yiaddr、クライアントMACを取り出します。

python3 -c固定部分の対応を確認 'from pathlib import Path; import socket; rows=[Path(n).read_bytes() for n in ("request.bin","ack.bin")]; print([(hex(int.from_bytes(b[4:8],"big")),socket.inet_ntoa(b[16:20]),b[28:34].hex(":")) for b in rows])'XID・割当てIP・MAC

XID0x3903f326とMACが対応しています。要求のyiaddrは0、ACKのyiaddrは割当てる192.0.2.10です。送信元IPだけでは未設定の端末を特定できないため、取引IDやクライアント識別情報を照合します。

3どの提案を選んだか

Requestのオプション部分を表示します。236バイトの固定部分と4バイトのcookieの後から始まります。

xxd -g 1 -s 236 -l 32固定部分の後の32バイト request.bin選択した提案

63 82 53 63がcookieです。35 01 03はoption53・長さ1・DHCPREQUEST、32 04 c0 00 02 0aは要求IP192.0.2.10、36 04 c0 00 02 02は選んだサーバー192.0.2.2です。Offerが複数あっても、選択した相手をRequestで知らせます。

4ACKで受け取る設定

ACKのオプションを表示します。

xxd -g 1 -s 240 -l 60ACKのオプション ack.binリースと配布設定

option51の00000e10はリース3600秒、58の00000708はT1=1800秒、59の00000c4eはT2=3150秒です。さらにサブネットマスク、ルーター、DNSのIPが続きます。IPアドレスだけでなく、通信に必要な設定を合わせて受け取ります。

5更新と再結合の時刻

このACKのT1・T2・リース期限を使い、取得後の経過時間で状態を分類します。

python3 -cT1・T2・期限を比較 't1,t2,lease=1800,3150,3600; print([(t,"BOUND" if t<t1 else "RENEWING" if t<t2 else "REBINDING" if t<lease else "EXPIRED") for t in (1700,1800,3200,3600)])'経過秒による状態

T1で元のサーバーへ更新を試み、T2まで応答がなければ他のサーバーにも再結合を求めます。期限までACKが得られなければ、そのアドレスの使用を止めます。ここではACKに明示された時刻を使い、毎回同じ秒数が固定で使われるという意味ではありません。

6更新時は既にあるアドレスを使う

renewal.pcapは元のサーバーへのユニキャスト更新要求とACKです。

tcpdump -n -tt -vvv更新時の送受信先とciaddr -r renewal.pcapユニキャスト更新

送信元192.0.2.10、宛先192.0.2.2で、ciaddrに現在のアドレスが入ります。最初の選択時と違い、この更新要求にはRequested IPとServer Identifierのオプションを入れません。DHCPREQUESTという種類が同じでも、状態によって欄の使い方が変わります。

7更新後の期限を求める

取得から1800秒で更新が成功し、新たに3600秒のリースを受け取ったとします。

python3 -c更新後の基準時刻を適用 'renewed_at=1800; lease=3600; print(renewed_at+lease); print(renewed_at+1800,renewed_at+3150)'新しい期限と更新開始時刻

この例の新しい使用期限は元の時刻基準で5400秒、次のT1は3600秒、T2は4950秒です。最初の期限3600秒のままではなく、更新成功時の条件で管理し直します。

まとめ

同じ取引のXIDと端末情報を照合し、選択したサーバーと割当てIPを確認します。

T1・T2・リース期限と、更新成功後の期限を管理します。

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

シェル / PCAPと通信の記録

1
tcpdump -n -tt -vvv -r allocation.pcap
reading from file allocation.pcap, link-type EN10MB (Ethernet)
0.000000 IP (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.68 > 255.255.255.255.67: [udp sum ok] BOOTP/DHCP, Request from 02:00:00:00:00:0a, length 300, xid 0x3903f326, Flags [Broadcast] (0x8000)
	  Client-Ethernet-Address 02:00:00:00:00:0a
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: Discover
	    END (255), length 0
	    PAD (0), length 0, occurs 56
0.010000 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto UDP (17), length 328)
    192.0.2.2.67 > 255.255.255.255.68: [udp sum ok] BOOTP/DHCP, Reply, length 300, xid 0x3903f326, Flags [Broadcast] (0x8000)
	  Your-IP 192.0.2.10
	  Client-Ethernet-Address 02:00:00:00:00:0a
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: Offer
	    Server-ID (54), length 4: 192.0.2.2
	    Lease-Time (51), length 4: 3600
	    RN (58), length 4: 1800
	    RB (59), length 4: 3150
	    Subnet-Mask (1), length 4: 255.255.255.0
	    Default-Gateway (3), length 4: 192.0.2.1
	    Domain-Name-Server (6), length 4: 192.0.2.53
	    END (255), length 0
	    PAD (0), length 0, occurs 14
0.020000 IP (tos 0x0, ttl 64, id 2, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.68 > 255.255.255.255.67: [udp sum ok] BOOTP/DHCP, Request from 02:00:00:00:00:0a, length 300, xid 0x3903f326, Flags [Broadcast] (0x8000)
	  Client-Ethernet-Address 02:00:00:00:00:0a
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: Request
	    Requested-IP (50), length 4: 192.0.2.10
	    Server-ID (54), length 4: 192.0.2.2
	    END (255), length 0
	    PAD (0), length 0, occurs 44
0.030000 IP (tos 0x0, ttl 64, id 3, offset 0, flags [none], proto UDP (17), length 328)
    192.0.2.2.67 > 255.255.255.255.68: [udp sum ok] BOOTP/DHCP, Reply, length 300, xid 0x3903f326, Flags [Broadcast] (0x8000)
	  Your-IP 192.0.2.10
	  Client-Ethernet-Address 02:00:00:00:00:0a
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: ACK
	    Server-ID (54), length 4: 192.0.2.2
	    Lease-Time (51), length 4: 3600
	    RN (58), length 4: 1800
	    RB (59), length 4: 3150
	    Subnet-Mask (1), length 4: 255.255.255.0
	    Default-Gateway (3), length 4: 192.0.2.1
	    Domain-Name-Server (6), length 4: 192.0.2.53
	    END (255), length 0
	    PAD (0), length 0, occurs 14
2
python3 -c 'from pathlib import Path; import socket; rows=[Path(n).read_bytes() for n in ("request.bin","ack.bin")]; print([(hex(int.from_bytes(b[4:8],"big")),socket.inet_ntoa(b[16:20]),b[28:34].hex(":")) for b in rows])'
[('0x3903f326', '0.0.0.0', '02:00:00:00:00:0a'), ('0x3903f326', '192.0.2.10', '02:00:00:00:00:0a')]
3
xxd -g 1 -s 236 -l 32 request.bin
000000ec: 63 82 53 63 35 01 03 32 04 c0 00 02 0a 36 04 c0  c.Sc5..2.....6..
000000fc: 00 02 02 ff 00 00 00 00 00 00 00 00 00 00 00 00  ................
4
xxd -g 1 -s 240 -l 60 ack.bin
000000f0: 35 01 05 36 04 c0 00 02 02 33 04 00 00 0e 10 3a  5..6.....3.....:
00000100: 04 00 00 07 08 3b 04 00 00 0c 4e 01 04 ff ff ff  .....;....N.....
00000110: 00 03 04 c0 00 02 01 06 04 c0 00 02 35 ff 00 00  ............5...
00000120: 00 00 00 00 00 00 00 00 00 00 00 00              ............
5
python3 -c 't1,t2,lease=1800,3150,3600; print([(t,"BOUND" if t<t1 else "RENEWING" if t<t2 else "REBINDING" if t<lease else "EXPIRED") for t in (1700,1800,3200,3600)])'
[(1700, 'BOUND'), (1800, 'RENEWING'), (3200, 'REBINDING'), (3600, 'EXPIRED')]
6
tcpdump -n -tt -vvv -r renewal.pcap
reading from file renewal.pcap, link-type EN10MB (Ethernet)
0.000000 IP (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto UDP (17), length 328)
    192.0.2.10.68 > 192.0.2.2.67: [udp sum ok] BOOTP/DHCP, Request from 02:00:00:00:00:0a, length 300, xid 0x3903f326, Flags [none] (0x0000)
	  Client-IP 192.0.2.10
	  Client-Ethernet-Address 02:00:00:00:00:0a
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: Request
	    END (255), length 0
	    PAD (0), length 0, occurs 56
0.010000 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto UDP (17), length 328)
    192.0.2.2.67 > 192.0.2.10.68: [udp sum ok] BOOTP/DHCP, Reply, length 300, xid 0x3903f326, Flags [none] (0x0000)
	  Client-IP 192.0.2.10
	  Your-IP 192.0.2.10
	  Client-Ethernet-Address 02:00:00:00:00:0a
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: ACK
	    Server-ID (54), length 4: 192.0.2.2
	    Lease-Time (51), length 4: 3600
	    RN (58), length 4: 1800
	    RB (59), length 4: 3150
	    Subnet-Mask (1), length 4: 255.255.255.0
	    Default-Gateway (3), length 4: 192.0.2.1
	    Domain-Name-Server (6), length 4: 192.0.2.53
	    END (255), length 0
	    PAD (0), length 0, occurs 14
7
python3 -c 'renewed_at=1800; lease=3600; print(renewed_at+lease); print(renewed_at+1800,renewed_at+3150)'
5400
3600 4950

演習完了