[정리] TCP의 기능과 패킷 구조 - 1

TCP(Transmission Control Protocol)는 transport 계층의 대표적인 연결지향 프로토콜이다. TCP는 기본적으로 하위계층에서의(network, datalink 계층) 패킷 손실, 중복, 오류 등 모든 전송 문제를 검출하고 해결한다. TCP의 연결이 설정되고 통신하는 동안 두 종단 사이에는 다수의 옵션이 교환된다. 때문에 UDP에 비해 기능과 역할이 매우 복잡하고 다양하지만 신뢰성 있는 통신이 가능하다. 


[그림 1] TCP 구조


위 그림은 TCP의 구조를 나타내는데 이 중 위부터 다섯줄(20byte)이 TCP 헤더이다. 


Source Port : 패킷을 송신하는 시스템의 포트번호를 나타낸다. 클라이언트의 경우 예약된 0-1023의 well-known 포트번호를 제외한 번호를 임의로 생성하고 서버의 경우 일반적인 서비스 제공 시 well-known한 포트를 사용한다. ex) HTTP(80), FTP(20, 21)


Destination Port : 패킷을 수신할 시스템의 포트번호를 나타낸다. 


Sequence Number : 세그먼트(각 계층 패킷의 구분을 위해 4계층-세그먼트, 3계층-패킷, 2계층-프레임으로 구분한다.) 데이터의 순서번호를 표기한다. 3Way-Handshacking 수행 시 ISN(Initial Sequence Number)의 기능도한다. RFC1122에 따르면 시스템의 클럭에서 추출된 값을 부여한다. Sequence Number는 Acknowledge Number와 함께 통신의 신뢰성을 보장한다.


Acknowledge Number : 상대방으로부터 수신한 데이터의 바로 다음에 수신할 데이터 순서 번호를 나타낸다. Sequence Number의 확인 응답으로서 데이터 전송자에게 패킷을 잘 받았으니 그 다음 패킷을 송신하라는 의미로 사용된다. 상대방이 다음에 전송할 패킷의 Sequence Number이기도 하다.


Header Length : TCP 헤더의 전제 길이를 byte 단위로 표현한다.


Reserved : 미래를 위해 예약된 필드로 항상 0으로 설정된다. 


Flags

CWR : 송신자가 자신의 윈도우 사이즈를 줄인다.

ECE : 혼잡 감지 시 수신자가 ECE를 설정하여 송신자에게 알린다.  // * ECN 관련 글 : http://mr-zero.tistory.com/20

URG : Urgent Point 필드와 함께 사용되고 플래그 설정 시 TCP는 해당 세그먼트를 전송 큐의 제일 앞으로 보낸다. 

ACK : SYN에 대한 확인의 의미이다. 3Way-Handshacking에서의 SYN과 reset을 제외하고 모든 세그먼트에 ACK가 설정된다.

PSH : 일반적으로 모든 데이터를 전송하고 마지막에 보내는 신호로 수신측은 데이터를 즉시 전송하라는 의미이다.

RST : 송신자가 유효하지 않은 연결을 시도할 때 거부하는데 이용되고 또한 통신의 연결 및 종료를 정상적으로 할 수 없을때 사용된다.

SYN : 통신 시작 시 연결을 요청하고 ISN을 교환한다.

FIN : 데이터 전송을 종료한다.


Window Size : 송신 시스템에서 자신이 수용하는 한 버퍼의 크기를 byte단위로 나타낸다. 통신하는 동안 송수신자는 해당 필드를 통해 수신사이즈를 계속 변경한다.


Checksum : 데이터가 전송 중에 손실되지 않고 원본과 동일한지 검사한다. 


Urgent Point : Urgent flag 설정 시 urgent 데이터의 마지막 byte의 일련번호를 Urgent Point 필드에 저장한다. 해당 부분까지 긴급하게 처리를 요한다는 의미이다.


[그림 2] TCP 패킷


[그림 2]는 3Way-Handshacking 수행 시 첫 SYN 패킷의 내용을 캡쳐한 것이다. checksum은 퍼포먼스 문제로 인해 default로 disable 되어 있어 'validation disabled'로 표기된다. IP패킷에서는 checksum 확인이 가능하고 설정에서 변경 할 수 있다. 또, 분석의 용이를 위해 ISN을 0으로 표기하는데 설정에서 변경이 가능하다. (위 패킷의 ISN은 343143064이다.) 그리고 그림의 패킷은 SACK옵션이 설정 되었음을 알 수 있다.  // * ISN 설정 내용 :http://wiki.wireshark.org/TCP_Relative_Sequence_Numbers


TCP 연결은 두개의 IP와 두개의 포트번호를 통해 이루어진다. 좀더 명확하게 각 종단점은 한개의 IP, 한개의 포트번호 쌍을 가지고 TCP 통신에 임한다. 일반적으로 연결은 준비 - 데이터 전송 - 종료의 3단계로 이루어진다. TCP 통신을 요청하는 쪽을 Active Opener(client), 통신의 상대편을 Passive Opener(server)라 칭한다. 이러한 TCP의 준비와 종료 단계가 아래 [그림 3]에 상세히 나와 있다. 


[그림 3] 연결 준비 및 종료 절차


위 그림에서 connection set-up은 흔히 3Way-Handshacking 이라 부르는 과정으로 client와 server간 세션을 성립하는 단계이다. 


1.  client는 server로 부터 원하는 서비스(ex. 80 HTTP)의 포트번호에  client의 ISN 값을 sequence number에 설정하고 SYN 패킷을 보낸다. 


2.  server는 자신의 ISN을 sequence number에 설정하여 SYN패킷과 순서1에 대한 응답으로 ISN(client)+1 값을  ackno-wledge number에 설정하여 전송한다. 


3.  client는 순서2의 SYN에 대한 응답으로 ISN(server)+1 값으로  acknowledge number를 설정하여 전송한다. 이때 seq-uence number의 값은 순서1의 ISN(client)+1로 설정되는데 이는 순서2의 acknowledge number와 동일하다.


[그림 4] 3Way-Handshacking


실제 3Way-Handshacking 과정을 캡쳐한 [그림 4]에서 빨간 사각형의 번호가 clinet ISN이고 노란 사각형의 번호가 server ISN이다. 


[그림 3]의  connection close는 통신의 종료과정으로 4Way-Handshacking, 4Release 로도 불린다. 종료는 일반적으로 client가 시작하는 것이 보통이지만 server가 종료를 시작 할 수도 있고, 드물지만 동시에 종료하는 경우도 존재한다. 


1. client는 server가 받기를 기대하는(종료 하기 전 마지막 패킷의 acknowledge number)를 sequence number로 설정하고(그림의 K) 마지막 패킷의 sequence number를 acknowledge number로 설정하여(그림의 L) FIN과 바로 전 패킷에 대한 응답으로 ACK를 함께 전송한다. 


2. server는 client가 보낸 FIN에 응답하기 위해 K+1 값을 acknowledge number로 설정하고 ACK패킷을 전송한다.


3. 순서2의 진행 후 server의 애플리케이션이 내부적으로 종료절차를 진행하면서 FIN을 전송한다. 순서4의 ACK를 수신하지 못한 경우 수신할 때 까지 FIN을 재전송한다.


4. cilent는 server의 FIN에 대한 응답으로 L+1 값을 acknowledge number에 설정하고 ACK패킷을 전송한다.


[그림 5] 4Release


TCP 종료 과정을 캡쳐한 [그림 5]에서 빨간 사각형이 [그림 3]의 K이고 노란 사각형이 L이다. 위 내용들의 sequence number와 acknowledge number의 변화가 이해되지 않는다면 윗부분의 TCP 헤더의 기능 부분을 다시 한번 읽기를 권한다. 이상의 내용은 TCP의 정상적인 준비와 종료과정을 나타낸다. 이외에 극히 일부 애플리케이션에서는 Half close를 진행하는데 흔한 경우는 아니다. Half close는 client가 server에게 FIN을 보내고 server로부터 FIN을 수신하기 전까지 데이터를 계속 전송받길 원할 때 사용된다. 


[그림 6] Half close


Half close 상태에서 전송가능한 데이터 세그먼트 수의 제한은 없다. server는 데이터 전송이 완료되면 FIN을 보내 통신을 종료한다. 


[그림 7] 동시 연결


흔한 경우는 아니지만 두 애플리케이션이 동시에 연결을 요청하는 경우도있다. 이 경우 정상적인 3Way-Handshacking과는 다르게 양 종단이 서로 SYN을 전송하고 SYN,ACK를 수신한다. 이런 상황은 각 종단은 잘 알려진 IP와 포트번호를 가지고 있으며 이를 상대편 종단이 알고 있어야 가능하다.


[그림 8] 동시 종료


동시 연결과 마찬가지로 동시 종료도 가능한데, 이는 보통의 4release 절차와 세그먼트 개수는 같지만 순서에 차이가 있다.


통신의 개시에 ISN을 이용하였는데 이는 TCP의 IP와 포트번호 쌍 그리고 checksum 값이 유효한 경우 각 연결이 모두 정상적이며 동일한 통신으로 인지하는 것을 방지하기 위함이다. ISN은 시간에 따라 변경되므로 자연스레 각 연결은 서로다른 ISN을 가지게 되고 이는 각 연결을 서로 다르게 처리 할 수 있게 한다. 또, 보안을 위해 ISN을 추측하기 어렵게 만들거나 암호화를 하여야 패킷의 위조를 방지 할 수 있다.


[그림 9] TCP 상태 변화도


위 그림은 TCP의 통신 준비 및 종료시 각 상태와 단계에 따라 변화하는 변화를 도식화 한 것이다. 다만 CLOSED는 실제 상태가 아닌 변화도의 논리적인 시작과 끝을 나타내기 위해 사용되었다. 


Active open : (일반적으로 client) 준비 단계


Passive open : (일반적으로 server) 준비 단계


Active close : (일반적으로 client) 종료 단계


Passive close : (일반적으로 server) 종료 단계


Simultaneous open : 동시 준비 단계


Simultaneous close : 동시 종료 단계


ESTABLISHED 상태는 두 종단간 데이터 통신이 가능한 상태이다. SYN_RCVD에서 LISTEN 상태로의 천이는 Simultaneous open이 아닌 SYN_SENT로부터 천이한 경우에만 가능한데 이는 ACK가 아닌 RST(재설정)패킷을 받은경우 LISTEN으로 돌아가 다른 요청을 기다린다는 의미이다. 이러한 상태들은 netstat -an으로 직접 확인이 가능하다.


[그림 10] netstat -an 결과


앞서 이야기 했던 3Way-handshacking과 4release 과정의 상태변화를 나타내면 아래 [그림 11]과 같다.


[그림 11] 통신 준비, 종료 시 상태 천이


TIME_WAIT는 MSL(Maximum Segment Lifetime)의 2배의 시간을 대기하는 상태이다. MSL은 네트워크 상에서 세그먼트가 폐기되기 전까지 존재할 수 있는 최대 시간으로 TTL과 비슷한 성격을 가지고 있다. RFC0793에서는 2분으로 정의하고 있지만 일반적으로는 30초, 1분, 2분 등이 사용된다. 대부분의 경우 MSL 값은 수정 될 수 있는데 윈도우의 경우 HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 의 TcpTimedWaitDelay DWORD의 값을 변경하면 된다. 


server가 LAST_ACK 상태로 진입한 경우 client로부터 ACK를 수신해야만 완전한 종료가 가능하다. 하지만 client가 송신한 ACK가 손실된 경우 server는 계속 LAST_ACK 상태도 대기해야만 한다. 이때 server는 client에게 ACK를 수신할 때까지 FIN을 재전송 하고 FIN을 수신한 clinet는 ACK를 송신하게 된다. 때문에 server의 정상적인 종료를 위해 client는 TIME_WAIT 상태에서 2MSL간 대기해야 한다. 또, 2MSL동안 대기함으로써 TCP 통신에 사용된 IP, 포트 번호쌍을 재사용 할 수 없게 한다. 동일한 IP와 포트번호 쌍을 사용하기 위해선 2MSL 대기 시간이 종료되거나 이전 통신에 사용된 ISN보다 큰 ISN을 통신에 이용할 경우 사용이 가능하다. 2MSL의 시간 대기로 얻는 다른 효과는 이전 통신에서 지연되었던 세그먼트가 동일한 IP와 포트번호를 사용하는 새로운 연결의 일부로 해석되는 것을 방지한다. 


TCP 헤더에는 옵션을 포함할 수 있는데 기본 TCP 사양에 포함된 옵션은 EOL, MSS, NOP 뿐이고 그 후에 다수의 옵션이 정의되었다. 


[표 1] TCP 옵션 목록


모든 옵션은 유형을 지정하는 1byte의 kind 필드로 시작하고 그 다음 len 필드가 위치한다. len필드는 kind를 포함한 옵션의 전체 길이를 나타낸다. 


NOP(NO Operation)

NOP 옵션은 필요시 송신측이 필드를 4바이트의 배수가 되도록 채우기 위한 옵션이다. 


EOL(End Of Line)

EOL 옵션은 처리해야 할 더이상의 옵션 목록이 없는 리스트의 끝임을 알린다.


MSS(Maximun Segment Size)

MSS는 송,수신 시 사용할 수 있는 가장 큰 세그먼트 크기이다. MSS는 TCP와 IP의 헤더를 제외한 오직 TCP 데이터 바이트만 계산한다. 연결이 설정되면 각 종단은 자신의 SYN패킷과 함께 MSS 옵션을 통해 자신의 MSS를 상대에게 알리며, 통신의 중간에는 MSS 크기는 변경이 불가능 하다. IPv4에서 일반적인 MSS 값은 1460byte이다.(1518 byte - 이더넷 헤더 18byte IP 헤더 20byte - TCP 헤더 20byte = 1460) MSS 옵션은 송수신측의 협상이 아닌 제한된 값으로서 MSS 크기 이상의 세그먼트는 허용하지 않는다.


SACK(Selective Acknowledgement)

송수신 호스트가 SACK 기능을 지원함을 옵션을 통해 나타낸다. 

// * SACK 관련 글 : http://packetlife.net/blog/2010/jun/17/tcp-selective-acknowledgments-sack/


WSOPT(Window Scaling)

window size 값을 왼쪽으로 쉬프트하여 크기를 늘리는 옵션이다.

// * WSOPT 관련 글 : http://packetlife.net/blog/2010/aug/4/tcp-windows-and-window-scaling/


TSOPT(Time Stamp)

RTT 값을 계산하기 위한 옵션으로 PAWS(Protection Against Wrapped Sequence number)에도 사용될 수 있다.


TCP-AO(Authentication)

TCP 연결의 보안을 향상시키기 위한 옵션으로 아직 사용하지 않는다.


[그림 12] TCP 옵션


[그림 12]는 실제 TCP 패킷 옵션을 캡쳐한 화면이다. 해당 패킷은 MSS와 SACK, WS 옵션이 사용되었고 option의 기본 구조는 kind와 length 필드로 구성 되었음을 알 수 있다.



* [그림 3,6,7,8,9,11], [표 1]의 출처는 TCP/IP Illustrated Volume 1임을 밝힌다.