先日、Linuxのちょっと変わった(と私は思うんですが)ARPの実装に惑わされました。

今まで私は、ホストがARP Requestを受け取った場合、そのARP Requestを受け取ったインターフェースに付いているIPアドレス(複数ある場合あり)が、ARP Requestのtarget IP addressになっているかどうか検査され、イコールになっている場合にのみARP Replyを返す、という動きをするものと思っていました。大抵のOSはそういうふうに動くと思うんですが(少なくともFreeBSDはそううごくハズ)、Linuxはちょっと違った動きをするんですね。

Linuxは、ARP Replyを返すかどうかの判断をする際に、そのインターフェースに付いているIPアドレスだけではなくて、そのホストにローカルについているアドレス全てに対して検査し、ローカルなアドレスならARP Replyを返します。

例えば、ホストが2つネットワークインターフェース(eth0, eth1)を持っていて、eth0に付いているIPアドレスが10.0.0.1/24、eth1に付いているIPアドレスが20.0.0.1/24だったとします。このような場合、eth1から10.0.0.1に対するARP Requestを受け取った場合でも、Linuxはこれに応答するんです。

通常のネットワークトポロジーではそもそもこのようなことは起こりませんが、例えば、eth0とeth1を同じスイッチに繋いでしまうなど、いいかげんな事をすると、このようなことが起こります。このようなことをしても、通常、あまり問題にはならないので、テストでスイッチの数が足らない場合などは、こんな構成組んじゃう場合も多いはず。が、このような環境でLinuxを使っていると混乱が生じます。

仮に、先の例でeth0とeth1を同じスイッチに収容したとしましょう。で、他のホストが10.0.0.1に対してARP Requestをしたとします。Linuxでは(すくなくともデフォルトでは)このARP Requestに対してeth0もeth1も答えます。よって、最初にARP Requestを出したホストのARPテーブルには、タイミングによってeth0もしくはeth1のMACアドレスが学習されることになります。普通の通信はeth0、eth1どちらのMACアドレスが学習されても問題なく動くかもしれませんが、場合によっては期待した動きにならないケースが出てきます。例えば、eth0のアドレスでしかHTTP(80/tcp)をLISTENしていないような場合、で、TCP SYNを出す時点でeth1のMACアドレスがARPテーブルにあると、eth1にSYNが出てしまい、RSTされてしまうことになるでしょう。

このような動作を避けるため、LinuxにはARP Filterという機能が備わっています。これをenableにすると、ARP Replyを返すときに一工夫するようになります。ARP Requestに含まれるsender IP addressに対してRouting TableをLookupし、戻るべきインターフェースを決定して、そのMACアドレスを使ってARP Replyを返します。先のようにブロードキャストドメインを共有しているようなトポロジーの場合、このARP Filterを使っても、複数ARP Replyが返ることを止めることはできませんが、その答えをバラバラにせずconsistentなものにできる、という効果があるわけです。

Linuxがなんでこんな実装になっているのかは不明です。識者の方、ご存知でしたら教えて下さい。