요약

  • TCP는 데이터 오프셋(Data Offset)을 사용해 페이로드 위치를 찾지만, 옵션 목록 끝(End of Option List, EOL)은 선언된 헤더 안에서 의미 있는 옵션 파싱을 더 일찍 멈출 수 있다.
  • 무동작(No-Operation, NOP)은 뒤따르는 옵션을 정렬할 수 있지만, 수신기는 어떤 옥텟 경계에서 시작하는 옵션이든 여전히 파싱할 수 있어야 한다.

가변 헤더를 잠깐 살펴보기

TCP 수신기가 옵션 영역을 바이트 단위로 이동한다고 상상해 보자. Kind 1을 만난다. 이 바이트는 No-Operation이므로 참조할 길이 필드가 없고 파서는 한 옥텟 전진한다. 다음에는 종류(kind) 뒤에 길이가 오는 옵션이 온다. 파서는 해당 옵션의 의미를 구현했든 아니든 그 길이를 사용해 옵션 전체를 건너뛸 수 있다. 그다음 Kind 0, End of Option List에 도달한다. 의미 있는 옵션은 끝났지만, 데이터 오프셋 필드는 애플리케이션 데이터가 시작되기 전에 헤더 옥텟이 여러 개 남아 있다고 말한다. 남은 옥텟은 0 패딩이다.

이 작은 장면에는 서로 다른 네 가지 제어가 담겨 있다. 데이터 오프셋은 TCP 헤더와 페이로드 사이의 물리적 경계를 정한다. 종류 옥텟은 다음 부분을 어떻게 파싱해야 하는지 식별한다. 일반 옵션의 길이는 다음 옵션까지의 거리를 제공한다. EOL은 옵션 목록이 사용 가능한 헤더 공간보다 짧을 때 논리적 목록을 종료한다. 이 작업 중 어느 것도 다른 작업에서 안전하게 추론할 수 없다.

이 배열은 1981년 RFC 793에 이미 존재했다. RFC 9293은 이를 현재 TCP 명세에 유지하고 수신기 의무 몇 가지를 더 분명하게 표현했다. 이 연속성이 중요한 이유는 옵션 확장성이 모든 미래 의미를 알아보는 데 있는 것이 아니라, 인식하지 못하는 것을 어떻게 지나칠지 아는 데 달려 있기 때문이다.

두 가지 1바이트 예외

TCP의 기본 문법은 두 가지 형태를 허용한다. 대부분의 옵션은 종류와 길이로 시작하고, 길이가 포함하는 데이터가 뒤따른다. 길이는 종류와 길이 바이트 자체를 포함해 센다. EOL과 NOP는 예외로, 각각 하나의 종류 바이트로 구성되며 길이 필드가 없다.

예외들은 서로 바꿔 쓸 수 없다. EOL(Kind 0)은 전체 목록이 끝났음을 뜻한다. 각 옵션 뒤의 구분자가 아니다. 마지막 옵션이 데이터 오프셋이 선언한 헤더 경계에 자연스럽게 도달하면 EOL이 반드시 나타날 필요는 없다. EOL이 일찍 나타나면 수신기가 해석해야 할 내용이 바뀔 뿐, 페이로드가 시작되는 위치는 바뀌지 않는다.

NOP(Kind 1)는 종료 의미를 갖지 않는다. 송신자는 흔히 뒤따르는 옵션의 시작을 선호하는 워드 경계로 옮기기 위해 옵션 사이에 NOP를 둘 수 있다. 파서는 1바이트를 소비하고 계속 진행한다. 따라서 독립적인 배치 지시는 가짜 길이 보유 옵션을 만들거나 이웃 옵션의 의미를 바꾸지 않고 위치를 변경할 수 있다.

정렬은 송신자 편의였지 전송 규격의 요구사항은 아니었다

‘정렬’이라는 말은 TCP가 하는 것보다 더 강한 약속을 암시할 수 있다. RFC 793은 송신자가 NOP를 사용한다는 보장이 없으며 수신기는 워드 경계에서 시작하지 않는 옵션에도 대비해야 한다고 밝혔다. RFC 9293은 이 규칙을 명시적 요구사항으로 유지한다.

이 비대칭성은 중요하다. 송신자는 자체 구현 편의를 위해 바이트를 배치할 수 있다. 그러나 그 선호를 상호운용성의 조건으로 만들 수는 없다. 모든 다중 바이트 옵션이 워드 경계에서 시작한다고 가정하는 수신기는 프로토콜이 허용하는 세그먼트를 거부하거나 잘못 읽게 된다. NOP는 송신자의 배치 선택을 넓히면서 수신기의 파서는 일반적으로 남겨 둔다.

명세는 NOP가 모든 기계에서 처리를 더 빠르게 만든다고 약속하지도 않는다. NOP의 프로토콜 역할은 더 좁다. 1옥텟을 소비하며 다음 옵션의 위치를 바꾸는 데 사용할 수 있다. 하드웨어 비용, 메모리 접근 지연, 구현 전략은 그 보장된 의미 밖에 있다.

의미의 끝과 바이트의 끝

EOL이 존재하는 이유는 의미 있는 옵션과 예약된 헤더 공간의 길이가 다를 수 있기 때문이다. TCP 헤더는 데이터 오프셋에 의해 32비트 워드 단위로 측정되지만, 옵션 자체의 길이는 사용 가능한 영역을 다 쓰지 않을 수 있다. EOL은 송신자가 해석이 여기서 멈춘다고 말할 수 있게 한다. RFC 9293은 남은 헤더 바이트가 0 패딩이어야 한다고 요구한다.

이 구분은 두 가지 위험한 지름길을 막는다. 첫째, 수신기는 EOL 직후에 페이로드가 시작된다고 처리해서는 안 된다. 페이로드는 여전히 데이터 오프셋 경계에서 시작한다. 둘째, 패딩을 추가 옵션의 연속으로 처리해서는 안 된다. EOL은 논리적 목록을 닫고, 고정 헤더 경계는 데이터 위치를 찾는 권위 있는 기준으로 남는다.

0 패딩은 사용하지 않는 나머지를 예측 가능하게 만든다. 옵션 의미를 담지 않고, 선언된 TCP 헤더 길이를 바꾸지도 않는다. 이는 소박한 형태의 프로토콜 규율이다. 사용하지 않는 공간은 경계가 정해져 있고 정해진 표현을 가지며, 모호한 확장 채널이 되지 않는다.

길이 필드는 낯선 옵션을 통과 가능하게 만든다

더 큰 확장성 메커니즘은 이 두 예외를 둘러싼 규칙에 나타난다. EOL과 NOP를 제외한 모든 옵션은 미래 옵션을 포함해 길이 필드를 가져야 한다. RFC 9293은 또한 길이 필드가 있을 때 구현이 지원하지 않는 옵션을 오류 없이 무시하도록 요구한다.

이는 오래된 수신기가 새 옵션을 이해해야 한다는 뜻이 아니다. 다음으로 중요한 바이트를 찾을 수 있을 만큼 문법을 이해해야 한다는 뜻이다. 따라서 길이는 구조적 증거다. 옵션의 범위를 설명할 뿐, 그 옵션이 신뢰할 만한지, 유용한지, 지원되는지는 말하지 않는다.

이 설계는 불법 길이가 왜 심각한지도 설명한다. 파서를 전진시켜야 하는 형식에서 길이가 0이면 단순한 루프가 같은 위치에 갇힐 수 있다. 고정된 종류·길이 접두사보다 짧은 길이도 마찬가지로 앞뒤가 맞지 않는다. RFC 9293은 구현이 불법 옵션 길이를 처리하고 연결을 재설정하며 원인을 기록하도록 제안한다. 확장성은 알 수 없는 의미가 안전하게 경계 지어질 때만 작동한다.

1981년부터 현재 표준까지 유지된 것

RFC 793은 가변 옵션, 두 가지 형식 사례, EOL, NOP, 0 패딩, 정렬되지 않은 옵션을 허용해야 하는 수신기의 필요를 설명했다. RFC 9293은 그 문법을 새 메커니즘으로 대체하지 않았다. 현재 TCP 요구사항을 통합했다. EOL, NOP, MSS가 필수 옵션 집합을 이루고, 옵션은 모든 세그먼트에 나타날 수 있으며, 지원되지 않는 길이 보유 옵션은 무시되고, 미래 옵션에는 길이 필드가 필요하며, 수신기는 불법 길이에 대처해야 한다.

역사적 교훈은 Kind 0이나 Kind 1이 개별적으로 정교했다는 것이 아니다. 작은 구문 규칙들이 서로 다른 경계를 보호했다는 점이다. 한 필드는 헤더의 물리적 끝을 표시한다. 한 코드는 목록의 논리적 끝을 표시할 수 있다. 한 코드는 의미를 더하지 않고 바이트를 차지할 수 있다. 길이를 가진 옵션은 그 의미를 모르더라도 건너뛸 수 있다.

출처