このページの目次
ARP と NDP
IPアドレスからMACアドレスへの変換——IPv4ではARP(ブロードキャスト要求+ユニキャスト応答+キャッシュ老化)、IPv6ではNDP(マルチキャスト近接ノード発見+SLAAC自動設定)を使用します。レイヤー2とレイヤー3の間のこの架橋は、ローカルネットワーク通信の第一歩です。
概要
ARP がなければ、IP パケットはイーサネット上で転送できません——ARP は IP アドレスを MAC アドレスに変換する接着剤プロトコルです。ARP リクエスト(「192.168.1.1 は誰?」)はブロードキャストフレームとして送信され、宛先ホストが自身の MAC を返信します。IPv6 では NDP が ARP に代わり、ICMPv6 を基盤としており、アドレス解決に加え、ルーター発見(RS/RA)とアドレス自動設定(SLAAC)も提供します。ARP は 1982 年に設計されましたが、「LAN 内の全員を信頼する」というセキュリティモデルは現代では非常に脆弱です。しかし、低オーバーヘッドと単純さゆえに、閉鎖型ネットワークでは依然として効率的です。
ARP プロトコル (RFC 826)
フレーム形式
ARP は Ethernet フレーム内に直接カプセル化されます (EtherType=0x0806)。IP レヤーは存在しません:
Hardware Type: 2 bytes — 1 = Ethernet
Protocol Type: 2 bytes — 0x0800 = IPv4
HW Addr Len: 1 byte — 6 (MAC)
Protocol Addr Len: 1 byte — 4 (IPv4)
Operation: 2 bytes — 1=Request, 2=Reply, (RARP は 3/4、ほとんど使われない)
Sender HW Addr: 6 bytes (MAC)
Sender Protocol Addr: 4 bytes (IPv4)
Target HW Addr: 6 bytes (Request では 0 パディング)
Target Protocol Addr: 4 bytes (IPv4)
Request はブロードキャスト MAC (ff:ff:ff:ff:ff:ff) 宛てに送信され、Reply はユニキャストです。ARP メッセージの総長は 28 bytes + イーサネットヘッダー 18 bytes = 46 bytes です——これはイーサネットの最小ペイロード長にちょうど一致し、パディングは不要です。
カーネル ARP 状態機械
各 neighbor entry には以下の状態があります (include/net/neighbour.h):
NUD_NONE: 新規作成、状態なし
NUD_INCOMPLETE: ARP リクエストを送信済み、応答を待機中 — タイムアウト後に再試行
NUD_REACHABLE: 直近で到達可能を確認(通常状態)
NUD_STALE: 期限切れだがクリーンアップされていない — 次回使用時に DELAY に移行
NUD_DELAY: TCP 確認応答を待機中(5秒間)
NUD_PROBE: ARP プロブを送信(最大3回、1秒間隔)
NUD_FAILED: プロブが3回失敗 — entry を削除
状態遷移:
重要: base_reachable_time(デフォルト 30秒)経過後の NUD_STALE 状態では、能動的に ARP を送信しません。カーネルは、実際にその近接ノード宛てにデータパケットを送信する際にのみ、DELAY → PROBE のチェーンを通じて到達可能性を確認します。これにより、ネットワークがアイドル状態のときに意味のない ARP トラフィックが大量に発生するのを防ぎます。
/proc によるチューニング
Gratuitous ARP
送信元 IP = 宛先 IP かつ送信元 MAC != 0 の ARP Request のことです。イーサネットブロードキャスト + 宛先 IP が自身の IP という、「自問自答」の特殊な形式です。用途は以下の通りです:
- IP の切り替え(VRRP/HSRP/CARP): 仮想 IP が1つの物理ホストから別のホストに移行 → Gratuitous ARP を送信 → スイッチと近接ノードの ARP キャッシュを更新 → 新所有者が即座にトラフィックを受信
- 重複 IP の検出: IP を設定直後 → Gratuitous ARP を送信 → 応答があれば → IP 衝突
- ARP キャッシュの更新: 「私の MAC は変わっていないが、キャッシュを更新してほしい」と能動的に通知(あまり使われない)
Proxy ARP
ルーターが、同じ物理セグメントにいない宛先の ARP Request に代わって応答します:
Host A: (10.0.1.100 に ping を送信、ただしこれは自分のサブネット外)
→ ARP 「10.0.1.100 は誰?」
Router: (Host A の ARP を確認し、10.0.1.0/24 へのルートを持っている)
→ ARP Reply(自身の MAC で): 「10.0.1.100 は <router_MAC> です」
Host A: → IP パケットをルーターの MAC 宛てに送信
Router: → ルーティングテーブルに従って転送
非推奨——設定ミス(Host A は 10.0.1.100 がローカル到達可能と考えるのではなく、ルーターへのルートを持つべき)を隠蔽してしまいます。Linux のデフォルトは proxy_arp=0 です。
NDP (IPv6 近接ノード発見, RFC 4861)
NDP は ICMPv6 を介して 5 種類のメッセージを伝達し、ARP の全機能を置き換えるとともに、アドレス自動設定を追加しました:
ICMPv6 メッセージ形式
すべての NDP メッセージは、IPv6 Next Header=58 の後に共通の ICMPv6 ヘッダー形式を共有します:
Type (1 byte): 133=RS, 134=RA, 135=NS, 136=NA, 137=Redirect
Code (1 byte): 0 (Redirect を除く)
Checksum: ICMPv6擬似ヘッダーチェックサム(IPv6 src/dst を含む)
Reserved: (4 bytes、ゼロ)
Message Body: タイプ固有のオプション
オプション(TLV 形式):
[Type 1] [Length 1] [Value...]
Length = 8バイト単位(Type+Length 自体を含む)
→ Type=1 Len=1 → 合計 8バイト、Value=6バイト
主要オプション:
Type 1: ソース/ターゲットリンク層アドレス(MAC)
Type 2: プレフィックス情報(RA に含まれる: プレフィックス + プレフィックス長 + フラグ)
Type 3: リダイレクトヘッダー
Type 5: MTU(RA)
Neighbor Solicitation (NS, type 135)
宛先: 「この IPv6 は誰?MAC を教えて」
宛先 IPv6 の solicited-node マルチキャストアドレス宛てに送信:
ff02::1:ff<宛先 IPv6 アドレスの下位24ビット>
送信元アドレス: 送信インターフェースの link-local(または、すでに持っている場合はユニキャスト)
オプション: Source Link-Layer Address(自身の MAC)
Neighbor Advertisement (NA, type 136)
宛先: 「この IPv6 私です、私の MAC は X です」
NS の送信元アドレス(ユニキャスト)または全ノード(マルチキャスト、DAD 衝突の場合)宛てに送信
フラグ:
R (Router): 送信者がルーター(RA の Router Lifetime に基づく)
S (Solicited): NS への応答である(1 に設定)
O (Override): 既存の NCE を上書き(0 の場合、受信者は古いエントリを保持可能)
オプション: Target Link-Layer Address
SLAAC (ステートレスアドレス自動設定, RFC 4862)
ホスト:
1. link-local アドレスを生成: fe80::/64 プレフィックス + IID
2. RS (Router Solicitation, type 133) を送信
送信元: ::(未指定アドレス)または link-local
宛先: ff02::2(all-routers マルチキャスト)
ルーター:
3. RA (Router Advertisement, type 134) で応答
送信元: ルーターの link-local
宛先: RS への応答の場合 → RS の送信元アドレス宛て(ユニキャスト)
定期的な場合 → ff02::1(all-nodes マルチキャスト)
RA には Prefix Information (Option 2) が含まれる:
プレフィックス: (例: 2001:db8:abcd::)
プレフィックス長: (例: 64)
L (on-link): このプレフィックスは同じリンク上にある → 直接通信可能
A (autonomous): このプレフィックスを使用して SLAAC → グローバルアドレスを生成
Valid Lifetime: アドレスの有効期間(通常 2592000秒 = 30日)
Preferred Lifetime: アドレスの「優先」期間(到達後、deprecated だが削除はされない)
RA には Router Lifetime が含まれる:
0: このルーターはデフォルトゲートウェイとして使用不可
>0: デフォルトルートの有効秒数(最大 65535 ≈ 18時間)
SLAAC アドレス生成
EUI-64(非推奨、MAC が露出する):
MAC: aa:bb:cc:dd:ee:ff
→ IID: aa:bb:cc:ff:fe:dd:ee:ff
(fffe を挿入、U/L ビットを反転)
→ 完全なアドレス: 2001:db8:abcd:0:a8bb:ccff:fedd:eeff/64
プライバシー拡張 (RFC 8981、Linux デフォルト):
ランダム IID + 短い有効期間 → 定期的に新しいアドレスを生成
→ 古いアドレスは deprecated だが保持(既存接続は切断されない)
→ 新しいアドレスは preferred → 新しい接続では新しいアドレスを使用
→ ネットワーク間での追跡を防止
DAD (重複アドレス検出)
生成されたすべての IPv6 アドレス(link-local または global)は、使用可能になる前に DAD を実行する必要があります:
1. NS を送信: target = 検証するアドレス, 送信元 = ::(未指定アドレス)
2. NA が返信された場合(DAD 衝突)→ そのアドレスは使用不可
3. DupAddrDetectTransmits 回まで送信(デフォルト 1回)
4. NA が 0 個受信 → アドレス使用可能 → tentative から preferred へ移行
注意: DAD はアドレス設定時、ネットワーク接続直後に実行する必要があります
→ 2台のデバイスが同時に同じアドレスを生成した場合(衝突確率は極めて低い)→ 2台目が検出 → 無効化
ARP の攻撃面と防御
ARP スプーフィング(Man-in-the-Middle):
攻撃者: 偽造された ARP Reply を送信 → 宛先の IP を攻撃者の MAC にマッピング
→ トラフィックが攻撃者にリダイレクト
防御:
1. DAI (Dynamic ARP Inspection): スイッチが ARP の正当性をチェック
(DHCP snooping DB + 静的バインディング)— 企業ネットワーク向け
2. 暗号化トンネル: ARP がポイズニングされても → トラフィックは暗号化 → 中間者は閲覧不可
これが唯一の完全な解決策である——トラフィックが
WireGuard/Tailscale で暗号化されていれば、ARP スプーフィングは無効
3. 静的 ARP: `ip neigh add 192.168.1.1 lladdr xx:xx:xx dev eth0 nud permanent`
最も安全だが、大規模な管理は不可能
参考
- RFC: 826 (ARP), 4861 (NDP), 4862 (SLAAC), 8981 (Privacy Extensions)
- ソースコード:
net/ipv4/arp.c,net/core/neighbour.c(ARP 状態機械),net/ipv6/ndp.c - ツール:
ip neigh,arp -a,arping -I eth0 192.168.1.1,tcpdump arp/tcpdump icmp6
キーワード: ARP, NUD 状態機械, gratuitous ARP, proxy ARP, NDP, NS/NA, RS/RA, SLAAC, EUI-64, DAD, solicited-node multicast