본문 바로가기
분산 컴퓨팅 이론

NAT-PMP 에러 처리

by dbadoy 2023. 4. 22.

본 내용을 다루기에 앞서, NAT-PMP(Port Mapping Protocol)에 대한 간단한 설명으로 글을 시작한다.

NAT-PMP는 NAT 내부에 할당된 [IP, 포트]와 NAT의 포트를 매핑시켜, 사설 네트워크의 호스트가 외부 네트워크의 호스트와 end-to-end 통신 할 수 있도록 돕는 프로토콜이다. 예를 들어, 다른 네트워크에 있는 사용자가 '192.168.10.111' 라는 사설 IP를 갖고 있는 나에게 연결하려는 상황을 가정해보자. 외부에 있는 호스트는 나에게 직접 접근할 방법이 없다. 만약 내가 NAT 관리자라면 포트 포워딩을 해주면 되겠지만, 그렇지 않은 사용자가 대다수이다. 이럴 때 NAT-PMP를 사용한다면 [internal: '192.168.10.111:8080', external:1111](예시) 요청을 보내 NAT에 매핑을 생성할 수 있다. 외부 네트워크 호스트는 NAT의 공용 IP, 포트 1111번으로 요청을 보내 '192.168.10.111:8080' (나)와 직접 통신을 할 수 있게 된다.

NAT의 포트와 내부 IP:PORT를 매핑해주는 것인데, 포트 번호는 2^16의 크기를 갖는 한정된 자원이다. 매핑 과정에서 요청자는 자신이 사용할 외부 포트 번호를 요청에 담아 NAT에 전송하게 되는데, NAT에 해당 포트 번호가 이미 선점되어 있다면 어떻게 작동할까? '에러를 리턴한다'라는 답이 바로 떠오른다. 하지만 NAT-PMP는 요청한 포트 번호가 선점된 경우를 에러로 정의하지 않으며, 사용 가능한 임의의 포트를 할당하여 성공을 리턴한다. 

사용자가 요청한 작동이 아닌데 성공으로 간주한다니, 이래도 괜찮은 걸까?

RFC-6886에서 해당 에러 처리에 대한 근거에 대해 적은 부분을 살펴보자.

9.4.  Atomic Allocation Operations (원자 할당 작업)

   NAT-PMP의 유용한 속성 중 일부는 신뢰할 수 있고 성공적인 프로토콜인 DHCP에서 영감을
   받았습니다. 예를 들어 DHCP를 사용하면 클라이언트가 원하는 IP 주소를 요청할 수 있지만 해당
   주소가 이미 사용 중인 경우 DHCP 서버는 대신 사용 가능한 다른 주소를 할당합니다.

   이에 따라 NAT-PMP는 클라이언트가 원하는 외부 포트를 요청할 수 있도록 하며, 해당 외부
   포트가 다른 클라이언트에서 이미 사용 중인 경우 NAT-PMP 서버는 대신 사용 가능한 다른 외부
   포트를 할당합니다.

   UPnP IGD는 이 작업을 수행하지 않습니다. UPnP IGD 클라이언트가 이미 할당된 외부 포트를
   요청하면 두 가지 중 하나가 발생합니다.

   일부 UPnP IGD 홈 게이트웨이는 이전 매핑을 새 매핑으로 자동으로 덮어쓰므로 이전
   클라이언트의 연결이 끊어집니다. 이전 클라이언트가 포트 매핑을 갱신하면 새 매핑을 덮어쓰고
   두 클라이언트가 동일한 외부 포트를 두고 무한정 싸우며 안정적인 연결을 달성하지 못합니다.

   다른 IGD 홈 게이트웨이는 포트가 이미 사용 중인 경우 "Conflict" 오류를 반환합니다. 이는
   적어도 클라이언트에게 무슨 일이 일어났는지 알려주지만 클라이언트에게 무엇을 해야 하는지
   알려주지는 않습니다. NAT 게이트웨이(사용 가능한 포트를 알고 있는)가 클라이언트에 하나를
   할당하는 대신 NAT 게이트웨이는 클라이언트(알지 못함)가 운이 좋을 때까지 추측을 계속하게
   합니다. 이 문제는 많은 클라이언트가 UPnP IGD를 사용하지 않는 한 경미하지만 포트 매핑을
   요청하는 네트워크의 클라이언트 수가 증가함에 따라 점진적으로 악화됩니다. 또한 UPnP IGD는
   미리 할당된 포트 범위를 각 클라이언트에 할당하는 새로운 정책에서 특히 제대로 작동하지
   않습니다. 클라이언트에 TCP 포트 범위 63488-64511이 할당되고 UPnP IGD 클라이언트가
   TCP 포트 80을 요청하고 성공할 때까지 연속적으로 증가하는 포트를 시도하면 UPnP IGD
   클라이언트는 성공하기 전에 63,409개의 요청을 발행해야 합니다.

(RFC-6886 제안 당시 UPnP를 대체하기 위해 나온 프로토콜이라 그런지, NAT-PMP의 장점에 대해 적는 9챕터에 UPnP의 안좋은 점을 잔뜩 적어 놓은 것이 재밌다. RFC-6886을 읽으면 UPnP 문서는 안 봐도 될 정도)

NAT의 메모리나 연산 장치는 고성능이 아니기 때문에 NAT 장치에 많은 요청이 들어오면 과부하가 걸리고, 만약 비정상 작동한다면 해당 네트워크에 존재하는 모든 호스트에 피해를 입히게 된다(때문에 RFC문서에서도 클라이언트는 NAT 게이트웨이가 관리할 수 있는 속도로 작동하도록 허용한 것을 존중하라고 명시한다). 

위 환경을 염두에 두고, 요청한 포트가 선점되어 있을 때 에러를 리턴하는 경우를 가정해보자.

요청한 포트가 선점되어 에러를 받은 사용자는 사용할 수 있는 번호를 찾을 때 까지 계속하여 NAT에 요청할 것이다. 만약 모든 포트 번호가 선점되어 있다면 포트 번호 범위인 65,535(-1,024)번을 요청하게 될 수도 있다. 사용자 여러 명이 해당 작업을 수행하는 경우를 상상해보면 이는 적은 요청이 아님을 알 수 있다. 이때, 포트가 선점되어 있는 경우 사용 가능한 포트를 할당해준다면 문제가 해결된다.

물론 사용자가 정말 원하는 포트 번호로 지정해야만 하는 경우도 있을 것이고, 이러한 경우 클라이언트 입장에서 더 많은 작업을 해야 된다. 하지만 NAT-PMP는 명확하게 NAT 장치의 부하를 줄이는 것을 선택해, 이미 선점된 포트 번호 요청 시 사용 가능한 외부 포트를 할당한 뒤 성공을 리턴하는 방식으로 구현했고 이는 충분히 근거가 있어 보인다.

단, 일반적인 에러 처리 방식이 아니다 보니 프로토콜 사용 과정에서 원하지 않는 작동을 유발할 가능성이 있다. 예를 들어보자. 문서를 읽지 않은 사용자는 포트 매핑 요청이 일반적인 에러 처리(요청을 수행하지 못하면 에러 리턴)를 할 것이라 생각한다. 사용자는 외부 포트 1000번에 대한 매핑 요청을 한 뒤, 성공을 리턴받는다면 [NAT 공용 IP:1000] 값을 통신하고 싶은 상대에게 전달하고, end-to-end 통신을 시도한다. 하지만 NAT는 1000번 포트가 이미 선점되어 임의의 포트 번호 4000번에 매핑한 뒤 성공을 리턴했다. 실제 NAT에는 [요청자:4000]으로 할당이 되었지만, 사용자는 [요청자:1000]으로 할당이 된 것으로 판단하여 잘못된 포트 번호를 상대방에게 전달하게 되고, 결과적으로 정상적인 통신이 이루어지지 않게 된다. 사용자는 이 문제를 추적하기 어렵고, 다른 곳에서 문제를 찾을 확률이 높다.

실제로 이더리움 재단에서 개발하고 있는 P2P 클라이언트 구현체 go-etheruem에서 NAT-PMP의 에러 처리 방식에 대한 고려 없이 위와 같은 방법으로 구현되어 있었고, 2022년 12월에 해당 PR로 수정되었다.

기존에는 사용자가 요청한 작업이 제대로 수행되지 않으면 에러를 리턴하는 것이 당연하다고 생각했으나, 상황에 따라 요청의 내용과 상관없이 일단 작업을 수행하는 것이 더 나은 선택일 수 있다(흔한 경우는 아니며, 위 예시처럼 사용자들에게 추가적인 설명 필요).

질문에 대해 무조건적인 답을 정해놓지 말고 여러 상황을 고려해 유연하게 대처를 할 수 있도록, 결정할 때 여러 번 고민할 필요가 있다.

'분산 컴퓨팅 이론' 카테고리의 다른 글

[번역] There is No Now  (0) 2023.04.27
[번역] Istanbul Byzantine Fault Tolerance (EIP-650)  (0) 2023.03.28