흐름제어
수신 호스트는 여러 작업을 하기 때문에 자신에게 들어오는 메시지를 바로바로 읽지 못한다. 이 점을 인지하지 못하고 송신 호스트가 계속해서 메시지를 보내게 된다면 수신 버퍼가 가득차게 되어 오버플로우가 일어나게 된다.
TCP는 수신 버퍼의 오버플로우를 방지하기 위해서 수신호스트가 메시지를 읽는 속도와 송신 호스트가 메시지를 보내는 속도를 일치시키는 작업을 하게 된다. 이를 '흐름제어'라고 한다.
TCP는 송신 호스트가 수신 윈도우 라는 세그먼트 변수를 유지하여 흐름제어를 제공한다. 파일 전송 환경에서 수신 윈도우를 알아보자.
TCP 연결 상에서 A호스트가 B호스트에서 큰 파일을 전송한다고 가정해보자. B호스트는 이 TCP 연결에 수신 버퍼를 할당한다. 이를 RcvBuffer 라고 한다. B호스트는 시간이 날 때마다 수신 버퍼의 데이터를 읽으며 두 가지 변수를 정의한다.
- LastByteRead: B호스트가 수신 버퍼에서 읽은 마지막 바이트의 수
- LastByteRcvd: 수신 버퍼에 저장된 데이터 스트림의 마지막 바이트의 수
오버플로우를 허용하지 않기 위해서 LastByteRcvd - LastByteRead <= RcvBuffer 와 같은 식이 성립해야 한다.
따라서 RcvBuffer - (LastByteRcvd - LastByteRead) = rwnd 라고 여유 공간 변수를 설정할 수 있다.
이제 설정된 rwnd를 송신 호스트에서 어떻게 사용하는지 알아보자
송신 호스트는 두 가지 변수를 정의한다
- LastByteSent: A호스트가 B호스트로 보낸 데이터 스트림의 마지막 바이트의 수
- LastByteAcked: A호스트가 B호스트로부터 받은 전송이 완료된 데이터 스트림의 마지막 바이트의 수
따라서 LastByteSent - LastByteAcked 의 의미는 A가 보낸 아직 전송 확인이 안된 데이터의 양이다. 이는 명백히 수신 버퍼에 남아있는 데이터의 양과 일치할 것이다. 송신 호스트 A는 LastByteSent - LastByteAcked <= rwnd 값을 항상 확인하며 데이터 오버플로우를 체크하게 된다.
위 방식에는 사소한 기술적인 문제가 있는데 이를 알아보고 어떻게 해결하는지도 알아보자
호스트 B의 수신 버퍼가 가득차서 rwnd=0 을 A로 보냈다고 가정하자.(B는 A에게 보내려는 데이터가 없다.) A는 rwnd가 0이기 때문에 아무런 데이터도 보낼 수 없다. 이때 B의 프로세스가 데이터를 읽어서 B의 수신 버퍼가 조금 비워지게 되었다. 하지만 A와 B사이에 TCP연결이 사실상 끊어졌기 때문에 A는 B의 수신버퍼가 비워진 사실을 알 수 가 없다.
이 문제를 해결하기 위해서 TCP 명세서에는 A호스트와 B호스트 사이의 수신 윈도우(rwnd)가 0일 때 A호스트가 B호스트로 1바이트의 데이터를 계속해서 보내도록 요구하라고 되어있다. B호스트의 수신 버퍼가 비워지면 세그먼트의 rwnd값이 갱실될 것이고 A 호스트는 다시 정상적으로 데이터를 보낼 수 있을 것이다.
TCP 연결
어떻게 TCP 연결이 설정되는지 알아보자.
- 클라이언트 TCP는 서버 TCP에게 특별한 TCP 세그먼트를 전송한다. 이 세그먼트는 TCP SYN 세그먼트라고 하며, SYN 플래그 비트가 1로 설정되어있다. 추가로 클라이언트는 최초의 순서번호를 임의로 선택하여, 세그먼트의 순서번호 필드에 이 번호를 넣는다. 특정 보안 공격들을 피하기 위해서 최초 순서번호를 적절히 선택하는것을 유의해야한다.
- TCP SYN 세그먼트를 포함하는 IP 데이터그램이 서버 호스트에 도착했을 때, 서버는 데이터그램으로부터 TCP SYN 세그먼트를 추출한다. 그리고 클라이언트로 TCP 연결 승인 세그먼트를 전송한다. 연결 승인 세그먼트는 메시지를 전혀 포함하지 않는다. 하지만 3개의 중요한 정보를 가지고 있다. 첫째, SYN비트는 1로 설정된다. 둘째, TCP세그먼트 헤더의 확인응답 번호 필드는 TCP SYN 세그먼트의 순서번호 필드 + 1 로 설정된다. 마지막으로 서버는 자신의 최초의 순서번호를 임의로 설정한다. 이렇게 설정된 연결 승인 세그먼트는 SYNACK 세그먼트라고 불린다.
- 클라이언트가 SYNACK 세그먼트를 수신하면, 송신 버퍼와 다른 메시지 변수를 할당할 수 있다. 이제 세그먼트의 SYN 비트는 0으로 설정되고 세그먼트 페이로드에 데이터가 삽입되어 운반될 수 있다.
TCP 연결 종료
- 클라이언트가 연결 종료를 원할 때, TCP의 FIN 플래그 비트를 1로 설정해서 서버로 전송한다.
- 서버는 클라이언트에게 확인 세그먼트를 보낸다.
- 서버도 FIN 플래그 비트를 1로 설정한 세그먼트를 클라이언트로 전송한다.
- 서버에서 FIN 플래그 비트가 1로 설정된 세그먼트를 받은 시점부터 대기시간이 적용되고 클라이언트는 즉시 서버로 확인 세그먼트를 전송한다.
- 특정한 대기시간 이후에 현재 TCP 연결이 종료된다.
서버측에서도 먼저 TCP 연결 종료를 요청할 수 있다.
TCP 리셋
TCP RST 플래그 비트를 활용하는 경우를 알아보자
서버가 자신이 가지고 있는 소켓과 관련이 없는 포트번호와 출발지 IP 주소를 가진 TCP 세그먼트를 수신하면 RST 비트를 사용하게 된다.
예를 들어 목적지 포트 80을 포함하는 TCP SYN 세그먼트를 수신하지만 호스트는 포트 80에서 연결을 수락하는 프로세스가 없다고 가정하자. 서버는 클라이언트로 특별한 리셋 세그먼트를 보내게 된다. 이 의미는 서버는 해당 세그먼트에 대한 소켓을 가지고 있지 않으니 세그먼트를 재전송 하지말라는 뜻이다.
RST 비트를 활용하는 사례에서 '포트 스캔'이 있다.
포트스캔은 서버에서 열려있는 TCP/UDP 포트를 검색하는 것을 의미한다. 포트스캔을 수행하는 도구 중에서 nmap이라는 툴이 있는데 이 nmap 포트스캐닝 툴이 어떻게 동작하는지 알아보자.
nmap은 호스트에 목적지 포트 6789를 가진 TCP SYN 세그먼트를 보낼 것이다. 이때 세가지 다른 결과가 발생한다.
- nmap은 서버로부터 TCP SYNACK 세그먼트를 수신한다 : 애플리케이션이 TCP포트 6789를 가지고 실행중임을 의미한다.
- nmap은 서버로부터 TCP RST 세그먼트를 수신한다 : TCP SYN 세그먼트가 서버에 도달했으나 서버는 6789번 포트를 사용중이 아님을 의미한다. 하지만 nmap 사용자는 포트 6789로 흐르는 세그먼트가 경로상에서 어떤 방화벽에도 차단되지 않는다는 것을 알 수 있다.
- nmap은 아무것도 받지 못한다 : 이는 TCP SYN 세그먼트가 중간에 있는 방화벽에 가로막혀 서버로 전송되지 못함을 의미한다.
'CS > Network' 카테고리의 다른 글
[Network] 네트워크 계층 : 라우터(Router) (0) | 2021.10.01 |
---|---|
[Network] 네트워크 계층 : 개요와 서비스 모델 (0) | 2021.10.01 |
[Network] 트랜트포트 계층 : TCP 연결 (연결 지향형) (0) | 2021.09.24 |
[Network] 트랜스포트 계층 : 다중화와 역다중화 (0) | 2021.09.17 |
[Network] 트랜스포트 계층 : 개요(Intro) (0) | 2021.09.16 |