NAT를 넘어 P2P 통신하기

Sharlotte ·2024년 5월 19일
0
post-thumbnail

NAT를 넘어 P2P 통신하기

유저들이 서로에게 3byte의 가벼운 데이터를 최대 20ms 레이턴시 내로 주고받게 하기 위하여.

!Notice
이 글에 첨부된 모든 이미지는 클릭 시 출처 사이트로 이동됩니다.

TL;DR
1. 일반적으로 그냥 P2P할려면 NAT가 페킷 내다버림
2. NAT가 내다버리지 않게 규칙을 만들어야 함. ← 홀펀칭
3. 규칙을 만드는 과정에서 논리적으로 홀펀칭이 불가능할 수 있음.
→ UPnP(자동 포트포워딩), Relay(중계 서버 거치기)같은 다른 방법 쓰면됨

P2P - Peer to Peer

P2P(peer-to-peer network) 혹은 동등 계층간 통신망(同等階層間通信網)은 비교적 소수의 서버에 집중하기보다는 망구성에 참여하는 기계들의 계산과 대역폭 성능에 의존하여 구성되는 통신망이다.
(...)
순수 P2P 파일 전송 네트워크는 클라이언트나 서버란 개념 없이, 오로지 동등한 계층 노드들(peer nodes)이 서로 클라이언트와 서버 역할을 동시에 네트워크 위에서 하게 된다.
https://www.wikiwand.com/ko/P2P

Peer to Peer 네트워크는 송신자인 서버와 수신자인 클라이언트의 역할이 나뉜 CS(client-server)와 대조적으로, 모두가 서로에게 송/수신을 하는 하는 개념입니다.

P2P 통신은 멀티미디어(치지직/아프리카의 그리드 같은), 파일 공유, 블록체인, 게임 등 많은 분야에서 응용되어왔습니다. 게임에선 P2P를 아래와 같은 장점때문에 체택하는 경우가 있습니다.

P2P 게이밍의 고유한 이점

  1. 신속한 응답과 지연 시간 감소: P2P 게임에서는 플레이어가 직접 연결을 설정하므로 멀리 떨어진 중앙 집중식 서버를 통해 통신이 이루어지지 않습니다. 이러한 즉각성은 밀리초가 중요한 빠른 속도의 게임에서 특히 유용하며, 보다 유동적인 게임 플레이를 가능하게 합니다.
  2. 경제성과 효율성: 게임 회사의 가장 큰 과제 중 하나는 중앙 집중식 서버 유지와 관련된 높은 오버헤드입니다. P2P 시스템은 이러한 비용을 획기적으로 절감하여 게이머에게 비용을 절감하고 게임 개발에 더 많은 투자를 할 수 있는 경제적인 솔루션을 제시합니다.
  3. 적응형 확장성: 플레이어 수가 증가하면 P2P 네트워크는 연결을 추가하여 손쉽게 조정하므로 플레이어 수에 관계없이 게임플레이를 원활하게 유지할 수 있습니다. 반대로 중앙 집중식 서버는 종종 과부하가 걸릴 수 있습니다.
  4. 강력한 복원력: P2P의 분산형 설계는 네트워크의 일부에 문제가 발생하더라도 게임 경험이 위태로워지지 않도록 보장합니다. 중앙 서버에 문제가 발생하면 모든 플레이어의 경험이 중단될 수 있는 중앙 집중식 시스템과 달리 P2P 네트워크는 일관된 게임플레이를 제공합니다.
    DeepL에 의해 번역됨
    https://medium.com/tashi-gg/peer-to-peer-gaming-9991600c6707

물론 강력한 중앙 서버가 없으므로 동기화와 핵에 관련된 문제가 있을 수 밖에 없고, 이 문제를 해결하기 위해 peer 하나가 호스트 격을 맡는 방법도 있습니다.
게임 멀티플레이어는 이것까지 합하여 총 3가지 유형이 있습니다.

그런데 저는 여기서 한가지 의문이 들었습니다.
어릴 적 해왔던 스타크래프트나 마인크레프트에서 친구와 게임하기 위해 서버를 열려면 포트포워딩을 해야 했습니다. 그때 알게 된 포트포워딩은 단순히 공유기의 특정 포트가 내 핸드폰의 주소로 향하게 만든다 라고만 알고 있었습니다. 즉, 공유기 설정에서 수동으로 규칙을 설정했던겁니다.

그런데 P2P 네트워크로 통신중인 게임들은 어떻게 내가 설정하지 않아도 알아서 공유기를 거쳐 내 핸드폰과 인터넷 사이 플레이어들을 연결해준걸까요?

그때 저는 한 스택 오버플로우 답변에서 P2P입장에서 NAT에 의해 생긴 문제를 해결하기 위해 UDP 홀펀칭이란 개념을 봤습니다.

둘째, UDP 홀 펀칭에 대해 이야기하고 있을 수 있습니다. 이는 세 번째 랑데부 서버를 통해 세 번째 댓글 호스트를 사용하여 NAT 라우터/게이트웨이 뒤에 있는 두 호스트 간의 연결을 유지하는 데 사용되는 기술 또는 알고리즘입니다.
DeepL에 의해 번역됨
https://stackoverflow.com/a/16909905/24182996

NAT

NAT(Network Address Translator, 네트워크 주소 변환기)는 통신을 할 때 IP 패킷에 담긴 IP 및 포트, 목적지의 IP 및 포트를 재기록하여 트레픽을 주고받는 기술입니다. 대표적으로 집에 있는 라우터가 이 기술을 통해 하나의 공인 주소로 여러 기기가 인터넷과 통신할 수 있게 만듭니다.

Mapping Behavior

NAT가 어떻게 일련의 주소들을 다른 주소로 바꾸는지에 대해선 주로 세가지 동작 유형이 있습니다.
일반적으로 이 동작 유형들은(아래의 필터링도 그렇지만) 목적지 주소에 의존치 않거나, 목적지 주소의 IP에만 의존하거나, 목적지 주소의 IP와 Port 모두에 의존하는 세가지의 경우에 따라 나뉩니다. Mapping Behavior의 경우엔 조건에 따라 포트가 바뀌죠.

이 글에선 굳이 Mapping Behavior를 자세히 다룰 필요가 없으므로 더 자세히 알고 싶다면 이 포스트 또는 RFC4787 문서를 읽어보세요.

Filtering Behavior

NAT는 주소를 다른 주소로 바꾸는 기술이지만, 또한 수신한 트레픽의 주소에 따라 트레픽을 드랍하는 일종의 방화벽 역할도 겸합니다. 이를 Filtering Behavior라 하여 수신지 출처 주소에 무관히 허용하거나, 정해진 IP만 허용하거나, 정해진 IP와 port만 허용하는 조건들이 있습니다.

바로 이 Filtering Behavior가 무조건(Endpoint Independent Filtering)이지 않을 때, P2P 통신을 하기 위해 상대 주소에 패킷을 보낼려 하면 패킷이 도달하지 못하고 드랍되는 경우가 발생합니다.

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.
경로의 NAT는 나가는 연결에 임시 공용 엔드포인트를 할당하고 해당 세션을 구성하는 패킷의 주소와 포트 번호를 변환하는 한편, 특별히 구성하지 않는 한 일반적으로 들어오는 모든 트래픽을 차단합니다.
DeepL에 의해 번역됨
https://bford.info/pub/net/p2pnat/ Introduction - 1번째 문단

Filtering Behaviour는 구체적으로 세가지 유형이 있으며 대체로 NAT를 통해 트레픽을 송신한 주소에 대해 허용한단 특징이 있습니다.

  • Endpoint-Independent Filtering: 어디서든지 트레픽이 올 수 있습니다.
  • Address-Dependent Filtering: 수신한 주소의 IP에서만 올 수 있습니다.
  • Address and Port-Dependent Filtering: 수신한 주소의 IP/Port에서만 올 수 있습니다.

Bryan Ford의 Peer-to-Peer Communication Across Network Address Translators에선 이와 같이 조건부로 트레픽을 드랍하는 것에 대해 outbound NAT라 말하며 이 NAT가 들어오는 트래픽이 이 사설 네트워크로부터 시작된 "세션"이 아닌 이상 드랍한다고 말합니다.

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.

NAT를 거쳐 P2P 통신하기

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는 피어 투 피어 프로토콜과 충돌하는데, 이는 통신하려는 두 피어가 서로 다른 두 NAT의 "뒤에"(사설 네트워크 쪽에) 있을 때 어느 쪽이 세션을 시작하려고 하면 다른 쪽의 NAT가 이를 거부하기 때문입니다.
DeepL에 의해 번역됨
https://bford.info/pub/net/p2pnat

그리고 이 Filtering Behavior의 특성때문에 만약 NAT가 EIF(Endpoint-Independent Filtering)가 아니라면 무조건 NAT 자신이 페킷을 보내야만 보낸 곳으로부터 페킷을 받을 수 있으니 같은 유형의 NAT는 서로가 서로에게 세션을 걸 수 없는 일이 벌어집니다.

릴레이(Relay)

이 문제를 해결할 수 있는 가장 간단한 방법은 그냥 언제든지 통신이 가능한 중개 서버를 거치는 것입니다. 두 클라이언트가 서로 직접 통신하지 않고, 중개 서버를 통해 통신한다면 그것은 P2P가 아니라 P2P를 위한 Client/Server에 불과합니다. 네, 느립니다.

Connect Reversal

애초에 이 문제는 "같은 유형의 NAT"는 서로가 서로에게 세션을 걸 수 없어서 생긴 문제입니다.
그러니깐 둘 중 한쪽이라도 NAT가 없는 - 공인 IP를 가지고 있는 상태라면 NAT가 있는 쪽에서 그 공인 IP로 페킷을 보내어 세션을 시작하면 됩니다.

홀 펀칭

앞선 두 방법은 확실하지만 큰 대가가 있거나 제한이 있습니다.

넷메니아즈의 P2P와 NAT: NAT 통과 기법 소개 (RFC 5128) - 2편: UDP Hole Punching 포스트가 이 기술을 쉽게 설명하고 있습니다. (뿐만이 아니라 상술한 개념들에 대해서도 모두 자세히 설명하고 있습니다.)

앞서 말했듯이 NAT는 filtering behavior라 하여 NAT가 수신받은 페킷의 출발지가 송신했던 목적지와 어떻게 같냐에 따라 페킷을 드랍하는 동작을 가지고 있습니다. 즉, 내부 사설망으로부터 시작된 세션의 페킷이 아니면 외부 페킷을 받지 않겠단 것입니다.
그러나 상대 호스트의 NAT가 내 NAT로부터 도착한 페킷을 드랍했더라도 내 NAT는 상대 호스트에 대한 필터링 규칙이 등록되어서 상대 호스트로부터 오는 패킷을 허용할 수 있게 됩니다.

즉,
1. 두 호스트가 중계 서버에 자신의 내부/외부 IP를 보냅니다.
2. 두 호스트가 각자 상대의 호스트와 연결하기 위해 중계 서버로부터 서로의 IP를 가져옵니다.
3. 가져온 IP로 홀펀칭을 시도합니다.

3-1. 위 사진의 오른쪽에서 Host A가 먼저 페킷을 보내어 NAT A에게 구멍을 만들고,
3-2. HOST B가 NAT B를 거쳐 그 구멍을 통해 NAT A를 통과하여 HostA에 페킷을 건내는 동시에
3-3. NAT B에 구멍을 만들어서 Host A가 드디어 페킷을 넘길 수 있게 만듭니다.
3-4. 그럼 Host A도 Host B에 페킷을 보내게 되며 P2P 통신이 성공하게 됩니다.

단점

이 방법은 서로 다른 NAT 뒤에 숨은 두 기기들이 P2P를 실현할 수 있단 점에서 강력합니다.
그러나 몇가지 단점이 있습니다.

  1. 타임아웃을 막기 위해 keep-alive 패킷을 지속적으로 보내야 합니다.

    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.

    UDP 전송 프로토콜은 NAT를 통과하는 세션의 수명을 확인할 수 있는 안정적이고 애플리케이션에 독립적인 방법을 NAT에 제공하지 않기 때문에 대부분의 NAT는 단순히 유휴 타이머를 UDP 변환에 연결하여 일정 기간 동안 트래픽이 사용되지 않으면 구멍을 닫습니다. 안타깝게도 이 타이머에 대한 표준 값은 없으며, 일부 NAT는 20초의 짧은 시간 제한을 설정하기도 합니다.
    DeepL에 의해 번역됨
    https://bford.info/pub/net/p2pnat/

  2. 비 EIM-NAT에 대해선 사용할 수 없습니다.

ymmetric NAT

UDP hole punching will not work with symmetric NAT devices (also known as bi-directional NAT) which tend to be found in large corporate networks. In symmetric NAT, the NAT's mapping associated with the connection to the well known STUN server is restricted to receiving data from the well-known server, and therefore the NAT mapping the well-known server sees is not useful information to the endpoint.
UDP 홀 펀칭은 대규모 기업 네트워크에서 흔히 볼 수 있는 대칭형 NAT 장치(양방향 NAT라고도 함)에서는 작동하지 않습니다. 대칭형 NAT에서는 잘 알려진 STUN 서버와의 연결과 관련된 NAT의 매핑이 잘 알려진 서버로부터 데이터를 수신하는 것으로 제한되므로 잘 알려진 서버가 보는 NAT 매핑은 엔드포인트에 유용한 정보가 되지 못합니다.
DeepL에 의해 번역됨
https://www.wikiwand.com/en/UDP_hole_punching

Symmetric NAT는 RFC3489에서 정의된 APDM, APDF(Address & Port Dependent Mapping/Filtering) 동작을 가지는 NAT 유형입니다.
이 NAT는 동작에서 짐작하듯이 하나의 IP가 오직 하나의 IP와만 통신이 가능하게 규칙이 정해져 있습니다.
그러니깐 앞서 설명한 홀펀칭의 단계에서, 애초에 Host A가 중계 서버로 보낸 외부 IP(155.99.25.11:62000)와 실제로 Host B가 보낼 Host A의 외부 IP(155.99.25.11:62001)와 다르다는겁니다.
중계 서버에서 가져온 Host A의 IP인 155.99.25.11:62000는 중계 서버 IP인 100.100.100:12341 에서만 보낼 수 있으니 Host B의 IP인 132.12.33.11:8000 에선 155.99.25.11:62000 로 페킷을 보낼 수 없단겁니다. 100.100.100:12341 전용이니깐요.


EIM-NAT


새로 개정된 RFC 5780 정의에선 총 9가지(Mapping과 Filtering로 만든 가능한 모든 경우의 수)의 NAT 유형들을 소개합니다.
사실 Mapping가 보낼 호스트가 다를 때마다 포트가 달라지는 동작이라면, 그 호스트에 매칭될 내 포트를 미리 중계 서버에 보내줘야 한단건데... 타임 스톤도 아니고 미래에 NAT가 무슨 포트로 내부 IP를 바꿀줄 알고 그 포트를 미리 압니까?
그러므로 비-EIM-NAT들은 홀펀칭 기술을 사용할 수 없습니다.

참고로 많은 자료들이 홀펀칭을 EIM에 한정치 않고 Symmetric NAT에선 작동하지 않단 말 위주로 이야기를 하는데, 이는 위 사진의 RFC-3489에서 정의된 NAT 유형의 이름들 중 EIM-NAT와 Symmetric NAT가 아닌 NAT들은 이름이 없기 때문이라고 봅니다...


대체 솔루션

따라서, 홀펀칭만으로 P2P 통신을 해결하는 것은 무리가 있습니다. 오히려 P2P 통신에서 홀펀칭으로 해결할 수 없는 환경에 대해 다른 일반적인/베타적인 해결법을 도입하는게 맞습니다.

UPnP

유니버설 플러그 앤 플레이는 포트포워딩을 자동화하는 프로토콜인 SSDP를 이용하고 있는 범용 표준 프로토콜입니다.
upnp 프로토콜은 기본적으로 켜져 있으며 유저에 의해 꺼질 수 있습니다.

Relay

P2P 구현에 있어서 가장 최후의 보루에 위치한 방법.
말이 P2P지 clients-server를 엮어다 놓은 것에 불과합니다.
p2p의 장점을 내려놓게 되겠지만(특히 속도라던가..속도라던가) 100% 성공한다는 무적의 안전성을 지닙니다. 이게 안되면 뭘 해도 그 서버와 통신이 안되는겁니다.

결론

NAT를 넘어 P2P 통신을 하는 방법에서 희귀도를 매긴다면 Connect Reversal < Hole Punching < UPnP < Relay가 된다.
실제로 P2P를 구현하게 된다면 이 일련의 방법들을 희귀도 순으로 시도하는걸 상상해볼 수 있겠다. (connect reversal는 한 기기가 공인 ip여야 하니 그냥 제외할수도 있겠다)

Ref.

RFC 4787 - Network Address Translation (NAT) Behavioral Requirements for Unicast UDP
Peer-to-Peer Communication Across Network Address Translators
Peer-to-Peer (P2P) Architecture
Tistory - 분산시스템) 시스템 아키텍쳐(2) - 비중앙 집중식
Medium - Peer-to-Peer Gaming
온라인 게임 Peer to Peer (P2P) 방식과 Client/Server (CS)
StackOverflow - How do you create a peer to peer connection without port forwarding or a centeralized server?

Tistory - [UDP 홀펀칭(Hole Punching)] - UDP 홀펀칭(1/2)
Tistory - [UDP 홀펀칭(Hole Punching)] - UDP 홀펀칭(2/2)
Tistory - 3. HOLE PUNCHING이란
Understanding Different NAT Types and Hole-Punching
Wikipedia - UDP hole punching
Medium - NAT with UDP hole punching
StackOverflow - Fully Decentralized P2P?

Wikipedia - 네트워크 주소 변환
Naver Blog - 방화벽 NAT(Network Address Translation)
Brunch - NAT는 무엇이며, 왜 필요한 것인가?
Velog - 14.-NAT와-포트포워딩
Velog - 네트워크 기본: NAT
photon forum - public ip address of client
Netmanias - NAT (Network Address Translation) 소개 (RFC 3022/2663)
Netmanias - NAT 장비는 이렇게 만들어야 하는데... (RFC 4787) - 1편: Mapping Behavior
Netmanias - NAT 장비는 이렇게 만들어야 하는데... (RFC 4787) - 2편: Filtering Behavior
Netmanias - NAT 장비는 이렇게 만들어야 하는데... (RFC 4787) - 3편: Deterministic Properties
Netmanias - STUN(RFC 3489)과 STUN(RFC 5389/5780)의 차이
Netmanias - P2P와 NAT: NAT 통과 기법 소개 (RFC 5128) - 1편: Relaying & Connection Reversal
Netmanias - P2P와 NAT: NAT 통과 기법 소개 (RFC 5128) - 2편: UDP Hole Punching

profile
샤르르르

0개의 댓글