Posted at

Solaris11にバージョンアップ後、TCP接続が失敗するようになった

Solaris11.3にバージョンアップ後、ネットワーク接続数が多くなると、ソケット接続が失敗する現象が稀に発生し、処理をやり直すことになりました。原因は、TIME_WAITとエフェメラルポートのポート番号の設定方式に起因するとわかたっため、備忘録として記載しました。


事象

Solaris10からSolaris11.3へバージョンアップ後、サーバ間(サーバA⇒サーバB)のコネクション接続でconnect()から

ECONREFUSED が復帰し、接続不可となる事象が発生しました。

接続数が多い時に発生しましたが、Solaris10の時と、接続数、通信量は変わっていませんでした。

なお、接続ポートにはエフェメラルポートを使用していました。


原因 

 ネットワークトレースを取るなどして再現調査を行った結果、以下が判りました。


  • コネクションをクローズ後、一定時間(デフォルトは60秒)、そのコネクションをTIME-WAIT状態で保持します。

    これはTCPの仕様です。その際、使用していたポート番号もOSで保持しています。

  • 次の接続で、ポート番号に直前にクローズされたコネクションで使用していたポート番号(エフェメラルポート)が割り振られました。

    そのため、TIME-WAIT状態とみなされ、connectでRSTが復帰されました。

    これは、Solaris11からエフェメラルポートがランダムに選択されるように動作が変わったため、まれに直前で使用していたポート番号と同じ番号が割り振られたためでした。

    (Solaris10以前は+1カウント方式なので同じポート番号が割り振られる可能性は極めて低いものでした)

SolarisではTIME-WAIT状態の保持期間中に同じクライアントのIPアドレス・ポート番号から  

接続要求があった場合はRFC793の規約に準じてRSTを返す実装になっています。

RFC793より新しいRFC1122ではTIME-WAITでも新しい接続要求を受け付けてよいとされており、

WindowsやLinuxはこれに準じているようで、RSTは発生しませんでした。


対処・教訓

TIME-WAIT時間の変更など考えましたが、他に与える影響を考慮し、

最終的には、RSTが返る場合に備えて、リトライを組み込むようにしました。

実際に3回リトライとし、その後問題は発生していません。

Windows、Linuxでは、この問題は発生しませんが、Solaris転用を考慮する必要がある場合、

connectでRSTが復帰した場合、リトライ処理は組み込んでおくべきかと思います。