Search

'분류 전체보기'에 해당되는 글 113건

  1. 2011.08.31 Hole Punching
  2. 2011.08.31 Hole Puching
  3. 2011.08.30 AVI 파일 다루기
  4. 2011.08.25 정적 라이브러리에서 MFC 사용

Hole Punching

Program RFC/Algorithm 2011. 8. 31. 15:35 Posted by HisPark

Peer-to-Peer Communication Across Network Address Translators

Bryan Ford
Massachusetts Institute of Technology
baford (at) mit.edu

 

Pyda Srisuresh
Caymas Systems, Inc.
srisuresh (at) yahoo.com

 

Dan Kegel
dank (at) kegel.com

J'fais des trous, des petits trous
toujours des petits trous

- S. Gainsbourg

 

P2P관련된 기술 하나인 펀칭에 대한 아주 좋은 논문을 번역해 봤습니다. 다행히 원작자인 Bryan Ford님이 번역본의 게재를 허락해주셨습니다. 하지만 번역을 너무 믿지 마시고 원문과 함께 읽기를 권해드립니다. 오역된 부분을 발견하시면 이메일로 알려주시길 부탁드립니다.

 

번역자 - 리안 (양영욱)
youngwook.yang (at) gmail.com

http://young2write.wordpress.com/

 

저작물은 크리에이티브 커먼즈 저작자표시-비영리-변경금지 2.0 대한민국 라이선스에 따라 이용할 있습니다. 이용허락조건을 보려면, http://creativecommons.org/licenses/by-nc-nd/2.0/kr/ 클릭하거나, 크리에이티브 커먼즈 코리아에 문의하세요.


원문 : http://www.brynosaurus.com/pub/net/p2pnat/

Abstract:

네트워크 주소 변환 (NAT) P2P통신에 있어서 알려진 어려움들을 야기한다. 이는 관련 Peer들이 임의의 global IP주소와 연결되지 않을 수도 있기 때문이다. 몇몇 NAT 선회 기법들이 알려져 있으나 문서화가 미미하고 또한 그들의 확실성, 상대적인 장점등에 관한 데이터는 더욱 불충분하다. 문서는 이들 기법중 가장 간단하지만 가장 확실하고 실용적인 NAT 선회 기법, " 펀칭" 분석한다. 펀칭은 UDP 통신에 대해서는 어느 정도 이해되어져 있지만, 우리는 이것이 어떻게 TCP 통신에서도 믿을만 하게 이용되는지 살펴 이다. 기법의 신뢰도 데이터를 다양한 NAT들을 상대로 하여 테스트한 결과, 해당 NAT 82% UDP 대한 펀칭을 지원하고 64% TCP 대한 펀칭을 지원했다. NAT 벤더들이 점점 Voice over IP 온라인 게임 프로토콜과 같은 중요한 P2P 어플리케이션의 필요를 알아감에 따라 미래에는 펀칭에 대한 지원이 증가하게 것이다.

 

1. Introduction

인터넷의 거대한 성장과 방대한 보안 과제들이 결합되어 많은 어플리케이션들의 삶을 힘들게 많드는 방향으로 인터넷이 진화하게끔 해왔다. 인터넷 본래의 유니폼 주소 체계, 다시 말해 모든 노드들이 고유의 global IP주소를 가지고 다른 노드들과 직접 통신을 하는 방식은 사실상 지금 현존하는 주소 체계로 바뀌게 되었다. 지금 방식은 global 주소들로 이루어진 세계와 private 주소들로 이루어진 세계들이 네트워크 주소 변환 (NAT) 통하여 연결되는 주소 체계이다. 새로운 주소 체계에서는 그림 1에서 보여지는 바와 같이 오직 "main" 있는 노드들, global 주소 세계에 있는 노드들 만이 네트워크 상의 다른 노드들과 쉽게 연결할 있다. 오직 이들만이 global하게 전달 가능한(routable) IP 주소들을 가지기 때문이다. Private 세계에 있는 노드들은 같은 네트워크 안에 있는 노드들과 연결을 있으며, global주소 세계에 사는 " 알려진" 노드와 TCP 혹은 UDP 연결을 있다. 과정에서 NAT들은 안에서 밖으로 향하는 연결에 대하여 임시적으로 public 종점을 할당하고 세션을 구성하는 패킷들에 있는 주소와 포트를 변환한다. 이와 달리, 일반적으로 밖에서 안으로 향하는 모든 트래픽에 대해선 특별한 설정이 되어있지 않는 차단한다.

Figure 1: Public and private IP address domains

방식은 클라이언트가 private 네트워크에 있고 서버가 global 세계에 존재하는 전형적인 클라이언트/서버 통신에 적합하다. 반면에, 텔레컨퍼런싱이나 온라인 게임에 사용되는 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세션에 포트 310000 자신의 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 주소로 부터 해당되는 pubilc 시작 종점인 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 "semin-public" 주소, 10.0.1.2:45000 으로 보내는 방식일 것이다. 하지만 불행히도, A B 주소를 방법은 없다. 왜냐하면 서버 S 오직 클라이언트의 진정한 전역 public 주소만 보기 때문이다. 또한 A B 주소들을 어떻게든 알았다 하여도 이들이 사용가능 할지는 미지수이다. 이는 ISP prviate 주소 세계의 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 publc 종점을 향해서 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 비동기 coonect() 반드시 어떤 시점에서 대개 "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 받은 것이다. 피어들의 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)BS 연결을 닫고 자신의 로컬 포트에 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 헤어핀 변환(섹션 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 테스트, 보고 에러등이 이런 요소들이다.

   

UDP

TCP

   

Hole

 

Hole

 
   

Punching

Hairpin

Punching

Hairpin

NAT Hardware

               
 

Linksys

45/46

(98%)

5/42

(12%)

33/38

(87%)

3/38

(8%)

 

Netgear

31/37

(84%)

3/35

(9%)

19/30

(63%)

0/30

(0%)

 

D-Link

16/21

(76%)

11/21

(52%)

9/19

(47%)

2/19

(11%)

 

Draytek

2/17

(12%)

3/12

(25%)

2/7

(29%)

0/7

(0%)

 

Belkin

14/14

(100%)

1/14

(7%)

11/11

(100%)

0/11

(0%)

 

Cisco

12/12

(100%)

3/9

(33%)

6/7

(86%)

2/7

(29%)

 

SMC

12/12

(100%)

3/10

(30%)

8/9

(89%)

2/9

(22%)

 

ZyXEL

7/9

(78%)

1/8

(13%)

0/7

(0%)

0/7

(0%)

 

3Com

7/7

(100%)

1/7

(14%)

5/6

(83%)

0/6

(0%)

OS-based NAT

               
 

Windows

31/33

(94%)

11/32

(34%)

16/31

(52%)

28/31

(90%)

 

Linux

26/32

(81%)

3/25

(12%)

16/24

(67%)

2/24

(8%)

 

FreeBSD

7/9

(78%)

3/6

(50%)

2/3

(67%)

1/1

(100%)

All Vendors

310/380

(82%)

80/335

(24%)

184/286

(64%)

37/286

(13%)

 

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.



Edit this page (if you have permission) | Report abuse

Google Docs -- Web word processing, presentations and spreadsheets.

'Program RFC/Algorithm' 카테고리의 다른 글

홀펀칭 화상통화...등등  (0) 2012.01.04
RTCP-SR 에서 ntp timestamp  (0) 2011.11.14
Hole Puching  (0) 2011.08.31

Hole Puching

Program RFC/Algorithm 2011. 8. 31. 15:30 Posted by HisPark

Peer-to-Peer Communication Across Network Address Translators

Bryan Ford
Massachusetts Institute of Technology
baford (at) mit.edu

Pyda Srisuresh
Caymas Systems, Inc.
srisuresh (at) yahoo.com

Dan Kegel
dank (at) kegel.com

J'fais des trous, des petits trous
toujours des petits trous

- S. Gainsbourg

Abstract:

Network Address Translation (NAT) causes well-known difficulties for peer-to-peer (P2P) communication, since the peers involved may not be reachable at any globally valid IP address. Several NAT traversal techniques are known, but their documentation is slim, and data about their robustness or relative merits is slimmer. This paper documents and analyzes one of the simplest but most robust and practical NAT traversal techniques, commonly known as "hole punching." Hole punching is moderately well-understood for UDP communication, but we show how it can be reliably used to set up peer-to-peer TCP streams as well. After gathering data on the reliability of this technique on a wide variety of deployed NATs, we find that about 82% of the NATs tested support hole punching for UDP, and about 64% support hole punching for TCP streams. As NAT vendors become increasingly conscious of the needs of important P2P applications such as Voice over IP and online gaming protocols, support for hole punching is likely to increase in the future.

1 Introduction

The combined pressures of tremendous growth and massive security challenges have forced the Internet to evolve in ways that make life difficult for many applications. The Internet's original uniform address architecture, in which every node has a globally unique IP address and can communicate directly with every other node, has been replaced with a new de facto Internet address architecture, consisting of a global address realm and many private address realms interconnected by Network Address Translators (NAT). In this new address architecture, illustrated in Figure 1, only nodes in the "main," global address realm can be easily contacted from anywhere in the network, because only they have unique, globally routable IP addresses. Nodes on private networks can connect to other nodes on the same private network, and they can usually open TCP or UDP connections to "well-known" nodes in the global address realm. NATs on the path allocate temporary public endpoints for outgoing connections, and translate the addresses and port numbers in packets comprising those sessions, while generally blocking all incoming traffic unless otherwise specifically configured.

Figure 1: Public and private IP address domains

The Internet's new de facto address architecture is suitable for client/server communication in the typical case when the client is on a private network and the server is in the global address realm. The architecture makes it difficult for two nodes on different private networks to contact each other directly, however, which is often important to the "peer-to-peer" communication protocols used in applications such as teleconferencing and online gaming. We clearly need a way to make such protocols function smoothly in the presence of NAT.

One of the most effective methods of establishing peer-to-peer communication between hosts on different private networks is known as "hole punching." This technique is widely used already in UDP-based applications, but essentially the same technique also works for TCP. Contrary to what its name may suggest, hole punching does not compromise the security of a private network. Instead, hole punching enables applications to function within the the default security policy of most NATs, effectively signaling to NATs on the path that peer-to-peer communication sessions are "solicited" and thus should be accepted. This paper documents hole punching for both UDP and TCP, and details the crucial aspects of both application and NAT behavior that make hole punching work.

Unfortunately, no traversal technique works with all existing NATs, because NAT behavior is not standardized. This paper presents some experimental results evaluating hole punching support in current NATs. Our data is derived from results submitted by users throughout the Internet by running our "NAT Check" tool over a wide variety of NATs by different vendors. While the data points were gathered from a "self-selecting" user community and may not be representative of the true distribution of NAT implementations deployed on the Internet, the results are nevertheless generally encouraging.

While evaluating basic hole punching, we also point out variations that can make hole punching work on a wider variety of existing NATs at the cost of greater complexity. Our primary focus, however, is on developing the simplest hole punching technique that works cleanly and robustly in the presence of "well-behaved" NATs in any reasonable network topology. We deliberately avoid excessively clever tricks that may increase compatibility with some existing "broken" NATs in the short term, but which only work some of the time and may cause additional unpredictability and network brittleness in the long term.

Although the larger address space of IPv6 [3] may eventually reduce the need for NAT, in the short term IPv6 is increasing the demand for NAT, because NAT itself provides the easiest way to achieve interoperability between IPv4 and IPv6 address domains [24]. Further, the anonymity and inaccessibility of hosts on private networks has widely perceived security and privacy benefits. Firewalls are unlikely to go away even when there are enough IP addresses: IPv6 firewalls will still commonly block unsolicited incoming traffic by default, making hole punching useful even to IPv6 applications.

The rest of this paper is organized as follows. Section 2 introduces basic terminology and NAT traversal concepts. Section 3 details hole punching for UDP, and Section 4 introduces hole punching for TCP. Section 5 summarizes important properties a NAT must have in order to enable hole punching. Section 6 presents our experimental results on hole punching support in popular NATs, Section 7 discusses related work, and Section 8 concludes.

2 General Concepts

This section introduces basic NAT terminology used throughout the paper, and then outlines general NAT traversal techniques that apply equally to TCP and UDP.

2.1 NAT Terminology

This paper adopts the NAT terminology and taxonomy defined in RFC 2663 [21], as well as additional terms defined more recently in RFC 3489 [19].

Of particular importance is the notion of session. A session endpoint for TCP or UDP is an (IP address, port number) pair, and a particular session is uniquely identified by its two session endpoints. From the perspective of one of the hosts involved, a session is effectively identified by the 4-tuple (local IP, local port, remote IP, remote port). The direction of a session is normally the flow direction of the packet that initiates the session: the initial SYN packet for TCP, or the first user datagram for UDP.

Of the various flavors of NAT, the most common type is traditional or outbound NAT, which provides an asymmetric bridge between a private network and a public network. Outbound NAT by default allows only outbound sessions to traverse the NAT: incoming packets are dropped unless the NAT identifies them as being part of an existing session initiated from within the private network. Outbound NAT conflicts with peer-to-peer protocols because when both peers desiring to communicate are "behind" (on the private network side of) two different NATs, whichever peer tries to initiate a session, the other peer's NAT rejects it. NAT traversal entails making P2P sessions look like "outbound" sessions to both NATs.

Outbound NAT has two sub-varieties: Basic NAT, which only translates IP addresses, and Network Address/Port Translation (NAPT), which translates entire session endpoints. NAPT, the more general variety, has also become the most common because it enables the hosts on a private network to share the use of a single public IP address. Throughout this paper we assume NAPT, though the principles and techniques we discuss apply equally well (if sometimes trivially) to Basic NAT.

2.2 Relaying

The most reliable--but least efficient--method of P2P communication across NAT is simply to make the communication look to the network like standard client/server communication, through relaying. Suppose two client hosts and have each initiated TCP or UDP connections to a well-known server , at 's global IP address 18.181.0.31 and port number 1234. As shown in Figure 2, the clients reside on separate private networks, and their respective NATs prevent either client from directly initiating a connection to the other. Instead of attempting a direct connection, the two clients can simply use the server to relay messages between them. For example, to send a message to client , client simply sends the message to server along its already-established client/server connection, and server forwards the message on to client using its existing client/server connection with .

Figure 2: NAT Traversal by Relaying

Relaying always works as long as both clients can connect to the server. Its disadvantages are that it consumes the server's processing power and network bandwidth, and communication latency between the peering clients is likely increased even if the server is well-connected. Nevertheless, since there is no more efficient technique that works reliably on all existing NATs, relaying is a useful fall-back strategy if maximum robustness is desired. The TURN protocol [18] defines a method of implementing relaying in a relatively secure fashion.

2.3 Connection Reversal

Some P2P applications use a straightforward but limited technique, known as connection reversal, to enable communication when both hosts have connections to a well-known rendezvous server and only one of the peers is behind a NAT, as shown in Figure 3. If wants to initiate a connection to , then a direct connection attempt works automatically, because is not behind a NAT and 's NAT interprets the connection as an outgoing session. If wants to initiate a connection to , however, any direct connection attempt to is blocked by 's NAT. can instead relay a connection request to through a well-known server , asking to attempt a "reverse" connection back to . Despite the obvious limitations of this technique, the central idea of using a well-known rendezvous server as an intermediary to help set up direct peer-to-peer connections is fundamental to the more general hole punching techniques described next.

Figure 3: NAT Traversal by Connection Reversal

3 UDP Hole Punching

UDP hole punching enables two clients to set up a direct peer-to-peer UDP session with the help of a well-known rendezvous server, even if the clients are both behind NATs. This technique was mentioned in section 5.1 of RFC 3027 [10], documented more thoroughly elsewhere on the Web [13], and used in recent experimental Internet protocols [17,11]. Various proprietary protocols, such as those for on-line gaming, also use UDP hole punching.

3.1 The Rendezvous Server

Hole punching assumes that the two clients, and , already have active UDP sessions with a rendezvous server . When a client registers with , the server records two endpoints for that client: the (IP address, UDP port) pair that the client believes itself to be using to talk with , and the (IP address, UDP port) pair that the server observes the client to be using to talk with it. We refer to the first pair as the client's private endpoint and the second as the client's public endpoint. The server might obtain the client's private endpoint from the client itself in a field in the body of the client's registration message, and obtain the client's public endpoint from the source IP address and source UDP port fields in the IP and UDP headers of that registration message. If the client is not behind a NAT, then its private and public endpoints should be identical.

A few poorly behaved NATs are known to scan the body of UDP datagrams for 4-byte fields that look like IP addresses, and translate them as they would the IP address fields in the IP header. To be robust against such behavior, applications may wish to obfuscate IP addresses in messages bodies slightly, for example by transmitting the one's complement of the IP address instead of the IP address itself. Of course, if the application is encrypting its messages, then this behavior is not likely to be a problem.

3.2 Establishing Peer-to-Peer Sessions

Suppose client wants to establish a UDP session directly with client . Hole punching proceeds as follows:

  1. initially does not know how to reach , so asks for help establishing a UDP session with .
  2. replies to with a message containing 's public and private endpoints. At the same time, uses its UDP session with to send a connection request message containing 's public and private endpoints. Once these messages are received, and know each other's public and private endpoints.
  3. When receives 's public and private endpoints from , starts sending UDP packets to both of these endpoints, and subsequently "locks in" whichever endpoint first elicits a valid response from . Similarly, when receives 's public and private endpoints in the forwarded connection request, starts sending UDP packets to at each of 's known endpoints, locking in the first endpoint that works. The order and timing of these messages are not critical as long as they are asynchronous.

We now consider how UDP hole punching handles each of three specific network scenarios. In the first situation, representing the "easy" case, the two clients actually reside behind the same NAT, on one private network. In the second, most common case, the clients reside behind different NATs. In the third scenario, the clients each reside behind two levels of NAT: a common "first-level" NAT deployed by an ISP for example, and distinct "second-level" NATs such as consumer NAT routers for home networks.

It is in general difficult or impossible for the application itself to determine the exact physical layout of the network, and thus which of these scenarios (or the many other possible ones) actually applies at a given time. Protocols such as STUN [19] can provide some information about the NATs present on a communication path, but this information may not always be complete or reliable, especially when multiple levels of NAT are involved. Nevertheless, hole punching works automatically in all of these scenarios without the application having to know the specific network organization, as long as the NATs involved behave in a reasonable fashion. ("Reasonable" behavior for NATs will be described later in Section 5.)

3.3 Peers Behind a Common NAT

First consider the simple scenario in which the two clients (probably unknowingly) happen to reside behind the same NAT, and are therefore located in the same private IP address realm, as shown in Figure 4. Client has established a UDP session with server , to which the common NAT has assigned its own public port number 62000. Client has similarly established a session with , to which the NAT has assigned public port number 62005.

Figure 4: UDP Hole Punching, Peers Behind a Common NAT

Suppose that client uses the hole punching technique outlined above to establish a UDP session with , using server as an introducer. Client sends a message requesting a connection to . responds to with 's public and private endpoints, and also forwards 's public and private endpoints to . Both clients then attempt to send UDP datagrams to each other directly at each of these endpoints. The messages directed to the public endpoints may or may not reach their destination, depending on whether or not the NAT supports hairpin translation as described below in Section 3.5. The messages directed at the private endpoints do reach their destinations, however, and since this direct route through the private network is likely to be faster than an indirect route through the NAT anyway, the clients are most likely to select the private endpoints for subsequent regular communication.

By assuming that NATs support hairpin translation, the application might dispense with the complexity of trying private as well as public endpoints, at the cost of making local communication behind a common NAT unnecessarily pass through the NAT. As our results in Section 6 show, however, hairpin translation is still much less common among existing NATs than are other "P2P-friendly" NAT behaviors. For now, therefore, applications may benefit substantially by using both public and private endpoints.

3.4 Peers Behind Different NATs

Suppose clients and have private IP addresses behind different NATs, as shown in Figure 5. and have each initiated UDP communication sessions from their local port 4321 to port 1234 on server . In handling these outbound sessions, NAT has assigned port 62000 at its own public IP address, 155.99.25.11, for the use of 's session with , and NAT has assigned port 31000 at its IP address, 138.76.29.7, to 's session with .

Figure 5: UDP Hole Punching, Peers Behind Different NATs

In 's registration message to , reports its private endpoint to as 10.0.0.1:4321, where 10.0.0.1 is 's IP address on its own private network. records 's reported private endpoint, along with 's public endpoint as observed by itself. 's public endpoint in this case is 155.99.25.11:62000, the temporary endpoint assigned to the session by the NAT. Similarly, when client registers, records 's private endpoint as 10.1.1.3:4321 and 's public endpoint as 138.76.29.7:31000.

Now client follows the hole punching procedure described above to establish a UDP communication session directly with . First, sends a request message to asking for help connecting with . In response, sends 's public and private endpoints to , and sends 's public and private endpoints to . and each start trying to send UDP datagrams directly to each of these endpoints.

Since and are on different private networks and their respective private IP addresses are not globally routable, the messages sent to these endpoints will reach either the wrong host or no host at all. Because many NATs also act as DHCP servers, handing out IP addresses in a fairly deterministic way from a private address pool usually determined by the NAT vendor by default, it is quite likely in practice that 's messages directed at 's private endpoint will reach some (incorrect) host on 's private network that happens to have the same private IP address as does. Applications must therefore authenticate all messages in some way to filter out such stray traffic robustly. The messages might include application-specific names or cryptographic tokens, for example, or at least a random nonce pre-arranged through .

Now consider 's first message sent to 's public endpoint, as shown in Figure 5. As this outbound message passes through 's NAT, this NAT notices that this is the first UDP packet in a new outgoing session. The new session's source endpoint (10.0.0.1:4321) is the same as that of the existing session between and , but its destination endpoint is different. If NAT is well-behaved, it preserves the identity of 's private endpoint, consistently translating all outbound sessions from private source endpoint 10.0.0.1:4321 to the corresponding public source endpoint 155.99.25.11:62000. 's first outgoing message to 's public endpoint thus, in effect, "punches a hole" in 's NAT for a new UDP session identified by the endpoints (10.0.0.1:4321, 138.76.29.7:31000) on 's private network, and by the endpoints (155.99.25.11:62000, 138.76.29.7:31000) on the main Internet.

If 's message to 's public endpoint reaches 's NAT before 's first message to has crossed 's own NAT, then 's NAT may interpret 's inbound message as unsolicited incoming traffic and drop it. 's first message to 's public address, however, similarly opens a hole in 's NAT, for a new UDP session identified by the endpoints (10.1.1.3:4321, 155.99.25.11:62000) on 's private network, and by the endpoints (138.76.29.7:31000, 155.99.25.11:62000) on the Internet. Once the first messages from and have crossed their respective NATs, holes are open in each direction and UDP communication can proceed normally. Once the clients have verified that the public endpoints work, they can stop sending messages to the alternative private endpoints.

3.5 Peers Behind Multiple Levels of NAT

In some topologies involving multiple NAT devices, two clients cannot establish an "optimal" P2P route between them without specific knowledge of the topology. Consider a final scenario, depicted in Figure 6. Suppose NAT is a large industrial NAT deployed by an internet service provider (ISP) to multiplex many customers onto a few public IP addresses, and NATs and are small consumer NAT routers deployed independently by two of the ISP's customers to multiplex their private home networks onto their respective ISP-provided IP addresses. Only server and NAT have globally routable IP addresses; the "public" IP addresses used by NAT and NAT are actually private to the ISP's address realm, while client 's and 's addresses in turn are private to the addressing realms of NAT and NAT , respectively. Each client initiates an outgoing connection to server as before, causing NATs and each to create a single public/private translation, and causing NAT to establish a public/private translation for each session.

Figure 6: UDP Hole Punching, Peers Behind Multiple Levels of NAT

Now suppose and attempt to establish a direct peer-to-peer UDP connection via hole punching. The optimal routing strategy would be for client to send messages to client 's "semi-public" endpoint at NAT , 10.0.1.2:55000 in the ISP's addressing realm, and for client to send messages to 's "semi-public" endpoint at NAT , namely 10.0.1.1:45000. Unfortunately, and have no way to learn these addresses, because server only sees the truly global public endpoints of the clients, 155.99.25.11:62000 and 155.99.25.11:62005 respectively. Even if and had some way to learn these addresses, there is still no guarantee that they would be usable, because the address assignments in the ISP's private address realm might conflict with unrelated address assignments in the clients' private realms. (NAT 's IP address in NAT 's realm might just as easily have been 10.1.1.3, for example, the same as client 's private address in NAT 's realm.)

The clients therefore have no choice but to use their global public addresses as seen by for their P2P communication, and rely on NAT providing hairpin or loopback translation. When sends a UDP datagram to 's global endpoint, 155.99.25.11:62005, NAT first translates the datagram's source endpoint from 10.0.0.1:4321 to 10.0.1.1:45000. The datagram now reaches NAT , which recognizes that the datagram's destination address is one of NAT 's own translated public endpoints. If NAT is well-behaved, it then translates both the source and destination addresses in the datagram and "loops" the datagram back onto the private network, now with a source endpoint of 155.99.25.11:62000 and a destination endpoint of 10.0.1.2:55000. NAT finally translates the datagram's destination address as the datagram enters 's private network, and the datagram reaches . The path back to works similarly. Many NATs do not yet support hairpin translation, but it is becoming more common as NAT vendors become aware of this issue.

3.6 UDP Idle Timeouts

Since the UDP transport protocol provides NATs with no reliable, application-independent way to determine the lifetime of a session crossing the NAT, most NATs simply associate an idle timer with UDP translations, closing the hole if no traffic has used it for some time period. There is unfortunately no standard value for this timer: some NATs have timeouts as short as 20 seconds. If the application needs to keep an idle UDP session active after establishing the session via hole punching, the application must send periodic keep-alive packets to ensure that the relevant translation state in the NATs does not disappear.

Unfortunately, many NATs associate UDP idle timers with individual UDP sessions defined by a particular pair of endpoints, so sending keep-alives on one session will not keep other sessions active even if all the sessions originate from the same private endpoint. Instead of sending keep-alives on many different P2P sessions, applications can avoid excessive keep-alive traffic by detecting when a UDP session no longer works, and re-running the original hole punching procedure again "on demand."

4 TCP Hole Punching

Establishing peer-to-peer TCP connections between hosts behind NATs is slightly more complex than for UDP, but TCP hole punching is remarkably similar at the protocol level. Since it is not as well-understood, it is currently supported by fewer existing NATs. When the NATs involved do support it, however, TCP hole punching is just as fast and reliable as UDP hole punching. Peer-to-peer TCP communication across well-behaved NATs may in fact be more robust than UDP communication, because unlike UDP, the TCP protocol's state machine gives NATs on the path a standard way to determine the precise lifetime of a particular TCP session.

4.1 Sockets and TCP Port Reuse

The main practical challenge to applications wishing to implement TCP hole punching is not a protocol issue but an application programming interface (API) issue. Because the standard Berkeley sockets API was designed around the client/server paradigm, the API allows a TCP stream socket to be used to initiate an outgoing connection via connect(), or to listen for incoming connections via listen() and accept(), but not both. Further, TCP sockets usually have a one-to-one correspondence to TCP port numbers on the local host: after the application binds one socket to a particular local TCP port, attempts to bind a second socket to the same TCP port fail.

For TCP hole punching to work, however, we need to use a single local TCP port to listen for incoming TCP connections and to initiate multiple outgoing TCP connections concurrently. Fortunately, all major operating systems support a special TCP socket option, commonly named SO_REUSEADDR, which allows the application to bind multiple sockets to the same local endpoint as long as this option is set on all of the sockets involved. BSD systems have introduced a SO_REUSEPORT option that controls port reuse separately from address reuse; on such systems both of these options must be set.

4.2 Opening Peer-to-Peer TCP Streams

Suppose that client wishes to set up a TCP connection with client . We assume as usual that both and already have active TCP connections with a well-known rendezvous server . The server records each registered client's public and private endpoints, just as for UDP. At the protocol level, TCP hole punching works almost exactly as for UDP:

  1. Client uses its active TCP session with to ask for help connecting to .
  2. replies to with 's public and private TCP endpoints, and at the same time sends 's public and private endpoints to .
  3. From the same local TCP ports that and used to register with , and each asynchronously make outgoing connection attempts to the other's public and private endpoints as reported by , while simultaneously listening for incoming connections on their respective local TCP ports.
  4. and wait for outgoing connection attempts to succeed, and/or for incoming connections to appear. If one of the outgoing connection attempts fails due to a network error such as "connection reset" or "host unreachable," the host simply re-tries that connection attempt after a short delay (e.g., one second), up to an application-defind maximum timeout period.
  5. When a TCP connection is made, the hosts authenticate each other to verify that they connected to the intended host. If authentication fails, the clients close that connection and continue waiting for others to succeed. The clients use the first successfully authenticated TCP stream resulting from this process.

Unlike with UDP, where each client only needs one socket to communicate with both and any number of peers simultaneously, with TCP each client application must manage several sockets bound to a single local TCP port on that client node, as shown in Figure 7. Each client needs a stream socket representing its connection to , a listen socket on which to accept incoming connections from peers, and at least two additional stream sockets with which to initiate outgoing connections to the other peer's public and private TCP endpoints.

Figure 7: Sockets versus Ports for TCP Hole Punching

Consider the common-case scenario in which the clients and are behind different NATs, as shown in Figure 5, and assume that the port numbers shown in the figure are now for TCP rather than UDP ports. The outgoing connection attempts and make to each other's private endpoints either fail or connect to the wrong host. As with UDP, it is important that TCP applications authenticate their peer-to-peer sessions, due of the likelihood of mistakenly connecting to a random host on the local network that happens to have the same private IP address as the desired host on a remote private network.

The clients' outgoing connection attempts to each other's public endpoints, however, cause the respective NATs to open up new "holes" enabling direct TCP communication between and . If the NATs are well-behaved, then a new peer-to-peer TCP stream automatically forms between them. If 's first SYN packet to reaches 's NAT before 's first SYN packet to reaches 's NAT, for example, then 's NAT may interpret 's SYN as an unsolicited incoming connection attempt and drop it. 's first SYN packet to should subsequently get through, however, because 's NAT sees this SYN as being part of the outbound session to that 's first SYN had already initiated.

4.3 Behavior Observed by the Application

What the client applications observe to happen with their sockets during TCP hole punching depends on the timing and the TCP implementations involved. Suppose that 's first outbound SYN packet to 's public endpoint is dropped by NAT , but 's first subsequent SYN packet to 's public endpoint gets through to before 's TCP retransmits its SYN. Depending on the operating system involved, one of two things may happen:

The first behavior above appears to be usual for BSD-based operating systems, whereas the second behavior appears more common under Linux and Windows.

4.4 Simultaneous TCP Open

Suppose that the timing of the various connection attempts during the hole punching process works out so that the initial outgoing SYN packets from both clients traverse their respective local NATs, opening new outbound TCP sessions in each NAT, before reaching the remote NAT. In this "lucky" case, the NATs do not reject either of the initial SYN packets, and the SYNs cross on the wire between the two NATs. In this case, the clients observe an event known as a simultaneous TCP open: each peer's TCP receives a "raw" SYN while waiting for a SYN-ACK. Each peer's TCP responds with a SYN-ACK, whose SYN part essentially "replays" the peer's previous outgoing SYN, and whose ACK part acknowledges the SYN received from the other peer.

What the respective applications observe in this case again depends on the behavior of the TCP implementations involved, as described in the previous section. If both clients implement the second behavior above, it may be that all of the asynchronous connect() calls made by the application ultimately fail, but the application running on each client nevertheless receives a new, working peer-to-peer TCP stream socket via accept()--as if this TCP stream had magically "created itself" on the wire and was merely passively accepted at the endpoints! As long as the application does not care whether it ultimately receives its peer-to-peer TCP sockets via connect() or accept(), the process results in a working stream on any TCP implementation that properly implements the standard TCP state machine specified in RFC 793 [23].

Each of the alternative network organization scenarios discussed in Section 3 for UDP works in exactly the same way for TCP. For example, TCP hole punching works in multi-level NAT scenarios such as the one in Figure 6 as long as the NATs involved are well-behaved.

4.5 Sequential Hole Punching

In a variant of the above TCP hole punching procedure implemented by the NatTrav library [4], the clients attempt connections to each other sequentially rather than in parallel. For example: (1) informs via of its desire to communicate, without simultaneously listening on its local port; (2) makes a connect() attempt to , which opens a hole in 's NAT but then fails due to a timeout or RST from 's NAT or a RST from itself; (3) closes its connection to and does a listen() on its local port; (4) in turn closes its connection with , signaling to attempt a connect() directly to .

This sequential procedure may be particularly useful on Windows hosts prior to XP Service Pack 2, which did not correctly implement simultaneous TCP open, or on sockets APIs that do not support the SO_REUSEADDR functionality. The sequential procedure is more timing-dependent, however, and may be slower in the common case and less robust in unusual situations. In step (2), for example, must allow its "doomed-to-fail" connect() attempt enough time to ensure that at least one SYN packet traverses all NATs on its side of the network. Too little delay risks a lost SYN derailing the process, whereas too much delay increases the total time required for hole punching. The sequential hole punching procedure also effectively "consumes" both clients' connections to the server , requiring the clients to open fresh connections to for each new P2P connection to be forged. The parallel hole punching procedure, in contrast, typically completes as soon as both clients make their outgoing connect() attempts, and allows each client to retain and re-use a single connection to indefinitely.

5 Properties of P2P-Friendly NATs

This section describes the key behavioral properties NATs must have in order for the hole punching techniques described above to work properly. Not all current NAT implementations satisfy these properties, but many do, and NATs are gradually becoming more "P2P-friendly" as NAT vendors recognize the demand for peer-to-peer protocols such as voice over IP and on-line gaming.

This section is not meant to be a complete or definitive specification for how NATs "should" behave; we provide it merely for information about the most commonly observed behaviors that enable or break P2P hole punching. The IETF has started a new working group, BEHAVE, to define official "best current practices" for NAT behavior. The BEHAVE group's initial drafts include the considerations outlined in this section and others; NAT vendors should of course follow the IETF working group directly as official behavioral standards are formulated.

5.1 Consistent Endpoint Translation

The hole punching techniques described here only work automatically if the NAT consistently maps a given TCP or UDP source endpoint on the private network to a single corresponding public endpoint controlled by the NAT. A NAT that behaves in this way is referred to as a cone NAT in RFC 3489 [19] and elsewhere, because the NAT "focuses" all sessions originating from a single private endpoint through the same public endpoint on the NAT.

Consider again the scenario in Figure 5, for example. When client initially contacted the well-known server , NAT chose to use port 62000 at its own public IP address, 155.99.25.11, as a temporary public endpoint to representing 's private endpoint 10.0.0.1:4321. When later attempts to establish a peer-to-peer session with by sending a message from the same local private endpoint to 's public endpoint, depends on NAT preserving the identity of this private endpoint, and re-using the existing public endpoint of 155.99.25.11:62000, because that is the public endpoint for to which will be sending its corresponding messages.

A NAT that is only designed to support client/server protocols will not necessarily preserve the identities of private endpoints in this way. Such a NAT is a symmetric NAT in RFC 3489 terminology. For example, after the NAT assigns the public endpoint 155.99.25.11:62000 to client 's session with server , the NAT might assign a different public endpoint, such as 155.99.25.11:62001, to the P2P session that tries to initiate with . In this case, the hole punching process fails to provide connectivity, because the subsequent incoming messages from reach NAT at the wrong port number.

Many symmetric NATs allocate port numbers for successive sessions in a fairly predictable way. Exploiting this fact, variants of hole punching algorithms [9,1] can be made to work "much of the time" even over symmetric NATs by first probing the NAT's behavior using a protocol such as STUN [19], and using the resulting information to "predict" the public port number the NAT will assign to a new session. Such prediction techniques amount to chasing a moving target, however, and many things can go wrong along the way. The predicted port number might already be in use causing the NAT to jump to another port number, for example, or another client behind the same NAT might initiate an unrelated session at the wrong time so as to allocate the predicted port number. While port number prediction can be a useful trick for achieving maximum compatibility with badly-behaved existing NATs, it does not represent a robust long-term solution. Since symmetric NAT provides no greater security than a cone NAT with per-session traffic filtering, symmetric NAT is becoming less common as NAT vendors adapt their algorithms to support P2P protocols.

5.2 Handling Unsolicited TCP Connections

When a NAT receives a SYN packet on its public side for what appears to be an unsolicited incoming connection attempt, it is important that the NAT just silently drop the SYN packet. Some NATs instead actively reject such incoming connections by sending back a TCP RST packet or even an ICMP error report, which interferes with the TCP hole punching process. Such behavior is not necessarily fatal, as long as the applications re-try outgoing connection attempts as specified in step 4 of the process described in Section 4.2, but the resulting transient errors can make hole punching take longer.

5.3 Leaving Payloads Alone

A few existing NATs are known to scan "blindly" through packet payloads for 4-byte values that look like IP addresses, and translate them as they would the IP address in the packet header, without knowing anything about the application protocol in use. This bad behavior fortunately appears to be uncommon, and applications can easily protect themselves against it by obfuscating IP addresses they send in messages, for example by sending the bitwise complement of the desired IP address.

5.4 Hairpin Translation

Some multi-level NAT situations require hairpin translation support in order for either TCP or UDP hole punching to work, as described in Section 3.5. The scenario shown in Figure 6, for example, depends on NAT providing hairpin translation. Support for hairpin translation is unfortunately rare in current NATs, but fortunately so are the network scenarios that require it. Multi-level NAT is becoming more common as IPv4 address space depletion continues, however, so support for hairpin translation is important in future NAT implementations.

6 Evaluation of Existing NATs

To evaluate the robustness of the TCP and UDP hole punching techniques described in this paper on a variety of existing NATs, we implemented and distributed a test program called NAT Check [16], and solicited data from Internet users about their NATs.

NAT Check's primary purpose is to test NATs for the two behavioral properties most crucial to reliable UDP and TCP hole punching: namely, consistent identity-preserving endpoint translation (Section 5.1), and silently dropping unsolicited incoming TCP SYNs instead of rejecting them with RSTs or ICMP errors (Section 5.2). In addition, NAT Check separately tests whether the NAT supports hairpin translation (Section 5.4), and whether the NAT filters unsolicited incoming traffic at all. This last property does not affect hole punching, but provides a useful indication the NAT's firewall policy.

NAT Check makes no attempt to test every relevant facet of NAT behavior individually: a wide variety of subtle behavioral differences are known, some of which are difficult to test reliably [12]. Instead, NAT Check merely attempts to answer the question, "how commonly can the proposed hole punching techniques be expected to work on deployed NATs, under typical network conditions?"

6.1 Test Method

NAT Check consists of a client program to be run on a machine behind the NAT to be tested, and three well-known servers at different global IP addresses. The client cooperates with the three servers to check the NAT behavior relevant to both TCP and UDP hole punching. The client program is small and relatively portable, currently running on Windows, Linux, BSD, and Mac OS X. The machines hosting the well-known servers all run FreeBSD.

6.1.1 UDP Test

To test the NAT's behavior for UDP, the client opens a socket and binds it to a local UDP port, then successively sends "ping"-like requests to servers 1 and 2, as shown in Figure 8. These servers each respond to the client's pings with a reply that includes the client's public UDP endpoint: the client's own IP address and UDP port number as observed by the server. If the two servers report the same public endpoint for the client, NAT Check assumes that the NAT properly preserves the identity of the client's private endpoint, satisfying the primary precondition for reliable UDP hole punching.

Figure 8: NAT Check Test Method for UDP

When server 2 receives a UDP request from the client, besides replying directly to the client it also forwards the request to server 3, which in turn replies to the client from its own IP address. If the NAT's firewall properly filters "unsolicited" incoming traffic on a per-session basis, then the client never sees these replies from server 3, even though they are directed at the same public port as the replies from servers 1 and 2.

To test the NAT for hairpin translation support, the client simply opens a second UDP socket at a different local port and uses it to send messages to the public endpoint representing the client's first UDP socket, as reported by server 2. If these messages reach the client's first private endpoint, then the NAT supports hairpin translation.

6.1.2 TCP Test

The TCP test follows a similar pattern as for UDP. The client uses a single local TCP port to initiate outbound sessions to servers 1 and 2, and checks whether the public endpoints reported by servers 1 and 2 are the same, the first precondition for reliable TCP hole punching.

The NAT's response to unsolicited incoming connection attempts also impacts the speed and reliability of TCP hole punching, however, so NAT Check also tests this behavior. When server 2 receives the client's request, instead of immediately replying to the client, it forwards a request to server 3 and waits for server 3 to respond with a "go-ahead" signal. When server 3 receives this forwarded request, it attempts to initiate an inbound connection to the client's public TCP endpoint. Server 3 waits up to five seconds for this connection to succeed or fail, and if the connection attempt is still "in progress" after five seconds, server 3 responds to server 2 with the "go-ahead" signal and continues waiting for up to 20 seconds. Once the client finally receives server 2's reply (which server 2 delayed waiting for server 3's "go-ahead" signal), the client attempts an outbound connection to server 3, effectively causing a simultaneous TCP open with server 3.

What happens during this test depends on the NAT's behavior as follows. If the NAT properly just drops server 3's "unsolicited" incoming SYN packets, then nothing happens on the client's listen socket during the five second period before server 2 replies to the client. When the client finally initiates its own connection to server 3, opening a hole through the NAT, the attempt succeeds immediately. If on the other hand the NAT does not drop server 3's unsolicited incoming SYNs but allows them through (which is fine for hole punching but not ideal for security), then the client receives an incoming TCP connection on its listen socket before receiving server 2's reply. Finally, if the NAT actively rejects server 3's unsolicited incoming SYNs by sending back TCP RST packets, then server 3 gives up and the client's subsequent attempt to connect to server 3 fails.

To test hairpin translation for TCP, the client simply uses a secondary local TCP port to attempt a connection to the public endpoint corresponding to its primary TCP port, in the same way as for UDP.

6.2 Test Results

The NAT Check data we gathered consists of 380 reported data points covering a variety of NAT router hardware from 68 vendors, as well as the NAT functionality built into different versions of eight popular operating systems. Only 335 of the total data points include results for UDP hairpin translation, and only 286 data points include results for TCP, because we implemented these features in later versions of NAT Check after we had already started gathering results. The data is summarized by NAT vendor in Table 1; the table only individually lists vendors for which at least five data points were available. The variations in the test results for a given vendor can be accounted for by a variety of factors, such as different NAT devices or product lines sold by the same vendor, different software or firmware versions of the same NAT implementation, different configurations, and probably occasional NAT Check testing or reporting errors.

 

Table 1: User Reports of NAT Support for UDP and TCP Hole Punching

  

UDP

TCP

  

Hole

 

Hole

 
  

Punching

Hairpin

Punching

Hairpin

NAT Hardware

        
 

Linksys

45/46

(98%)

5/42

(12%)

33/38

(87%)

3/38

(8%)

 

Netgear

31/37

(84%)

3/35

(9%)

19/30

(63%)

0/30

(0%)

 

D-Link

16/21

(76%)

11/21

(52%)

9/19

(47%)

2/19

(11%)

 

Draytek

2/17

(12%)

3/12

(25%)

2/7

(29%)

0/7

(0%)

 

Belkin

14/14

(100%)

1/14

(7%)

11/11

(100%)

0/11

(0%)

 

Cisco

12/12

(100%)

3/9

(33%)

6/7

(86%)

2/7

(29%)

 

SMC

12/12

(100%)

3/10

(30%)

8/9

(89%)

2/9

(22%)

 

ZyXEL

7/9

(78%)

1/8

(13%)

0/7

(0%)

0/7

(0%)

 

3Com

7/7

(100%)

1/7

(14%)

5/6

(83%)

0/6

(0%)

OS-based NAT

        
 

Windows

31/33

(94%)

11/32

(34%)

16/31

(52%)

28/31

(90%)

 

Linux

26/32

(81%)

3/25

(12%)

16/24

(67%)

2/24

(8%)

 

FreeBSD

7/9

(78%)

3/6

(50%)

2/3

(67%)

1/1

(100%)

All Vendors

310/380

(82%)

80/335

(24%)

184/286

(64%)

37/286

(13%)

 

Out of the 380 reported data points for UDP, in 310 cases (82%) the NAT consistently translated the client's private endpoint, indicating basic compatibility with UDP hole punching. Support for hairpin translation is much less common, however: of the 335 data points that include UDP hairpin translation results, only 80 (24%) show hairpin translation support.

Out of the 286 data points for TCP, 184 (64%) show compatibility with TCP hole punching: the NAT consistently translates the client's private TCP endpoint, and does not send back RST packets in response to unsolicited incoming connection attempts. Hairpin translation support is again much less common: only 37 (13%) of the reports showed hairpin support for TCP.

Since these reports were generated by a "self-selecting" community of volunteers, they do not constitute a random sample and thus do not necessarily represent the true distribution of the NATs in common use. The results are nevertheless encouraging: it appears that the majority of commonly-deployed NATs already support UDP and TCP hole punching at least in single-level NAT scenarios.

6.3 Testing Limitations

There are a few limitations in NAT Check's current testing protocol that may cause misleading results in some cases. First, we only learned recently that a few NAT implementations blindly translate IP addresses they find in unknown application payloads, and the NAT Check protocol currently does not protect itself from this behavior by obfuscating the IP addresses it transmits.

Second, NAT Check's current hairpin translation checking may yield unnecessarily pessimistic results because it does not use the full, two-way hole punching procedure for this test. NAT Check currently assumes that a NAT supporting hairpin translation does not filter "incoming" hairpin connections arriving from the private network in the way it would filter incoming connections arriving at the public side of the NAT, because such filtering is unnecessary for security. We later realized, however, that a NAT might simplistically treat any traffic directed at the NAT's public ports as "untrusted" regardless of its origin. We do not yet know which behavior is more common.

Finally, NAT implementations exist that consistently translate the client's private endpoint as long as only one client behind the NAT is using a particular private port number, but switch to symmetric NAT or even worse behaviors if two or more clients with different IP addresses on the private network try to communicate through the NAT from the same private port number. NAT Check could only detect this behavior by requiring the user to run it on two or more client hosts behind the NAT at the same time. Doing so would make NAT Check much more difficult to use, however, and impossible for users who only have one usable machine behind the NAT. Nevertheless, we plan to implement this testing functionality as an option in a future version of NAT Check.

6.4 Corroboration of Results

Despite testing difficulties such as those above, our results are generally corroborated by those of a large ISP, who recently found that of the top three consumer NAT router vendors, representing 86% of the NATs observed on their network, all three vendors currently produce NATs compatible with UDP hole punching [25]. Additional independent results recently obtained using the UDP-oriented STUN protocol [12], and STUNT, a TCP-enabled extension [8,9], also appear consistent with our results. These latter studies provide more information on each NAT by testing a wider variety of behaviors individually, instead of just testing for basic hole punching compatibility as NAT Check does. Since these more extensive tests require multiple cooperating clients behind the NAT and thus are more difficult to run, however, these results are so far available on a more limited variety of NATs.

7 Related Work

UDP hole punching was first explored and publicly documented by Dan Kegel [13], and is by now well-known in peer-to-peer application communities. Important aspects of UDP hole punching have also been indirectly documented in the specifications of several experimental protocols, such as STUN [19], ICE [17], and Teredo [11]. We know of no existing published work that thoroughly analyzes hole punching, however, or that points out the hairpin translation issue for multi-level NAT (Section 3.5).

We also know of no prior work that develops TCP hole punching in the symmetric fashion described here. Even the existence of the crucial SO_REUSEADDR/SO_REUSEPORT options in the Berkeley sockets API appears to be little-known among P2P application developers. NatTrav [4] implements a similar but asymmetric TCP hole punching procedure outlined earlier in Section 4.5. NUTSS [9] and NATBLASTER [1] implement more complex TCP hole punching tricks that can work around some of the bad NAT behaviors mentioned in Section 5, but they require the rendezvous server to spoof source IP addresses, and they also require the client applications to have access to "raw" sockets, usually available only at root or administrator privilege levels.

Protocols such as SOCKS [14], UPnP [26], and MIDCOM [22] allow applications to traverse a NAT through explicit cooperation with the NAT. These protocols are not widely or consistently supported by NAT vendors or applications, however, and do not appear to address the increasingly important multi-level NAT scenarios. Explicit control of a NAT further requires the application to locate the NAT and perhaps authenticate itself, which typically involves explicit user configuration. When hole punching works, in contrast, it works with no user intervention.

Recent proposals such as HIP [15] and FARA [2] extend the Internet's basic architecture by decoupling a host's identity from its location [20]. IPNL [7], UIP [5,6], and DOA [27] propose schemes for routing across NATs in such an architecture. While such extensions are probably needed in the long term, hole punching enables applications to work over the existing network infrastructure immediately with no protocol stack upgrades, and leaves the notion of "host identity" for applications to define.

8 Conclusion

Hole punching is a general-purpose technique for establishing peer-to-peer connections in the presence of NAT. As long as the NATs involved meet certain behavioral requirements, hole punching works consistently and robustly for both TCP and UDP communication, and can be implemented by ordinary applications with no special privileges or specific network topology information. Hole punching fully preserves the transparency that is one of the most important hallmarks and attractions of NAT, and works even with multiple levels of NAT--though certain corner case situations require hairpin translation, a NAT feature not yet widely implemented.

Acknowledgments

The authors wish to thank Dave Andersen for his crucial support in gathering the results presented in Section 6. We also wish to thank Henrik Nordstrom, Christian Huitema, Justin Uberti, Mema Roussopoulos, and the anonymous USENIX reviewers for valuable feedback on early drafts of this paper. Finally, we wish to thank the many volunteers who took the time to run NAT Check on their systems and submit the results.

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.

 

Bryan Ford 2005-02-17

'Program RFC/Algorithm' 카테고리의 다른 글

홀펀칭 화상통화...등등  (0) 2012.01.04
RTCP-SR 에서 ntp timestamp  (0) 2011.11.14
Hole Punching  (0) 2011.08.31

AVI 파일 다루기

Program Visual C++ 2011. 8. 30. 17:18 Posted by HisPark

가장 기본적인 방법만 소개 드리겠습니다.

Video For Windows라는 윈도우즈의 비디오 라이브러리가 있는데

모두 Windows API함수로 되어 있어 쉽게 사용할 있습니다.

 

vfw.h 파일을 인클루드시키고 컴파일한 프로그램을 링크시킬 vfw32.lib

함께 링크하면 됩니다.

 

함수 사용 순서는 다음과 같습니다.

 

AVIFile 라이브러리를 시작시킵니다. [AVIFileInit()]

작업하고자 하는 비디오 파일을 엽니다. [AVIFileOpen()]

비디오 파일의 비디오 스트림을 엽니다. [AVIFileGetStream()]

필요하다면 스트림의 정보를 얻습니다. [AVIStreamInfo()]

프레임 추출 작업을 준비시킵니다. [AVIStreamGetFrameOpen()]

필요한 프레임 데이터를 추출합니다. [AVIStreamGetFrame()]

프레임 추출 작업을 종료합니다. [AVIStreamGetFrameClose()]

열려 있는 스트림을 닫습니다. [AVIStreamRelease()]

열려 있는 파일을 닫습니다. [AVIFileRelease()]

AVIFile 라이브러리를 종료시킵니다. [AVIFileExit()]

 

이와 같이 스트림의 GetFrame기능을 사용하면 프로그래머가 일일이 코덱을 사용해서

압축을 해제하는 일이 필요 없고 시스템에 코덱만 깔려 있으면 알아서 복원해 주니

사용하기가 아주 편리합니다. , 이때 추출된 프레임 영상 데이터는 Packed DIB형태

이므로 여기에 BITMAPFILEHEADER 추가하면 그대로 BMP파일로 저장할 있습니다.

또한 영상 데이터가 DIB라서 직접 픽셀 데이터를 조작하기가 쉬어 여러가지 영상 처리

기법을 구사할 있습니다.

===========================================================================================

Avi 파일을 여는 부분<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

Avi파일을 다루는 함수는 기본적으로 Windows API 제공된다. 프로그램에서는 LoadAviFile()함수가 그런 API 이용하여 Avi파일을 열고 있다. 먼저 AVIFileOpen()함수를 이용해서 파일의 핸들을 얻고 AVIFileInfo()함수를 통해서 헤더에 해당하는 정보를 얻어온다. 여기서 얻어온 헤더정보를 이용해서 Avi파일을 처리하게 된다.

void CAviDialog::LoadAviFile()

{

int ErrorCode;

gcpavi=0;

m_bSearchArea=false;

m_MouseRect=CRect(-1,-1,-1,-1);

// Avi 파일을 개방한다.

ErrorCode = AVIFileOpen(&ppAviFile, AviFilePath, OF_READ, NULL);

if (ErrorCode == AVIERR_BADFORMAT)

MessageBox("AVIERR_BADFORMAT", NULL, MB_OK);

else if (ErrorCode == AVIERR_MEMORY)

MessageBox("AVIERR_MEMORY", NULL, MB_OK);

else if (ErrorCode == AVIERR_FILEREAD)

MessageBox("AVIERR_FILEREAD", NULL, MB_OK);

else if (ErrorCode == AVIERR_FILEOPEN)

MessageBox("AVIERR_FILEOPEN", NULL, MB_OK);

AVIFileInfo(ppAviFile, &pAviInfo, sizeof(pAviInfo));

AviPlayRate = 1000 / (pAviInfo.dwRate / pAviInfo.dwScale);

// Refresh time

m_sFS.Format("%df/s", pAviInfo.dwRate / pAviInfo.dwScale);

UpdateData(FALSE);

// 파일에 포함된 스트림의 종류를 얻는다. audio, midi, text, video

// 파일에 포함된 스트림의 종류만큼 반복한다.

// pavi값을 배열에 저장해야 한다.

for (int i = 0; i <= MAXNUMSTREAMS; i++)

{

if (AVIFileGetStream(ppAviFile, &pavi, 0L, i) != AVIERR_OK)

break;

if (i == MAXNUMSTREAMS)

{

AVIStreamRelease(pavi);

MessageBox("Exceeded maximum number of streams", NULL, MB_OK);

break;

}

gapavi[i] = pavi;

}

gcpavi = i;

for (i = 0; i < gcpavi; i++)

{

// 스트림헤더의 정보를 얻는다.

AVIStreamInfo(gapavi[i], &avis, sizeof(avis));

switch (avis.fccType)

{

case streamtypeVIDEO:

// 전체 프레임 수를 얻는다.

length = AVIStreamLength(gapavi[i]);

// 전체프레임수를 나타낸다.

m_sFrameRate.Format("%d/%d", 1, length);

UpdateData(FALSE);

l= sizeof(format);

// 비트맵 정보를 읽는다.

AVIStreamReadFormat(gapavi[i], AVIStreamStart(gapavi[i]), &format, &l);

bi = (LPBITMAPINFOHEADER)format;

pgf = AVIStreamGetFrameOpen(gapavi[i], NULL);

// BITMAPINFOHEADER 정보를 설정한다.

bi = (LPBITMAPINFOHEADER)format;

// BITMAPFILEHEADER 정보를 설정한다.

bf.bfType = 19778;

// 트루컬러는 RGBQUAD 정보가 없다.

bf.bfSize =

sizeof(BITMAPINFOHEADER) +

sizeof(BITMAPFILEHEADER) +

bi->biWidth * bi->biHeight * 3;

bf.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);

bf.bfReserved1 = 0;

bf.bfReserved2 = 0;

// 현재 나타낼 프레임을 첫번째로 설정한다.

AviFramePosition = 0;

break;

case streamtypeAUDIO:

LPWAVEFORMAT pwf = (LPWAVEFORMAT)abFormat;

l = sizeof(wf);

length = AVIStreamLength(gapavi[i]);

AVIStreamReadFormat(gapavi[i], AVIStreamStart(gapavi[i]), &wf, &l);

/*fp=fopen("sample.pcm", "wb");

for (pos=0; pos<length; pos++)

{

AVIStreamRead(gapavi[i], (long) pos, 1L, lpAudio, sizeof(lpAudio), &s, NULL);

fwrite(lpAudio, s, 1, fp);

}

fclose(fp);*/

break;

}

}

Invalidate();

}

Avi 파일에서 프레임별로 Bmp파일을 추출하는 부분

AVIStreamGetFrame()함수는 원하는 위치의 프레임을 얻어오는 역할을 수행하는 API함수이다. 일단 함수를 통해서 얻어온 프레임은 다시 비트맵의 정보를 가지고 있는 LPBITMAPINFOHEADER구조체를 통해서 비트맵화 된다.

void CAviDialog::GetAviFrame(int AviFramePosition, HDC dc)

{

// 현재 나타낼 이미지의 프레임을 얻는다.

if ((gf = (unsigned char *) AVIStreamGetFrame(pgf, AviFramePosition))==NULL && CameraState==NONACTIVE)

MessageBox("CODEC 컴퓨터에 설치되어 있지 않습니다.", NULL, MB_OK);

else

{

bi = (LPBITMAPINFOHEADER)gf;

bi->biXPelsPerMeter = 0;

bi->biYPelsPerMeter = 0;

bitmapinfo.bmiHeader.biBitCount = bi->biBitCount;

bitmapinfo.bmiHeader.biClrImportant = bi->biClrImportant;

bitmapinfo.bmiHeader.biClrUsed = bi->biClrUsed;

bitmapinfo.bmiHeader.biCompression = bi->biCompression;

bitmapinfo.bmiHeader.biHeight = bi->biHeight;

bitmapinfo.bmiHeader.biWidth = bi->biWidth;

bitmapinfo.bmiHeader.biPlanes = bi->biPlanes;

bitmapinfo.bmiHeader.biSize = bi->biSize;

bitmapinfo.bmiHeader.biSizeImage = bi->biSizeImage;

bitmapinfo.bmiHeader.biXPelsPerMeter = bi->biXPelsPerMeter;

bitmapinfo.bmiHeader.biYPelsPerMeter = bi->biYPelsPerMeter;

gf += sizeof(BITMAPINFOHEADER);

rect.left = 8;

rect.top = 7;

rect.right = bitmapinfo.bmiHeader.biWidth + 8;

rect.bottom = bitmapinfo.bmiHeader.biHeight + 7;

// 이미지를 다이얼로그에 나타낸다.

if (m_bHSBMode!=TRUE)

{

// 이미지 분석모드가 아닐경우

SetDIBitsToDevice(dc, 8, 7, bitmapinfo.bmiHeader.biWidth, bitmapinfo.bmiHeader.biHeight ,

0 ,0, 0, (WORD)bitmapinfo.bmiHeader.biWidth, gf, &bitmapinfo, DIB_RGB_COLORS);

} else

{

// 이미지 분석모드일 경우

AnalysisImage(gf);

SetDIBitsToDevice(dc, 8, 7, bitmapinfo.bmiHeader.biWidth, bitmapinfo.bmiHeader.biHeight ,

0 ,0, 0, (WORD)bitmapinfo.bmiHeader.biWidth, m_BinImg, &bitmapinfo, DIB_RGB_COLORS);

}

}

}

[출처] AVI 파일 다루기 (차니의 컴퓨터 마을) |작성자 newchany

정적 라이브러리에서 MFC 사용

Program Visual C++ 2011. 8. 25. 13:20 Posted by HisPark

정적 라이브러리에서 MFC 사용




MFC
응용프로그램 배포 , MFC 사용 방법을 프로젝트 설정에서 설정해준다.

 

 

공유 DLL에서 MFC 사용 : 응용프로그램 배포 mfc**.dll 파일을 함께 배포.

정적 DLL에서 MFC 사용 : 응용프로그램의 실행 파일에 mfc**.dll 포함되어 배포되기 때문에 응용프로그램만 배포.

 

 

배포 응용프로그램만 배포하는게 관리하기도 편하고 오작동의 가능성도 적기 때문에 "정적 DLL 에서 MFC 사용" 으로 프로젝트를 설정하여 개발하게 된다.

 

응용프로그램이 DLL 경우 위와 같이 설정하면 컴파일 다음과 같은 오류 메시지가 발생한다.

fatal error C1189: #error : Please use the /MD switch for _AFXDLL builds


다음과 같이 설정해 주자.

 

 

1. 전처리기 정의

 

C/C++ 전처리기 전처리기 정의

"_AFXDLL" 추가

 




2.
코드 생성

C/C++ 코드 생성 런타임 라이브러리

Debug 모드일 경우 : "다중 스레드 디버드 DLL(/MDd)" 설정

Release 모드일 경우 : "다중 스레드 DLL(/MD)" 설정

 

 

'Program Visual C++' 카테고리의 다른 글

MFC CInternetSession클래스 사용시_유의점  (0) 2011.12.01
AVI 파일 다루기  (0) 2011.08.30
Event, Thread  (0) 2011.08.25
MFC 더블버퍼링 메모리 버퍼 이용  (0) 2011.08.25
RAW Data Draw  (0) 2011.08.11