홀 펀칭에 대해 잘 성명되어 있는 내용을 번역한 내용이 있어서 스크랩 했습니다.
Peer-to-Peer Communication Across Network Address Translators
홀 펀칭(hole punching)기술에 대하여 이 저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 수 있습니다. 이용허락조건을 보려면, http://creativecommons.org/licenses/by-nc-nd/2.0/kr/ 에서 확인하세요.
번역자: 리안 (양영욱)
http://young-writing.blogspot.com/
원문: http://www.brynosaurus.com/pub/net/p2pnat/
Abstract
네트워크 주소 변환(NAT)은 P2P통신에 있어서 잘 알려진 어려움들을 야기한다. 이는 관련 Peer들이 임의의 글로벌 IP주소로부터 연결되지 않을 수도 있기 때문이다. 몇몇 NAT 선회 기법들이 알려져 있으나 문서화가 미미하고 또한 그들의 확실성 혹은 상대적인 장점 등에 관한 데이터는 더욱 불충분하다. 이 문서는 이들 기법 중 가장 간단하나 가장 확실하고 실질적인 NAT 선회 기법인 "홀 펀칭" 을 분석한 것 이다. 홀 펀칭은 UDP 통신에 대해서는 어느 정도 이해되어 있지만, 우리는 이것이 어떻게 TCP 통신에서도 신뢰성 있게 이용되는지를 보일 것 이다. 다양하게 출시 되어있는 NAT들을 상대로 이 기법의 신뢰도 데이터를 모은 결과, 테스트된 NAT들 중 82%가 UDP에 대한 홀 펀칭을 지원하고 약 64%가 TCP에 대한 홀 펀칭을 지원했다. NAT 벤더들이 점점 Voice over IP와 온라인 게임 프로토콜과 같은 중요한 P2P 어플리케이션의 필요를 알아감에 따라 미래에는 홀 펀칭에 대한 지원이 증가하게 될 것이다.
1. Introduction
인터넷의 거대한 성장과 방대한 보안 과제들이 결합되어 생긴 압력이 많은 어플리케이션들의 삶을 힘들게 만드는 방향으로 인터넷이 진화하게끔 압박해왔다. 인터넷 본래의 유니폼 주소 체계, 다시 말해 모든 노드들이 고유의 글로벌 IP주소를 가지고 모든 다른 노드들과 직접적으로 통신을 할 수 있는 방식은 사실상 지금 현존하는 주소 체계로 바뀌게 되었다. 이 방식은 글로벌 주소들로 이루어진 세계와 private 주소들로 이루어진 세계들이 네트워크 주소 변환(NAT)를 통하여 연결되는 주소 체계이다. 이 새로운 주소 체계에서는 그림 1에서 보여지는 바와 같이 오직 "main"에 있는 노드들, 즉 글로벌 주소 세계에 있는 노드들 만이 네트워크 상의 다른 노드들과 쉽게 연결된다. 이것은 오직 이들만이 글로벌하게 전달 가능한(routable) IP 주소들을 가지기 때문이다. Pirvate에 있는 노드들은 같은 네트워크 안에 있는 노드들과 연결을 할 수 있으며, 글로벌 주소 세계에 사는 "잘 알려진" 노드와 TCP 혹은 UDP 연결을 할 수 있다. 이 과정에서 NAT들은 밖으로 향하는 연결에 대하여 임시적으로 public 종점을 할당하고 이 세션을 구성하는 패킷들에 있는 주소와 포트를 변환한다. 이와 달리, 일반적으로 안으로 향하는 모든 트래픽에 대해선 특별한 설정이 되어있지 않는 한 차단한다.
Figure 1: Public and private IP address domains
이 현존하는 새 주소 구조는 클라이언트가 Private 네트워크에 있고 서버가 글로벌 주소 세계에 존재하는 전형적인 상황에 대한 클라이언트/서버 통신에 적합하다. 반면에, 텔레컨퍼런싱이나 온라인 게임에 사용되는 P2P 통신 프로토콜에 중요한, 즉 서로 다른 Private 네트워크에 존재하는 두 노드가 직접적으로 통신하게 하는 것을 어렵게 한다. 당연히 NAT들이 존재하는 이 상황에서도 이런 프로토콜들을 잘 작동하게 하는 방법이 필요하다.
서로 다른 Private 네트워크에 존재하는 호스트들 간의 P2P 연결을 구성하는 가장 효과적인 방법 중 하나가 "홀 펀칭"이라는 이름으로 알려져 있다. 이 방법은 이미 UDP기반의 어플리케이션들 사이에서 널리 이용되고 있으며 또한 본질적으로 TCP에도 적용된다. 이름이 주는 인상과 달리 홀 펀칭은 Private 네트워크의 보안을 해치지 않는다. 대신, 홀 펀칭은 대부분의 NAT들의 기본 보안 정책 안에서 어플리이케이션이 작동하도록 한다. 경로 상의 NAT들에게 효과적으로 신호를 보내 P2P 통신의 세션들을 "요청"하게 함으로써 이 세션들이 수락되게 한다. 이 문서는 UDP와 TCP에 대한 홀 펀칭을 위한 것이며 홀 펀칭을 가능하게 하는데 중요한 어플리이케이션의 요소와 NAT 방식에 대해 세세히 살펴볼 것이다.
불행하게도, 모든 NAT에서 작동하는 선회 방법은 없다. 왜냐하면 NAT 작동 방식이 표준화되어 있지 않기 때문이다. 이 문서는 현존하는 NAT들의 홀 펀칭 지원에 대한 실험의 결과를 제공한다. 우리의 데이터는 유저들이 인터넷을 통해 서로 다른 벤더들의 다양한 NAT들을 넘어 우리의 "NAT Check" 툴을 실행하여 제공된 결과에서 나온 것이다. 이 정보들은 자발적으로 선택된 사용자들로부터 모아진 관계로 실제로 인터넷에 출시된 NAT들의 분포상황을 나타내지 않는다. 하지만 결과는 전반적으로 고무적이다.
기본적인 홀 펀칭을 알아보는 한편, 우리는 또한 보다 복잡함의 대가를 치르고서 더욱 다양한 NAT들에 대해 홀 펀칭이 작동하도록 하는 변수들도 짚어 볼 것이다. 하지만 우리의 주 관심사는 합당한 네트워크 망 상에서 "잘 작동하는" NAT들에 대해 명확하고 튼튼히 작동하는 가장 간단한 홀 펀칭 기법을 개발하는 것이다. 우리는 의도적으로 과도하게 영리한 기법들을 다루지 않는다. 이들이 비록 "깨진" NAT들에 대해 일시적인 호환성을 제공할지 모르나 이는 항상 동작하는 것도 아니거니와 장기적으로 볼 때 추가적인 예측 불가능성과 네트워크 취약성을 일으킬 수 있기 때문이다.
비록 IPv6 [3] 의 보다 큰 주소 공간이 결국에는 NAT의 필요성을 줄일지도 모르나, 단기적으로 오히려 이는 NAT의 필요성을 더욱 증가 시킬 것이다. 왜냐하면 NAT 자체가 바로 IPv4와 IPv6주소 도메인 사이의 상호 운영성을 이룰 수 있는 가장 쉬운 방법을 제공하기 때문이다 [24]. 거기다 더해, Private 네트워크 상에 있는 호스트들의 익명성과 비접근성은 보안과 비공개의 장점들로 여겨진다. 충분한 IP 주소들이 있다 하여도 방화벽이 사라지진 않을 것이다. IPv6 방화벽들도 내부로 향하는 요청되지 않은 트래픽을 기본적으로 막을 것이고 이는 홀 펀칭을 IPv6 어플리케이션들 에게도 유용하게 만들 것이다.
이 문서의 나머지는 다음과 같이 정리되어 있다. 섹션 2는 기본적이 용어와 NAT 선회에 대한 개념을 소개한다. 섹션 3은 UDP에 대한 홀 펀칭에 대해 자세히 알아보고 섹션 4는 TCP에 대한 홀 펀칭을 소개한다. 섹션 5는 NAT가 홀 펀칭을 위해서 반드시 가져야 하는 중요한 특성들을 요약한다. 섹션 6은 대중적인 NAT들의 홀 펀칭 지원 여부에 대한 실험 결과를 보여주고 섹션 7은 관련된 연구들에 대해 논의하며 섹션 8에서 결론을 짓는다.
2. General Concepts
이 섹션에서는 이 문서에서 사용되는 기본적인 NAT 용어들을 소개한다. 그리고 UDP와 TCP에 같이 적용되는 일반적인 NAT 선회기법들을 개괄한다.
2.1 NAT Terminology
이 문서는 RFC 2663 [21]에 정의된 NAT 용어들과 분류법, 그리고 RFC 3489 [19]에 최근 추가로 정의된 용어들을 사용한다.
특별히 중요한 개념은 세션이다. TCP 혹은 UDP를 위한 세션 종점은 IP 주소와 포트 번호로 이루어진 하나의 쌍이며 하나의 고유한 세션은 두 개의 세션 종점들로 이루어진다. 연관된 호스트들 중 한 호스트의 관점에서 보면, 세션은 4개의 요소(로컬 IP, 로컬 포트, 리모트 IP, 리모트 포트)들로 효과적으로 식별된다. 세션의 방향은 보통 세션을 처음 시작한 패킷의 흐름 방향이다. 즉, TCP의 경우 초기 SYN 패킷이고 UDP의 경우 첫 번째 유저 데이터 그램이다.
다양한 NAT의 방식 중 가장 일반적인 형은 traditional 혹은 outbound NAT 이다. 이 방식은 private 네트워크와 public 네트워크 사이의 비대칭(asymmetric) 다리를 제공한다. 기본적으로 outbound NAT는 오직 외부로 향하는 (outbound) 세션들만이 NAT를 선회할 수 있게 허용한다. 내부로 향하는 패킷들의 경우, NAT가 이 패킷들이 속한 세션이 자신의 private 네트워크에서 시작된 세션이라고 식별하지 않는 한 차단된다. Outbound NAT는 P2P 프로토콜들과 충돌을 일으키는데 이는 두 peer들이 서로 다른 두 NAT "뒤에" 존재하는 상황에서 서로 통신을 하려 할 때, 누가 세션을 시작하려고 하든 나머지 peer의 NAT가 이를 거절하기 때문이다. NAT 선회는 양쪽 NAT들에게 P2P 세션들이 "밖으로 향하는" 세션들로 보이도록 만든다.
Outbound NAT는 두 개의 변형을 가진다. Basic NAT는 오직 IP 주소만을 변환하고 Network Address/Port Translation(NAPT) 는 전체 세션 종점을 변환한다. 더 일반적인 형인 NAPT는 가장 보편화가 되었는데 이는 private 네트워크에 있는 호스트들이 하나의 public IP주소를 공유할 수 있게 해주기 때문이다. 이 문서에서 우리는 NAT가 NAPT라고 가정할 것이나, 우리가 논의할 기본 원리와 기법들은 Basic NAT에도 똑같이 잘 (혹은 때때로 차이를 무시해도 상관없이) 적용된다.
2.2 Relaying
NAT 를 넘나드는 가장 확실한 (그러나 가장 비효과적인) P2P 통신 방법은 중계(relaying)를 통해서 간단하게 표준 클라이언트/서버 모델처럼 만드는 것 이다. 두 호스트 A와 B가 잘 알려진 서버 S에 UDP 혹은 TCP 연결을 시작했다고 하자. 이 때 S의 IP 주소는 18.181.0.31, 포트 번호는 1234라 하자. 그림2에서 보이듯, 두 클라이언트는 서로 다른 private 네트워크 상에 존재하며 각각의 상응하는 NAT가 상대 클라이언트와 직접적으로 연결을 시작하는 것을 막고 있다. 직접 연결하는 대신에 두 클라이언트는 둘 사이의 메시지를 중계해주는 역할로 서버 S를 이용할 수 있다. 예를 들어, 클라이언트 B에게 메시지를 보내려면 클라이언트 A는 그냥 그 메시지를 서버 S로 이미 구성된 클라이언트/서버 연결을 통하여 보낸다. 그러면 서버 S는 이미 존재하는 B와의 클라이언트/서버 연결을 이용해 이를 클라이언트 B로 전달한다.
Figure 2: NAT Traversal by Relaying
두 클라이언트가 서버에 연결할 수 있는 한 중계는 항상 작동한다. 이 기법의 단점은 서버의 프로세싱 파워와 네트워크 대역폭을 소모한다는 것이다. 또한 아무리 서버가 잘 연결되어 있다 하더라도 두 클라이언트 사이의 통신 지연이 늘어날 가능성이 있다. 그럼에도 불구하고, 모든 NAT들을 상대로 확실히 동작하는 효과적인 기법이 없기 때문에, 최대의 확실성이 요구된다면 중계는 유용한 대책이다. TURN 프로토콜 [18]은 중계를 상대적으로 안전하게 구현하는 법을 정의한다.
2.3 Connection Reversal
어떤 P2P 어플리케이션들은 보다 명확하지만 제한적인 기법을 사용한다. 이 기법은 "거꾸로 연결"(connection reversal) 이라고 알려져 있는데, 그림 3에 나오듯, 두 호스트가 모두 잘 알려진 랑데뷰 서버 S에 연결되어 있고 오직 하나의 피어(peer)만이 NAT 뒤에 있을 경우 통신을 가능케 한다. 만약에 A가 B를 향해 연결을 시작하려 하면 이 직접 연결 시도는 자동적으로 작동한다. 왜냐하면 B는 NAT 뒤에 있지 않고 A의 NAT는 이 연결을 밖으로 향하는 세션으로 해석하기 때문이다. 하지만, 만약 B가 A를 향해서 연결을 시작하려 하면, A를 향한 어떤 직접 연결 시도든 A의 NAT에 의해 차단 당하게 된다. 그 대신에 B는 잘 알려진 서버 S를 통해서 연결 요청을 중계할 수 있다. 다시 말해 A에게 B를 향해 거꾸로 연결을 시도하라고 요청하는 것 이다. 이 기법의 명확한 한계와 상관없이, 랑데뷰 서버를 이용해서 P2P 연결의 셋업을 돕는 핵심 아이디어는 다음에 기술되는 더 일반적인 홀 펀칭 기법의 기본이 된다.
Figure 3: NAT Traversal by Connection Reversal
3. UDP Hole Punching
UDP 홀 펀칭은 두 클라이언트가 모두 NAT 뒤에 있다고 하더라도 잘 알려진 랑데뷰 서버를 통해서 직접적인 P2P UDP 세션을 구성하게 하는 기법이다. 이 기법은 RFC 3027의 5.1 섹션 [10]에 언급되었으며, 이 웹 [13]에서 더 완전히 문서화가 되었고 최근의 실험적인 인터넷 프로토콜들 [17,11]에서 사용되었다. 또한 온라인 게임을 위한 다양한 고유의 프로토콜들도 역시 UDP 홀 펀칭을 사용한다.
3.1 The Rendezvous Server
홀 펀칭은 이미 두 클라이언트 A와 B가 랑데뷰 서버 S와 활성화된 UDP 세션을 가지고 있다고 가정한다. 클라이언트가 S에 등록할 때, 서버는 해당 클라이언트를 위한 두 종점을 기록한다. 하나는 S와 이야기하기 위해 클라이언트가 믿는 자신의 종점(IP 주소, UDP 포트)이고 다른 하나는 서버가 보는 클라이언트의 종점(IP 주소, UDP 포트)이다. 우리는 첫 번째 종점을 클라이언트의 private 종점이라 하고 두 번째를 클라이언트의 public 종점이라 한다. 서버는 클라이언트의 private 종점을 클라이언트에서 보내지는 등록 메시지 본체의 한 필드에서 얻을 수 있을 것 이고, public 종점의 경우 등록 메시지의 IP와 UDP 헤더에 있는 소스 IP 주소와 소스 UDP 포트 필드들로부터 얻을 수 있을 것이다. 만약에 클라이언트가 NAT 뒤에 있지 않다면, private과 public 종점은 반드시 같을 것이다.
불완전한 몇몇 NAT들은 IP 주소처럼 보이는 UDP 데이터그램의 4바이트 필드를 스캔하여 그것이 IP헤더에 있는 IP 주소 인 것처럼 해석하는 것으로 알려져 있다. 이런 동작에 대해 안전하게 대처하기 위해, 어플리이케이션들은 메시지 본체에 들어있는 IP주소를 흐리게 만들기를 원할지도 모른다. 예를 들어, IP 주소 자체 대신에 IP주소의 1의 보수로 변환하는 식으로 말이다. 물론, 어플리이케이션이 자신의 메시지들을 암호화한다면 이런 동작이 문제가 될 소지는 없을 것이다.
3.2 Establishing Peer-to-Peer Sessions
클라이언트 A가 클라이언트 B와 직접적으로 UDP 세션을 구성하고 싶다고 하자. 홀 펀칭은 다음과 같이 진행된다.
1. 처음에 A는 어떻게 B에 닿아야 할지 모른다. 따라서 A는 S에게 B와 UDP 세션을 구성하기 위해 도움을 요청한다.
2. S는 A에게 B의 public과 private 종점을 담은 메시지로 답한다. 그와 동시에, S는 B와 연결된 UDP 세션을 이용해서 B에게 A의 public과 private 종점을 담은 연결 요청 메시지를 보낸다. 이 메시지들이 수신되고 나면, A와 B는 서로의 public과 private 종점들을 알게 된다.
3. A가 B의 public과 private 종점들을 S로 부터 받으면, A는 UDP 패킷을 두 종점 모두에게 보내기 시작한다. 다음으로 어떤 종점이든 첫 번째로 B로 부터 유효한 답변을 이끌어 낸 종점과 고정되어 붙는다. 이와 비슷하게, S로부터 전될된 연결 요청 메시지에 있는 A의 public과 private 종점들을 B가 받게 되면, B는 A의 각각의 종점들에게 UDP 패킷을 보내기 시작하며 첫 번째로 동작하는 종점과 고정되어 붙게 된다. 이 메시지들이 비동기적이라는 조건하에 순서와 타이밍은 중요하지 않다.
이제 우리는 UDP 홀 펀칭이 3가지 각각 특정의 네트워크 상황을 어떻게 다루는지 생각할 것이다. "쉬운" 경우라고 할 수 있는 첫 번째 상황은 바로 두 클라이언트가 같은 NAT 뒤에 존재하는 경우이다. 두 번째 경우는 가장 일반적인 것으로 클라이언트들이 서로 다른 NAT 뒤에 사는 것이다. 세 번째 경우는 각각의 클라이언트가 두 레벨의 NAT 뒤에 있는 상황이다. 인터넷 서비스 제공 업체에서 배치된 일반적인 "첫 번째 레벨" NAT와 홈 네트워크를 위한 별개의 "두 번째 레벨" NAT 라우터들이 예가 된다.
어플리케이션 자신이 정확하게 물리적인 네트워크 레이아웃을 판별하기란 대개 어렵거나 불가능하다. 따라서 주어진 시간에 위의 상황들 (혹은 다른 많은 가능한 상황들) 식별하는 것도 마찬가지다. STUN [19] 과 같은 프로토콜들이 통신 경로 상의 NAT들에 대한 일정 정보를 제공할 수 있지만 이런 정보들이 항상 완전하거나 믿을만한 것은 아니다. 특히 다중 레벨의 NAT들이 연관되었을 때 그렇다. 이런 상황에도 불구하고, 홀 펀칭은 이 모든 경우에 대해 어플리케이션이 특정 네트워크 구성을 알 필요가 없이 자동적으로 동작한다. 단, 연관된 NAT들이 합리적인 방식으로 동작할 경우에 한해서다. (NAT의 "합리적인" 동작들에 대해서 섹션 5에서 설명될 것이다.)
3.3 Peers Behind a Common NAT
첫 번째로 생각해 볼 상황은 두 클라이언트가 (아마도 모르게) 같은 NAT 뒤에 사는 경우이다. 따라서 그림 4에서 보여지듯, 이들은 같은 private IP 주소 세상에 자리하고 있게 된다. 클라이언트 A는 서버 S와 UDP 세션을 구성하였고 NAT는 자신만의 public 포트 번호 62000을 이에 할당 하였다. 클라이언트 B도 비슷하게 S와 UDP연결을 구성했으며 NAT는 이에 public 포트 번호 62005를 할당 하였다.
Figure 4: UDP Hole Punching, Peers Behind a Common NAT
클라이언트 A가 위에서 개괄된 홀 펀칭 기법을 사용하여 서버 S를 소개자로 이용, B와 UDP 세션을 구성한다고 해보자. 클라이언트 A는 S에게 B에 대한 연결 요청 메시지를 보낸다. S는 A에게 B의 public과 private 종점들로 답을 하고 또한 A의 public과 private 종점들을 B로 전달한다. 이후 양쪽 클라이언트 모두 이들 종점들을 향해서 UDP 데이터그램을 상대방에게 직접적으로 보내길 시도한다. public 종점으로 향하는 메시지들은 그들의 종착지에 갈 수도 있고 못 갈수도 있는데 이는 해당 NAT가 아래 섹션 3.5에서 기술되는 hairpin 변환을 지원하냐 안하냐에 달려있다. 반면에, private 종점들로 향하는 메시지들은 그들의 종착지에 도달한다. 그리고 또한 private 네트워크를 통한 이 직접 루트가 NAT를 통하는 간접 루트보다 빠를 것 이므로, 클라이언트들은 이후의 정식 연결을 위해서 십중팔구 private 종점들을 선택할 것이다.
NAT가 hairpin 변환을 지원한다고 가정하면, private 종점들에게도 public 종점들과 같이 연결을 시도해야 하는 복잡함을 없앨 수도 있다. 이는 어플리케이션이 같은 NAT 뒤에 있는 로컬 연결을 해당 NAT를 불필요하게 거쳐가게 하는 것을 대가를 치러야 한다. 하지만 섹션 6에서 우리의 결과가 보여주듯, hairpin 변환은 다른 "P2P에 친근한" NAT 동작들에 비해 아주 적게 퍼져있다. 따라서, 지금 당장은 어플리케이션들이 public과 private 종점들을 모두 사용함으로써 실제의 이득을 얻을 것 같다.
3.4 Peers Behind Different NATs
그림 5에 서 나오듯, 클라이언트 A와 B가 서로 다른 NAT들 뒤에 private 주소를 가진다고 해보자. A와 B는 각자 그들의 로컬 포트 4321에서 서버 S의 포트 1234로 UDP 연결을 시작했다. 이 outbound 세션들을 다루기 위해, NAT A는 S와 연결된 A의 세션에 포트 62000을 자신의 public IP, 155.99.25.11에 할당 하였다. NAT B는 S와 연결된 B세션에 포트 31000을 자신의 IP주소, 138.76.29.7에 할당했다.
Figure 5: UDP Hole Punching, Peers Behind Different NATs
A 는 자신의 private 종점 10.0.0.1:4321을 자신의 등록 메시지를 통해 서버로 보낸다. 이때 10.0.0.1은 자신의 private 네트워크 상에서의 IP주소이다. S는 전달된 A의 private 종점과 함께 자신이 보는 A의 public 종점도 함께 기록한다. 이 경우에, A의 public 종점은 155.99.25.11:62000이다. 이는 NAT에 의해 해당 세션에 임의로 할당된 종점이다. 이와 비슷하게 B가 등록을 할 경우에 S는 B의 private 종점인 10.1.1.3:4321과 B의 public 종점인 138.76.29.7:31000을 기록한다.
이제 클라이언트 A는 B와의 직접적인 UDP 통신 세션을 만들기 위해 위에서 설명된 홀 펀칭 방식을 따르게 된다. 먼저, A는 S에게 B와의 연결을 위한 도움을 요청하는 메시지를 보낸다. S는 이에 대한 답으로 B의 public과 private 종점을 A에게 보낸다. 또한 A의 public과 private 종점을 B로 보낸다. A와 B는 각각 서로의 종점들을 향해 UDP 데이터그램을 보내기를 시작한다.
A 와 B가 서로 다른 private 네트워크 상에 있기 때문에 그들 각각의 private IP주소는 전역적으로 전달 가능하지 않다. 따라서 이들 종점으로 보내진 메시지들은 잘못된 호스트에 도달하거나 아니면 어떤 호스트에도 도달하지 않을 것이다. 많은 NAT들이 DHCP서버로서의 역할을 하고 있는데 주로 NAT벤더들에 의해 기본으로 선택된 private 주소 pool로 부터 아주 결정론적인 방식으로 IP주소들을 할당하는 식이다. 이 때문에 실질적으로는, B의 private 종점으로 향하는 A의 메시지는 A의 private 네트워크 상에 있는 B와 같은 private IP주소를 가지게 된 어떤 다른 호스트로 전달될 것이다. 따라서 어플리케이션은 반드시 모든 메시지들을 무슨 방식으로든 인증함으로써 이런 길 잃은 메시지들을 강하게 걸러내야 한다.
그럼, 이제 그림 5에서 보여지듯, B의 public 종점으로 보내진 A의 첫 번째 메시지를 생각해보자. 이 밖으로 향하는 메시지가 A의 NAT를 통과함에 따라, NAT는 이 메시지가 밖으로 향하는 새로운 세션의 첫 번째 UDP 패킷임을 알게 된다. 이 새로운 세션의 시작 종점(10.0.0.1:4321)은 A와 S사이에 이미 존재하는 세션과 같다. 하지만 도착 종점은 다르다. 만약 NAT A가 잘 작동한다면, A의 private 종점의 ID를 보존한다. 이는 private 시작 종점인 10.0.0.1:4321 주소로부터 이 해당되는 public 시작 종점인 155.99.25.11:62000 주소로 일관적으로 변환하는 ID이다. 따라서 B의 public 종점으로 향하는 A의 첫 번째 메시지는 사실상 A의 NAT에 "구멍을 뚫은 것이다." 이 홀 펀칭은 바로 새로운 세션을 위한 것인데, 이 세션은 A의 private 네트워크상에서 두 종점들 (10.0.0.1:4321 - A의 private 주소, 138.76.29.7:31000 - B의 public 주소)로 정의된다. 또한 이 세션은 메인 인터넷 상에서 두 종점들(155.99.25.11:62000 - A의 public 주소, 138.76.29.7:31000 - B의 public 주소)로 정의된다.
만약 B의 public 종점으로 향하는 이 A의 메시지가 B의 NAT이 도달했는데 아직 A를 향하는 B의 첫 번째 메시지가 B의 NAT를 건너기 전 이라고 하자. 이 경우 B의 NAT는 내부로 향하는 A의 메시지를 요청되지 않은 트래픽으로 분류, 걸러낼 것이다. 반면에, A의 public 주소로 향하는 B의 첫 번째 메시지는 앞서 설명과 마찬가지로 B의 NAT에 구멍을 열게 된다. 이는 새로운 세션을 위한 것으로 B의 private 네트워크에서 두 종점들(10.1.1.3:4321 - B의 private 주소, 155.99.25.11:62000 - A의 public 주소)로 정의됨과 동시에 인터넷 상에서는 (138.76.29.7:31000 - B의 public 주소, 155.99.25.11:62000 - A의 public 주소)로 정의된다. A와 B가 각자 자신들의 NAT들을 한번 건너기만 하면, 구멍들이 각각의 방향으로 열리고 UDP 통신이 정상적으로 진행될 수 있다. 클라이언트들은 public 종점들이 작동한다는 것을 한번 확인하고 나면 또 다른 방편이었던 private 종점들로 메시지들을 보내는 것을 멈출 수 있다.
3.5 Peers Behind Multiple Levels of NAT
다중 NAT 장치들에 연관된 어떤 망들에서는, 두 클라이언트가 그 망의 특정한 지식이 없이는 "최적의" P2P연결을 구성을 할 수가 없다. 그림 6에 나와있는 마지막 상황을 생각해보자. 인터넷 서비스 제공 업체 (ISP)가 많은 고객들을 적은 public IP주소 만으로 다중 수신하기 위해 큰 기업형 NAT C를 설치했다고 해보자. 여기에 해당 인터넷 업체의 고객들이 집에서 그들의 홈 네트워크를 구성하기 위해 각자의 ISP 제공 IP주소에 NAT A와 B를 설치했다고 해보자. 서버 S와 NAT C만이 오직 전역적으로 전달 가능한 IP 주소를 가지고 있다. NAT A와 B의 public 주소는 사실 ISP 주소 세계 안에 있는 private 주소이다. 한편, 클라이언트 A와 B의 주소는 NAT A와 B의 세계에 있는 private 주소인 것이다. 전과 마찬가지로 각각의 클라이언트가 서버를 향해 밖으로 향하는 연결을 시작한다. 이는 NAT A와 B 각각 하나의 public/private 변환을 생성하며 또한 NAT C가 이 각각의 세션에 해당하는 public/private 세션 구성을 야기한다.
Figure 6: UDP Hole Punching, Peers Behind Multiple Levels of NAT
그럼, 이제 A와 B가 홀 펀칭을 통해서 직접 P2P 연결 구성을 시도한다고 해보자. 아마 최적의 라우팅 방식은 클라이언트 A가 NAT B에 있는 클라이언트 B의 "semi-public" 주소, 10.0.1.2:55000 (ISP 주소 공간)으로 메시지를 보내고 클라이언트 B역시 NAT A에 있는 클라이언트 A의 "semi-public" 주소, 10.0.1.2:45000으로 보내는 방식일 것이다. 하지만 불행히도, A와 B가 이 주소를 알 방법은 없다. 왜냐하면 서버 S는 오직 클라이언트의 진정한 전역 public 주소만 보기 때문이다. 또한 A와 B가 이 주소들을 어떻게든 알았다 하여도 이들이 사용가능 할지는 미지수이다. 이는 ISP의 private 주소 세계의 IP주소 할당과 클라이언트의 private 세계에서의 IP 주소 할당이 충돌할 수 있기 때문이다. (예를 들어서, NAT C에서 NAT A로 할당한 IP주소가 NAT B에서 클라이언트 B로 할당한 주소인 10.1.1.3 일지도 모르는 것이다. (역주: 이러면 B는 자기 자신에게 메시지를 보내는 결과가 나와버린다.)
따라서 P2P통신을 위해 클라이언트들은 서버 S에게 보여지는 그들의 전역 public 주소를 이용하는 수 밖에는 없다. 그리고 NAT C에서 제공하는 hairpin 혹은 loopback 변환에 의존해야 한다. A가 B의 전역 종점, 155.99.25.11:62005으로 UDP 데이터그램을 보낼 때 NAT A는 먼저 데이터그램의 시작 종점을 10.0.0.1:4321을 10.0.1.1:45000으로 변환한다. 이제 이 데이터그램은 NAT C에 도착하고 이 데이터그램의 도착 주소가 NAT C의 변환된 public 종점들 중에 하나라는 것을 알아차린다. 만약 NAT C가 잘 동작한다면, 데이터그램의 시작과 도착 종점을 모두 변환하고 이 데이터그램을 "돌려서" private 네트워크로 보낸다. 이제 데이터그램의 시작 종점은 155.99.25.11:62000 이고 도착 종점은 10.0.1.2:55000 이다. 끝으로 B의 private 네트워크로 이 데이터그램이 들어가면서 NAT B가 이 데이터그램의 도착 주소를 변환, B에 도착하게 된다. A로 가는 길도 비슷하게 작동한다. 많은 NAT들은 아직 hairpin 변환을 지원하지 않는다. 하지만 NAT 벤더들이 이 문제를 인지함에 따라서 점점 보편화 될 것이다.
3.6 UDP Idle Timeouts
UDP 통신 프로토콜은 NAT를 가로지르는 세션의 수명을 알 수 있는 어플리케이션 독립적이고 믿을 만한 방법을 NAT에게 제공하지 않는다. 이 때문에 대부분의 NAT들은 단순하게 UDP 변환들과 타이머를 연관시켜서, 만약 특정 시간 동안 어떤 트래픽도 없으면 구멍(hole)을 닫아 버린다. 불행히도 이 특정 시간에 대한 어떤 표준 값도 없다. 어떤 NAT들은 20초 정도로 짧은 시간을 가진다. 만약 어플리케이션이 홀 펀칭을 통해서 구성한 세션을 계속해서 살아있게 유지하려면, 반드시 주기적으로 "keep-alive" 패킷들을 보내서 NAT에 있는 해당 주소 변환의 상태가 사라지지 않게 해야 한다.
불행히도 많은 NAT들은 특정한 두 종점들로 정의된 세션과 UDP Idle 타이머를 연관시키기 때문에, 한 세션으로만 keep-alive 패킷들을 보내는 것으로 다른 세션들을 살아있게 유지할 수 없다. 비록 이 모든 세션들이 같은 private 종점들에서 시작되었다고 하더라도 말이다. 이렇듯 다른 많은 P2P세션들로 keep-alive 패킷을 보내는 대신에, UDP세션이 더 이상 작동하지 않는 것을 감지하고 "필요에 따라" 다시 원래의 홀 펀칭 방법을 실행하는 것으로 어플리케이션은 지나친 keep-alive 트래픽을 막을 수 있다.
4. TCP Hole Punching
NAT들 뒤에 있는 호스트들 사이의 P2P TCP 연결을 구성하는 것은 UDP보다 약간 복잡하다. 하지만 프로토콜 레벨에서 보면 TCP 홀 펀칭은 UDP와 아주 흡사하다. UDP 홀 펀칭 만큼 널리 알려지지 않았기에, 적은 NAT들만이 TCP 홀 펀칭을 지원한다. 하지만 관련된 NAT들이 이를 지원하기만 한다면, TCP 홀 펀칭 역시 UDP 홀 펀칭 만큼 빠르고 믿을만하다. 사실 잘 동작하는 NAT들 사이를 가로지르는 P2P TCP 연결이 UDP 통신 보다 튼실하다. 왜냐하면 UDP와 달리 TCP 프로토콜의 상태 기계가 NAT들에게 특정 TCP 세션의 수명을 정확하게 결정할 수 있는 표준 방법을 제공하기 때문이다.
4.1 Sockets and TCP Port Reuse
TCP 홀 펀칭을 구현하는데 가장 걸림돌이 되는 것은 프로토콜 문제가 아니라 API 문제이다. 표준 버클리 소켓 API가 클라이언트/서버 패러다임을 기반으로 디자인되었기 때문에, 이 API는 한 소켓이 connect()로 밖으로 향하는 연결을 시작하거나 혹은 listen()과 accept()을 통해서 안으로 향하는 연결들을 들을 수 있다. 하지만 둘 다는 안 된다. 거기다 더해, TCP 소켓들은 대개 로컬 호스트 상에서 TCP 포트와 일대일 관계를 가진다. 어플리케이션이 한 소켓에 특정 로컬 TCP 포트를 할당하고 나면, 두 번째 소켓을 같은 포트에 할당하려는 시도는 실패한다.
하지만 TCP 홀 펀칭이 작동하려면, 우리는 하나의 포트로 안으로 향하는 TCP 연결들을 들어야 함과 동시에 밖으로 향하는 다수의 TCP 연결을 시작해야 한다. 다행히도 모든 메이저 운영 체제들이 대개SO_REUSEADDR라고 불리는 특별한 TCP 소켓 옵션을 지원하는데, 이는 어플리케이션이 다수의 소켓을 같은 로컬 종점으로 바인딩 할 수 있게 해준다. 단, 모든 소켓들이 이 옵션을 켜놓은 상태여야 한다. BSD 시스템들은 SO_REUSEPORT 옵션을 소개했는데, 이는 주소 재사용과 별개로 포트 재사용을 제어할 수 있게 해준다. 이런 시스템에서는 두 옵션 모두 반드시 켜져 있어야 한다.
4.2 Opening Peer-to-Peer TCP Streams
클라이언트 A가 클라이언트 B와 TCP 연결을 설정하기를 원한다고 해보자. 우리는 이미 두 클라이언트 A와 B가 잘 알려진 랑데뷰 서버 S와 연결되어 있다고 가정한다. UDP의 경우와 마찬가지로, 서버는 각각 클라이언트의 public 종점과 private 종점을 기록하고 있다. 프로토콜 레벨에서 보면 TCP 홀 펀칭은 UDP의 경우와 거의 동일하게 동작한다.
1. 클라이언트 A는 S와 연결된 TCP 세션을 사용하여 S에게 B와 연결을 위한 도움을 요청한다.
2. S는 A에게 B의 public 과 private TCP 종점들을 보내주고 동시에 A의 public과 private 종점들을 B에게 보낸다.
3. S에게 등록하기 위해 사용했던 똑같은 로컬 TCP 포트들로부터, A와 B는 각자 비동기적으로 S에게 보고되어있던 상대방의 public과 private 종점들을 향해 밖으로 향하는 연결 만들기를 시도한다. 동시에 그들 각자의 로컬 TCP 포트들에서 안으로 향하는 연결을 듣고 있는다.
4. A와 B는 밖으로 향하는 연결 시도가 성공하거나 또는 안으로 향하는 연결이 나타날 때까지 기다린다. 만약 밖으로 향하는 연결 시도가 "connection reset" 이나 "host unreachable"등의 에러로 인해 실패하게 되면, 짧은 시간 뒤에 (예를 들어, 1초 뒤에) 그냥 다시 연결을 시도한다. 이를 어플리케이션이 정한 최대 timeout까지 계속한다.
5. TCP 연결이 만들어지면, 호스트들은 각자가 서로 의도했던 호스트와 연결되었는지를 확인하기 위해 인증을 한다. 만약 인증이 실패하면, 클라이언트들은 연결을 종료하고 다른 것들이 성공할 때까지 기다린다. 클라이언트들은 이 과정을 통해서 첫 번째로 성공적으로 인증된 TCP 연결을 사용한다.
동시에 서버 S와 임의의 개수의 상대방과의 통신을 하기 위해 딱 하나의 소켓만을 필요로 하는 UDP와 달리, 각각의 TCP 클라이언트 어플리케이션은 그림 7에 나오듯 하나의 로컬 TCP 포트에 바운드된 여러 개의 소켓을 다뤄야 한다. 각각의 클라이언트들은 서버 S와의 연결을 나타내는 소켓, 안으로 향하는 연결을 수락하기 위한 수신 소켓, 그리고 상대방의 public과 private TCP 종점들을 향해 밖으로 향하는 연결을 시작하기 위한 최소한 두 개 이상의 소켓이 필요하다.
Figure 7: Sockets versus Ports for TCP Hole Punching
그림 5에 나오듯, A와 B가 서로 다른 NAT들 뒤에 있는 일반적인 경우를 따져보자. 포트 번호들이 UDP 포트가 아니라 TCP 포트라고 해보자. A와 B의 private 종점으로 향하는 연결들은 실패하거나 잘못된 호스트와 연결된다. UDP의 경우와 같이, TCP 어플리케이션들도 그들의 P2P 세션들을 인증하는 것이 중요하다. 로컬 네트워크 상에 있는 호스트가 원래 연결하고자 하는 원격 private 네트워크상의 호스트와 똑같은 private IP를 가지는 경우에, 실수로 잘못된 연결이 생길 수 있는 가능성이 있기 때문이다.
반면에 각자의 public 종점으로 향하는 연결들의 경우, 이에 해당하는 NAT들로 하여금 A와 B의 직접적인 TCP연결을 활성화 시켜주는 "구멍"을 열게 한다. 만약 이 NAT들이 잘 작동한다면, 그들 사이의 새로운 P2P TCP 연결이 자동적으로 형성된다. 만약 B의 첫 번째 SYN패킷(A로 향하는)이 B의 NAT에 닿기 전에, A의 첫 번째 SYN 패킷(B로 향하는)이 B의 NAT에 닿으면, B의 NAT는 이를 요구되지 않은 안으로 향하는 연결로 간주하고 이 패킷을 버린다. 하지만, 이후에 B의 첫 번째 SYN 패킷(A로 향하는)은 통과되게 되는데, 이는 A의 NAT가 이 SYN을 B로의 향하는 연결의 한 부분으로 간주하기 때문이다. B로의 밖으로 향하는 연결을 A의 SYN이 시작했다고 보는 것이다. (역주: 간단히 말해 A의 SYN 패킷이 이미 A의 NAT에 "구멍"을 뚫어 놓은 것이다.)
4.3 Behavior Observed by the Application
TCP 홀 펀칭 와중에 클라이언트 어플리케이션이 보게 되는 그들의 소켓에 벌어질 일들은 타이밍과 더불어 해당 TCP구현에 달려있다. B의 public 종점으로 향하는 A의 첫 번째 outbound SYN 패킷이 NAT B에 의해 버려졌지만, 그 뒤에 A의 public 종점으로 향하는 B의 첫 번째 SYN 패킷이 A의 TCP가 SYN을 다시 보내기 전에 A에 도착했다고 해보자. 운영 체제에 따라서 두 가지 중 한 가지의 경우가 일어난다.
A의 TCP 구현은 들어오는 SYN의 세션 종점들이 A가 시작하려 했던 밖으로 향하는 세션들의 그것들과 짝임을 알게 된다. 따라서 A의 TCP 스택은 이 새로운 세션을 A가 B로 향해 connect()를 사용했던 로컬 어플리케이션 상의 소켓에 연관시킨다. 어플리케이션의 비동기 connect() 호출은 성공하고 listen 소켓에서는 아무 일도 일어나지 않는다.
수신된 SYN 패킷이 전에 A가 보낸 outbound SYN에 대한 ACK를 포함하고 있지 않기 때문에, A의 TCP는 B의 public 종점을 향해서 SYN-ACK 패킷을 회신한다. 이 패킷의 SYN 부분은 단지 똑같은 시퀀스 번호를 사용하는 A의 원래 outbound SYN의 반복이다. B의 TCP가 A의 SYN-ACK를 받기만 하면, A의 SYN을 위한 자신의 ACK로 답변을 하고 TCP 세션은 양쪽 상에 모두 연결된 상태로 돌입하게 된다.
또 다른 경우에는, A의 TCP 구현이 A가 안으로 향하는 연결 시도에 대한 활성화된 listen 소켓이 있다는 것을 감지할 지도 모른다. B의 SYN이 안으로 향하는 연결 시도로 보이기 때문에, A의 TCP는 새로운 TCP 세션과 이와 연관되는 새 stream 소켓을 생성하고 이 소켓을 어플리케이션이 자신의 listen 소켓에 accept() 호출하는 것을 통해 전달한다. 다음으로 A의 TCP는 B에게 위의 경우와 같이 SYN-ACK를 회신하고 TCP연결 설정은 보통의 클라이언트/서버 스타일 형식으로 나아간다.
B를 향한 A의 이전 outbound connect()가 사용했던 source와 destination 종점들이, 이제 아까 막 accept()를 통해서 어플리케이션으로 넘겨진 다른 소켓에 의해 사용되고 있기 때문에, A의 비동기 connect()는 반드시 어떤 시점에서 대개 "address in use" 에러와 함께 실패한다. 그럼에도 불구하고 어플리케이션은 B와의 P2P통신을 위한 작동하는 stream 소켓을 가지고 있으므로 이 실패를 무시한다.
첫 번째 경우는 대개 BSD를 기반으로 하는 운영체제들 상에서 나타나고, 두 번째 경우는 Linux나 Windows경우에 보다 자주 나타난다.
4.4 Simultaneous TCP Open
홀 펀칭 과정 중, 다수의 연결 시도들의 타이밍이 맞아 떨어져서, 두 클라이언트 모두의 밖으로 향하는 최초의 SYN 패킷들이 상대방의 NAT에 닿기 전에 자신의 해당 로컬 NAT에 당도하였다고 하자. 이 경우 각각의 NAT는 밖으로 향하는 TCP 세션들을 열게 된다. 이런 "운 좋은" 경우에는, 두 NAT들은 어떤 초기 (역주: 안으로 향하는) SYN 패킷도 거절하지 않게 되고 이 초기 SYN 패킷들은 NAT들 사이의 선상을 가로지른다. 이 경우에, 클라이언트들은 "simultaneous TCP open"이 라는 현상을 보게 된다. 이는 각각의 peer들이 모두 SYN-ACK를 기다리는 와중에 "생짜(raw)" SYN을 받은 것이다. 각 peer들의 TCP들은 SYN-ACK로 회신을 하며, 이때 SYN은 이전에 보낸 것과 같은 것이고 ACK 부분은 다른 peer로 부터 받은 SYN에 대한 수신 확인이다.
이 경우 각각의 어플리케이션들이 겪게 될 상황은 앞에서 설명된 바와 같이 TCP 구현이 어떻게 동작하냐에 따라 달라진다. 만약에 두 클라이언트 모두 앞에서 설명한 두 번째 경우와 같이 구현되었다면, 모든 비동기 connect() 함수 호출은 실패할 것이다. 하지만 그럼에도 불구하고 각각의 클라이언트에서 돌아가는 어플리케이션들은 accept()를 통해서 P2P TCP통신이 작동하는 새 연결을 가진다. 이것은 마치 마법과 같이 이 TCP 연결이 선상에서 "스스로 만들어져서" 서로의 종점에서 수동적으로 받아들여지기만 한 것과 같은 것이다! 어플리케이션이 P2P TCP 소켓들이 connect()에서 생겼는지 accept()에서 생겼는지 상관하지 않는다면, 이 과정은 RFC 793 [23]에 명시된 표준 TCP 상태 기계를 제대로 구현한 어떤 TCP에서도 동작하는 결과를 낳게 된다.
UDP를 위한 섹션 3에서 토의된 여러 다른 네트워크 구성에 따른 시나리오들은 TCP의 경우도 똑같이 적용된다. 예를 들어, TCP 홀 펀칭은 그림 6에서 나오는 다중 레벨의 NAT 경우에 동작한다. 관련된 NAT들이 잘 작동한다면 말이다.
4.5 Sequential Hole Punching
위의 TCP 홀 펀칭 방식의 변형으로 NatTrav 라이브러리 [4] 가 구현한 것은, 클라이언트들이 동시가 아닌 서로 순차적으로 접속을 시도한다. 예를 들어, (1)A가 S를 통해서 B에게 연결하고 싶다고 말하지만 A의 로컬 포트로 동시에 연결을 기다리진(listening) 않는다. (2)B가 A를 향해 connect()를 시도하고 이는 B의 NAT에 구멍을 연다. 하지만 이후 타임아웃이나 A의 NAT, 혹은 A 자신으로부터의 RST로 인해 호출은 실패한다. (3)B는 S의 연결을 닫고 자신의 로컬 포트에 listen()을 한다. (4)S는 다음으로 A와의 연결을 끊고, 이는 A에게 B로 직접 connect()를 시도하라는 신호가 된다.
이 순차적 방법은 simultaneous TCP open을 제대로 구현하지 못한 XP 서비스 팩 2 이전의 윈도우에서 특히 유용할 수 있다. 혹은 SO_REUSEADDR 기능을 제대로 지원하지 못하는 소켓 API의 경우도 마찬가지이다. 하지만, 순차적 방법은 보다 타이밍 의존적이고 일반적인 경우에 느릴 수 있으며 일반적이지 않은 상황에서는 덜 튼튼하다. 예를 들어, 스텝 (2)에서 B는 반드시 "실패하기로 되어있는" connect() 시도에게 충분한 시간을 주어서 적어도 하나의 SYN 패킷이 자신 쪽의 네트워크에 있는 모든 NAT들을 다 돌아다닐 수 있게 해야 한다. 너무 짧은 지연 시간은 이 과정을 실패하게 하는 잃어버린 SYN의 위험을 초래할 것이고, 너무 긴 지연 시간은 홀 펀칭을 위해 요구되는 전체 시간을 증가 시킬 것이다. 또한 순차적 홀 펀칭 방식은 양쪽 클라이언트에서 서버 S로 연결을 "소모"한다. 이는 새로 만들어져야 할 P2P 연결마다 클라이언트들이 S를 향해 새로운 연결을 열어야 하기 때문이다. 반대로, 평행 홀 펀칭 방식은 전형적으로 양쪽 클라이언트가 밖으로 향하는 connect() 시도를 하자마자 완료된다. 또한 각각의 클라이언트가 S와의 단일 연결을 무기한으로 유지하고 재사용할 수 있게 한다.
5. Properties of P2P-Friendly NATs
이 섹션은 NAT가 위에서 설명된 홀 펀칭 기법이 제대로 작동하기 위해서 반드시 가져야 하는 주요 속성들에 대해 설명할 것이다. 모든 NAT 구현들이 이 속성들을 만족시키진 않지만, 많은 NAT 구현들이 그러하다. 또한 NAT 벤더들이 voice over IP와 온라인 게임과 같이 P2P 프로토콜의 요구를 알아감에 따라 NAT들이 점차적으로 더 "P2P와 사이 좋게" 되고 있다.
이 섹션은 NAT들이 "반드시" 이래야 한다는 완벽한 또는 결정적인 명세를 의도하는 것이 아니다. 우리는 단지 P2P 홀 펀칭을 가능하게 하거나 또는 작동하지 않게 하는 가장 일반적으로 관찰되는 방식들에 대한 정보를 제공할 뿐이다. IETF는 NAT의 작동 방식에 대해 공식적인 "현존하는 최고의 방식들"을 정의하기 위해 BEHAVE라는 새로운 작업 그룹을 시작했다. 당연히 NAT벤더들은 공식적인 작동 방식 표준이 체계화 됨에 따라 IETF 작업 그룹을 직접 따라야 한다.
5.1 Consistent Endpoint Translation
여기서 설명된 홀 펀칭 기법은 오직 NAT가 private 네트워크 상에 주어진 TCP혹은 UDP의 소스의 종점을 NAT에 의해 조정되는 "하나"의 public 주소로 일관되게 연관을 시킬 경우에만 자동적으로 작동한다. 이렇게 작동하는 NAT는 cone NAT라고 RFC 3489 [19]와 다른 곳에서 일컬어진다. 왜냐하면 NAT가 하나의 private 종점에 기반하는 모든 세션을 NAT상의 같은 public 종점으로 "모으기" 때문이다. (역주: cone = 깔때기)
예를 들어, 그림 5의 시나리오를 다시 생각해보자. A가 처음 잘 알려진 서버 S로 연결 할 때, NAT A가 자신만의 IP주소인 155.99.25.11에 포트 62000을 A의 종점을 나타내는 임시 public 종점으로 선택했다. 나중에 A가 똑같은 local private 종점으로부터 B의 public 종점으로 메시지를 보냄으로써 B를 향해서 P2P 세션을 구성하려고 시도할 때, A는 NAT A가 이 private 종점의 정체성을 유지하며 존재하는 public 종점 155.99.25.11:62000을 재사용하는 것에 의존한다. 왜냐하면, A를 위한 그 public 종점이 바로 B가 자신의 답신 메시지들을 보낼 곳이기 때문이다.
오직 클라이언트/서버 프로토콜들만을 지원하기 위해 디자인된 NAT들은 이런 방식으로 private의 종점들의 정체성을 보존할 필요가 없다. 이런 NAT들이 RFC 3489 용어법에 있는 symmetric NAT이다. 예를 들어, S와 연결된 클라이언트 A의 세션에 public 종점 155.99.25.11:62000을 할당하고 난 이후에, 이 NAT가 155.99.25.11:62001과 같은 다른 public 종점을 B와 연결을 시작하려는 P2P 세션에 할당할지도 모른다. 이런 경우, 홀 펀칭은 연결성 제공에 실패한다. 이는 이후에 B에서 들어오는 메시지들이 잘못된 포트 번호로 NAT에 도착하기 때문이다.
많은 symmetric NAT들이 쉽게 예상할 수 있는 방식으로 연속되는 세션들에 대해서 포트 번호들을 할당한다. 이 사실을 이용해서, 홀 펀칭 알고리즘들의 변형들이 [9,1] symmetric NAT들까지 넘나들며 "많은 경우에" 작동하게 만들어질 수 있다. 이는 STUN [19] 과 같은 프로토콜을 이용해서 먼저 NAT의 작동 방식을 탐색하고 이후에 이 결과를 이용해서 NAT가 다음 새 세션에 할당할 public 포트 번호를 "예측"하는 것이다. 하지만 이런 예측 기법들은 움직이는 대상을 추적하는 것과 매한가지 이고 이 와중에 많은 것들이 잘못될 수 있다. 예를 들어, 예측된 포트가 이미 사용 중 이라면 NAT는 다른 포트 번호로 점프할 것이다. 또는 같은 NAT뒤에 있는 다른 어플리케이션이 잘못된 타이밍에 세션을 시작하여 이 예측된 포트가 할당될 수 있다. 예측 기법이 기존에 제대로 작동하지 않는 NAT들을 상대로 최대한의 호환성을 성취하는데 유용할 수 있지만, 이것이 장기적으로 확실한 답이 되지는 않는다. Symmetric NAT들이 세션당 트래픽 필터링을 하는 cone NAT 보다 더 높은 보안을 제공하지 않기 때문에, NAT 벤더들이 P2P 프로토콜들을 지원하기 위한 그들의 알고리즘을 적용함에 따라 symmetric NAT들은 점점 덜 일반적이 되고 있다.
5.2 Handling Unsolicited TCP Connections
NAT가 자신의 public 쪽에서 SYN 패킷을 받았는데 이것이 요청되지 않은 내부로 향하는 연결 시도라면, 그냥 조용히 이 SYN packet을 버리는 것이 중요하다. 어떤 NAT들은 대신에 TCP RST (역주: connection reset) 혹은 ICMP 에러 보고까지 되돌려 보내면서 이런 안으로 향하는 연결을 적극적으로 거부한다. 이는 TCP 홀 펀칭을 방해하게 된다. 섹션 4.2에 있는 홀 펀칭 과정의 네 번째 스텝에 서술된 바와 같이, 이런 방식은 어플리케이션들이 밖으로 향하는 연결을 다시 시도하는 한 아주 치명적이지는 않다. 하지만 일시적인 에러들이 발생하는 것이 홀 펀칭 과정을 길게 만들 수 있다.
5.3 Leaving Payloads Alone
적은 수의 현존하는 NAT들은 IP주소처럼 보이는 4byte 값을 패킷으로 부터 "맹목적으로" 스캔하고 이를 패킷 헤더에 있는 IP 주소마냥 해석하는 것으로 알려져 있다. 사용중인 어플리케이션 프로토콜의 관해서 아무것도 알지 못한 체 말이다. 이런 나쁜 방식은 다행히 일반적이지 않고 어플리케이션들이 자신이 보내는 메시지들에 있는 IP 주소들을 알아보지 못하게 만드는 것으로 방어가 가능하다. 예를 들어, 원하는 IP 주소의 2의 보수를 보내는 식이다.
5.4 Hairpin Translation
섹션 3.5에서 설명했듯이, 어떤 다중 레벨 NAT의 경우에 TCP 혹은 UDP 홀 펀칭이 작동하기 위해서는 hairpin 변환 지원을 요구한다. 예를 들어, 그림 6에서 나온 시나리오는 NAT C가 hairpin 변환을 지원하느냐에 달려있다. 불행히도 현재 NAT들 사이에서는 hairpin 변환이 드물다. 그러나 다행히도 이것을 요구하는 네트워크 시나리오 역시 드물다. 하지만 IPv4 주소 공간 고갈이 계속됨에 따라 다중 레벨 NAT가 점점 일반적이 되고 있다. 따라서 미래의 NAT 구현에서 hairpin 변환 지원은 중요하다.
6. Evaluation of Existing NATs
이 문서에서 설명된 TCP 와 UDP 홀 펀칭 기법의 현존하는 다양한 NAT들에 대한 튼실함을 평가하기 위해, 우리는 NAT Check [16]라고 불리는 테스트 프로그램을 개발하고 배포하였다. 또한 인터넷 사용자들에게 그들의 NAT에 관한 데이터를 요청하였다.
NAT Check의 주요 목적은 안정적인 UDP와 TCP 홀 펀칭을 위해 가장 중요한 두 가지 행동 속성들을 테스트하는 것이다. 종점 변환 시 일관된 정체성 보존(섹션 5.1)과 요구되지 않은 내부로 향하는 TCP SYN들을 RST나 ICMP 에러로 거부하는 대신에 그냥 조용히 버리는 것(섹션 5.2)이 이 두 가지이다. 여기다 더해, NAT Check는 따로 NAT가 hairpin 변환(섹션 5.4)을 지원하는지, 그리고 NAT가 요구되지 않은 내부로 향하는 트래픽을 완전히 차단하는지 여부도 테스트한다. 마지막 속성은 홀 펀칭에 영향을 주지는 않지만 NAT 방화벽 규정에 대해 유용한 정보를 제공한다.
NAT Check는 NAT 행동 개개의 모든 면을 테스트하려고 하지 않는다. 이들은 매우 다양하고 미묘한 차이점들이 있음이 알려져 있고, 또한 그 중의 어떤 것들은 믿을 만한 테스트를 하기가 어렵다. [12] 대신에, NAT Check는 단지 "제안된 홀 펀칭 기법들이 전형적인 네트워크 조건들 아래, 이미 출시된 NAT들에서 얼마나 일반적으로 작동할 것인가?" 라는 질문에 답변하려고 한다.
6.1 Test Method
NAT Check는 테스트 하려는 NAT 뒤에 있는 컴퓨터에서 실행되는 클라이언트 프로그램과 각각 다른 전역 IP 주소를 가진 세 개의 잘 알려진 서버들로 구성되어있다. 클라이언트는 TCP와 UDP 홀 펀칭에 관련된 NAT 작동 방식을 체크하기 위해 세 개의 서버와 협동한다. 클라이언트 프로그램은 작고 상대적으로 이식성이 있다. 현재 Windows, Linux, BSD, 그리고 Mac OS X 에서 실행된다. 잘 알려진 서버들을 실행하는 컴퓨터들은 모두 FreeBSD를 사용한다.
6.1.1 UDP Test
UDP를 위한 NAT의 행동 방식을 테스트하기 위해서, 클라이언트는 소켓을 하나 열고 지역 UDP 포트를 할당한다. 그리고 그림 8에서 나온 바와 같이 연속해서 ping과 같은 요청을 서버 1과 서버 2에 보낸다. 이 서버들은 각각 클라이언트의 ping에 대한 클라이언트의 public UDP 종점(서버에서 보는 클라이언트의 IP 주소와 UDP 포트 번호)을 담은 답변을 회신한다. 만약 두 서버가 모두 같은 public 종점을 클라이언트에게 보고하면, NAT Check는 NAT가 클라이언트의 private 종점에 대한 정체성을 적절하게 보존하고 있다고 여긴다. 믿을 만한 UDP 홀 펀칭을 위한 이 주요 선행조건이 만족된다고 말이다.
Figure 8: NAT Check Test Method for UDP
서버 2가 UDP 요청을 클라이언트로부터 받았을 때, 클라이언트에게 직접 회신하는 것과 함께 해당 요청을 서버 3에게 보낸다. 이 다음에 서버 3은 자신의 IP 주소로부터 클라이언트로 회신을 한다. 만약 NAT의 방화벽이 제대로 각각의 세션에 대해서 "요청되지 않은" 내부로 향하는 트래픽을 차단한다면, 비록 이 답신이 서버 1과 서버 2에서 오는 답신과 똑같은 public 포트로 향한다고 하더라도 클라이언트 절대 서버 3으로부터의 답신을 볼 수가 없다.
NAT가 hairpin 변환을 지원하는지 테스트하기 위해서, 클라이언트는 간단히 두 번째 UDP 소켓을 다른 포트번호로 열고 이를 사용해서 클라이언트의 첫 번째 UDP 소켓의 public 종점 (서버 2가 알려준) 으로 향해 메시지를 보낸다. 만약 이 메시지가 클라이언트의 첫 번째 private 종점에 도착하면 이는 NAT가 hairpin 변환을 지원한다는 것이다.
6.1.2 TCP Test
TCP 테스트도 UDP와 비슷한 형식을 취한다. 클라이언트는 하나의 로컬 TCP 포트를 이용하여 서버 1 과 2에게 밖으로 향하는 세션을 시작한다. 그리고 서버 1과 2에서 보고되는 public 종점들이 같은지를 확인한다. 이는 믿을 만한 TCP 홀 펀칭을 위한 첫 번째 선행 조건이다.
이 조건을 만족한다고 하여도 안으로 향하는 요청되지 않은 연결 시도에 대해 NAT가 반응하는 방식이 TCP 홀 펀칭의 속도와 신뢰성에 영향을 미치기 때문에, NAT Check는 이 또한 테스트한다. 서버 2가 클라이언트의 요청을 받았을 때, 클라이언트로 바로 답변을 보내는 대신에 이 요청을 서버 3에게 전달하고 서버 3이 "이제 출발" 신호를 보낼 때까지 기다린다. 서버 3은 이 전달된 요청을 받으면 클라이언트의 public TCP 종점을 향해서 안으로 향하는 연결을 시도 하고 이 연결이 성공하거나 실패할 때까지 5초간 기다린다. 이후 서버 2에게 "이제 출발"이라는 신호를 보내고 최대 20초까지 다시 기다리기를 계속한다. 클라이언트가 마침내 서버 2의 답변(서버 2가 서버 3의 "이제 출발" 신호를 기다리며 지연시킨) 을 받게 되면, 클라이언트는 서버 3에게 밖으로 향하는 연결을 시도하고 이는 결과적으로 서버 3과의 simultaneous TCP oepn을 발생시킨다.
이 테스트 과정 중에 어떤 일이 일어나는지는 다음에 나오는 NAT의 방식에 따라 달라진다. 만약 NAT가 제대로 서버 3의 안으로 향하는 "요청되지 않은" SYN 패킷을 버려준다면, 서버 2가 클라이언트에게 답신하기 전의 5초 동안에 클라이언트의 listen 소켓에서는 아무 일도 일어나지 않는다. 클라이언트가 마침내 서버 3으로 향하는 자신만의 연결을 시작했을 때, NAT를 통하는 구멍을 열면서 이 연결 시도는 바로 성공하게 된다. 이와 달리, 만약 NAT가 서버 3의 SYN을 버리지 않고 그냥 통과시켜주면 (홀 펀칭을 위해선 좋지만 보안을 위해서는 이상적이지 않다.), 서버 2의 답신을 받기 전에 클라이언트는 자신의 listen 소켓에서 이 안으로 향하는 TCP 연결을 받게 된다. 끝으로, 만약 NAT가 적극적으로 서버 3의 SYN을 TCP RST 패킷들을 보내면서 거부하게 되면, 서버 3은 포기를 하고 클라이언트의 이어지는 서버 3으로의 연결시도도 실패한다.
TCP 경우의 hairpin 변환을 테스트하기 위해, UDP의 경우와 동일한 방법으로 클라이언트는 두 번째 local TCP port를 이용해서 자신의 첫 번째 TCP 포트에 해당하는 public 종점을 향해 연결을 시도한다.
6.2 Test Results
우리가 모은 NAT Check 데이터는 68개의 다른 벤더들로부터 나온 다양한 NAT들을 포함하는 380개의 리포트로 구성되어있다. 또한 NAT 기능들은 여러 다른 버전의 8개의 인기 있는 운영체제에 구현되어있다. 오직 이중에 335개의 리포트만 UDP hairpin 변환에 대한 결과를 포함하고 있고 286개만이 TCP를 위한 결과를 가지고 있다. 이는 우리가 이미 결과들을 모으기 시작한 이후에 이 기능을 NAT Check의 후기 버전에서 구현했기 때문이다. 데이터 결과는 NAT 벤더 별로 테이블 1에 정리되어있다. 이 테이블은 최소 5개 이상의 데이터가 유효한 개개의 벤더들만 보여준다. 이 결과에서 주어진 하나의 벤더에 대한 다양성은 여러 가지 요소들로부터 설명될 수 있다. 예를 들어, 같은 벤더의 다른 NAT 디바이스 또는 다른 상용 제품 라인, 같은 NAT 구현에 다른 소프트웨어 혹은 다른 펌웨어 버전, 다른 구성, 그리고 혹은 가끔 일어나는 NAT Check의 테스트, 보고 에러 등이 이런 요소들이다.
Table 1: User Reports of NAT Support for UDP and TCP Hole Punching
UDP에 해당하는 전체 380개의 데이터 중 310개(82%)의 경우에서 NAT가 일관적으로 클라이언트의 private 종점을 변환하는 것으로 나타났다. UDP 홀 펀칭과의 기본 호환성을 보여주는 것이다. 하지만 이에 비해 hairpin 변환의 경우 일반성이 매우 떨어지는데, UDP hairpin 변환의 결과를 포함하는 335개의 데이터 중에 오직 80개 (24%) 만이 hairpin 변환을 지원한다고 나왔다.
TCP에 해당하는 전체 286개의 데이터 중에 184개 (64%)가 TCP 홀 펀칭과의 호환성을 보여준다. 다시 말해서 NAT가 일관적으로 클라이언트의 private TCP 종점을 변환하고 안으로 향하는 요청되지 않은 연결에 대해서 RST 패킷을 회신하지 않는 다는 것이다. Hairpin 변환은 역시 일반성이 많이 떨어지는데 오직 37개 (13%) 결과만이 TCP에 대한 hairpin 지원을 한다고 나왔다.
이 결과들은 지원자들의 "스스로 선택한" 커뮤니티에 의해서 만들어졌기 때문에, 이들은 랜덤한 샘플들로 이루어져 있지도 않고 따라서 일반적으로 쓰이는 NAT들의 분포 상황을 꼭 맞게 보여주지도 않는다. 그럼에도 불구하고 이 결과들은 고무적이다. 대다수의 일반적으로 출시된 NAT들이 이미 적어도 싱글 NAT 레벨의 경우에 UDP와 TCP 홀 펀칭을 지원하는 것으로 나타나기 때문이다.
6.3 Testing Limitations
NAT Check의 현재 테스트 프로토콜은 어떤 경우에 결과를 잘못 이끌 수 있는 몇 가지 한계점을 가지고 있다. 먼저 우리는 최근에 몇몇 NAT 구현들이 알지도 못하는 어플리케이션의 메시지에서 맹목적으로 IP 주소를 변환한다는 것을 알게 되었다. 현재 NAT Check 프로토콜은 전달하는 IP 주소를 암호화하지 않으므로 이런 현상으로부터 보호되지 않는다.
둘째로, NAT Check의 현재 hairpin 변환 체크는 불필요하게 부정적인 결과를 초래할 수도 있다. 이는 이 테스트를 위해 완전한 양방향 홀 펀칭 방식을 이용하지 않기 때문이다. 현재 NAT Check는 hairpin 변환을 지원하는 NAT는 NAT의 public 쪽에서 오는 안으로 향하는 연결을 차단하는 것과 다르게 자신의 private 네트워크에서 오는 hairpin 연결은 차단하지 않는다고 가정한다. 왜냐하면 이런 차단은 보완을 위해 불필요하기 때문이다. 하지만 나중에 우리는 NAT가 단순히 모든 자신의 public 포트로 향하는 트래픽을 이것의 진원지와는 상관없이 "믿을 수 없는" 것으로 여길지도 모른다는 것을 알게 되었다. 우리는 아직 어떤 방식이 보다 일반적인지 모른다.
끝으로, NAT에 클라이언트가 오직 하나만 있을 때는 클라이언트의 private 종점을 특정한 private 포트로 일관적으로 변환하지만, 만약 두 개 혹은 그 이상의 클라이언트들이 private 네트워크에서 다른 IP 주소를 가지고 같은 private 포트 번호로 NAT를 통해서 통신하려고 할 때는 symmetric NAT 혹은 이 보다 더 안 좋은 방식으로 바뀌는 NAT 구현들이 존재한다. NAT Check는 사용자에게 두 개 혹은 그 이상의 클라이언트 호스트를 NAT 뒤에서 동시에 돌릴 것을 요구해야만 이런 방식을 감지할 수 있다. 하지만 NAT뒤에 하나의 사용 가능한 PC만 가지고 있는 사용자에게는 불가능한 일이다. 그럼에도 불구하고, 우리는 이 실험 기능을 미래의 NAT Check 버전에 옵션으로 구현할 계획을 하고 있다.
6.4 Corroboration of Results
위에서 나온 것과 같은 테스트의 어려움에도 불구하고, 우리의 결과들은 전체적으로 큰 ISP에 의해서 보강되었다. 이 ISP는 그들의 네트워크에 86%의 NAT를 차지하고 있는 3개의 라우터 NAT 벤더들이 모두 UDP 홀 펀칭과 호환이 되는 NAT들을 생산하고 있다는 것을 발견했다 [25]. UDP 종속적인 STUN 프로토콜 [12]과 이것의 TCP 확장 버전인 STUNT [8,9] 를 사용하여 추가로 얻어진 독립적인 결과들 또한 우리의 결과와 일치하는 것으로 드러났다. 이런 후기 연구들은 NAT Check가 하듯이 단순히 기본적인 홀 펀칭 호환성만을 테스트하는 대신에, 보다 폭 넓게 다양한 방식들을 하나하나 테스트하여, 각각의 NAT에 대해 보다 많은 정보를 제공한다. 하지만 이렇게 확장된 테스트들은 다수의 클라이언트들이 NAT뒤에서 협력해야 하고 따라서 실행되기가 어렵기 때문에, 현재까지는 보다 제한된 NAT들에 대해서만 이 결과들이 유효하다.
7. Related Work
UDP 홀 펀칭은 최초로 Dan Kegel [13] 에 의해서 조사되고 공개적으로 문서화되었으며 이는 지금까지 P2P 어플리케이션 커뮤니티에 널리 알려져 있다. UDP 홀 펀칭의 중요한 면들 역시 여러 실험적인 프로토콜들의 명세서에서 간접적으로 문서화가 되어왔다. 이런 프로토콜들로는 STUN[19], ICE [17] 그리고 Teredo [11] 등이 있다. 하지만 우리는 홀 펀칭을 완전히 분석하거나 다중 레벨 NAT (섹션 3.5)에서의 hairpin 변환 문제 등을 지적하는 공개된 작업 물은 지금까지 없는 것으로 안다.
또한 우리 이전에 TCP 홀 펀칭을 여기서 설명한 것과 같이 symmetric 방식으로 개발한 작업 물도 없는 것으로 알고 있다. 심지어 버클리 소켓 API에 있는 SO_REUSEADDR/SO_REUSEPORT 옵션들의 중요성조차도 P2P 어플리케이션 개발자들 사이에서 거의 알려지지 않았다. NatTrav [4] 가 비슷하지만 이는 앞서 섹션 4.5에서 언급한 asymmetric TCP 홀 펀칭을 구현했다. NUTSS [9] 과 NATBLASTER [1] 는 섹션 5에서 언급 된 나쁜 NAT 작동 방식에 대해서도 동작이 되는 보다 복잡한 TCP 홀 펀칭을 구현했다. 하지만 이들은 랑데뷰 서버가 소스 IP 주소들을 속여야 하며 또한 클라이언트 어플리케이션들이 "생짜(raw)" 소켓들을 만져야 한다. 이는 대개 오직 루트나 관리자의 특권이 있어야 가능한 것이다.
SOCKS [14], UPnP [26], 그리고 MIDCOM [22] 과 같은 프로토콜들은 명시적으로 NAT와 협동을 통해서 어플리케이션들이 NAT를 선회할 수 있게 해준다. 하지만 이 프로토콜들은 NAT 벤더들에 의해 일관적 혹은 널리 지원되지 않고 있다. 또한 증가하고 있는 다중 레벨 NAT 경우를 해결하는 것으로 보이지 않는다. 명시적으로 NAT를 조정하는 것은 어플리케이션에게 NAT를 찾고, 아마 스스로 인증 할 것을 요구할 것이다. 이는 대개 사용자 구성과 연관되어있다. 반대로 홀 펀칭이 작동하는 경우에는, 아무런 사용자 간섭 없이 작동한다.
HIP [15] 과 FARA [2] 같은 최근의 제안들은 호스트의 주체성(identity)과 위치의 연관성을 끊는 방식으로 인터넷의 기본 구조를 확장한다 [20]. IPNL [7], UIP [5,6] 그리고 DOA [27] 는 이런 식의 구조에서 NAT를 가로지르는 방식을 제안한다. 길게 보면 아마 이런 확장성들이 필요할 것이지만, 홀 펀칭은 지금 현존하는 네트워크 기반에서 어떤 프로토콜 스택의 업그레이드 없이 어플리케이션이 작동하게 해준다. 그리고 어플리케이션이 정의하는 "호스트 주체성(identity)"이라는 개념을 남겨둔다.
8. Conclusion
홀 펀칭은 NAT가 존재할 때 P2P 연결을 구성하는 다목적의 기술이다. 연관된 NAT가 요구되는 몇 개의 방식만 따라준다면, 홀 펀칭은 TCP와 UDP 모두의 경우에 일관적이고 튼실하게 작동한다. 그리고 어떤 특별한 권한이나 특수한 네트워크 망과 상관없이 일반적인 어플리케이션들에 의해 구현될 수 있다. 홀 펀칭은 완전한 투명성을 유지하는데 이는 가장 중요한 NAT의 특징과 장점들 중에 하나다. 또한 다중 레벨의 NAT에서도 작동한다. (비록 몇몇 문제 상황이 아직 널리 구현되어있지 않은 NAT의 특성인 hairpin 변환을 요구하지만.)
Acknowledgments
우리 작가들은 섹션 6에 나오는 결과들을 모으는데 중요한 지원을 해준 Dave Andersen에게 감사를 전한다. 또한 Henrik Nordstrom, Christian Huitema, Justin Uberti, Mema Roussopoulos 그리고 익명의 USENIX 리뷰어들에게도 이 문서의 초본에 대해 값진 피드백을 준 것에 감사한다. 끝으로 NAT Check를 사용해서 우리에게 결과를 보내준 많은 지원자들 모두에게 감사한다.
Bibliography
1. Andrew Biggadike, Daniel Ferullo, Geoffrey Wilson, and Adrian Perrig.
NATBLASTER: Establishing TCP connections between hosts behind NATs.
In ACM SIGCOMM Asia Workshop, Beijing, China, April 2005.
2. David Clark, Robert Braden, Aaron Falk, and Venkata Pingali.
FARA: Reorganizing the addressing architecture.
In ACM SIGCOMM FDNA Workshop, August 2003.
3. S. Deering and R. Hinden.
Internet protocol, version 6 (IPv6) specification, December 1998.
RFC 2460.
4. Jeffrey L. Eppinger.
TCP connections for P2P apps: A software approach to solving the NAT problem.
Technical Report CMU-ISRI-05-104, Carnegie Mellon University, January 2005.
5. Bryan Ford.
Scalable Internet routing on topology-independent node identities.
Technical Report MIT-LCS-TR-926, MIT Laboratory for Computer Science, October 2003.
6. Bryan Ford.
Unmanaged internet protocol: Taming the edge network management crisis.
In Second Workshop on Hot Topics in Networks, Cambridge, MA, November 2003.
7. Paul Francis and Ramakrishna Gummadi.
IPNL: A NAT-extended Internet architecture.
In ACM SIGCOMM, August 2002.
8. Saikat Guha and Paul Francis.
Simple traversal of UDP through NATs and TCP too (STUNT).
http://nutss.gforge.cis.cornell.edu/.
9. Saikat Guha, Yutaka Takeday, and Paul Francis.
NUTSS: A SIP-based approach to UDP and TCP network connectivity.
In SIGCOMM 2004 Workshops, August 2004.
10. M. Holdrege and P. Srisuresh.
Protocol complications with the IP network address translator, January 2001.
RFC 3027.
11. C. Huitema.
Teredo: Tunneling IPv6 over UDP through NATs, March 2004.
Internet-Draft (Work in Progress).
12. C. Jennings.
NAT classification results using STUN, October 2004.
Internet-Draft (Work in Progress).
13. Dan Kegel.
NAT and peer-to-peer networking, July 1999.
http://www.alumni.caltech.edu/~dank/peer-nat.html.
14. M. Leech et al.
SOCKS protocol, March 1996.
RFC 1928.
15. R. Moskowitz and P. Nikander.
Host identity protocol architecture, April 2003.
Internet-Draft (Work in Progress).
16. NAT check.
http://midcom-p2p.sourceforge.net/.
17. J. Rosenberg.
Interactive connectivity establishment (ICE), October 2003.
Internet-Draft (Work in Progress).
18. J. Rosenberg, C. Huitema, and R. Mahy.
Traversal using relay NAT (TURN), October 2003.
Internet-Draft (Work in Progress).
19. J. Rosenberg, J. Weinberger, C. Huitema, and R. Mahy.
STUN - simple traversal of user datagram protocol (UDP) through network address translators (NATs), March 2003.
RFC 3489.
20. J. Saltzer.
On the naming and binding of network destinations.
In P. Ravasio et al., editor, Local Computer Networks, pages 311-317. North-Holland, Amsterdam, 1982.
RFC 1498.
21. P. Srisuresh and M. Holdrege.
IP network address translator (NAT) terminology and considerations, August 1999.
RFC 2663.
22. P. Srisuresh, J. Kuthan, J. Rosenberg, A. Molitor, and A. Rayhan.
Middlebox communication architecture and framework, August 2002.
RFC 3303.
23. Transmission control protocol, September 1981.
RFC 793.
24. G. Tsirtsis and P. Srisuresh.
Network address translation - protocol translation (NAT-PT), February 2000.
RFC 2766.
25. Justin Uberti.
E-mail on IETF MIDCOM mailing list, February 2004.
Message-ID: <402CEB11.1060906@aol.com>.
26. UPnP Forum.
Internet gateway device (IGD) standardized device control protocol, November 2001.
http://www.upnp.org/.
27. Michael Walfish, Jeremy Stribling, Maxwell Krohn, Hari Balakrishnan, Robert Morris, and Scott Shenker.
Middleboxes no longer considered harmful.
In USENIX Symposium on Operating Systems Design and Implementation, San Francisco, CA, December 2004.
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
'컴퓨터' 카테고리의 다른 글
N2T (Notion2Tistory) 이용하면서 YouTube 영상 포함시키기 (3) | 2023.12.06 |
---|---|
N2T (Notion2Tistory) 이용하여 블로그 포스팅 하기 (1) | 2023.12.06 |
2019년도 정부 인공지능 R&D 사업 통합설명회에 다녀왔습니다. (0) | 2019.01.10 |
[스크랩] ISO 국가 코드 목록 (0) | 2018.09.07 |
Notepad++ 맞춤법 검사 기능 (0) | 2018.01.11 |