Jの衝動書き日記

さらりーまんSEの日記でございます。

受信しているはずのUDPパケットをアプリケーション側では受け取れない(Redhat6)

 お仕事でハマった事例があったので、メモを残しておく。

問題詳細

 以下のような事象が発生した。
 負荷分散サーバ(LB)上で動作するアプリケーション(AP1)は受信したUDPパケットを負荷分散先(S1)に送信する。だが、 S1上で動作するアプリケーション(AP2)はそのパケットを受け取れなかった。
 S1上でパケットキャプチャした結果、LBから送信されたパケットはS1上で受信できるのは確認できた。しかし、AP2ではそれを受け取れない。
 一方、AP1で中継させずにLBから直接UDPパケットをS1に対して送信すると、AP2はこれを受け取れる。
 図にすると以下のような感じになる。

 

f:id:newWell:20150512135907j:plain

 

解析

 S1上には複数のIFが存在し、そのうちの一つのIF(bond3)に設定されたIPアドレスに向けてAP1はパケットを送信している。一方AP2は、S1上のすべてのIFに対してUDPポートをOPENしている。
 AP2に対してAP1から送信したUDPパケットの送信元IPアドレスはbond3とは異なるネットワークアドレスのものであり、AP2に対してLBから直接送信したUDPパケットの送信元IPアドレスはbond3と同じネットワークアドレスのものだった。

 これより、AP2は、bond3を介してLBからパケットを受け取っているが、

  1. 送信元IPアドレスがbond3と同じネットワークアドレスの場合はこれを受け取れ、
  2. 送信元IPアドレスがbond3と異なるネットワークアドレスの場合は受け取れていない

 ことがわかった。

 

原因

 rp_filterが有効になっているためと判明した。rp_filterはスプーフィング対策――スプーフィングとはプライベートアドレスを詐称してファイアウォールを通りぬける手法――の設定項目で、これが有効である場合、次の条件にすべて一致するパケットは破棄される。

  1. IFで受信したパケットの送信元IPアドレスがIFのネットワークアドレスと一致しない
  2. IFで受信したパケットの送信元IPアドレスもしくはそのネットワークアドレスがIFのルーティングに設定されていない
  3. パケットを受信した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だとデフォルトで有効となっているっぽい。