---
title: ARP と NDP
url: https://doc.liz6.com/ja/networking/01-l2-data-link/02-arp-and-ndp
locale: ja
area: networking
tags:
- networking
- l2-data-link
date: 2026-06-30
modified: 2026-07-16
description: IPアドレスからMACアドレスへの変換——IPv4ではARP（ブロードキャスト要求＋ユニキャスト応答＋キャッシュ老化）、IPv6ではNDP（マルチキャスト近接ノード発見＋SLAAC自動設定）を使用します。レイヤー2とレイヤー3の間のこの架橋は、ローカルネットワーク通信の第一歩です。
---

# 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 を削除
```

状態遷移:

<svg viewBox="0 0 720 420" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="ARP カーネル状態機械: NUD 近接ノード表項の作成から無効化までの完全な状態遷移">
  <defs>
    <marker id="arpnud-ah" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="420" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">カーネル ARP 状態機械: NUD 近接ノード表項のライフサイクル</text>

  <rect x="20" y="54" width="130" height="40" rx="7" fill="#e2e8f0"/>
  <text x="85" y="70" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">NUD_NONE</text>
  <text x="85" y="85" text-anchor="middle" font-size="10" fill="#475569">新規作成・状態なし</text>

  <rect x="210" y="54" width="190" height="40" rx="7" fill="#e0e7ff" stroke="#c7d2fe"/>
  <text x="305" y="70" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">NUD_INCOMPLETE</text>
  <text x="305" y="85" text-anchor="middle" font-size="10" fill="#4f46e5">ARP request 送信済み・reply 待機中</text>

  <rect x="500" y="54" width="160" height="40" rx="7" fill="#dcfce7" stroke="#4ade80"/>
  <text x="580" y="70" text-anchor="middle" font-size="12" font-weight="700" fill="#166534">NUD_REACHABLE</text>
  <text x="580" y="85" text-anchor="middle" font-size="10" fill="#15803d">直近で到達可能確認（通常）</text>

  <rect x="210" y="140" width="190" height="40" rx="7" fill="#ffffff" stroke="#ef4444" stroke-width="1.5"/>
  <text x="305" y="156" text-anchor="middle" font-size="12" font-weight="700" fill="#dc2626">NUD_FAILED</text>
  <text x="305" y="171" text-anchor="middle" font-size="10" fill="#dc2626">entry 削除（再作成が必要）</text>

  <rect x="500" y="140" width="160" height="40" rx="7" fill="#ffedd5"/>
  <text x="580" y="156" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">NUD_STALE</text>
  <text x="580" y="171" text-anchor="middle" font-size="10" fill="#c2410c">期限切れだが未クリーンアップ</text>

  <rect x="330" y="226" width="190" height="40" rx="7" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="425" y="242" text-anchor="middle" font-size="12" font-weight="700" fill="#3730a3">NUD_DELAY</text>
  <text x="425" y="257" text-anchor="middle" font-size="10" fill="#4f46e5">TCP 確認応答を待機中（5秒）</text>

  <rect x="330" y="312" width="190" height="40" rx="7" fill="#ffedd5"/>
  <text x="425" y="328" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">NUD_PROBE</text>
  <text x="425" y="343" text-anchor="middle" font-size="10" fill="#c2410c">ARP probe 送信（最大3回、1秒間隔）</text>

  <line x1="150" y1="74" x2="205" y2="74" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="177" y="66" text-anchor="middle" font-size="10.5" fill="#475569">ARP request 送信</text>

  <line x1="305" y1="94" x2="305" y2="136" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="313" y="118" font-size="10.5" fill="#475569">タイムアウト（1秒）</text>

  <line x1="400" y1="74" x2="496" y2="74" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="448" y="66" text-anchor="middle" font-size="10.5" fill="#475569">ARP reply 受信</text>

  <line x1="580" y1="94" x2="580" y2="136" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="588" y="118" font-size="10.5" fill="#475569">30秒で期限切れ</text>

  <line x1="540" y1="180" x2="463" y2="222" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="505" y="196" text-anchor="middle" font-size="10.5" fill="#475569">データパケットがこの entry 宛てに送信</text>

  <polyline points="520,240 695,240 695,64 660,64" fill="none" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="605" y="233" text-anchor="middle" font-size="10.5" fill="#475569">5秒以内に TCP 確認応答 → ARP 不要！</text>

  <line x1="425" y1="266" x2="425" y2="308" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="433" y="290" font-size="10.5" fill="#475569">5秒経過しても確認応答なし</text>

  <polyline points="520,332 705,332 705,84 660,84" fill="none" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="682" y="76" text-anchor="middle" font-size="10.5" fill="#475569">ARP reply</text>

  <polyline points="330,332 140,332 140,160 205,160" fill="none" stroke="#475569" stroke-width="1.6" marker-end="url(#arpnud-ah)"/>
  <text x="132" y="250" text-anchor="end" font-size="10.5" fill="#475569">3回失敗</text>

  <rect x="40" y="368" width="640" height="44" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="56" y="386" font-size="12.5" fill="#115e59">重要: NUD_STALE では能動的に ARP を送信しない——カーネルは、</text>
  <text x="56" y="402" font-size="12.5" fill="#115e59">実際にその近接ノード宛てにデータパケットを送信する際にのみ、</text>
</svg>

重要: `base_reachable_time`（デフォルト 30秒）経過後の NUD_STALE 状態では、**能動的に ARP を送信しません**。カーネルは、実際にその近接ノード宛てにデータパケットを送信する際にのみ、DELAY → PROBE のチェーンを通じて到達可能性を確認します。これにより、ネットワークがアイドル状態のときに意味のない ARP トラフィックが大量に発生するのを防ぎます。

### /proc によるチューニング

```bash
cat /proc/sys/net/ipv4/neigh/default/gc_stale_time    # STALE entry の生存時間（デフォルト 60秒）
cat /proc/sys/net/ipv4/neigh/default/base_reachable_time # REACHABLE 状態のタイムアウト（デフォルト 30秒）
cat /proc/sys/net/ipv4/neigh/default/delay_first_probe_time # DELAY 状態の持続時間（デフォルト 5秒）
cat /proc/sys/net/ipv4/neigh/default/mcast_solicit     # マルチキャストプロブ回数（デフォルト 3）
```

### Gratuitous ARP

送信元 IP = 宛先 IP かつ送信元 MAC != 0 の ARP Request のことです。イーサネットブロードキャスト + 宛先 IP が自身の IP という、「自問自答」の特殊な形式です。用途は以下の通りです:

1. IP の切り替え（VRRP/HSRP/CARP）: 仮想 IP が1つの物理ホストから別のホストに移行 → Gratuitous ARP を送信 → スイッチと近接ノードの ARP キャッシュを更新 → 新所有者が即座にトラフィックを受信
2. 重複 IP の検出: IP を設定直後 → Gratuitous ARP を送信 → 応答があれば → IP 衝突
3. 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*
