net.ipv4.tcp_tw_recycle は NAT の内側からの SYN を落としてしまう

NAT の内側にある複数のウェブサーバから外部のとある API サーバを叩いた時に、SYN_SENT ステータスのままになる(SYN/ACK が返ってこない)事があります。
特に混雑時間帯に顕著化します。

他のネットワークからは問題がないことと、同じ NAT の内側にいる別の忙しくないサーバからは現象が出ることから、そのネットワークの NAT あたりを疑っていましたが、どうも調べていくと Linux の設定と実装の問題のようで。

TIME_WAIT ステータスを早く回収するために net.ipv4.tcp_tw_recycle を有効にするというサイトをよく見かけますがこれが罠で、同一 IP アドレスから送信された TCP パケットのタイムスタンプフィールドを見て、60秒以内に古いものが来たらそのまま捨てられてしまいます。

本来の P2P なネットワークなら問題ありませんが、NAT の内側に複数のサーバがいる場合は NAT がタイムスタンプを書き換えない限り、タイムスタンプが増加することを保証できません。
クライアントの環境が特定できないサービスでは使うべきではないでしょう。

これの消極的な解決方法はこちら側で以下を無効に設定をして、TCP タイムスタンプを使用しないようにします。

FreeBSD: net.inet.tcp.rfc1323
Linux: net.ipv4.tcp_timestamps

以下が参考になります。

kernel: TCP: time wait bucket table overflow の解消とTIME_WAITを減らすチューニング
ただし、サーバ側で net.ipv4.tcp_tw_recycle が有効で、クライアント側でTCPのタイムスタンプオプションが有効(Linuxの場合net.ipv4.tcp_timestamps = 1)だと、NAT/LBを超えたときにSYNを落としてしまい、接続障害になる。 ユーザー向けに使っているとSB携帯などで障害が発生してしまうようなので、使わないほうがいいかも。

気ままにインフラエンジニア

2007-05-21
この処理により、同一IPから60秒以内に前回のTCPセッションの最終パケットより前のTCPタイムスタンプを持ったSYNパケットが来ると該当パケットを落としてしまう。

LowPriority

Linux kernel のソースを見てみたところ、確かに tcp_ipv4.c に当該箇所がありました。

やはり mohta 先生の言葉通り NAT が悪でしょうか……。
と思ったらなにやら新しい End to End NAT なるものの記事を見つけました。
そうですか、mohta 先生が NAT を考える時代になりましたか……。

technical,UNIX

Posted by yokky