3-way handshake & 4-way handshake

손효재·2021년 12월 16일
1

Network

목록 보기
6/6

3 way handshake

포트 상태 정보

  • CLOSED : 닫힌 상태
  • LISTEN : 열린 상태로 연결 요청 대기 중
  • SYN_RCV : SYNC 요청을 받고 응답을 기다리는 중
  • ESTABLISHED : 포트 연결 상태

플래그 정보

TCP 헤더에는 CONTROL BIT(플래그 비트, 6bit)가 존재하는데, 여기에 연결제어 정보가 기록된다.
각 bit에는 “URG-ACK-PSH-RST-SYN-FIN”의 의미를 가지며, 해당 위치의 bit가 1이면 해당 패킷이 어떤 내용을 담고 있는 패킷인지 나타낸다.

  • SYN(Synchronize Sequence Number) 연결 요청 플래그 / 000010
    TCP에서 세션을 성립할때 가장 먼저 보내는 패킷이다.
    Sequence Number(32bit)를 랜덤으로 설정하여 세션연결에 사용한다.
  • ACK (Acknowledgement) 응답 플래그 / 010000
    응답 확인, 패킷을 받았다는 것을 알려주는 패킷이다.
    ACK 응답을 통해 보낸 패킷의 성공과 실패를 판단하여 재전송하거나 다음 패킷을 전송한다.
  • FIN (Finish) 연결 종료 플래그 / 000001
    세션 연결을 종료시킬 때 사용되며, 더이상 전송할 데이터가 없음을 의미한다.
  • RST(Reset) 연결 재설정 플래그 / 000100
    비정상적인 세션 연결 끊기로, 현재 접속중인 곳과 즉시 연결을 끊을 때 사용한다.
  • PSH 밀어넣기
  • URG 긴급 데이터 플래그

TCP/IP 프로토콜 통신간에 데이터를 전송하기 전에 먼저 정확한 전송을 보장하기 위해 사전에 세션을 수립하는 과정이다.

Step1. Client → Server (SYN)

클라이언트는 서버와 연결하기 위해 SYN을 보낸다.
송신자가 최초로 데이터를 전송할 때, Sequence Number를 임의의 랜덤 숫자로 지정하고, SYN 플래그 비트를 1로 설정한 세그먼트를 전송한다. (seq : x)

Client : CLOSED → SYN_SENT / Server : LISTEN (열린 상태로 연결 요청 대기 중)

Step2. Server → Client (SYN + ACK)

서버가 SYN(x)을 받고, 응답신호인 ACK와 SYN 패킷을 보낸다.
ACK Number 필드를 Sequence Number + 1로 지정하고, SYN와 ACK 플래그 비트를 1로 설정한 세그먼트를 전송한다. (seq : y, ACK : x + 1)
❗️그리고 Server는 Client의 접속을 연결하기 위해, RAM(메모리, Backlog Queue)에 일정 공간을 확보한다.

Server : SYN_RCV (SYNC 요청을 받고 응답을 기다리는 중)

Step3. Client → Server (ACK)

클라이언트는 서버의 응답으로 ACK(x + 1)와 SYN(y)을 받고,
마지막으로 ACK(y + 1) 패킷을 전송하여 연결을 맺는다. 이때, 전송할 데이터가 남아있으면 전송할 수 있다.

Client : ESTABLISED (포트 연결 상태) / Server : SYN_RCV → ACK → ESTABLISED

“2-way handshake를 하지 않는 이유는 무엇일까?”

최초 클라이언트가 서버에 연결 요청을 보내고, 서버는 이에 응답하여 클라이언트에게 응답신호를 보내면서 2-way handshake만으로도 연결이 가능할 것 같은데 굳이 3-way handshake를 사용하는 이유는 무엇인가?

만약, 클라이언트가 서버에 요청했는데, 딜레이가 많이 되어 클라이언트는 다시 서버에 연결을 요청하는 상황이다.

이때, 서버는 과거의 연결 요청에 대한 순서 번호로 응답을 하게되면서, 클라이언트는 마지막에 보낸 패킷과는 다른 잘못된 순서로 패킷이 왔기 때문에 그 패킷을 버리게 된다.

따라서 2-way handshake라면 서버 입장에서 클라이언트가 응답을 제대로 받았는지 모르고 혼자 연결되었다고 오해할 수 있는 상황인 것이다.

이는 TCP의 양방향 연결이 깨지면서 신뢰성을 보장할 수 없게된다. 그래서 3-way handshake를 통해 양쪽이 모두 제대로 연결되었는지 보장해주어야 한다.

“SYN Flooding이란?”

클라이언트에서 연결요청의 SYN를 보내고, 마지막 ACK 패킷을 보내지 않으면, Server는 Client의 연결을 받아들이기 위해 RAM(메모리, Backlog Queue) 공간을 점점 더 많이 확보해둔 상태에서 대기한다.

의도적으로 SYN 요청을 지속적으로 보낸다면, Server의 RAM이 꽉 차게되면서, 메모리가 초과하여 더이상 연결할 수 없는 상태가 되면서 서비스가 불가능해진다.

해결방법

1. SYN Cookie

방화벽 단에서 SYN을 먼저 받고, SYN Cookie를 포함한 SYN+ACK를 보내는 방법이다.

일정 시간동안 SYN Cookie에 대한 정상적인 응답패킷이 들어오지 않으면 방화벽에서 차단해버리고, 정상적인 패킷이 들어오면 통신이 가능하게 하는 방식이다.

  1. SYN이 들어오면, syn_cookie가 포함된 SYN+ACK을 보낸다.
  2. 그리고 syn_cookie에 적절히 대응하는 정상적인 ACK 패킷이 들어오면, 세션을 새로 열어 새로운 3-way-handshake 패킷을 서버에 연결해준다.

이는 처음 접속이 클라이언트에서 실패하고 재접속하는 것처럼 보일 수 있지만 순식간에 이루어져서 괜찮다.

2. SYN Proxy

방화벽 단에서 정상적인 3-way-handshake과정이 이루어지면,그 연결을 다시 서버에게 재현시켜주는 방식이다.

SYN PROXY도 역시, syn_cookie를 이용해서 정상적인 3-way-handshake인지를 확인하는데,
새로 세션을 열지 않아도 방화벽 단에서 server에 다시 재현해주는 proxy server의 느낌이다.

4-way-handshake

TCP에서 서버와 클라이언트의 연결을 해제(세션 종료)하는데 필요한 과정으로, FIN 플래그를 이용한다.

갑작스런 연결 해제 (Abrupt connection release)

RST 세그먼트가 전송되면 갑작스러운 연결 해제가 수행된다.

  • 존재하지 않는 TCP 연결에 대해 비SYN 세그먼트가 수신된 경우
  • 열린 커넥션에서 일부 TCP 구현은 잘못된 헤더가 있는 세그먼트가 수신된 경우
    RST 세그먼트를 보내, 해당 커넥션을 닫아 공격을 방지한다.
  • 일부 구현에서 기존 TCP 연결을 종료해야 하는 경우

정상적인 연결 해제 (Graceful connection release)

정상적인 연결 해제에서는 양쪽이 모두 커넥션을 닫을 때까지 연결되어 있다.

Half-Close 기법

FIN 패킷에 실질적으로 ACK가 포함되어 있는 것을 알 수 있는데, 이는 연결을 완전히 종료하지 않고 반만 종료하는 HALF-CLOSE 기법을 사용하기 때문이다.

연결 해제를 요청하는 FIN 패킷에는 승인번호를 담아 보내는데, 이는 “연결 종료는 할거지만, 승인번호까지 처리했으니 남은 데이터가 있다면 전송해라”는 의미이다.

이후 수신자가 남은 데이터를 모두 보낸이후 FIN 패킷을 보냄으로써 데이터가 모두 처리되었다는 뜻이고, 나머지 반을 닫으면서 안전하게 종료할 수 있다.

Step 1. Client → Server (FIN ( + ACK))

클라이언트가 close()를 호출하여 접속 해제를 요청하고, 서버에게 FIN 플래그를 보낸다.
(실질적으로 ACK도 포함되어 있다.)

Step 2. Server → Client (ACK)

서버는 확인했다는 ACK를 클라이언트에게 보내고 CLOSE_WAIT 상태가 되면서, 자신의 통신이 끝날때까지 기다린다. 아직 남은 데이터가 있다면 모두 전송한다.
클라이언트는 ACK를 받은 후에 서버가 남은 데이터 처리를 끝내고 FIN을 보낼 때까지 기다린다.(FIN_WAIT)

Step 3. Server → Client (FIN)

서버가 남은 데이터를 모두 보냈다면, FIN 패킷을 보낸 후, 승인을 기다리는 LAST_ACK 상태가 된다.

Step 4. Client → Server (ACK)

클라이언트는 FIN을 받고, 확인했다는 ACK를 서버에게 보낸다. 이때, 아직 서버에게 받지 못한 데이터를 기다리기 위해 TIME_WAIT 상태가 된다. (의도치않은 에러로 연결이 데드락에 빠지는 것을 방지)
서버는 ACK를 받은 이후 소켓을 닫고,(Closed) TIME_WAIT 시간이 지나면 클라이언트도 닫는다.(Closed)

왜 종료 후에 바로 끝나지 않고, TIME_WAIT 상태로 대기하는 것 일까?

아직 서버로부터 받지 못한 데이터가 있을 수 있기때문에 TIME_WAIT 상태로 기다린다. 일정 시간대기 이후 CLOSED한다.

0개의 댓글