TCP 연결
- TCP는 애플리케이션 프로세스가 다른 프로세스에게 데이터를 보내기 전에, 두 프로세스간에 핸드셰이크를 먼저 해야 하므로 연결지향형이다.
- 즉, 데이터 전송을 보장하는 파라미터들을 각자 설정하기 위한 어떤 사전 세그먼트들을 보내야 한다.
- TCP 연결이 설정되면, 두 애플리케이션 프로세스는 서로 데이터를 보낼 수 있다.
- TCP 프로토콜은 오직 종단시스템에서만 동작하므로, 중간의 네트워크 요소들(라우터, 스위치)은 TCP 연결 상태를 유지하지 않는다.
- TCP 연결은 전이중 서비스(full-duplex)를 제공한다. 만약 호스트 A의 프로세스와 호스트 B의 프로세스 간에 TCP 연결이 있다면, 애플리케이션 데이터는 B에서 A로 흐르는 동시에 A에서 B로 흐를 수 있다.
- 또한, TCP 연결은 항상 단일 송신자와 단일 수신자 간의 점대점이므로, 멀티캐스팅(단일 송신 동작으로 한 송신자가 여러 수신자에게 데이터를 전송)은 불가능하다.
연결 설정 이후
- 두 애플리케이션 프로세스는 서로 데이터를 보낼 수 있게 됐다.
- 클라이언트 프로세스는 소켓(프로세스의 관문)을 통해 데이터의 스트림을 전달한다.
- 데이터가 소켓을 통해 전달되면, 이제 데이터는 클라이언트에서 동작하고 있는 TCP에게 맡겨진다.
- TCP는 초기 3-way handshake 동안에 준비된 버퍼 중의 하나인 송신 버퍼로 데이터를 보낸다.
- 때때로 TCP는 송신 버퍼에서 데이터 묶음을 만들어서 네트워크로 보낸다.
- 세그먼트로 모아 담을 수 있는 최대 데이터 양은 최대 세그먼트 크기(MSS)로 제한된다.
TCP 세그먼트
- 세그먼트는 네트워크 계층에 전달되어, 네트워크 계층 IP 데이터그램 안에 각각 캡슐화된다.
- TCP가 상대에게서 세그먼트를 수신했을 때, 세그먼트의 데이터는 TCP 연결의 수신 버퍼에 위치한다.
- TCP 연결의 양 끝은 각각 자신의 송신 버퍼와 수신 버퍼를 갖고 있다.
- 즉, TCP 연결은 한쪽 호스트에서의 버퍼, 변수, 프로세스에 대한 소켓 연결과 다른쪽 호스트에서의 버퍼, 변수, 프로세스에 대한 소켓 연결의 집합으로 이루어진다.
TCP 세그먼트 구조
TCP 헤더 필드 + 클라이언트 데이터 필드
- 헤더는 상위 계층 애플리케이션으로부터 다중화/역다중화를 할 때 사용하는 출발지/목적지 포트번호를 포함한다. 또한 체크섬 필드를 포함한다. (여기까지는 UDP와 동일)
- 32비트 순서번호 필드와 32비트 확인응답 번호 필드는 신뢰적인 데이터 전송 서비스 구현에서 TCP 송신자와 수신자에 의해 사용된다.
- 16비트 수신 윈도 필드는 흐름 제어에 사용된다.
- 4비트 헤더 길이 필드는 32비트 워드 단위로 TCP 헤더의 길이를 나타낸다.
- 옵션 필드:
- 선택적이고 가변적인 길이를 갖는다.
- 송신자와 수신자가 최대 세크먼트 크기(MSS)를 협상하거나 고속 네트워크에서 사용하기 위한 윈도 확장 요소로 이용된다.
- 플래그 필드: 6비트를 포함한다.
- ACK 비트: 확인응답 필드에 있는 값이 유용함을 가리키는데 사용된다.
- RST, SYN, FIN 비트: 연결 설정과 해제에 사용된다.
- PSH 비트: 이것은 수신자가 데이터를 상위 계층에 즉시 전달해야 함을 가리킨다.
- URG 비트: 이 세그먼트에서 송신 측 상위 계층 개체가 '긴급'으로 표시하는 데이터임을 가리킨다.
순서 번호, 확인 응답 번호
- 이 두 필드는 TCP의 신뢰적인 데이터 전송 서비스의 가장 중요한 부분이다.
- TCP는 데이터를 구조화되어 있지 않고, 단지 순서대로 정렬되어 있는 바이트 스트림으로 본다.
- 세그먼트에 대한 순서번호는 세그먼트에 있는 첫번째 바이트의 바이트 스트림 번호이다.
- TCP는 전이중 방식이므로, 호스트 A가 호스트 B로 데이터를 송신하는 동안에 호스트 B로부터 데이터를 수신하게 해준다.
- 호스트 A가 자신의 세그먼트에 삽입하는 확인응답 번호는 호스트 A가 호스트 B로부터 기대하는 다음 바이트의 순서번호이다.
- 호스트 A가 호스트 B로부터 0~535의 바이트를 포함하는 어떤 세그먼트와 900~1000의 바이트를 포함하는 다른 세그먼트를 수신했다고 가정하자. (어떤 이유 때문인지 몰라도, 호스트 A는 536~899의 바이트를 아직 수신하지 않았음)
- 이 상황에서 호스트 A는 호스트 B의 데이터 스트림을 재생성하기 위해 536번째의 바이트를 아직 기다리고 있으므로, B에 대한 A의 다음 세그먼트는 확인응답 필드에 536을 가질 것이다.
- TCP는 스트림에서 첫번째 잃어버린 바이트까지만 확인응답하기 때문에, 누적 확인응답을 제공한다고 한다.
- 위 예에서 호스트 A는 세번째 세그먼트(900~1000 값의 바이트)를 두번째 세그먼트(536~999 값의 바이트)가 수신되기 전에 수신했으므로 세번째 세그먼트는 순서가 틀리게 도착했다.
- 이 상황에서 호스트는 TCP 구현 개발자에 의해 두가지 선택을 할 수 있다.
- 수신자가 순서가 바뀐 세그먼트를 즉시 버린다.
- 수신자는 순서가 바뀐 데이터를 보유하고, 빈 공간에 잃어버린 데이터를 채우기 위해 기다린다. (이 방법이 네트워크 대역폭 관점에서 효율적이고 실제에서도 쓰이는 방법이다)
텔넷
- 원격 로그인을 위해 사용되는 유명한 애플리케이션 계층 프로토콜
- TCP 상에서 실행되며, 한 쌍의 호스트들 사이에서 동작하도록 설계되었다.
- 호스트 A가 호스트 B와 텔넷 세션을 시작한다고 가정하자. (호스트 A가 세션을 시작하므로 클라이언트, 호스트 B가 서버)
- 사용자가 'C'를 입력하고, 커피를 마시는 동안 위 그림처럼 3개의 세그먼트가 송신될 것이다. (3-way handshake)
- 첫번째 세그먼트 (클라이언트 → 서버)
- 클라이언트의 초기 순서번호 42: Seq = 42
- 두번째 세그먼트 (서버 → 클라이언트)
- 수신하는 서버에게 데이터에 대한 확인응답을 제공: ACK=43 (클라이언트에게 바이트 42를 성공적으로 수신했고 앞으로 바이트 43을 기다린다는 것을 의미함)
- 수신한 문자 'C'를 반대로 반향되게 함: data = 'C' (데이터 필드에 'C'의 ASCII 표현을 함)
- 서버의 초기 순서번호 79: Seq = 79
- 세번째 세그먼트 (클라이언트 → 서버)
- 서버로부터 수신한 데이터에 대한 확인응답을 하는 것
- 이 세그먼트는 빈 데이터 필드를 갖는다.
- 즉, 확인응답은 어떤 클라이언트-서버 데이터와 함께 피기백되지 않는다.
- 확인응답 80: Seq = 80 (클라이언트가 바이트 79를 성공적으로 수신했고 앞으로 바이트 80을 기다림)
- 서버로부터 수신한 데이터에 대한 확인응답을 하는 것
왕복시간(RTT) 예측과 타임아웃
- 왕복시간(RTT): 세그먼트가 전송된 시간부터 긍정 확인응답될 때까지의 시간
- 타임아웃 시간은 RTT보다 좀 커야만 불필요한 재전송이 발생하지 않는다.
- SampleRTT(세그먼트에 대한 RTT 샘플): 세그먼트가 송신된(IP에게 넘겨진) 시간으로부터 그 세그먼트에 대한 긍정응답이 도착한 시간까지의 시간 길이
- 대부분의 TCP는 한번에 하나의 SampleRTT 측정만을 시행한다.
- 즉, 어떤 시점에서 SampleRTT는 전송되었지만 현재까지 확인응답이 없는 세그먼트 중 하나에 대해서만 측정된다.
- 이는 대략 왕복 시간마다 SampleRTT의 새로운 값을 얻게 한다.
- SampleRTT 값은 라우터에서의 혼잡과 종단시스템에서의 부하 변화 때문에 세그먼트마다 다르기 때문에, 대체로 RTT를 추정하기 위해 SampleRTT값의 평균(EstimatedRTT)을 채택한다. TCP는 EstimatedRTT을 유지한다.
- 긍정 확인응답을 수신하고 새로운 SampleRTT를 획득하자마자 TCP는 EstimatedRTT를 갱신한다.
- EstimatedRTT는 SampleRTT의 가중평균이고, 예전 샘플보다 최근 샘플에 높은 가중치를 준다. (최신 샘플들이 네트워크 상의 현재 혼잡을 더 잘 반영한다) → 지수적 가중 이동 평균(exponential weighted moving average, EWMA)
- DevRTT는 RTT 변화율을 의미한다. 이는 SampleRTT가 EstimatedRTT로부터 얼마나 많이 벗어나는지에 대한 예측이다.
- DevRTT는 SampleRTT와 EstimatedRTT 값 차이의 EWMA이다.
- 재전송 타임아웃의 주기는 EstimatedRTT보다 크거나 같아야 하지만(불필요한 재전송을 피하기 위함), 또 너무 크면 안된다.(즉각적인 재전송을 못함)
- 즉, 타임아웃 값은 EstimatedRTT에 약간의 여윳값을 더한 것으로 설정하는 것이 바람직하다.
- TimeoutInterval = EstimatedRTT + 4*DevRTT
3-way handshake
- 하나의 호스트(클라이언트)에서 운영되는 프로세스가 다른 호스트(서버)안의 또 다른 프로세스와 연결을 시작하길 원한다고 가정하자.
- 클라이언트의 TCP는 다음과 같은 방법으로 서버와 TCP 연결 설정을 시작한다.
1단계
- 클라이언트의 TCP는 서버 TCP에게 특별한 TCP 세그먼트(SYN)를 송신한다.
- 세그먼트 헤더에 SYN 비트를 1로 설정한다.
- 애플리케이션 계층 데이터를 포함하지 않는다.
- 클라이언트는 최초 순서번호(client_isn)를 선택하고, 최초의 TCP SYN 세그먼트의 순서번호 필드에 이 번호를 넣는다.
- 이 세그먼트는 IP 데이터그램 안에서 캡슐화되고 서버로 송신된다.
2단계
- SYN 세그먼트를 포함하는 IP 데이터그램이 도착하면, 서버는 데이터그램으로부터 TCP SYN 세그먼트를 추출한다.
- 연결에 TCP 버퍼와 변수를 할당한다.
- 클라이언트 TCP로 연결 승인 세그먼트(SYNACK)를 송신한다.
- SYN 비트는 1로 설정된다.
- 애플리케이션 계층 데이터를 포함하지 않는다.
- TCP 세그먼트 헤더의 확인응답 필드는 client_isn+1 로 설정된다.
- 서버는 최초 순서번호(server_isn)를 선택하고, TCP 세그먼트 헤더의 순서번호 필드에 이 번호를 넣는다.
3단계
- SYNACK 세그먼트를 수신하면, 클라이언트는 연결에 버퍼와 변수를 할당한다.
- 클라이언트 호스트는 서버로 또 다른 세그먼트(ACK)를 송신한다.
- 클라이언트는 TCP 세그먼트 헤더의 확인응답 필드 안에 server_isn+1 을 넣어서, 서버의 SYNACK 세그먼트를 확인한다.
- 연결이 설정되었기 때문에 SYN 비트는 0으로 설정한다.
- 세번째 단계는 클라이언트에서 서버로의 데이터를 세그먼트 페이로드에서 운반할 수 있다.
→ 위의 3단계가 완료되면, 클라이언트와 서버 호스트들은 각각 서로에게 데이터를 포함하는 세그먼트를 보낼 수 있다.
SYN 플러드 공격
- TCP의 3-way handshake에서 서버는 수신된 SYN에 대한 응답으로 연결 변수와 버퍼를 할당하고 초기화한다.
- 그 다음, 서버는 응답으로 SYNACK를 보내고, 클라이언트의 ACK 세그먼트를 기다린다.
- 클라이언트가 이 3-way handshake의 세번째 단계를 완료하기 위한 ACK를 보내지 않으면, 결국 서버가 절반만 열린 연결을 종료하고 할당된 자원을 회수한다. → 이는 SYN 플러드 공격의 무대가 된다.
- SYN 플러드 공격은 고전적인 서비스 거부(Denial of Service, DoS) 공격이다.
- 공격자는 handshake의 세번째 단계를 완료하지 않은 상태에서 무수한 TCP SYN 세그먼트를 보낸다.
- 서버의 연결 자원이 반쪽 연결에 할당된다.
- 결국 서버의 연결 자원이 소진됨에 따라 합법적인 클라이언트들의 서비스가 거부된다.
- 이에 대한 방어책으로 SYN 쿠키가 존재한다.
'CS > 네트워크' 카테고리의 다른 글
라우터의 내부 (1) | 2023.12.10 |
---|---|
네트워크 계층 (0) | 2023.12.10 |
파이프라이닝된 신뢰적인 데이터 전송 프로토콜 (0) | 2023.11.16 |
신뢰적인 데이터 전송 원리 (0) | 2023.11.11 |
비연결형 트랜스포트: UDP (0) | 2023.11.11 |