Skip to main content

TCP 헤더 읽어드립니다

원티드 프리온보딩 백엔드 인턴십을 진행하면서 TCP 헤더 분석하기라는 발표 주제로 만든 PPT 자료와 설명을 작성하였습니다.

TCP는 연결을 지향할 뿐 연결 자체 달성을 보장해주지는 않는다.

Source port & Destination port

tcp-header1

  • 출발지 포트와 도착지 포트는 각각 16비트로 구성됩니다.
  • 예를 들어, 클라이언트가 서버로 요청을 전송하는 패킷에서 소스 포트는 클라이언트의 동적 포트가 할당될 것이고 도착지 포트는 웹 서버의 포트인 80 또는 443 포트가 사용됩니다.

tcp-header2

Sequence number

tcp-header3

tcp-header4

  • 시퀀스 번호는 전송되는 바이트 스트림에 순서를 매기는 역할을 합니다.
  • TCP는 스트림 지향 프로토콜이기 때문에 연결을 통해 전송되는 바이트 스트림에 연속적인 번호를 부여합니다.
  • 시퀀스 번호는 전송하는 데이터의 순서를 의미하여 총 32비트가 할당됩니다.
  • 이 시퀀스 번호 덕분에 수신자는 세그먼트의 순서가 엇갈려 도착하더라도 쪼개진 세그먼트의 순서를 파악하여 올바른 순서로 데이터를 재조립합니다.

Acknowledgment number

tcp-header5

tcp-header6

  • 연결 설정과 연결 해제 시 발생하는 핸드쉐이크(3-way handshake) 과정에서 상대방이 보낸 시퀀스 번호 + 1로 자신의 승인 번호를 만들어내지만, 실제로 데이터를 주고 받을 때는 상대방이 보낸 시퀀스 번호 + 자신이 받은 데이터의 bytes로 승인 번호를 생성합니다.

Data offset

tcp-header7

tcp-header8

  • TCP 헤더의 경우 기본 20바이트이지만 TCP 옵션 필드에 데이터가 추가될 경우 20에서 최대 60바이트의 길이를 가질 수 있으므로 컴퓨터는 어디서부터가 TCP 헤더고 어디서부터가 TCP 데이터 영역인지 알 수 없습니다.
  • TCP 헤더와 데이터 영역을 구분하기 위해 Data offset 필드를 사용합니다.

tcp-header9

Flag

tcp-header10

tcp-header11

  • ECN 메커니즘을 사용하기 위한 플래그 필드 3비트(NS, CWR, ECE)가 생기기 전에는 Reserved(미래를 위해 미리 예약된 필드)가 6비트였으며 Flag 필드도 6비트로 URG, ACK, PSH, RST, SYN, FIN이 존재했었습니다.

tcp-header12

  • ECN 메커니즘을 도입하면서 RFC 3168 규약과 함께 기존에 6비트 영역이 확보된 Reserved의 3비트를 Flag 비트로 활용하였으며 Flag 필드에 NS, CWR, ECE가 추가되었습니다.

tcp-header13

  • 라우터는 RED(Random Early Detection) 알고리즘을 통해 혼잡 제어를 수행합니다.
  • RED는 통신의 폭주가 발생하기 전 이를 감지하여 원활한 통신을 가능하게 하는 알고리즘으로 혼잡 검출 시 RED 알고리즘은 패킷을 폐기함으로써 버퍼의 오버플로우를 방지합니다. RED 알고리즘 진행 절차는 우선 라우터의 큐가 관리자가 설정한 임계치 값에 근접하면 임의의 특정 플로우를 선택해 해당 패킷을 폐기하여 송신측에서 송신 속도를 늦출 수 있도록 합니다.
  • 중간 지점인 라우터에서 패킷이 유실될 경우 수신 측은 일정 시간 대기하다 송신측에게 재전송을 요구합니다.
  • 이러한 과정을 통해 수신측 대기시간과 송신측의 재전송 시간차를 사용하여 혼잡이 발생한 라우터 큐의 한계치를 낮춥니다.
  • 그러나 패킷을 폐기하는 방법은 오로지 전송중인 컴퓨터의 속도를 늦추는 데에만 필요하기 때문에 효율적이지 않으며 ECN은 이런 점을 보완하여 패킷의 손실 없이 지연을 감지하고 흐름을 늦춥니다.

임의의 패킷을 폐기하는 RED 알고리즘과는 달리 ECN 알고리즘은 지연 감지시에 라우터가 임의의 패킷인 IP header의 CE(Congestion Experienced) flag를 셋팅하고 수신자에게 전달합니다.

tcp-header14

ECN의 작동원리는 다음과 같으며 앞에서 대략적으로 살펴보고 뒤에서 정확한 원리를 설명합니다.

  • 중간 라우터에서 혼잡이 발생할 경우 라우터에서 큐를 모니터링하여 패킷이 현재 정체되고 있음을 감지합니다.
  • 혼잡을 겪는 라우터는 해당 라우터를 통과하는 패킷에 혼잡하다는 증거로 표시를 남겨 다음 라우터 또는 수신자에게 패킷을 전달합니다.

tcp-header15

  • 최종적으로 패킷을 전달받은 수신자는 패킷에 남겨진 혼잡 표시를 확인하고 송신자에게 자신이 받은 패킷이 전달되는 과정에서 혼잡을 겪었음을 알려줍니다.

tcp-header16

  • ECN 메커니즘은 기본적으로 지원되는 기능이 아니므로 송신자와 수신자 모두 ECN을 사용해야만 합니다.
  • 3-way handshake가 이루어질 때, 송신자가 수신자에게 전송하는 SYN 패킷에 ECE 비트를 1로 설정하여 "현재 송신자 호스트는 ECN을 사용할 수 있습니다"와 같이 알립니다.
  • SYN 패킷을 전달받은 수신자 호스트는 이를 확인하고 ECE이 지원된다면 송신자 호스트에 SYN+ACK 패킷을 전송할 때 ECE 비트를 1로 설정하여 "수신자 호스트도 ECN을 사용할 수 있습니다"와 같이 알립니다.
  • 이렇게 송신자 및 수신자 호스트 모두 ECN을 지원한다면 상호 통신간에 ECN 메커니즘에 의해서 혼잡을 경험할 경우 혼잡을 알리거나 수신하게 됩니다.

tcp-header17

  • ECN 메커니즘에서 사용하는 필드는 TCP 헤더에만 존재하지 않고 IP 헤더에도 존재합니다.
  • TCP 헤더의 경우 NS, CWR, ECE 세 가지 필드를 사용하여 ECN 메커니즘 연결을 확립하고 IP 헤더의 경우 Type of Service 필드 안에 마지막 2개 비트를 사용하며 이는 곧 2의 2승인 4가지 표현이 가능하며 뒤에서 설명하겠습니다.
  • 이전에 ECN 메커니즘을 3-way handshake로 ESTABLISHED시에 사용하는 필드는 TCP 헤더의 ECE 필드입니다.

tcp-header18

위에서 대략적인 동작원리 개요를 설명드렸다면 여기서는 세부적인 동작을 살펴보겠습니다.

  • 먼저 혼잡을 겪은 중간 라우터에서 다음 라우터로 패킷을 전달할 때 IP 헤더 내 ECN-CE 비트를 설정합니다.
  • 여기서, ECE-CN 비트는 위에서 보여드린 IP 헤더 내 Type Of Service 필드 마지막 2비트를 나타내며 11로 표시되어 전달됩니다.
  • 다음으로 이 패킷을 전달받은 수신자 호스트는 IP 헤더 내 ECE-CE 비트에 11로 표시되었음을 확인합니다. (11표시의 경우, 혼잡 발생)
  • 수신자 호스트는 패킷을 전달받는 과정에서 혼잡이 발생했다는 것을 호스트에게 알리기 위해 송신자 호스트에게 ACK 패킷 전송시에 TCP 헤더 내 ECE 비트를 설정하여 혼잡을 알립니다.
  • 이를 전달받은 송신자는 패킷 전송 속도를 조절하기 위해 윈도우 사이즈를 감소시킵니다.
  • 이후 송신자가 수신자에게 전송하는 세그먼트에는 CWR 비트가 설정되어 전달됩니다. 여기서 CWR 비트는 혼잡을 수신했다는 의미로 사용됩니다.

tcp-header19

  • 전체 IP 헤더 내에서 ECN-CE 필드가 어디있는지 확인합니다.

tcp-header20

  • IP 헤더 내 ECN-CE 필드가 나타내는 4가지 표현은 다음과 같습니다.

tcp-header21

  • URG: 이 포인터가 가리키는 긴급한 데이터는 우선순위가 높게 처리되어 우선적으로 처리되지만 요즘에는 많이 사용되지 않는다.
  • PSH: 수신측에게 이 데이터를 최대한 빠르게 응용프로그램에게 전달해달라는 플래그, 이 플래그가 0이라면 수신 측은 자신의 버퍼가 다 채워질 때까지 기다린다. 이 플래그가 1이라면 이 세그먼트 이후에 더 이상 연결된 세그먼트가 없음을 의미한다.
  • RST: 이미 연결이 확립되어 ESTABLISHED 상태인 상대방에게 연결을 강제로 리셋해달라는 의미
  • ACK: Acknowledgement (승인 번호), 필드에 값이 채워져있음을 알리는 플래그, 이 플래그가 0이라면 승인 번호 필드 자체가 무시된다.
  • SYN: Synchronize, 상대방과 연결을 생성할 때, 시퀀스 번호의 동기화를 맞추기 위한 세그먼트임을 의미
  • FIN: Finish, 상대방과 연결을 종료하고 싶다는 요청인 세그먼트를 의미

Window Size

tcp-header22

  • 그림에는 표시가 누락되었지만 Window Size는 총 16비트로 2의 16승인 65,535 만큼의 값을 표현할 수 있으며 이는 곧 최대 사이즈가 64KB임을 의미합니다.

tcp-header23

  • 윈도우 사이즈 필드에는 한 번에 전송할 수 있는 데이터의 양을 의미
  • 64KB라는 최대 크기는 옛날에 생긴 기준이며 요즘같이 대용량 고속 통신 환경에는 맞지 않는 경우도 있어 비트를 왼쪽으로 시프트하는 방식으로 윈도우 사이즈의 최대 크기를 키울 수 있으며 몇 번 시프트할 지는 옵션 필드의 WSCALE 필드를 사용하여 표기한다.

tcp-header24

  • 이 64KB가 고속의 인터넷 상황에서 얼마나 작은 사이즈인지를 가늠하기 위해서 먼저 RTT의 이해가 필요하며 RTT는 네트워크 통신에서 패킷이 송신자로부터 수신자까지 전송되었다가 다시 송신자로 돌아오는 데 걸리는 시간을 의미합니다.
  • Window Scale 옵션은 SYN 패킷에만 존재합니다.

tcp-header25

  • 먼저, 대역폭을 계산하기 위해서는 Window Size를 RTT로 나누는데 Window Size의 최댓값이 64KB임을 감안해도 10ms에서 51.2Mbps 대역폭은 여전히 작은 대역폭입니다.
  • 따라서 RTT수가 기본 수십 ms인 인터넷 환경에서 64KB로는 충분한 인터넷 속도를 낼 수 없으며 성능을 높이기 위해서는 Window Size를 증가시켜야 합니다.

tcp-header26

  • 윈도우 스케일링을 사용한 패킷을 관측하였으며 위 그림에서 나오는 패킷은 동일한 바이트 스트림에서의 SYN 패킷(상단 그림)과 ACK 패킷(하단 그림)입니다.
  • 위에서 설명드린 것과 동일하게 SYN 패킷에서 TCP 옵션 필드에 Window Scale이 사용되었음을 알 수 있고 값은 6을 나타냅니다. 이 숫자 6은 실제로 윈도우 사이즈에 2의 6승인 64를 곱하여 윈도우 사이즈를 증가함을 의미합니다.
  • ACK 패킷에서 Calculated window size를 통해 기존 윈도우 사이즈보다 증가된 윈도우 사이즈 크기를 확인할 수 있으며 Window size scaling factor를 통해 몇 배가 증가되었는지 확인할 수 있습니다.

Checksum

tcp-header27

  • 체크섬 필드는 데이터의 무결성을 확인하며 데이터가 송신자로부터 수신자까지 전송되는 도중에 비트의 변조나 오류가 발생했는지 확인하기 위해 사용됩니다.
  • 즉, 해당 세그먼트 또는 패킷이 전송 중에 변조되었는지를 확인하기 위한 필드입니다.
  • TCP 헤더의 체크섬 필드는 계산 전에 0으로 설정됩니다.

tcp-header28

체크섬 필드를 계산할 때는 TCP 헤더 뿐만 아니라 TCP 데이터, IP 헤더 내 일부 필드(출발지 주소, 도착지 주소, 예약, 프로토콜, TCP 길이)를 참조합니다.

  • Source Address: IP 헤더의 출발지 IP 주소 (32bits)
  • Destination Address: IP 헤더의 목적지 IP 주소 (32bits)
  • Reserved: 미리 예약된 필드 (8bits)
  • Protocol: IP 헤더의 프로토콜 필드 (TCP는 6, UDP는 17) (8bits)
  • TCP/UDP Length: TCP나 UDP 세그먼트 길이(16bits)

  • 흔히 체크섬을 계산할 때 참조되는 위에 있는 IP 헤더를 Pesudo Header라고 부릅니다.
  • 체크섬 계산에는 Pesudo Header, TCP Hedaer, TCP Data를 16비트로 나누어 계산합니다.
  • 예를 들어 Pesudo 헤더만 총 96비트로 16비트(2바이트)로 나눌 경우 6줄로 나누어집니다.

tcp-header29

  • 이렇게 16비트로 분할하여 수를 나타낼 경우 위 그림처럼 나타나게 됩니다.

tcp-header30

  • 체크섬이 계산되는 과정을 확인하기 위해서 16비트의 모든 수를 더하기보다 위 그림처럼 8비트의 4부분으로 가정하여 체크섬을 도출하겠습니다.

tcp-header31

  • 먼저 송신자가 수신자에게 세그먼트를 전송하기 전에 Checksum 필드에 넣을 값을 계산하기 위해 Pesudo IP Header, TCP Header, TCP Data를 총합한 값이 위와 같이 4부분으로 나뉘어졌다고 가정합니다.
  • 시퀀스 번호가 1인 1001 1001부터 2와 1110 0010, 그리고 4까지 차례로 더하게 되며 1 올림수가 발생할 경우 이를 carry라고 하며 carry된 1을 더하는 과정을 거쳐 4까지 더하게 되면 0010 0101 합계가 계산됩니다.
  • 계산된 합계에서 bitwise Not 연산을 하여 1101 1010으로 변환한 값을 checksum 필드에 입력하여 해당 세그먼트를 수신자에게 전달합니다.
  • 수신자는 전달받은 세그먼트의 checksum을 계산하기 위해 송신자와 동일하게 Pesudo IP Header와 TCP Header, TCP Data를 더해 총합을 계산하고 송신자가 전달한 checksum과 이를 덧셈합니다.
  • 덧셈한 결과가 모두 1(0xFFFF)이 나올 경우 변조가 없음을 의미하며 수신자로부터 데이터 변조가 없는 정상적인 세그먼트로 간주되어 승인됩니다.

Urgent Pointer

tcp-header32

  • Urgent Pointer 플래그가 1이라면 수신 측은 이 포인터가 가르키고 있는 데이터를 우선적으로 처리합니다.