listen backlog 【3.6】
1. 概要
listen()時に指定するbacklogに関するメモ。
2. backlogとsyn backlog
listen()システムコールでLISTEN状態にあるソケットはbacklogというキューを持つ。backlogにはクライアントからのTCP接続要求が確立(ESTABLISH状態)し、まだaccept()されていない接続が並ぶ。
Listenソケットはbacklog以外にもTCP接続中(SYN_RECV状態)の接続を格納しておくキューがある。各種ドキュメントではキューと呼んでいるが、実際にはハッシュテーブルで実装されている。ここではsyn backlogと呼ぶ。
backlogのキューはinet_csk(sk)->icsk_accept_queueの.rskq_accept_headと.rskq_accept_tailで管理され、syn backlogのハッシュはinet_csk(sk)->icsk_accept_queue.listen_opt->syn_tableで管理される(図1)。
図1 Listenソケットとbacklog
3. backlogサイズ
backlogのキュー長はlisten()時に指定されるbacklog引数の値に設定される。ただし、指定値がsysctlのnet.core.somaxconnより大きかった場合は、net.core.somaxconnの値に切り詰められる。
4. syn backlogのサイズ
syn backlogのサイズはbacklogとnet.ipv4.tcp_max_syn_backlogに基づき決定される。
syn backlogのサイズはbacklog値を8〜net.ipv4.tcp_max_syn_backlogの範囲に収めた後、一つ上の2のべき乗の値に切り上げた値になる。例えば、backlog長が200ならsyn backlogサイズは256、backlog長が256ならsyn backlogサイズは512となる(syn backlogの初期化処理はreqsk_queue_alloc()で行っている)。
表1 syn backlogサイズ計算例(tcp_max_syn_backlog = 400の場合)
backlogサイズ | syn backlogサイズ |
---|---|
128 | 256 |
200 | 256 |
256 | 512 |
512 | 512(*1) |
(*1) backlog(512)はtcp_max_syn_backlog(400)より大きいので、400を切り上げた512がsyn backlogのサイズになる。
syn backlogのサイズを大きくしようとするには、以下の点に注意する。
(a) net.ipv4.tcp_max_syn_backlogを大きくする
(b) listen()で指定するbacklog値を大きくする(apacheならListenBackLogディレクティブで指定できる)
(c) backlogの値自体はnet.core.somaxconnに切り詰められるため、net.core.somaxconnの方も大きくしておく。
syn backlogの最大サイズはbacklogサイズに基づいて決まるため、net.ipv4.tcp_max_syn_backlogを大きくするだけでなく、backlog自体が大きくなるように(b),(c)も行う必要がある。
syn backlogにSYN_RECVがいくつまでたまるか、およびあふれた時の動作は、net.ipv4.tcp_syncookiesの値によって異なる。
4-1. SYN_RECVがいくつたまるか(net.ipv4.tcp_syncookies = 0の場合)
net.ipv4.tcp_syncookies = 0の場合、SYN_RECVは以下のいずれかのうち、小さい方の数まで登録できる。
- syn backlogのサイズまで
- net.ipv4.tcp_max_syn_backlogの3/4まで
これを越えると、新たにSYN_RECVは登録せず廃棄する。このため、新たにTCP接続を確立できない。
計算例)
- backlog=512, tcp_max_syn_backlog=512の場合
syn backlogのサイズは1024、tcp_max_syn_backlogの4分の3は384なので、SYN_RECVは384まで登録される。
- backlog=128, tcp_max_syn_backlog=512の場合
syn backlogのサイズは256、tcp_max_syn_backlogの4分の3は384なので、SYN_RECVは256まで登録される。
net.ipv4.tcp_max_syn_backlogを大きくしても、net.core.somaxconnが小さいと、backlogも小さく切り詰められ、syn backlogのサイズも小さくなるので、SYN_RECVの最大登録数が思ったように増えないことになる。このため、SYN_RECVの最大登録数を増やそうと思ったら、4.節の(a)〜(c)を合わせて行う必要がある。
4-2. SYN_RECVがいくつたまるか(net.ipv4.tcp_syncookies = 1の場合)
net.ipv4.tcp_syncookies = 1の場合、SYN_RECVはsyn backlogのサイズまで登録される。
SYN_RECVがsyn backlogのサイズを越えると、SYN Flood攻撃と判断し、SYN cookiesの動作が始まる。
5. SYN_RECVキューイングに関連するソース
SYN_RECVのキューイング/廃棄の処理はtcp_ipv4.c::tcp_v4_conn_request()で行っている。
syn backlogのキューフルチェック
if ((sysctl_tcp_syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) { want_cookie = tcp_syn_flood_action(sk, skb, "TCP"); if (!want_cookie) goto drop; }
net.ipv4.tcp_syncookies = 0時のtcp_max_syn_backlog 4分の3チェック
else if (!sysctl_tcp_syncookies && (sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < (sysctl_max_syn_backlog >> 2)) && !tcp_peer_is_proven(req, dst, false)) { /* Without syncookies last quarter of * backlog is filled with destinations, * proven to be alive. * It means that we continue to communicate * to destinations, already remembered * to the moment of synflood. */ LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"), &saddr, ntohs(tcp_hdr(skb)->source)); goto drop_and_release; }