ç®æ¬¡
- ç®æ¬¡
- æ¦è¦
- tcpã½ã±ããããã°ã©ãã³ã°ã¨ã¯
- 3way-handshakeã¨ç¶æ é·ç§»
- backlogã¨ã¯
- ã¾ã¨ã
æ¦è¦
backlog=number sets the backlog parameter in the listen() call that limits the maximum length for the queue of pending connections. By default, backlog is set to -1 on FreeBSD, DragonFly BSD, and macOS, and to 511 on other platforms.
nginxã®listenã«è¨å®ã§ããbacklogãå ·ä½çã«ä½ããã¦ããã®ããçè§£ããããã®ã¡ã¢è¨äºã
tcpã½ã±ããããã°ã©ãã³ã°ã¨ã¯
tcpã使ã£ãããã°ã©ã ã¯ä»¥ä¸ã®ãããªå¦çãçµã¦éä¿¡ãè¡ãã(ã¢ã¼ããã¯ãã£ã«ãã£ã¦ç´°é¨ã¯ç°ãªãããããã¨ã¯ã»ã¼ä¸ç·)ãããã§ãã¤ã³ããªã®ãTCPã®å¦çã¯OSã§ãã£ã¦ããã¦ãã¨ããç¹ã§ããã¦ã¼ã¶ããã»ã¹çã«ã¯ã½ã±ããã«å¯¾ãã¦IOãã¦ãã°ãã¨ã¯éä¿¡ãè¡ããã¨ãå¯è½ã¨ãªã£ã¦ã¾ãã(raw_socketã¨ã使ã£ã¦èªåã§ãããã¨ãå¯è½)ã
ãµã¼ãå´
socket bind listen accept write
ã®é çªã
sock0 = socket(AF_INET, SOCK_STREAM, 0); # socketä½ã£ã¦ addr.sin_family = AF_INET; addr.sin_port = htons(12345); addr.sin_addr.s_addr = INADDR_ANY; bind(sock0, (struct sockaddr *)&addr, sizeof(addr)); #ãã¼ãçªå·ãªã©ãã½ã±ããã«å²ãå½ã¦ã listen(sock0, 5); # ã½ã±ãããæ¥ç¶å¾ ã¡ã®ç¶æ ã«ãã while (1) { len = sizeof(client); sock = accept(sock0, (struct sockaddr *)&client, &len); write(sock, "HELLO", 5); }
3way-handshakeã¨ç¶æ é·ç§»
ãã£ããæ¯ãè¿ããä»åã¯SYN_RECVEDã¨ESTABLISHEDã®éãããã§ä½ãèµ·ãã¦ãã®ããææ¡ãã¦ãã
backlogã¨ã¯
ãµã¼ãã¢ããªã±ã¼ã·ã§ã³ãlistenãã¦ããã½ã±ãããacceptãã¦ããªããç¢ºç«æ¸TCPã»ãã·ã§ã³ãä½åã¾ã§OSå´ã§ä¿æããããå®ç¾©ãããã®ãbacklogã¯listen(2)ããæã«æå®ã§ãããã®ã§ã¢ããªã±ã¼ã·ã§ã³æ¬¡ç¬¬ã§ã¯ãã©ã¡ã¼ã¿ã§ããããªããªã£ã¦ããå ´åããããnginxãapacheã¯ç¹ã«åé¡ãªãããããã¨ãã§ããã
syn-queueã¨accept-queueã¨ãããã®ãåå¨ãã¦åè ã¯ããã·ã¥ãã¼ãã«ãå¾è ã¯FIFOãã¥ã¼ã§å®è£ ããã¦ãã¾ããbacklogã4096ã¨è¨å®ãããsyn-queueãaccept-queueã4096ã¾ã§ããããè¨å®ããã¾ãã
2ã¤ã®ãã¥ã¼ã®ã¤ã¡ã¼ã¸ã¯ä»¥ä¸ã®ããã«ãªã£ã¦ãã¾ãã
syn-queue
SYN_RECEIVEDç¶æ
ã®ã½ã±ããã®æ°ãnet.ipv4.tcp_max_syn_backlog
ã¨ããæ°åã¨accept-queue
ã®æ°ããå²ãåºãããã
æåã®synãã¯ã©ã¤ã¢ã³ãããæ¥ã¦syn/ackãè¿ãackãæ¥ãã¾ã§ãããã®å¤ã«ãªããsyn/ackã®ãªãã©ã¤ã¯net.ipv4.tcp_synack_retries
ã¾ã§ããã
ã©ããã£ããæº¢ããã
SYN floodæ»æãåãããæº¢ãã¦ãã¾ãå¯è½æ§ããããSYN floodæ»æã¨ã¯ãã£ããããã¨synã大éã«çºè¡ããã¯ã©ã¤ã¢ã³ããããã¨ãã¦syn/ackãè¿ãã¦ãããã«å¯¾ããackãè¿ããªããããªæ»æãIPã¹ãã¼ãã£ã³ã°ã¨ãsyn/ackãè½ã¨ããã¡ã¤ã¢ã¦ã©ã¼ã«è¨å®ãããã¨ãã§ç°¡åã«è¡ããã(hping3ã¨ã使ãã°è² è·ãã¹ããã§ãã)
ãã¨ã¯synãæ¥ããã©syn/ackãè¿ããªãããããµã¼ããé«è² è·ã«ãªã£ã¦ããç¶æ³ã ã¨syn_recvã®å¤ã¯å¢ãç¶ãã¦ãããå¤é¨ããã®æ¥ç¶é度ãé常ã«å¤§ããå ´åã«èµ·ãããããããªããããããç¶æ³ã«ééãããã¨ã¯ãªãã®ã§ããã¯åãããªã...
溢ãããã©ããªãã
2ãã¿ã¼ã³åå¨ãããä»ã®Linuxã ã¨net.ipv4.tcp_syncookies
ãããã©ã«ãã§æå¹ã«ãªã£ã¦ããã¨æãã®ã§ããã¨ããããç¡å¹ãªå ´åã®ãã¿ã¼ã³ãæ¸ããTCP SYN floodæ»æãé²ãããã«éçºãããææ³ã®ã²ã¨ã¤ã§ããµã¼ãå´ã¯ SYN ãåãã¨ã£ãç´å¾ã«è¨æ¶é åãæ¶è²»ããã®ã§ã¯ãªãsyn/ackã«å¯¾ããackãæ¥ã¦åã㦠TCP æ¥ç¶ç¨ã®é åãå²ãå½ã¦æ¥ç¶ã確ç«ããã¦ããã¨ãããã®ã
net.ipv4.tcp_syncookies
ãç¡å¹
net.ipv4.tcp_syncookies = 0ã®å ´åãSYN_RECVã¯ä»¥ä¸ã®ããããã®ãã¡ãå°ããæ¹ã®æ°ã¾ã§ç»é²ã§ããã
ããããè¶ ããã¨æ°ãã«SYN_RECVã¯ç»é²ãã廿£ããããã®ãããæ°ãã«TCPæ¥ç¶ã確ç«ã§ããªããsynã¯dropããç¶ãã¯ã©ã¤ã¢ã³ãã®å®è£ 次第ã§ã¯ãããããããç¹ããã«ããã¨ããç¶æ³ã«é¥ãã
synã®dropã¯netstatã®çµ±è¨æ å ±ãè¦ãã³ãã³ãã¨ãã§ç¢ºèªã§ããã
$ netstat -s TcpExt: ... 1380 times the listen queue of a socket overflowed 1380 SYNs to LISTEN sockets dropped ...
net.ipv4.tcp_syncookies
ãæå¹
SYN_RECVã¯syn backlogã®ãµã¤ãºã¾ã§ç»é²ããããSYN_RECVãsyn backlogã®ãµã¤ãºãè¶ããã¨ãSYN Floodæ»æã¨å¤æããSYN cookiesã®åä½ãå§ã¾ããåä½ãããã¦ããã°SYN floodæ»æãåãã¦ããããæ°è¦æ¥ç¶ã§æ¥ç¶åé¡ã¯èµ·ããªããªãã
accept-queue
Acceptãã¥ã¼ã«ã¯ãå®å ¨ã«ç¢ºç«ãããæ¥ç¶ãå«ã¾ãã¾ããESTABLISHãªã½ã±ããã¨ãã¦åå¨ãã¦accept(2)ãã¢ããªã±ã¼ã·ã§ã³ãå¼ã³åºããã¨ã§Acceptãã¥ã¼ããåé¤ãããåãã¨ãªãã¾ãã
ããããã®è¨å®å¤ã¯ããããåå¥ã«ç®¡çãããã¨ãå¯è½ã§ããå¤ãã¯åå¤ãè¨å®ãããã¨æãã¾ãã(net.ipv4.tcp_max_syn_backlog
ãªã©ãæå³çã«å°ããè¨å®ãããã¨ã¯ãã¾ããªãããï¼ï¼)
ã©ããã£ããæº¢ããã
OSã3way-handshakeãå®äºãããã½ã±ãããã¦ã¼ã¶ããã»ã¹ãaccept(2)ããªãã¨æº¢ãã¾ããã¤ã¾ãaccept(2)ãå¼ã³åºãæããªããããããã»ã¹ãå¿ãã(IOãã¦ã³ãã§ãCPUãã¦ã³ãã§ã)å ´åã¯æºã¾ãç¶ãã¾ããæåã«ããããµã¼ããµã³ãã«ã«è¿½è¨ããã¦éãå¦çãå®è¡ãã¦ããæã«accept(2)ãå¼ã³åºããã«OSãESTABLISHãªæ¥ç¶ãéç£ãããaccept-queueã¯æº¢ãã¦ãã¾ãã¾ãããã©ãã£ãã¯ãå¤ãç°å¢ã ã¨æº¢ããå¯è½æ§ãããã¾ãã
while (1) { nanka_omoi_shori(); # ããã§åºãã£ã¦ããæã«OSãhandshakeãã¾ãã£ãã sock = accept(sock0, (struct sockaddr *)&client, &len); write(sock, "HELLO", 5); }
ãã¨ã¯ã¡ã¤ã³ããã»ã¹ãacceptãè¡ãåããã»ã¹ã¸ã¯ã©ã¤ã¢ã³ãããã®æ¥ç¶ã½ã±ãããåå¾ã§ãããããã®ãã¡ã¤ã«ãã£ã¹ã¯ãªãã¿ã渡ãã¨ãããã£ã¹ã¯ãªãã¿ããã·ã³ã°ã¨ããææ³ãç¨ããtcpãµã¼ãã®å ´åã¯ã¡ã¤ã³ããã»ã¹ãå¿ããã ãã®å ´åã¯å®¹æã«æº¢ããèµ·ãã¦ãã¾ãã
(ã¡ã¢)Nginxã®ã¢ã¼ããã¯ãã£
Nginxã¯ã¤ãã³ãé§åã®ã¢ã¼ããã¯ãã£ãåä¸ã®ãªã¹ãã³ã°ã½ã±ãããã¯ã¼ã«ã¼ã«çä¿¡æ¥ç¶ã«ã¤ãã¦éç¥ããåã¯ã¼ã«ã¼ã¯æ¥ç¶ãåå¾ãããã¨ãã¾ããnginx㯠epoll_waitãå©ç¨ãã¦ãã¤ãã³ãã®çºçãå¾ ã¡ã¤ãã³ããçºçãããå¦çãè¡ãã¨ããã¢ãã«ã§accept(2)ãredyã«ãªãã£ã¦ããã¤ãã³ãã¯ãã®epoll_waitã§æ¾ããå¦çãè¡ãããä»çµã¿ã«ãªã£ã¦ãã¾ãã
溢ãããã©ããªãã
tcp_abort_on_overflow=1
TCP/IPã¹ã¿ãã¯ã¯RSTãã±ããã§è¿çããæ¥ç¶ã¯SYNãã¥ã¼ããåãé¤ãããã
tcp_abort_on_overflow=0
ã¹ã¿ãã¯ã¯æ¥ç¶ãACKæ¸ã¿ã¨ã¿ãªããããªãã¡ããã®ACKãã±ããã¯ç¡è¦ãããæ¥ç¶ã¯SYNãã¥ã¼(SYN_RECV)ã®ã¾ã¾ã«ãã¦ããããã®å¾ããã«ãã¿ã¤ãã¼ãéå§ãããSYN/ACKãã±ãããå度éã(åéã¿ã¤ãã³ã°ã¯ã¨ã¯ã¹ããã³ã·ã£ã«ããã¯ãªã)ãã¯ã©ã¤ã¢ã³ãã¯å度ACKãã±ãããããããã¨ãã§ããã
Client<SYN-SENT>] --- [SYN] --> Server<LISTEN> Client<SYN-SENT>] <-- [SYN/ACK] --- Server<SYN-RECV> Client<ESTABLISHED> --- [ACK] --> Server<SYN-RECV> # ãã®ACKãç¡è¦ããããACKãæ¥ãªãã®ã§SYN/ACKããµã¼ãå´ã¯éã Client<ESTABLISHED> Server<ESTABLISHED>
ããã§ãã¤ã³ããªã®ã¯Clientã¯ESTABLISHã«ãªã£ã¦ããã¨ããç¹ãhttpã¯ã©ã¤ã¢ã³ããªããªã¯ã¨ã¹ãéä¿¡å¯è½ç¶æ ã«ç§»ãã®ã§connect timeoutã¿ãããªã®ã¯çºçããã«ãã ã®å¦çé å»¶ã®ããã«è¦ããã(ãµã¼ãã¯SYN_RECVãªç¶æ ã§ãã¼ã¿ãããã®ã§dropããã¯ã...ackã帰ã£ã¦ããªããã±ããã®åéãªã®ã§tcp_retries2ãããã®ãã©ã¡ã¼ã¿ãæå¹ã«ãªãããã ãæ¤è¨¼ãã¦ã¿ãªãã¨ããããã£ã¦ããªã)