tcp_tw_reuseがデフォルトで無効になっているのはなぜですか?

tcp_tw_reuseがデフォルトで無効になっているのはなぜですか?

みなさん、こんにちは。私の名前はシャオリンです。

ある読者が先週WeChatでインタビューしたとき、net.ipv4.tcp_tw_reuseパラメーターを開くとTIME_WAIT状態でTCP接続をすばやく再利用できるのに、なぜLinuxがデフォルトで閉じられるのかと尋ねられました。

写真

写真

いいやつ、本当に詳細にこんにちは!

読者からこの質問を見たときもびっくりしましたが、少し考えてみると、ようやくこの質問に答える方法がわかりました。

実際、この質問は、「TIME_WAIT状態の期間が短すぎるかどうかにかかわらず、何が問題になるのか」という変装で尋ねています。

tcp_tw_reuseパラメーターを開くと、TIME_WAIT状態のTCP接続をすばやく再利用できるため、TIME_WAIT状態の期間を短縮することと同じです。

一部の学生は、tcp_tw_reuseを使用してTIME_WAIT状態でTCP接続をすばやく再利用する場合、net.ipv4.tcp_timestampsパラメーターが有効(デフォルトは有効)であり、tcp_timestampsパラメーターが遅延レポートを回避できることを確認する必要があると尋ねる場合があります。ええと、これはTIME_WAIT状態がない場合の問題を解決しませんか?

いくつかの問題は解決しますが、完全ではありません。次に、この問題についてお話します。

写真

TIME_WAIT状態とは何ですか?

以下に示すように、TCPが4回手を振るプロセス:

写真写真

  • クライアントは接続を閉じるつもりです。このとき、クライアントはTCPヘッダーFINフラグが設定され1FINメッセージし、クライアントはFIN_WAIT_1状態に入ります。
  • サーバーはメッセージを受信した後、クライアントにACK応答、サーバーはCLOSED_WAIT状態に入ります。
  • クライアントはサーバーからACK応答と、FIN_WAIT_2状態になります。
  • サーバーがデータを処理するのを待った後、サーバーはクライアントにもFINメッセージ、サーバーはLAST_ACK状態に入ります。
  • クライアントはサーバーからFINメッセージと、ACK応答メッセージを返し、TIME_WAIT状態
  • サーバーはACK応答CLOSEと状態になり、サーバーは接続の終了を完了します。
  • 2MSL一定期間が経過すると、クライアントは自動的にCLOSE状態になり、クライアントも接続の終了を完了します。

ご覧のとおり、両方向にFINとACKが必要なため、クワッドウェーブと呼ばれることがよくあります。

ここで注意すべきことの1つは、TIME_WAIT状態は、接続がアクティブに閉じられている場合にのみ使用可能であるということです。

TIME_WAITは、「アクティブなクロージングパーティ」が切断されたときの最後の状態であり、この状態は* 2MSL(Maximum Segment Lifetime)*期間続き、その後CLOSED状態になります。

MSLは、ネットワーク上のTCPプロトコルのパケットの最大存続期間を指し、この時間を超えるデータはすべて破棄されます。RFC 793ではMSLは2分と規定されていますが、実際の実装では異なります。たとえば、Linuxのデフォルトは30秒であるため、2MSLは60秒です。

MSLは、ネットワーク層のIPパケットのTTLによって保証されます。TTLはIPヘッダーのフィールドであり、データグラムが通過できるルーターの数の上限を設定するために使用されます。パケットがルーターによって転送されるたびに、IPヘッダーのTTLフィールドは1ずつ減少し、0に減少すると、パケットは破棄されます。

MSLとTTLの違い:MSLの単位は時間ですが、TTLはルーティングホップの数です。したがって、メッセージが自然に強制終了されるようにするには、 MSLはTTL消費が0の時間以上である必要があります。

TTLの値は通常64です。LinuxはMSLを30秒に設定します。これは、データパケットが64台のルーターを通過する時間が30秒を超えないとLinuxが判断することを意味します。ネットワークから消えました

なぜTIME_WAIT状態を設計するのですか?

TIME_WAIT状態は、次の2つの主な理由で設計されています。

  • 履歴接続のデータが、同じ4倍の後続の接続によって誤って受信されるのを防ぎます。
  • 「接続を受動的に閉じる」パーティを正しく閉じることができることを確認してください。

理由1:履歴接続のデータが後で同じ4人の接続によって誤って受信されるのを防ぐ

この理由をよりよく理解するために、最初にシーケンス番号(SEQ)と初期シーケンス番号(ISN)を理解しましょう。

  • シーケンス番号はTCPのヘッダーフィールドであり、TCP送信側からTCP受信側へのデータストリームのバイトを識別します。TCPはバイトストリームの信頼できるプロトコルであるため、メッセージのシーケンスと信頼性を確保するために、TCP各送信方向の各バイトには、送信が成功した後の確認応答、損失後の再送信を容易にするための番号が割り当てられ、受信側での順序の乱れはありません。シーケンス番号は32ビットの符号なし番号であるため、4Gに達すると0にループバックします
  • 初期シーケンス番号。TCPが接続を確立すると、クライアントとサーバーはそれぞれ初期シーケンス番号を生成します。これは、各接続が異なる初期シーケンス番号を持つように、クロックに基づいて生成される乱数です。初期化シーケンス番号は、4マイクロ秒ごとにインクリメントし、を循環するのに4.55時間かかる32ビットカウンターと考えることができます

パッケージを入手しました。下の図のSeqはシリアル番号で、赤いボックスはそれぞれクライアントとサーバーによって生成された初期シリアル番号です。

写真

シリアル番号と初期化シリアル番号が無限に増加することはなく、初期値に折り返されるため、シリアル番号に基づいて新旧のデータを判断することはできません

TIME-WAITの待機時間がないか短すぎるとすると、遅延したパケットが到着するとどうなりますか?

写真

  • 接続を閉じる前にサーバーによって送信されたSEQ = 301メッセージ、ネットワークによって遅延されます。
  • 次に、サーバーは同じ4倍で新しい接続を再開し、以前に遅延SEQ = 301しクライアントに到着し、データパケットのシリアル番号はクライアントの受信ウィンドウ内にあるため、クライアントはこれを正常に受信します。ただし、これは以前の接続からデータパケットが残っているため、データの混乱などの深刻な問題が発生します。

履歴接続のデータが同じクワッドビハインドの接続によって誤って受信されるのを防ぐために、TCPはTIME_WAIT状態を設計し、状態は長期間持続2MSLします。この時間は、両方のデータパケットに十分です。破棄する方向、元の接続ネットワークに表示されるデータパケットはネットワーク内で自然に消えます。また表示されるデータパケットは、新しく確立された接続によって生成される必要があります。

理由2:「接続を受動的に閉じる」パーティを正しく閉じることができることを確認します

クライアント(アクティブクロージングパーティ)の最後のACKパケット(第4波)がネットワークで失われた場合、TCP信頼性の原則に従って、サーバー(パッシブクロージングパーティ)はFINパケットを再送信します。

クライアントにTIME_WAIT状態がなく、最後のACKメッセージの送信後に直接CLOSED状態になったとします。ACKメッセージが失われた場合、サーバーはFINメッセージを再送信し、クライアントは閉じた状態になります。サーバーによって再送信されたFINメッセージを受信すると、RSTメッセージが返されます。

写真

サーバーはこのRSTを受信し、エラー(ピアによって接続がリセットされた)として解釈します。これは、信頼できるプロトコルの正常な終了ではありません。

これを防ぐために、クライアントはピアがACKを受信するのに十分な時間待機する必要があります。ピアがACKを受信しない場合、TCP再送信メカニズムがトリガーされ、サーバーはFINを再送信します。 2つのMSLの時間。

写真

しかし、再送されたACKはまだ失われている可能性があると言うかもしれませんが、そうですが、TCPは非常に長い間待機しており、最善を尽くしています。

tcp_tw_reuseとは何ですか?

Linuxオペレーティングシステムでは、TIME_WAIT状態の期間は60秒です。これは、この60秒以内に、クライアントが常にこのポートを占有することを意味します。ポートリソースも限られていることを知っておく必要があります。通常、開くことができるポートは32768〜61000です。次のパラメータを使用して、指定した範囲を設定することもできます。

 net.ipv4.ip_local_port_range

次に、アクティブなクロージングパーティのTIME_WAIT状態が多すぎて、すべてのポートリソースが占有されている場合、新しい接続を作成できなくなります。

ただし、Linuxオペレーティングシステムには、TIME_WAIT状態の接続をすばやくリサイクルするための2つのシステムパラメータが用意されており、どちらもデフォルトで無効になっています。

  • net.ipv4.tcp_tw_reuse、このオプションが有効になっている場合、クライアント(接続イニシエーター)がconnect()関数を呼び出すと、カーネルはTIME_WAIT状態が1秒を超える接続をランダムに検出して、新しい接続に再利用します。このオプションは、接続イニシエーターにのみ適用されます。
  • net.ipv4.tcp_tw_recycleは、このオプションが有効になっている場合、TIME_WAIT状態の接続をすばやくリサイクルできるようにします。このパラメーターは** NATネットワークでは安全ではありません!**詳細については、この記事の紹介を参照してください:バイトインタビュー:SYNパケットはいつ破棄されますか?

上記の2つのパラメーターを有効にするには、前提条件があります。つまり、TCPタイムスタンプを開く必要があります。つまり、net.ipv4.tcp_timestamps = 1(デフォルトは1)です。

tcp_timestampsパラメーターをオンにすると、TCPヘッダーはタイムスタンプオプションを使用します。これには、RTTの正確な計算を容易にすることと、シーケンス番号の折り返し(PAWS)を防ぐことの2つの利点があります。最初にこの関数を紹介します。

シリアル番号は32ビットの符号なし整数で上限は4GBです。4GBを超えた後は、シリアル番号をラップアラウンドして再利用する必要があります。これは、インターネットの速度が遅い昔はそれほど問題にはなりませんでしたが、十分に高速なネットワークを介して大量のデータを転送する場合、シリアル番号のラップアラウンドタイムは短くなります。シーケンス番号が非常に短時間ラップアラウンドする場合、以前に遅延したパケットが到着した後もシーケンス番号がまだ有効であるという問題に再び直面します。

この問題を解決するには、TCPタイムスタンプが必要です。

次の例を試してください。TCP送信ウィンドウが1GBで、タイムスタンプオプションが使用されている場合、送信者は各TCPパケットにタイムスタンプ値を割り当て、各パケット時間が1ずつ増加すると想定して、この接続を使用します。 6GBのデータストリームを送信します。

写真

32ビットのシーケンス番号は、時間Dと時間Eの間で折り返されます。パケットが失われ、時間Bで再送信され、セグメントがネットワーク上を迂回して時間Fで再表示されたとします。TCPがラップされたパケットを認識しない場合、データの整合性が損なわれます。

タイムスタンプオプションを使用すると、上記の問題を効果的に防ぐことができます。失われたパケットが時間Fで再表示される場合、そのタイムスタンプは最新の有効なタイムスタンプ(5または6)よりも小さい2であるため、アンチワインディングシーケンス番号アルゴリズム(PAWS)それを破棄します。

巻き戻しシーケンス番号アルゴリズムでは、両方の当事者が最後に受信したパケットのタイムスタンプ(最近のTSval)を維持する必要があります。新しいパケットが受信されるたびに、パケットのタイムスタンプ値が読み取られ、最近のTSval値と比較されます。受信したデータパケットのタイムスタンプはインクリメントされません。これは、データパケットの有効期限が切れていることを意味し、データパケットは直接破棄されます。

tcp_tw_reuseがデフォルトで無効になっているのはなぜですか?

非常に多くの予兆が待ち受けているので、ついにこの問題について話すことができます。

tcp_tw_reuseを有効にすることのリスクは何ですか?2つの問題があると思います。

最初の質問

tcp_tw_reuseをオンにすると、tcp_timestampsもオンにする必要があることがわかっています。つまり、シリアル番号の履歴パケットをタイムスタンプで効果的に判断できます。

しかし、アンチワインディングシリアル番号アルゴリズムのソースコードを調べたところ、RSTメッセージのタイムスタンプが期限切れになっても、RSTメッセージのシリアル番号が相手の受信ウィンドウ内にある限り、まだ受け入れることができます

次のtcp_validate_incoming関数は、受信したTCPパケットが修飾されているかどうかを確認する関数です。最初のステップは、tcp_paws_discard関数を担当するPAWSチェックを実行することです。

static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, int syn_inerr)
{
    struct tcp_sock *tp = tcp_sk(sk);

    /* RFC1323: H1. Apply PAWS check first. */
    if (tcp_fast_parse_options(sock_net(sk), skb, th, tp) &&
        tp->rx_opt.saw_tstamp &&
        tcp_paws_discard(sk, skb)) {
        if (!th->rst) {
            ....
            goto discard;
        }
        /* Reset is accepted even if it did not pass PAWS. */
    }

tcp_paws_discardがtrueを返す場合、そのパケットは履歴パケットであることを意味するため、パケットを破棄する必要があります。ただし、このパケットが破棄されると、最初にそれがRSTパケットであるかどうかが判別されます。それがRSTパケットでない場合、パケットは破棄されます。つまり、RSTメッセージが履歴メッセージであっても、破棄されることはありません。

以下に示すようなシナリオがあるとします。

写真

  • クライアントは、サーバーによって監視されていないポートに対してHTTP要求を開始すると、サーバーはRSTメッセージを相手に返します。残念ながら、RSTメッセージはネットワークによってブロックされます
  • クライアントは2番目のTCPハンドシェイクを長時間受信していないため、SYN​​パケットを再送信します。同時に、サーバーはサービスを開き、対応するポートをリッスンします。次に、クライアントとサーバーは、TCP 3ウェイハンドシェイク、データ送信(HTTP応答応答)、および4つのウェーブハンドを実行します。
  • クライアントはtcp_tw_reuseをオンにしたため、TIME_WAIT状態のポートをすばやく再利用し、今と同じ4倍でサーバーとの接続を確立しました
  • 次に、ネットワークによって遅延されたRSTメッセージがこの時点でクライアントに到着し、RSTメッセージのシリアル番号はクライアントの受信ウィンドウ内にあります。アンチロールバックシーケンス番号アルゴリズムは期限切れを防ぐことはできません。 RSTの場合、RSTメッセージがクライアントに送信されます。クライアントはそれを受け入れ、クライアントの接続が切断されます

上記のシナリオは、TIME_WAIT状態のポートが迅速に再利用されるため、tcp_tw_reuseのリスクを回避することです。その結果、シリアル番号をラップするRSTメッセージによって新しい接続が切断される可能性があり、TIME_WAIT状態がスキップされない場合は2MSLの間留まり、このRSTメッセージは次の新しい接続では表示されません

PAWSが期限切れのRSTパケットを手放すためにチェックする理由について、この質問があるかもしれません。RFC 1323をめくりましたが、その中に次のような文があります。

RSTセグメントにはタイムスタンプを付けないこと、およびRSTセグメントはタイムスタンプに関係なく受け入れられるようにすることをお勧めします。古い重複RSTセグメントは非常にありそうになく、それらのクリーンアップ機能はタイムスタンプよりも優先される必要があります。

大まかな意味:RSTセグメントにはタイムスタンプを付けないことをお勧めします。また、RSTセグメントは、タイムスタンプに関係なく受け入れられます。古い重複RSTセグメントは非常にまれであり、それらのクリーンアップ機能はタイムスタンプよりも優先される必要があります。

RFC 1323は、過去のRSTメッセージを受信する可能性は非常に低いと述べています。この考えの理由は、TIME_WAIT状態の2MSL時間は、接続されたメッセージがネットワーク内で自然に消えるのに十分であるため、通常の動作は接続をクリアすることがタイムスタンプよりも優先されると考えてください。

前述のケースは、tcp_tw_reuse状態がオンになっていて、TIME_WAIT状態がスキップされたために発生しました。

一部の学生は、HTTPリクエストの後でも、遅延したRSTメッセージは存続できると言うでしょう。

HTTPリクエストは実際には非常に高速です。たとえば、次のパケットキャプチャは、完了するのに0.2秒しかかからず、MSLよりもはるかに短いため、遅延したRSTメッセージが存続する可能性があります。

写真

2番目の質問

次の図に示すように、tcp_tw_reuseをオンにすると、接続をTIME_WAIT状態ですばやく再利用できます。4番目のウェーブのACKメッセージが失われると、接続を受動的に閉じるパーティが正常に閉じることができなくなる可能性があります。

写真

要約する

tcp_tw_reuseの役割は、クライアントがTIME_WAIT状態でポートをすばやく再利用できるようにすることです。これは、TIME_WAIT状態をスキップすることと同じであり、次の2つの問題が発生する可能性があります。

  • PAWSは、RSTの有効期限が切れても破棄されないことを確認するため、過去のRSTパケットは次の同じ4つの接続を終了する可能性があります。
  • 第4波のACKメッセージが失われた場合、接続を受動的に閉じたパーティを正常に閉じることができない可能性があります。

TIME_WAIT状態は少し長く続き、不親切に見えますが、厄介なことを避けるように設計されています。

「UNIXネットワークプログラミング」という本は次のように述べています。TIME_WAITは私たちの友人であり、私たちを助けてくれます。この状態を避けようとしないでください

おすすめ

転載: blog.csdn.net/qq_34827674/article/details/123458353