ARP と NDP

このページの目次

同一サブネット内では ping が通るが、遠隔地にはアクセスできない場合、まずルーティングによって選択された次のホップ(ゲートウェイ)を確認し、その後、その次のホップの近傍解決を行います。ARP/NDP は現在のリンク上の隣接問題を解決するものであり、インターネット上の宛先を見つける役割は負いません。キャッシュに有効なマッピングが存在する場合や、ポイントツーポイントリンク、あるいは他の隣接メカニズムを使用している場合は、毎回解決要求を送信する必要はありません。

問題この記事の入口混同しないこと
ARP はゲートウェイに問い合わせるか、サーバーに問い合わせるかルーティングと解決の順序最終的な宛先 IP と次のホップ IP
28 バイトの ARP なのに 42/60 バイトがキャプチャされる理由ヘッダーのバイト計算プロトコル、イーサネットヘッダー、パディング、FCS
STALE は必ずクリアする必要があるか近傍の状態利用可能なマッピングと直近の到達可能性の証明
IPv6 でどのように近傍を発見するかNS/NA とマルチキャストアドレス解決、到達性プローブ、DAD
RA がアドレスとデフォルトルートに与える影響自動設定プレフィックスの L/A フラグ、アドレスのライフサイクル、ルーターのライフサイクル
暗号化トンネルはなりすましを防ぐかセキュリティの境界認証、転送、可用性

跨サブネット送信の例から

A が 192.0.2.10/24、デフォルトゲートウェイが 192.0.2.1 で、198.51.100.20 にアクセスしようとするとします。ルーティングの照合によりゲートウェイが次のホップとして選択され、A は接続されているインターフェースの近傍テーブルを照会します。マッピングがない場合にのみ、192.0.2.1 の MAC アドレスを解決し、遠隔地の 198.51.100.20 に対してブロードキャストは行いません。

まず次のホップを選択し、その MAC を解決する:跨サブネットでは遠隔地に対して直接 ARP を行わない ホスト A 192.0.2.10/24 ローカルリンクのゲートウェイ 192.0.2.1 ARP ブロードキャスト:192.0.2.1 の MAC は誰? ARP 応答:ゲートウェイ IP に対応するゲートウェイ MAC IP 宛先は依然として 198.51.100.20、レイヤー2の宛先はゲートウェイ MAC 最初の要求は所属するブロードキャストドメイン内で伝播します。図では関連するゲートウェイのみを描画しています。近傍キャッシュがヒットすれば、解決のやり取りをスキップできます。

インターフェースも近傍コンテキストの一部です。同じ IPv4 アドレスが異なる VLAN やネットワーク名前空間に存在する可能性があり、すべてのシステム状態を単一のグローバルな「IP→MAC 辞書」で記述することはできません。ルーティングの誤り、VLAN の不通、インターフェースの選択ミスなどが、近傍解決の失敗として現れることがあります。

ARP パケットと最小フレーム長

イーサネット上の IPv4 ARP は EtherType 0x0806 を使用し、IPv4 ヘッダーを経由せずにイーサネットに直接載せられます。その汎用フィールドは異なるアドレス長を許可し、一般的な Ethernet/IPv4 の組み合わせは以下の通りです。RFC 826

フィールドバイト数この例の値
ハードウェアタイプ / プロトコルタイプ2 + 21 / 0x0800
ハードウェア長 / プロトコル長1 + 16 / 4
操作21: リクエスト、2: 応答
送信元ハードウェア / プロトコルアドレス6 + 4送信元 MAC / IPv4
宛先ハードウェア / プロトコルアドレス6 + 4宛先 MAC / IPv4

合計 28 バイト です。タグ付けされていないイーサネットヘッダー 14 バイトを加えると 42 バイトになり、さらに 18 バイトのパディングを加えて FCS を含まない 60 バイトに達し、最後に 4 バイトの FCS を加えると 64 バイトになります。ここで言う最小イーサネットペイロードの 46 バイトは、28 バイトの ARP と 18 バイトのパディングで構成され、イーサネットヘッダーと FCS は含まれません。キャプチャ時にパディングと FCS が見えるかどうかは、キャプチャ位置とドライバに依存します。

最初の解決要求は通常、ブロードキャスト MAC アドレスを使用して送信されます。宛先ハードウェアアドレスはまだ不明であるためです。一般的な実装ではゼロで埋められますが、これはブロードキャスト範囲を決定するためのイーサネット宛先アドレスではありません。通常の応答は通常ユニキャストですが、公告など異なる使用形態も存在し、すべての ARP リクエスト/応答の方向性を固定することはできません。

近傍テーブル:MAC があることと到達可能であることは別問題

Linux は IPv4 ARP と IPv6 近傍の動作を汎用近傍フレームワークに統合しています。INCOMPLETE は解決中を示し、送信待ちのパケットは限られたキューで待機できます。REACHABLE は直近で到達確認が得られたことを示します。STALE はマッピングを保持していますが、確認は古くなっています。これはエントリが「壊れている」わけではありません。

近傍状態は対応関係と到達性を表す:STALE でも先に送信できる INCOMPLETE 解決中、キュー待ちの可能性あり REACHABLE 直近に到達性の確認あり STALE マッピングは存在するが、確認は古い DELAY データを先に送信し、確認を待つ PROBE / FAILED 能動プローブ / 解決失敗 有効な確認 確認が古くなる 再使用 待機中に確認なし 確認が得られれば REACHABLE に復帰可能 一般的なパスの例示であり、完全な状態遷移図ではありません。再試行/老化は設定可能であり、FAILED であっても近傍テーブルから即座にオブジェクトが削除されるわけではありません。

STALE エントリを使用する場合、通常は既存の MAC を使用してパケットを先に送信し、その後、上位層などからの確認を待つ DELAY 状態に入ります。必要に応じて PROBE に移行し、能動プローブを行います。すべてのプローブが枯渇すると FAILED 状態に入りますが、エントリの回収は後続のライフサイクル/ガーベジコレクションによって処理され、ip neigh から即座に消えることは保証されません。受信したパケットが到達性の証明となるかどうかは、タイプと状態に依存し、すべての ARP/NA が直接 REACHABLE を設定するわけではありません。

この記事では Linux 7.2 の net/core/neighbour.c に基づき、STALE→DELAY、確認待ち、プローブ失敗のパスを確認しました。実際には永続的、外部管理エントリなどの特殊な状態も存在しますが、図では一般的なパスのみを示しています。REACHABLE の期限が切れると、直近の使用状況に応じて直接 DELAY などの状態に移行することもあり、図を逐行の実装として扱うことはできません。

base_reachable_time_ms は到達時間の基準であり、実際の到達時間にはランダム化などの処理があります。delay_first_probe_time、retrans_time_ms、ucast_solicit、mcast_solicit はそれぞれ待機時間とプローブに影響します。gc_stale_time は未使用エントリの回収資格に関連しており、STALE エントリごとの固定の生存カウントダウンではありません。設定ではデフォルトテンプレートと特定のインターフェースを区別する必要があり、実際の状態を確認せずに固定の 30/60 秒で「チューニング」すべきではありません。

MAC があるのに、なぜ到達可能性の確認が必要なのか?

マッピングの存在、データの送信、到達性の証明の新鮮さを別々の項目として扱います。STALE から直接送信を試みて、確認を選択することもできます。また、待機時間を完全に経過させて失敗を観察することもできますが、すでに送信されたデータが空中で取り消されるわけではありません。マッピングをクリアしてから送信すると、キュー待ちが必要な別のパスを辿ることになります。

図を準備しています
MAC があるのに、なぜ到達可能性の確認が必要なのか?

STALE は依然として利用可能なマッピングを保持しており、初回使用時は送信してから確認を待つことができます。未知のアドレスの解決にはパケットの一時保存が必要です。プローブ失敗とエントリの即時削除は別のイベントです。

IPv4 競合探知とアドレス公告は異なる

RFC 5227 の ARP Probe は、候補アドレスをまだ使用していない段階で競合を探知するために使用されます。送信元 IPv4 は 0.0.0.0、宛先 IPv4 は候補アドレスです。これにより、まだ確認されていないマッピングを他のホストのキャッシュに書き込むのを防ぎます。ARP Announcement は、使用中のアドレスを宣言するものです。送信元と宛先の IPv4 は同じです。これらを単に「gratuitous ARP を1つ送信し、応答を待つ」として記述すると、同時探知と競合の判断を見逃すことになります。RFC 5227:IPv4 アドレス競合検出

フェイルオーバー時に公告を行うと、近傍マッピングの更新に役立ちます。スイッチングデバイスもソース MAC から入力ポートを学習できます。これらは異なるテーブルです。ポリシーフィルタ、静的エントリ、MAC の不変性、転送テーブルの収束待ちなどが、切り替え結果に影響を与える可能性があります。公告を送信した直後にゼロ損失を約束することはできません。

Proxy ARP は、あるデバイスが宛先に代わって応答し、自身の MAC を提供し、その後転送を代行するものです。前提として、送信元ホストがルーティング/リンクに基づいて、確かにその宛先を解決すると判断している必要があります。もし宛先がデフォルトゲートウェイ経由であると既に判断している場合、通常はゲートウェイに問い合せます。プロキシは明確に設計されたアクセスシーンで使用できますが、誤った on-link の判断を隠蔽する可能性もあるため、ルーティングとポリシーと組み合わせてその役割を説明する必要があります。

NDP メッセージ:正しい番号と検証ルール

NDP は ICMPv6 で運ばれ、基本メッセージには RS 133、RA 134、NS 135、NA 136、Redirect 137 があります。これらのメッセージの Code はすべて 0 です。共通ヘッダーには Type、Code、Checksum などの汎用部分のみが含まれ、その後の固定フィールドはメッセージタイプによって変化し、4 バイトの Reserved として一律に描くことはできません。チェックサムには IPv6 擬似ヘッダーが含まれます。受信時の検証では、Hop Limit=255 などの条件もチェックされ、ルーティングを越えて転送される偽造パケットを制限しますが、ローカル送信者を認証するものではありません。RFC 4861:メッセージフォーマットと検証

一般的な ND オプションタイプ意味
1ソースリンク層アドレス
2ターゲットリンク層アドレス
3プレフィックス情報
4リダイレクトヘッダー
5MTU

これらのオプションの Length は 8 バイト単位であり、タイプと長さフィールドを含みます。ゼロ長は無効です。イーサネットリンク層アドレスオプションは通常 8 バイトであり、Type 1 をソース/ターゲットアドレスの両方として呼び出し、その後の番号を全体としてずらすことはできません。

NS/NA:マルチキャストで範囲を狭めるが、1つのグループが複数のアドレスに対応する場合もある

初期解決では、通常、ターゲットの solicited-node マルチキャストアドレスに対して NS を送信します。このアドレスは ff02::1:ff00:0/104 にターゲット IPv6 の下位 24 ビットを加えたものです。イーサネット上の IPv6 マルチキャスト MAC は、33:33 に IPv6 マルチキャストアドレスの下位 32 ビットを加えたものを使用します。したがって、ターゲット 2001:db8::1234:5678 は ff02::1:ff34:5678 および 33:33:ff:34:56:78 に対応します。RFC 2464:IPv6 マルチキャストからイーサネットへのマッピング

複数の IPv6 アドレスが下位 24 ビットを共有する場合、それらは同じグループを共有することになります。受信者は NS の完全な Target Address を確認する必要があり、イーサネット宛先 MAC のみで「私宛てだ」と判断することはできません。既知の近傍の到達性プローブでは、ユニキャスト NS が使用される場合もあり、NS はマルチキャスト問い合わせのみではありません。

通常の solicited NA は、ターゲットリンク層アドレスおよび R/S/O フラグを携带してユニキャストで返信できます。R はルーターの役割を示し、S は対応する solicited 到達性の確認セマンティクスを示し、O は既存のマッピング更新に影響します。マルチキャスト NA の Solicited フラグを「これも応答だから」と一律に 1 に設定することはできません。キャッシュの更新は、元の状態とフラグを組み合わせて実行する必要があります。RFC 4861:アドレス解決と NA 処理

DAD:アドレスがない状態で競合がないかどうたずねる

通常の DAD では、候補アドレスは tentative(暫定)状態にあり、送信される NS のソースは ::、Target は候補アドレスです。Source Link-Layer Address オプションは含まれません。既存のアドレス所有者は対応する NA で返信できます。同じアドレスを同時に探知している2つのノードが、相手の NS を受信することで競合を発見することもあるため、NA のみをカウントしたり、「2番目のノード」だけが失敗すると考えたりすることはできません。

競合が観察されなかった場合にのみ、プロトコルフローに従ってアドレスが有効化されます。ただし、パケット損失、フィルタリング、リンクの分離により、DAD は唯一性の絶対的な証明ではありません。有効化の回数、適用される例外、最適化された DAD、OS のポリシーは区別する必要があり、あらゆるタイプのアドレスに対して同じフローを一律に規定することはできません。RFC 4862:DAD

RA:アドレスプレフィックス、on-link、デフォルトルートはそれぞれ独立して動作する

ホストは RS を介してルーター情報を要求でき、ルーターも定期的に RA を送信できます。RA の送信元は通常、ルーターのリンクローカルアドレスです。応答の宛先は、RS の送信元などの条件に応じて選択する必要があり、ソースが :: の RS に対して、RA を未指定アドレスへのユニキャストで返信することを要求することはできません。

Prefix Information の L はプレフィックスの on-link 情報を示し、A は自律的なアドレス構成に使用可能かどうかを示し、これらは独立しています。アドレス/プレフィックス長を1つ持つからといって、一致するすべてのアドレスが直接到達可能であると無条件に解釈してはいけません。ホストは on-link 情報とルーティング選択に従う必要があります。RFC 5942:IPv6 サブネットモデル

アドレスの Preferred Lifetime が期限切れになると deprecated 状態になり、通常、新しい通信には選択されませんが、既存の通信は依然として有効である限り継続できます。Valid Lifetime が期限切れになると、アドレスはもはや有効ではありません。RA の Router Lifetime は、そのルーターがデフォルトルートの候補となる期間を制御し、0 に設定しても、すべてのプレフィックスとアドレスが同時に無効になるわけではありません。具体的なライフサイクルの更新にはプロトコルのルールがあり、一律に固定の日数に設定することはできません。

SLAAC の IID は、適切な安定した不透明なメカニズムまたは一時アドレスポリシーを使用すべきであり、デフォルトで MAC から生成すべきではありません。RFC 8981 の一時アドレスは定期的に変化し、特定のアドレスに関連するリスクを低減しますが、すべてのネットワーク識別子を隠すものではなく、すべての Linux インストールでデフォルトで有効になっているわけでもありません。システム設定とアドレス選択ポリシーは実際に確認する必要があります。RFC 8981:一時アドレス拡張

アドレスとデフォルトルートは同時に期限切れになるか?

数式内の異なる寿命は、異なるオブジェクトに属します。t=70 を選択すると、アドレスは依然として有効ですが、もはや優先されず、この RA が提供するデフォルトルートはすでに期限切れになっています。L をオフにしても、自動的に A がオフになるわけではありません。タイムスライダーは、初期広告の後の結果を1回だけ観察し、フラグを変更すると、この広告を再構築します。

図を準備しています
アドレスとデフォルトルートは同時に期限切れになるか?

A と L は独立している。preferred の期限切れはアドレスを deprecated にするだけで、valid の期限切れになって初めて無効になる。Router Lifetime はデフォルトルートの候補を別に管理する。

セキュリティ:暗号化はコンテンツを保護するが、近傍の可用性は修復しない

ARP/NDP の基本的なアドレス宣言には、普遍的な送信元認証が欠けており、ローカルリンクでのなりすましはマッピング、ルーティング、またはアドレス構成に影響を与える可能性があります。アクセス制御、適切な ARP チェック/バインディング、RA 防護は、信頼できる入力とアドレスソースと組み合わせて設計する必要があります。静的近傍エントリは一部のマッピングを固定できますが、保守負荷をもたらし、すべてのリンク動作を保護するものではありません。

正しく認証された TLS または暗号化トンネルは、カバーされるセッションのコンテンツと整合性を保護できますが、攻撃者は依然としてパケットドロップ、ハンドシェイクの妨害、またはリンクの到達不能を引き起こす可能性があります。したがって、「WireGuard を使用すれば ARP スプーフィングは無効になる」という主張は成立しません。機密性、アイデンティティ、可用性はそれぞれ独立して判断する必要があります。

ローカルパケットとマルチキャストマッピングの再計算

import struct
from ipaddress import IPv4Address, IPv6Address

sha = bytes.fromhex('020000000001')
arp = struct.pack('!HHBBH', 1, 0x0800, 6, 4, 1)
arp += sha + IPv4Address('192.0.2.10').packed
arp += bytes(6) + IPv4Address('192.0.2.1').packed
assert len(arp) == 28
padding = max(0, 46-len(arp))
assert padding == 18 and 14+len(arp)+padding+4 == 64
assert struct.unpack('!H', arp[6:8])[0] == 1

target = IPv6Address('2001:db8::1234:5678')
solicited = IPv6Address(int(IPv6Address('ff02::1:ff00:0')) | (int(target)&0xffffff))
assert str(solicited) == 'ff02::1:ff34:5678'
mac = bytes.fromhex('3333') + solicited.packed[-4:]
assert mac.hex(':') == '33:33:ff:34:56:78'
assert bytes([1, 1]) + sha == bytes.fromhex('0101020000000001')
print('ARP フィールド/パディング、リクエスト操作コード、IPv6 マルチキャストマッピングが通過')

ローカルでバイト列を構築して再計算するだけであり、ネットワークに対して ARP/NS を送信したり、近傍キャッシュをクリアしたり、sysctl を変更したりすることはありません。

引き続き参照