본문 바로가기
Network Know-how 기록하기

[TCP]'TCP TIME_WAIT' & 'TCP Port number reused'

by 네트워크 엔지니어 환영 2023. 1. 15.
반응형

TCP CLOSE_WAIT & TIME_WAIT

  • TCP 연결 종료 단계인 4-way handshake는 1차 FIN/ACK, ACK 교환 과정과 2차 FIN/ACK, ACK 교환 과정으로 나뉨
  • 연결 종료를 먼저 원하는 측에서 1차 FIN/ACK을 상대방에게 전송하여 연결을 종료할 준비를 하고 연결 종료를 요청받은 쪽에서 2차 FIN/ACK을 상대에게 보내 연결을 종료할 준비를 함
  • 세션을 정상적으로 종료하기에 앞서 받지 못한 패킷이 없도록 4-way handshake를 진행하는 Endpoint(이하 엔드포인트)는 교환 과정에서 별도의 상태에 돌입함
  • 4-way handshake와 상태 변화를 순서대로 보면 다음과 같음(아래 그림의 빨간색 박스에 해당)

 

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

1. 클라이언트 혹은 서버(이하 Active Closer)가 연결을 끊기 위해 상대방에게 FIN/ACK(1차)을 전송함. 이를 능동적 연결 종료 요청이라고 하여 Active Close라 부름.
2. 이 때, Active Closer는 연결 상태를 'FIN_WAIT'로 변경함. 이는 연결을 사용 중인 Application(이하 애플리케이션)으로 하여금 연결 종료 명령, Close()를 명령하여 연결을 끊을 준비를 하는 것임.
3. 클라이언트 혹은 서버(이하 Passive Closer)가 FIN/ACK(1차)을 전달 받고 ACK를 상대방에게 전송하여 종료할 준비를 함. 이를 수동적 연결 종료 승낙이라 하여 Passive Close라 부름.
4. 이 때, Passive Closer는 연결 상태를 'CLOSE_WAIT'로 변경함. 이는 연결을 사용 중인 애플리케이션으로 하여금 연결 종료 명령, Close()를 명령하여 연결을 끊을 준비를 하는 것임.
5. Passive Closer는 FIN/ACK(2차)를 Active Closer에 전송하여 연결을 종료할 준비를 할 것을 요청함. 이때 애플리케이션의 Close() 명령 수행을 한 Passive Closer는 'LAST_ACK' 상태에 돌입하여 Active Closer가 ACK를 전송하기를 기다림.
6. FIN/ACK(2차)를 받은 Active Closer는 즉시 'TIME_WAIT'를 유지하며 Passive Closer에게 ACK를 전송하고 일정 시간을 기다렸다가 연결을 완전 종료함.
7. 'LAST_ACK' 상태에서 Passive Closer는 ACK를 전달받고 연결을 완전 종료함.
  • 'TIME_WAIT'는 TCP 4-way handshake 이후 "연결 종료"를 요청한 쪽(Active Closer)에서 일시적으로 유지하는 상태
  • 'TIME_WAIT'을 유지하는 시간은 MSL(Maximum Segment Lifetime)이라 부르며 1분 정도의 시간을 의미함. 보통의 'TIME_WAIT'는 2MSL, 2분임
  • TIME_WAIT가 필요한 이유는 2가지임
    • 첫 번째, 연결 종료 과정에서 미처 다 보내지 못한 패킷을 받기 위해 기다리기 위해 사용함
    • 두 번째, 'LAST_ACK' 상태에서 ACK를 기다리는 Passive Closer가 모종의 이유로 ACK를 분실하여 받지 못 했다면 'TIME_WAIT' 상태의 Active Closer에게 다시 FIN/ACK(2차)를 전송하여 ACK를 받을 수 있도록 일정 시간을 유지하는 것임.
  • 'TIME_WAIT'가 없다면 Passive Closer는 ACK만을 기다리며 LAST_ACK 상태를 끊임없이 유지하게 되고 Active Closer에게 끊임없이 FIN/ACK을 전달하며 ACK를 요구하게 되며 해당 연결을 종료하지 않음. 이는 클라이언트의 Local Port(이하 로컬 포트)를 계속 점거하며 해당 포트 번호로 새로운 세션을 열지 못하는 문제를 발생시킴.
반응형

TCP Port number reused

  • 'Port nubmer reused'는 말그대로 포트를 재활용하는 것임
  • 이미 사용하였던 5-tuple(source-ip, destionation-ip, protocol, source-port, destination-port)를 이용해 새로운 연결을 생성함
  • 위에서 언급한 로컬 포트는 세션을 열기 위해 사용하는 출발지 혹은 목적지 포트를 의미함
  • 보통 목적지 포트는 사용처가 정해져 있기에 번호 또한 Port 80, Port 443 등 정해져 있지만 출발지 포트는 사용할 수 있는 모든 포트를 사용하여 연결을 생성함
  • 가용한 출발지 포트는 Window의 경우, 49152 ~ 65535를 사용하고 Linux의 경우, 31768 ~ 61000을 사용하며 출발지 IP는 이 포트들 중 하나를 선택하여 목적지에 도달하고 연결을 생성함
  • 포트의 수량이 많아보이지만 이는 실제 사용을 보면 그리 많은 양이 아님
  • 포트 수량이 부족해지는 가장 대표적인 경우는 바로 Source IP NAT로 다수의 출발지 IP가 한 가지 공인 혹은 사설 IP로 변환되어 외부 네트워크로 나가게 되는데 이때 출발지 포트를 통해 Original IP를 구별함
  • 변환되는 Originial IP가 매우 많아지고 한 개의 IP가 다수의 연결을 생성할 때 출발지 포트가 부족해짐
  • 이를 어느정도 해소하기 위해 포트 번호를 재사용하여 연결을 안정적으로 생성하도록 유도함

 

발생 가능한 문제 현상 & 해결

  • 매우 많은 수의 사설 IP의 클라이언트가 1개 공인 혹은 사설 IP만을 사용하여 Source IP NAT를 실시하는 상태에서 서버에서 먼저 연결 종료 요청(Active Close)을 실시하고 'TIME_WAIT'에 들어간다면 문제가 발생할 수 있음
  • 앞서 언급한 것처럼 Active Closer는 2차 FIN/ACK 교환 과정에서 'TIME_WAIT'에 돌입함
  • 라이언트가 'LAST_ACK' 상태에서 ACK를 받고 완전히 연결을 종료한 후에 짧은 시간 내에 기존 포트를 재활용(TCP Port number reused)하여 새로운 연결을 요청한다면?
  • 서버는 기존 포트를 사용하는 연결의 'TIME_WAIT' 상태를 유지하므로 상태 유지 시간 동안 클라이언트의 모든 요청을 무시함
  • Wireshark에서 이를 확인해보면 4-way handshake를 정상적으로 실시한 후, 클라이언트가 포트를 재활용해 새로운 연결 생성을 요청하지만 서버가 이를 모두 받아들이지 않고 클라이언트가 'TCP Retransmission'을 세 차례 전송하고 나서 'RST' 패킷을 전송하는 것을 볼 수 있음
  • 단기적 해결 방법은 L4 스위치가 있다면 L4 스위치에서 클라이언트의 Source Port를 변환하여 서버가 다른 포트를 받게끔 하여 새로운 세션을 정상적으로 생성하도록 하는 방법이 있음. 다만 이는 DSR 구성에서는 사용할 수 없음
  • 장기적 해결 방법은 출발지 포트 수량을 넉넉히 하는 것, 다시 말해 Source IP NAT에 사용할 IP를 다수 확보하여 포트가 재활용될 정도로 부족해지는 현상을 막는 것임


* 참고 자료
https://tech.kakao.com/2016/04/21/closewait-timewait/
https://docs.likejazz.com/time-wait/
https://ask.wireshark.org/question/26247/tcp-port-numbers-reused/
https://smjeon.dev/etc/tcp-state/
https://docs.likejazz.com/time-wait/

댓글