network

서버 관리자를 위한 TCP 지식 - SYN_SENT, ESTABLISHED, TIME_WAIT

Hello World Study 2022. 7. 2. 07:44

 

TCP 가 뭔가요? 라고 누군가가 묻는다면

3 way handshaking 으로 연결을 맺고 송신 데이터에 대해 잘 받았다는 응답을 전달함으로써 신뢰성있는 전송이 가능한 프로토콜이다. 정도는 대답할 수 있을 겁니다. 

 

서버 관리자에는 이 정도 지식으로는 제대로 된 서버 관리가 어렵습니다.

이번 포스팅에서는 TCP 프로토콜에서 서버 관리자에게 꼭 필요한 부분만 골라서 알아보겠습니다.

 

TCP Status 

 

< 출처: https://ssup2.github.io/theory_analysis/TCP_Connection_State/&nbsp; + 빨간색으로 내용 추가>

위 그림만 보니 머리가 아픈 분이 계실겁니다. 모두 알 필요는 없으니 벌써 포기하지 맙시다.

우선 각 영역에 대해 알아봅시다.

connect(), bind(), listen() 처럼 괄호가 있는 부분은 시스템콜 함수를 나타냅니다.

C언어를 사용해봤다면 socket 통신시 connect(), bind(), send(), close() 등의 시스템콜 함수를 써서 통신을 했을 겁니다. 바로 그 시스템콜 함수를 나타냅니다. 

 

SYN_SENT, ESTABLISHED, TIME_WAIT, CLOSED 등은 소켓의 상태를 나타냅니다. 

연결중인지, 연결완료된것인지, 연결종료된 상태인지 등을 나타냅니다.

 

SYN, SYN+ACK, FIN, ACK 등은 TCP flag 를 나타냅니다. TCP 에는 소스포트, 목적지포트 가 가장 중요하나 그외에도 연결을 맺기 위한 패킷인지, 잘 수신했음을 나타내기 위한 패킷인지, 연결종료를 위한 패킷인지 등을 구분하기 위해 FLAG 필드가 있으며, 여기에는 SYN, ACK, FIN 등이 있습니다.

 

TCP flag 부분을 보면 빨간색으로 3 way hand shake 로 적혀있고 거기서 부터 

SYN,  SYN+ACK, ACK 이렇게 3개 패킷이 전송되는걸 알수 있습니다.

 

맨 아래에는 4 way hand shake 로 적혀있고 거기서 부터 

FIN, ACK , FIN, ACK  이렇게 4개 패킷을 전송하면서 연결종료를 하고 있습니다. 

 

서버 관리자에게 필요한 부분은 소켓 상태 즉 SYN_SENT, ESTABLISHED, TIME_WAIT, CLOSED 등이 뭘 뜻하는지 잘 알고 있어야 합니다.

 

우선 리눅스서버에 접속해서 좀 더 자세히 살펴보겠습니다.

$ netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN     
tcp        0      0 172.31.22.162:39750     169.254.169.254:80      TIME_WAIT  
tcp        0      0 172.31.22.162:39762     169.254.169.254:80      TIME_WAIT  
tcp        0      0 172.31.22.162:49334     52.79.128.16:80         TIME_WAIT  
tcp        0    384 172.31.22.162:22        13.209.1.57:48444       ESTABLISHED
tcp6       0      0 :::22                   :::*                    LISTEN

netstat 이라는 명령어로 네트워크 상태를 알수 있으며 -ant 는 TCP 정보를 알 수 있게 해주는 옵션입니다.

 

-help 옵션을 쓰면 아래처럼 해당 옵션이 어떤 의미인지 알수있습니다.

$ netstat -help
usage: netstat [-vWeenNcCF] [<Af>] -r         netstat {-V|--version|-h|--help}
       netstat [-vWnNcaeol] [<Socket> ...]
       netstat { [-vWeenNac] -i | [-cnNe] -M | -s [-6tuw] }       
        -n, --numeric            don't resolve names       
        -p, --programs           display PID/Program name for sockets       
        -a, --all                display all sockets (default: connected)
        ( 중략 )
<Socket>={-t|--tcp} {-u|--udp} {-U|--udplite} {-S|--sctp} {-w|--raw}
           {-x|--unix} --ax25 --ipx --netrom

netstat 에 대한 자세한 사용법은 아래 링크를 참고해주세요

https://semtul79.tistory.com/6

 

서버 관리자를 위한 network 지식- netstat

8080 포트로 서버로 접속하려고 하면 timeout 오류가 납니다. 서버 살아있는거 맞나요? 접속이 되었다가 안되었다가 합니다. 뭐가 문제죠? 서버 관리자라면 위와 같은 질문을 종종 받게 됩니다. 여

semtul79.tistory.com

 

SYN_SENT

3 way hand shake 를 위해 제일 먼저 보내는 게 SYN 플래그가 있는 패킷입니다. 

이걸 보낸 소켓의 상태가 SYN_SENT 입니다. 이름 자체가 SYN 보냈다 라는 뜻이니 이해가 쉽습니다. 

보통은 이 상태를 netstat 으로 보기가 쉽지 않습니다.

서버에서 SYN+ACK 를 곧바로 전송하고, 이를 수신하면 SYN_SENT 상태에서 ESTABLISHED 상태로 변하기 때문입니다. 보통은 1초 이내에 ESTABLISHED  로 변경되겠지요.

 

그런데 특별한 상황에서는 SYN_SENT 상태를 보기가 쉽습니다. 즉 서버가 SYN+ACK 를 보내지 않으면 계속 SYN_SENT 상태로 나오겠죠. 그럼 서버는 언제 SYN+ACK 를 보내지 않느냐 인데, 아래 2경우가 일반적입니다.

 

1) 요청한 포트로 LISTEN 하고 있는 프로세스가 없을때:

서버는 80 포트를 열어두지 않은 상태에서 클라이언트에서 80 포트로 SYN 패킷을 보낼 경우, SYN+ACK 를 보낼 프로세스가 없으니 아무런 응답을 보내지 않습니다. TCP 에서는 RESET 플래그 라는게 있고, 이게 지금과 같이 LISTEN 하고 있지 않은 포트로 요청이 온 경우, OS 단에서 RESET 패킷을 응답으로 보내주기도 합니다만, 해킹에 악용될 수 있어 보통은 RESET 패킷조차 안보내도록 OS 설정을 합니다. 

 

2) 방화벽과 같은 네트워크 상의 문제:

방화벽에서 80 포트로 요청이 오면 DENY 시키도록 RULE 이 걸려있다고 해봅시다. 그러면 SYN 패킷은 방화벽에 의해 DROP 이 됩니다. 보통 방화벽은 DROP만 시키지, 따로 RESET 패킷을 응답으로 보내지 않기에, SYN 패킷을 보낸 클라이언트는 하염없이 기다리다가 CONNNECTION TIMEOUT 에 의해 일정시간후에 소켓을 종료시킵니다.

위 2가지 경우의 실제 구분은 단순히 netstat 결과값으로 알 수 없으며, 서버 담당자에게 서비스가 해당 포트로 잘 동작하고 있는건지, 그리고 네트워크 담당자에게 방화벽 설정값을 확인한 후 명확히 알 수 있습니다. 

 

SYN_RECEIVED

서버입장에서 클라이언트로 부터 SYN 패킷을 수신했고, SYN+ACK 를 응답으로 보낸 상태입니다. 이후 ACK 를 수신할때까지 SYN_RECEIVED 상태입니다. 

이 상태 또한 정상적인 클라이언트라면 ACK 를 바로 보내기에 netstat 의 결과로 SYN_RECEIVED 를 보기가 쉽지 않습니다.

DoS 나 DDoS 공격과 같이 공격 프로그램에 의해 특정 서버로 요청이 몰릴수 있는데 이 때 공격 방법중의 하나로 SYN FLOODING 공격이 있습니다. 이름 그대로 SYN 패킷을 흘러넘치게 보낸다는 뜻입니다.

3 WAY HAND SHAKE 를 완전히 맺지 않고 SYN 만 보내고 ACK 를 보내지 않아서, 서버가 일정시간 계속 ACK 오기를 기다리도록 하여 , 서버의 CPU, MEM 사용 및 포트고갈을 유발시킵니다. 

만약 SYN_RECEIVED 상태만 엄청 많이 있다면 해커의 공격을 의심해 봐야 합니다. 

 

 

ESTABLISHED

통신 연결이 이루어진 상태입니다. 이 상태가 되어야 제대로된 통신이 가능합니다. 만약 application 에서 통신관련 오류가 나고 있는데, 소켓 상태가 ESTABLISHED 라면, 이건 실제 TCP 통신 단 문제가 아니라, 그 보다 상위 레이어인 application layer(L7) 프로토콜의 문제이거나 app 내의 버그로 인한 문제일 확률이 높습니다. 예를 들어 , HTTPS 통신 오류가 납니다. 근본원인이 만약 인증서의 유효기간 만료라고 합시다. 이 경우 ESTABLISHED  상태로 나옵니다.

아래 양쪽이 ESTABLISHED   상태가 된 이후에 SSL 통신을 하기 때문입니다. SSL 통신 초반에 인증서 교환 및 검증 작업을 하는데 이곳에서 에러가 난것이죠.

 

CLOSE_WAIT

이 상태가 많이 있다는 것은 온전히 서버의 버그입니다. 

클라이언트가 "종료" 패킷을 보냈고, 서버에서 이를 수신후 "하고 있던 일은 마무리 한 후 종료 패킷을 전송"하는 단계가 있습니다. C 언어에서는 close() 라는 시스템콜을 이용합니다. CLOSE_WAIT 은 이름 그대로 서버에서 close()를 호출하길 기다리고 있다는 겁니다. 즉 서버단에서 "종료 요청온 소켓에서 하고 있던 일을 마무리 못하고 있음 or hang 등으로 종료 요청 수신을 못했음" 와 같은 상태라는 겁니다.  잠깐은 CLOSE_WAIT 이 나오는게 정상이나, 1분넘게 이 상태로 있다면 서버로직의 문제를 의심해봐야 합니다. 

 

TIME_WAIT

TIME_WAIT 은 클라이언트 단에서만 나오는 상태입니다. 정확히는 연결 종료를 먼저 요청한 쪽에서 나옵니다.

보통 클라이언트가 연결 시도 및 연결 종료를 모두 먼저 요청하므로 클라이언트에서 주로 나옵니다.

TIME_WAIT 은 보통 60초 가량 지속되며, 이렇게 일정 시간 대기 상태를 줌으로써 완전한 연결 종료가 되는걸 보증합니다.  즉 TIME_WAIT 상태의 소켓이 많이 나온다고 버그가 있는게 아닙니다. 

다만, 클라이언트에서 동일 서버로 다량의 TCP CONNECTION 을 만들 경우, 그 연결들이 종료되면 모두 60초간 TIME_WAIT 상태가 됩니다. 

아래는 제 PC 에서 크롬브라우저의 여러 탭에서 동시에 접속할때 발생한 정보입니다. 

다른 정보는 모두 동일하고 로컬 pc 의 포트정보만 다릅니다. 

$ netstat -ant
  TCP    192.168.0.2:7995       204.79.197.200:443     TIME_WAIT       InHost
  TCP    192.168.0.2:8006       204.79.197.222:443     TIME_WAIT       InHost
  TCP    192.168.0.2:8007       204.79.197.222:443     TIME_WAIT       InHost
  (생략)

네이버에 연결시마다 로컬 PC 의 새로운 소스포트를 이용해서 연결을 하기 때문인데, 포트는 65535 개 이므로 이 말은 동시에 65535 개의 연결 및 연결종료 를 한 후, 곧바로 65535개의 연결을 시도할 경우 모두 연결 자체가 되지 않습니다.

모든 소스포트가 60 초간 TIME_WAIT 상태이므로 사용 가능한 포트가 없기 때문입니다.

좀 더 이해가 필요한 소켓관련 배경 지식은 아래링크의 "소켓을 구분짓는 요소들" 파트를 보시면 됩니다.

https://semtul79.tistory.com/6

결론적으로, 특정 서버로의 TIME_WAIT 상태가 6만개가 넘어갈 경우 추가적인 연결이 실패될수 있고, 이건 60초간 포트 사용금지라는 네트워크 규칙이 있기 때문입니다.

요즘에는 대용량 처리에 대한 요구사항이 많아서 리눅스의 커널 옵션 설정을 통해 60초간 모두 기다리지 않고도 안전하게 새로운 연결을 할 수 있는 방법이 있습니다. 자세한 건 본 페이지 맨 아래의 카카오 테크 링크를 보면 됩니다.

디폴트 설정은 60초간은 해당 포트 사용 금지라서 동일 서버로 너무 많은 요청을 보낼 경우 부하가 많은 시점에 연결 실패가 날 수 있다는 것만은 꼭 기억해두시기 바랍니다

 

ACTIVE_OPEN, PASSIVE_OPEN

서버, 클라이언트 라는 용어를 이용해서 설명을 했었는데, 좀 더 정확히 말하자면

ACTIVE OPEN, PASSIVE OPEN 이라고 말해야 합니다.

연결을 먼저 시도하는 곳을 ACTIVE OPEN 이라고 하고

누군가의 연결 요청에 의해 연결이 되는 것을 PASSIVE OPEN 이라고 합니다.

일반적으로는 클라이언트가 연결 시도를 먼저 하니 ACTIVE OPEN 인것이죠.

그러나, 서버의 경우, 

CLIENT <---> SERVER1 <---> SERVER2 와 같이 다른 서버를 호출하는 경우,

CLIENT <---> SERVER1 에서는 서버 입장이지만 ( PASSIVE OPEN )

SERVER1<-->SERVER2 에서는 클라이언트 입장입니다.  ( ACTIVE OPEN ) 

따라서 SERVER1은 서버이기도 하고, 클라이언트 이기도 합니다.  

 

 

보다 자세한 정보는 아래 링크를 참고하세요

https://tech.kakao.com/2016/04/21/closewait-timewait/

 

CLOSE_WAIT & TIME_WAIT 최종 분석

트래픽이 많은 웹 서비스를 운영하다보면 CPU는 여유가 있지만 웹서버가 응답을 제대로 처리하지 못하고 먹통이 되는 경우를 종종 보게 됩니다. 여러가지 이유가 있겠지만, 이 글에서는 가장 대

tech.kakao.com