読者です 読者をやめる 読者になる 読者になる

φ(.. )メモシテオコウ /proc/sys/net/ipv4/tcp_fastopenに設定する内容とか実装

kernel linux network

TCP fastopen(TFO)を使うのに/proc/sys/net/ipv4/tcp_fastopenに整数値をセットするようだけど、その数値の意味は?というのがDocumentation/networking/ip-sysctl.txtにあったのでめも。ついでに一部実装も見てますが。

tcp_fastopenファイルに設定する値は整数値で設定できる数値は排他的では無くて(printkのログ出力レベルみたいな)、論理和をとって複数の項目を設定することができるんですね。
それで設定できる内容はドキュメントのtcp_fastopenの項目に。

 476        The values (bitmap) are
 477        1: Enables sending data in the opening SYN on the client.
 478        2: Enables TCP Fast Open on the server side, i.e., allowing data in
 479           a SYN packet to be accepted and passed to the application before
 480           3-way hand shake finishes.
 481        4: Send data in the opening SYN regardless of cookie availability and
 482           without a cookie option.
 483        0x100: Accept SYN data w/o validating the cookie.
 484        0x200: Accept data-in-SYN w/o any cookie option present.
 485        0x400/0x800: Enable Fast Open on all listeners regardless of the
 486           TCP_FASTOPEN socket option. The two different flags designate two
 487           different ways of setting max_qlen without the TCP_FASTOPEN socket
 488           option.
 489
 490        Default: 0

デフォルト値は0だし、無効ってことですね。
そして1はクライアント側の項目ですね。

1: Enables sending data in the opening SYN on the client.

これでSYNパケット送信時にデータを送るのを有効に。

2はサーバ側ですね。

2: Enables TCP Fast Open on the server side, i.e., allowing data in a SYN packet to be accepted and passed to the application before 3-way hand shake finishes.

内容としては1と関係があって、クライアントがTCP FASTOPEN機能を使ったSYNパケットを送信して来たらそれを受け付けてデータをクライアントに返すようにする。これは3-way hand shake完了前に終わる。

tcp fastopenを試した人のブログだと/proc/sys/net/ipv4/tcp_fastopenを3に設定しているのはクライアント・サーバ共にtcp fastopenを有効にしている訳ですね。

次の4はクライアント側ですかね。

4: Send data in the opening SYN regardless of cookie availability and without a cookie option.

SYNパケットをクッキー無しで送る or クッキーオプション無しで送るのを許可する。このクッキーはlwn.netの記事のTFO cookieですな。

次は0x100でこれはサーバ側かな。

0x100: Accept SYN data w/o validating the cookie.

SYNパケットに有効なクッキーが無くてもOKにすると。

最後は、

0x400/0x800: Enable Fast Open on all listeners regardless of the TCP_FASTOPEN socket option. The two different flags designate two different ways of setting max_qlen without the TCP_FASTOPEN socket option.

すべてのリスナーをTFO有効にしてしまうというオプションですね。0x400と0x800の違いはmax_qlenが絡むのはわかるけど、何かと言うのはコードを見ないとだめですね。

TFOのリスナーはinclude/net/request_sock.hに構造体が定義されていて、このメンバ変数にmax_qlenがありますね。

 112/*
 113 * For a TCP Fast Open listener -
 114 *      lock - protects the access to all the reqsk, which is co-owned by
 115 *              the listener and the child socket.
 116 *      qlen - pending TFO requests (still in TCP_SYN_RECV).
 117 *      max_qlen - max TFO reqs allowed before TFO is disabled.
 118 *
 119 *      XXX (TFO) - ideally these fields can be made as part of "listen_sock"
 120 *      structure above. But there is some implementation difficulty due to
 121 *      listen_sock being part of request_sock_queue hence will be freed when
 122 *      a listener is stopped. But TFO related fields may continue to be
 123 *      accessed even after a listener is closed, until its sk_refcnt drops
 124 *      to 0 implying no more outstanding TFO reqs. One solution is to keep
 125 *      listen_opt around until sk_refcnt drops to 0. But there is some other
 126 *      complexity that needs to be resolved. E.g., a listener can be disabled
 127 *      temporarily through shutdown()->tcp_disconnect(), and re-enabled later.
 128 */
 129struct fastopen_queue {
 130        struct request_sock     *rskq_rst_head; /* Keep track of past TFO */
 131        struct request_sock     *rskq_rst_tail; /* requests that caused RST.
 132                                                 * This is part of the defense
 133                                                 * against spoofing attack.
 134                                                 */
 135        spinlock_t      lock;
 136        int             qlen;           /* # of pending (TCP_SYN_RECV) reqs */
 137        int             max_qlen;       /* != 0 iff TFO is currently enabled */

コメントによるとmax_qlenはTFO有効化前にいくつリクエストを受け付けるかって設定ですね。

この辺の定数を探してみるとinclude/net/tcp.hでここまで見てきた数値が定義されてます。さらにTFO_SERVER_ALWAYSなんてのもあるけど。

 225/* Bit Flags for sysctl_tcp_fastopen */
 226#define TFO_CLIENT_ENABLE       1
 227#define TFO_SERVER_ENABLE       2
 228#define TFO_CLIENT_NO_COOKIE    4       /* Data in SYN w/o cookie option */
 229
 230/* Process SYN data but skip cookie validation */
 231#define TFO_SERVER_COOKIE_NOT_CHKED     0x100
 232/* Accept SYN data w/o any cookie option */
 233#define TFO_SERVER_COOKIE_NOT_REQD      0x200
 234
 235/* Force enable TFO on all listeners, i.e., not requiring the
 236 * TCP_FASTOPEN socket option. SOCKOPT1/2 determine how to set max_qlen.
 237 */
 238#define TFO_SERVER_WO_SOCKOPT1  0x400
 239#define TFO_SERVER_WO_SOCKOPT2  0x800
 240/* Always create TFO child sockets on a TFO listener even when
 241 * cookie/data not present. (For testing purpose!)
 242 */
 243#define TFO_SERVER_ALWAYS       0x1000

では、この中でTFO_SERVER_WO_SOCKOPT1、TFO_SERVER_WO_SOCKOPT2がどこで使われるかというとnet/ipv4/af_inet.cのinet_listen()でした。まさしくな名前ですな。
このinet_listen()でどっちのフラグが立ってるかで処理が分かれます。といってもfastopen_init_queue()の2番目の引数(max_qlenの数)が変わるだけですが。

 222                if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) != 0 &&
 223                    inet_csk(sk)->icsk_accept_queue.fastopenq == NULL) {
 224                        if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) != 0)
 225                                err = fastopen_init_queue(sk, backlog);
 226                        else if ((sysctl_tcp_fastopen &
 227                                  TFO_SERVER_WO_SOCKOPT2) != 0)
 228                                err = fastopen_init_queue(sk,
 229                                    ((uint)sysctl_tcp_fastopen) >> 16);
 230                        else
 231                                err = 0;
 232                        if (err)
 233                                goto out;
 234                }

TFO_SERVER_WO_SOCKOPT1の時に渡すbacklogはinet_listen()の引数です。TFO_SERVER_WO_SOCKOPT2の場合はsysctl_tcp_fastopen(これは/proc/sys/net/ipv4/tcp_fastopenにセットした数値)を16bit右シフトしたものを使う。
ということで、これが0x400と0x800の違いですね。

ついでにfastopen_init_queue()を見てみます。これはinclude/linux/tcp.hにあります。

 388static inline int fastopen_init_queue(struct sock *sk, int backlog)
 389{
 390        struct request_sock_queue *queue =
 391            &inet_csk(sk)->icsk_accept_queue;
 392
 393        if (queue->fastopenq == NULL) {
 394                queue->fastopenq = kzalloc(
 395                    sizeof(struct fastopen_queue),
 396                    sk->sk_allocation);
 397                if (queue->fastopenq == NULL)
 398                        return -ENOMEM;
 399
 400                sk->sk_destruct = tcp_sock_destruct;
 401                spin_lock_init(&queue->fastopenq->lock);
 402        }
 403        queue->fastopenq->max_qlen = backlog;
 404        return 0;
 405}

やっていることはまあ普通な内容ですね。

さて、ここまでの内容をまとめると・・・
(´-`).。oO(特に理由が無ければ/proc/sys/net/ipv4/tcp_fastopenは3をセットしておけば良いんじゃないでしょうか

( ´Д`)ノ~