隣人かどうか — アドレス・マスク・ARP
一言でいうと
パケットを送る前にカーネルが最初に下す判断は、「相手は同じサブネットか」です。同じならARPで相手のMACを直接尋ね、違えばゲートウェイのMACに向けて送ります。マスクの1ビットが、その分かれ道です。
なぜ必要なのか
IPアドレスはどこにいるのかを表す論理アドレスで、フレームを実際に届けるのはNICに刻まれたMACアドレスです。両者には何の関係もありません。そのため、同じケーブル(同じL2)につながった相手に送るには「このIPを持っているのは誰か、MACは何か」と尋ねる必要があり、それがARPです。答えはネイバーテーブル(ARPキャッシュ)にしばらく残ります。
問題は、同じケーブルにいない相手です。ブロードキャストはリンクを越えられないので、ARPでは見つけられません。そこでカーネルは、まずマスクで「同じサブネットか」を判定し、違えばルーティングテーブルからゲートウェイを探して、ゲートウェイのMACでフレームを作ります。ゲートウェイが必ず同じサブネットにいなければならない理由がこれです。そのMACを尋ねられる必要があるからです。
どう動くのか
192.168.1.77/25から192.168.1.130へ送るとします。/25は先頭25ビットがネットワークなので、128で分かれます。77は0–127のブロック、130は128–255のブロックです。別のサブネットなので、カーネルはゲートウェイを探します。同じ2つのアドレスを/24にすると同じブロックになり、ARPで直接尋ねます。アドレスは何も変わっていないのに、マスクの1ビットで経路がまったく変わります。
ARPリクエストは、宛先MACがff:ff:ff:ff:ff:ffのブロードキャストです。自分のIPを見たホストだけがユニキャストで答えます。カーネルは答えをネイバーテーブルに入れ(REACHABLE)、しばらく使わないとSTALEに変え、次に使うときに再確認します。nud permanentで静的エントリを書き込むと、カーネルはそれを更新しません。そのため、間違ったMACを書き込むと、IPもルーティングも正常なのに通信だけが静かに止まります。相手のNICが「自分宛てではない」として捨てるだけで、エラーは何も返ってこないからです。
現場での姿
マスクの入力ミス。新しいサーバーに/24を/16と書いてしまいました。同じ/24内の通信は問題なく通ります。ところが、隣のアドレス帯(同じ/16内の別の/24)へ向かうパケットを、カーネルは「同じサブネット」とみなしてARPを送りますが、答えがありません。ゲートウェイを経由すればよいところを、直接探して失敗しているのです。症状は「一部のアドレス帯だけつながらない」で、ip neighにFAILEDのエントリが溜まっているのが手がかりです。
IPの重複。2台の機器が同じIPを持つと、ARPの答えが2つ返ってきて、後から届いたものが勝ちます。通信がつながったり切れたりします。ネイバーテーブルのMACが入れ替わるのを捉えれば、原因が見えてきます。
紛らわしいアドレス
サブネット内のアドレスがすべてホストに使えるわけではありません。192.168.1.0/24で.0はネットワークアドレス、.255はブロードキャストアドレスなのでホストには付けられず、使えるアドレスは256個ではなく254個です。マスクが狭くなるほど、この損失は大きくなります。/30はアドレス4個のうち2個しか使えないため、ルーター同士をつなぐ区間には昔から/30が使われてきました。最近はその区間では、/31を使います。ポイントツーポイントリンクにはブロードキャストが不要だと規約で定め、アドレス2個をどちらもホストに使います。
いくつかのアドレス帯は意味が決まっているので、症状を見ただけで原因を推測できます。
| アドレス帯 | 意味 | これが見えたら |
|---|---|---|
127.0.0.0/8 |
自分自身 | 外には出ません。サービスをここだけにバインドすると、ほかのホストから届きません |
169.254.0.0/16 |
リンクローカル | DHCPを取得できず、自分で付けたアドレスです。アドレスがあるのに通信できない典型例です |
10/8・172.16/12・192.168/16 |
プライベートアドレス帯 | インターネットにそのまま出られません。NATが必ず介在しています |
224.0.0.0/4 |
マルチキャスト | 特定のグループを購読したホストだけが受け取ります |
インターフェース1つに、アドレスを複数付けることもできます。このとき、送信パケットの送信元アドレスはカーネルが選びます。既定の規則は「宛先と同じサブネットにあるアドレスのうち最初のもの」で、そのため相手側のファイアウォールが特定の送信元だけを許可していると、通る日もあれば阻まれる日もあるように見えます。ip route get <상대 주소>を実行すると、カーネルが実際にどの送信元とどの経路を選ぶかがそのまま表示されます(プレースホルダーは相手のアドレスです)。推測せず、このコマンドで確認するほうが早いです。
最後に、ARPの変種を2つ知っておくと役に立ちます。Gratuitous ARPは、誰も尋ねていないのに「このIPは自分のMACだ」と知らせるもので、仮想IPを別の機器へ引き継ぐときに、周囲のネイバーテーブルを即座に更新させる用途で使います。フェイルオーバーしたのにトラフィックがしばらく停止した機器へ流れ続ける事故は、たいていこの通知が出ていないか、途中のスイッチで除外された場合です。プロキシARPは、ルーターが他の機器のIPに代わって答えるもので、便利に見えますが、どの機器が答えたのかを追跡しにくくなり、原因の特定を大きく妨げます。
次のラボですること
ip -brとip -jでインターフェースとアドレスを読み、ipcalcとPythonでサブネットを計算し、ダミーインターフェースにアドレスを付けてみます。そのあと、2つのネームスペースをvethでつないでARPリクエストとレスポンスをtcpdumpで捕まえ、静的エントリに間違ったMACを入れて通信が止まるのを目で確かめます。