お仕事でハマった事例があったので、メモを残しておく。
問題詳細
以下のような事象が発生した。
負荷分散サーバ(LB)上で動作するアプリケーション(AP1)は受信したUDPパケットを負荷分散先(S1)に送信する。だが、 S1上で動作するアプリケーション(AP2)はそのパケットを受け取れなかった。
S1上でパケットキャプチャした結果、LBから送信されたパケットはS1上で受信できるのは確認できた。しかし、AP2ではそれを受け取れない。
一方、AP1で中継させずにLBから直接UDPパケットをS1に対して送信すると、AP2はこれを受け取れる。
図にすると以下のような感じになる。
解析
S1上には複数のIFが存在し、そのうちの一つのIF(bond3)に設定されたIPアドレスに向けてAP1はパケットを送信している。一方AP2は、S1上のすべてのIFに対してUDPポートをOPENしている。
AP2に対してAP1から送信したUDPパケットの送信元IPアドレスはbond3とは異なるネットワークアドレスのものであり、AP2に対してLBから直接送信したUDPパケットの送信元IPアドレスはbond3と同じネットワークアドレスのものだった。
これより、AP2は、bond3を介してLBからパケットを受け取っているが、
ことがわかった。
原因
rp_filterが有効になっているためと判明した。rp_filterはスプーフィング対策――スプーフィングとはプライベートアドレスを詐称してファイアウォールを通りぬける手法――の設定項目で、これが有効である場合、次の条件にすべて一致するパケットは破棄される。
- IFで受信したパケットの送信元IPアドレスがIFのネットワークアドレスと一致しない
- IFで受信したパケットの送信元IPアドレスもしくはそのネットワークアドレスがIFのルーティングに設定されていない
- パケットを受信したIFはdefault routeとして設定されていない
なお、ルーティング設定はrouteコマンドか、ip route lsなどで確認可能。
例) bond0:192.168.1.20/24 → default route bond1: 10.168.1.20/24 bond2: 10.168.2.30/24
この場合、送信元IPアドレスが10.168.1.x以外のものがbond1に来るとそれは破棄される。bond0に来たパケットはどんなものであれ破棄されない。
解決方法
/etc/sysctl.conf中のnet.ipv4.conf.default.rp_filterに0を設定し、rebootを実施、もしくはIFを再起動する。sysctl -aだと各IFには反映されないので注意が必要。
net.ipv4.conf.default.rp_filterに2を設定してもいいが、その場合、パケットの送信元IPアドレスがいずれかのIFより到達可能である必要がある。上記例の場合、パケットの送信元IPアドレスがbond0〜bond2から到達可能であることが条件となる。
メモ
redhat5ではrp_filterの設定はデフォルトで無効(結果としてっぽいが)となっているのに対し、redhat6だとデフォルトで有効となっているっぽい。