요약

  • TCP 동시 오픈(simultaneous open)은 두 엔드포인트가 같은 소켓 쌍을 향해 액티브 오픈을 수행할 수 있게 합니다. 두 베어 SYN이 교차하고, 양쪽 모두SYN-RECEIVED에 진입하며, 교차한 SYN-ACK이 하나의 연결—경쟁하는 두 연결이 아닌—을 수립합니다.
  • 이 메커니즘이 작동하는 이유는 각 엔드포인트가 두 가지 로컬 사실을 증명하기 때문입니다: 상대의 현재 초기 시퀀스 번호를 수신했고, 자신의 번호도 상대가 확인했다는 것입니다. 영구적인 발신자/수신자 위계는 필요하지 않습니다.
  • NAT는 이후 일반적인 클라이언트-서버 패킷 순서를 TCP 상태 머신 전체인 것처럼 학습했습니다. RFC 5382는 정책이 이미 연결을 허용하는 경우, 아웃바운드 SYN 이후 인바운드 SYN을 수용하도록 NAT에 요구해야 했습니다.

핸드셰이크 다이어그램이 감춘 사회적 가정

TCP 설명 대부분은 클라이언트에서 서버로 이어지는 선으로 시작합니다: SYN, 그다음 SYN-ACK, 그다음 ACK. 이 그림은 유용하지만, 프로토콜이 실제로 만드는 결론보다 강한 결론을 부추깁니다. 한쪽은 발신하도록 태어난 것처럼, 다른 한쪽은 기다리도록 태어난 것처럼 보입니다.

RFC 793은 패시브 오픈과 액티브 오픈을 기술했지만, 동시에 두 프로세스가 서로에게 동시에 액티브 오픈을 수행하면 올바르게 연결된다고도 밝혔습니다. 이 유연성은 비동기적으로 동작하는 분산 구성 요소에게 중요했습니다. TCP가 진행하기 위해 어느 프로세스도 경쟁에서 이겨 공식 클라이언트가 될 필요가 없었습니다.

그 패킷 순서는 나중에 추가된 충돌 복구 트릭이 아니었습니다. 동시 개시는 원래 연결 동기화 설계의 일부였습니다. 평범한 다이어그램은 상태 머신의 한 경로를 보여줄 뿐, 역할에 대한 헌법적 배분이 아니었습니다.

두 개의 SYN이 교차했지만 두 연결이 만들어지지 않았다

피어 A가 초기 시퀀스 번호 100을, 피어 B가 300을 선택했다고 가정합니다. A는<SEQ=100><SYN>을 보내고SYN-SENT에 진입합니다. B도<SEQ=300><SYN>으로 똑같이 합니다. 두 세그먼트는 네트워크에서 교차합니다.

A가 B의 베어 SYN을 받을 때, 이미 자신이 하나를 보냈다는 이유로 패킷을 거부하지 않습니다. A는 B의 초기 수신 시퀀스를 기록하고,SYN-RECEIVED에 진입하며, 301을 확인하는 SYN-ACK을 보냅니다. B도 독립적으로 거울상의 연산을 수행하고 101을 확인합니다. SYN-ACK들이 교차하면 각 측은 상대가 자신의 시작 번호를 수신했음을 확인하고ESTABLISHED에 진입할 수 있습니다.

현재 통합 사양인RFC 9293은 그림 7에서 이 교정된 흔적을 유지합니다. 두 피어 모두CLOSED→SYN-SENT→SYN-RECEIVED→ESTABLISHED를 거칩니다. 시각적 패킷 순서는 교과서의 액티브/패시브 핸드셰이크와 다르지만, 논리적 증명은 같습니다: 각 측이 시퀀스 원점을 알리고, 상대의 것을 받고, 자신의 것이 확인받습니다.

SYN 뒤의 숫자가 증명을 담았다

SYN은 TCP 시퀀스 공간에서 한 위치를 차지합니다. A가 100에서 SYN을 보내면, B는 101을 확인합니다. 그 확인은 단순히 “패킷이 도착했다”고 말하지 않습니다. 수신자가 지금 어떤 시퀀스가 지정된 제어를 넘어섰는지 말합니다. 빈 ACK 자체는 시퀀스 공간을 차지하지 않으므로, TCP는 확인에 대한 확인을 무한히 확인해야 하는 함정에 빠지지 않습니다.

이 산술이 동시 오픈에 규율을 부여합니다. 피어들은 시퀀스 번호 공간을 합치지 않습니다. A는 자신의 초기 값부터 아웃바운드 스트림에 번호를 매기고, B는 다른 값부터 동일하게 합니다.RFC 6528은 이후 연결 4-튜플에 묶인 비밀 의사난수 함수 구성 요소로 초기 시퀀스 선택을 강화했습니다. 대칭적 개시가 대칭적이거나 예측 가능한 번호를 의미한 적은 없습니다.

따라서 상태는 각 엔드포인트가 로컬에서 검증할 수 있는 근거에서 나옵니다. 중앙 핸드셰이크 심판도 없고, SYN을 참으로 만드는 “서버” 같은 칭호도 없습니다. 소켓 식별자, 시퀀스 산술, 확인된 이력이면 충분합니다.

하나의 소켓 쌍은 하나의 연결을 의미했다

가장 유혹적인 구현 실수는 두 애플리케이션이connect를 호출했다는 이유만으로 A의 시도와 B의 시도를 별개의 대상으로 다루는 것이었습니다.RFC 1122는 이 놀라움을 직접 다루었습니다: 동시 시도는 두 개가 아니라 하나의 연결을 생성하며, 이는 의도된 사항입니다.

A의 관점에서 연결은 로컬 A 소켓 더하기 원격 B 소켓입니다. B의 관점에서 같은 쌍이 뒤집혀 있습니다. 네 값은 여전히 하나의 결합을 기술합니다. 두 연결을 만드는 것은 더 많은 정보를 보존하지 않습니다. 하나의 식별자를 복제하고, 애플리케이션이 어느 복제본이 데이터를 실어야 하는지 추측하게 만들 뿐입니다.

이는 프로토콜이 로컬 절차를 글로벌 현실로 끌어올리기를 거부한 유용한 예입니다. 두 개의 API 호출이 있었지만, 네트워크 대상은 공유된 엔드포인트 쌍과 동기화된 시퀀스 상태로 정의됩니다. 로컬 구현은 여러 보류 레코드를 가질 수 있지만, 수립된 결합을 제시하기 전에 그것들을 조정해야 합니다.

같은 상태가 서로 다른 두 이력을 기억할 수 있었다

TCP는 두 피어가 모두SYN-RECEIVED에 도달한 뒤 이력을 버릴 수 없습니다. RFC 1122와 RFC 9293은 구현이 그 상태가 패시브 오픈 이후인지 액티브 오픈 이후인지 기억하도록 요구합니다. 이 구분은 리셋이 도착하거나 오래된 중복 SYN이 새 시도를 흉내 낼 때 중요합니다.

패시브하게 수신 대기하던 엔드포인트는 동기화 중 유효한 리셋 후 수신 대기로 돌아갈 수 있습니다. 액티브하게 오픈한 엔드포인트는 다른 애플리케이션 기대를 가집니다. 따라서 상태 레이블만으로는 전체 상태가 아닙니다; 그 상태로 들어간 경로가 복구를 통제합니다.

RFC 793은 이미 오래된 중복 SYN이 동시 개시처럼 보일 수 있다고 경고했습니다. 이 설계는 교차 SYN을 금지함으로써 이를 해결하지 않습니다. 확인 응답, 현재 시퀀스 기대, 리셋 검증을 사용해 살아있는 랑데부와 같은 소켓 쌍의 이전 화신에 속한 잔해를 구분합니다.

교훈은 미묘합니다. 근거(provenance)를 지우며 상태 머신을 단순화하면 구현 다이어그램은 짧아지지만, 서로 다른 결과를 가진 두 이벤트를 프로토콜이 구분할 수 없게 만들 수 있습니다.

중간자는 유명한 이야기만 배웠다

오랫동안 교과서 다이어그램의 비대칭은 무해했습니다. 상태 머신을 소유한 쪽은 엔드포인트였기 때문입니다. 네트워크 주소 변환이 이를 바꾸었습니다. NAT는 아웃바운드 SYN 이후 매핑을 만들고, 흔히 다음 인바운드 패킷이 SYN-ACK일 것으로 기대합니다. 동시 오픈 중에 다음 인바운드 패킷은 또 다른 베어 SYN입니다.

RFC 5382는 그 SYN을 요청되지 않은 것으로 차단하는 NAT와 아웃바운드 SYN-ACK을 오처리하는 NAT를 문서화했습니다. 양쪽 엔드포인트는 TCP를 따르고 있었고, NAT는 이미 그 매핑에 대한 트래픽을 허용하고 있을 수 있지만, 중간자가 모든 유효 전이가 아니라 하나의 서사만 구현했기 때문에 연결은 실패했습니다.

정책과 파싱의 구분이 본질적입니다. 방화벽은 자신의 필터링 정책에 따라 연결을 거부할 수 있습니다. 그러나 진행 중인 연결을 정책이 허용한 이상, 표준이 정의한 전이를 기형으로 다루는 것은 더 엄격한 보안이 아니라, 상태 추적 안에 숨은 프로토콜 축소입니다.

따라서 RFC 5382는 NAT가 허용된 연결에 대해 동시 오픈과 다른 유효한 TCP 시퀀스를 처리하도록 요구했습니다. 그 요구는 NAT에게 정책 포기를 요구하지 않았습니다. NAT가 가장 흔한 관찰을 TCP의 전체 문법과 혼동하지 말 것을 요구한 것입니다.

여섯 초는 불확실성의 비용을 드러냈다

가장 어려운 경우는 원격 SYN이 로컬 아웃바운드 SYN이 매핑을 만들기 전에 NAT에 도착할 때입니다. 그 패킷은 요청되지 않은 것처럼 보이지만, 지연이나 느슨한 시계 조정이 곧 그것이 동시 오픈의 한쪽임을 드러낼 수 있습니다. 즉시 리셋이나 강한 ICMP 오류를 돌려보내면, 그렇지 않았다면 성공했을 연결을 원격 엔드포인트가 포기하게 만들 수 있습니다.

RFC 5382는 타협을 선택했습니다. NAT는 그 인바운드 SYN에 답하기 전에 최소 여섯 초를 기다려야 합니다. 그 간격 동안 대응하는 아웃바운드 SYN이 나타나면, NAT는 이른 패킷을 조용히 버리고 재전송이 매핑 존재 후에 도착하도록 허용합니다. 대응하는 시도가 나타나지 않으면, 보안 정책에 따라 오류를 보고할 수 있습니다.

이것은 마법 같은 핸드셰이크 타이머가 아닙니다. 불완전한 지식에 대한 명시적 비용입니다. 즉시 거부는 더 빠른 오류를 주지만 일부 유효한 랑데부 시도를 파괴합니다; 기다림은 선택권을 보존하지만 확실성을 지연하고 상태를 점유합니다. 중간자는 미래를 알 수 없으므로, 사양은 모호성이 존재하지 않는 것처럼 가장하는 대신 그 트레이드오프를 보이게 만듭니다.

P2P 소프트웨어는 오래된 대칭을 계속 유용하게 만들었다

동시 오픈은 두 엔드포인트가 변환기 뒤에 있고 어느 쪽도 요청되지 않은 인바운드 연결을 단순히 수용할 수 없을 때 새로운 중요성을 얻었습니다. 각 측은 상대를 향한 아웃바운드 상태를 만들 수 있었습니다; 타이밍과 매핑이 맞으면 SYN이 준비된 경로를 통해 교차할 수 있었습니다.

RFC 6544는 이를 액티브 및 패시브 후보와 함께 명시적인 ICE TCP 후보 유형으로 만들었습니다. S-O 후보는 각 에이전트가 상대를 향해 오픈을 시도할 것임을 말합니다. 이 문서는 그 경로를 보편적으로 신뢰할 수 있는 것으로 다루지 않습니다. 운영체제, NAT, 방화벽, 접속 네트워크가 다르기 때문에 여러 후보 유형을 수집할 것을 권고합니다.

그 자격 부여는 흔한 역사적 과장을 막습니다. 동시 오픈의 존재가 P2P TCP를 보편적으로 통과 가능하게 만들지는 않았습니다. 그것은 유효한 엔드포인트 메커니즘을 제공했을 뿐입니다. 성공은 여전히 주소 발견, 포트 매핑, 타이밍, 필터링 동작, API 지원에 달려 있었습니다. 직접 경로가 실패하면 릴레이는 여전히 필요했습니다.

표준의 유효성과 배포상 도달 가능성은 서로 다른 주장입니다. TCP에서 필수인 메커니즘도, 주변 시스템이 그것을 축소하거나 가리면 운영상 사용 불가능할 수 있습니다.

지속 가능한 설계는 근거를 갖춘 대칭이었다

동시 오픈은 작은 기능이지만, 중요한 인터넷 설계 선택을 담습니다. 공유 계층은 상호운용성에 필요한 최소 사실을 규정합니다: 하나의 소켓 쌍, 두 개의 새로운 시퀀스 원점, 수신을 증명하는 확인 응답, 양쪽 엔드포인트가 검증할 수 있는 상태 전이. 어느 피어가 발신자인지 선언할 영구 권위를 요구하지 않습니다.

미래의 결정은 로컬에 남습니다. 애플리케이션은 액티브, 패시브 또는 동시 후보를 선택할 수 있습니다. 방화벽은 경로 허용 여부를 선택할 수 있습니다. 구현은 더 풍부하거나 가난한 API를 노출할 수 있습니다. 그러나 로컬 선택이 “다른 유효한 피어 전이는 존재할 수 없다”는 거짓 진술로서 공통 계층에 몰래 반입되어서는 안 됩니다.

이후의 NAT 문제는 엔드포인트가 너무 많은 자율성을 가졌기 때문이 아니었습니다. 중간자가 유행하는 관습—아웃바운드 발신자, 인바운드 응답자—을 조용히 프로토콜 법으로 다시 규정했기 때문입니다. RFC 5382는 허가와 충실한 상태 추적을 분리하여 그 경계를 수리했습니다.

양쪽 끝이 동시에 호출했을 때, TCP는 승자를 뽑지 않았습니다. 각 측에게 무엇을 보내고 받았는지 증명하라고 요구했습니다. 하나의 연결이 성립한 것은, 다이어그램이 시사하는 사회적 역할보다 검증 가능한 공유 상태가 더 중요했기 때문입니다.

출처와 증거의 한계

RFC 793은 원래의 동시 개시를 확립합니다.RFC 1122는 지원을 필수로 만들고 하나의 연결이라는 결과를 명확히 합니다.RFC 5382는 NAT 실패와 동작 요구사항을 문서화합니다.RFC 6528은 보안 ISN 맥락을 제공합니다.RFC 6544는 이후 ICE TCP 사용을 보여줍니다.RFC 9293은 현재 통합 TCP 사양입니다.

이 문서들은 현재 시장 점유율, 모든 OS API 동작 또는 모든 NAT의 컴플라이언스를 확립하지 않습니다. 패킷 손실과 재전송도 논리적 상태 전이를 바꾸지 않으면서 관찰된 흔적을 바꿀 수 있습니다.