Gnutella: 자신을 만든 세계보다 오래 살아남은 프로토콜

5 days ago 3
  • Gnutella는 잊힌 파일 공유 프로토콜에 가깝지만, 탈중앙 기술을 의식하지 않은 수백만 사용자가 MP3 다운로드 문제를 해결한 실제 사례였음
  • 2000~2001년 미국 인터넷 보급률 50%, 저렴해진 MP3 플레이어, 스트리밍 한계, 직접 파일 관리 문화가 채택을 밀어줌
  • 서버 없는 구조와 단일 장애점 부재 덕분에 AOL의 취소 이후에도 되돌리기 어려웠고, 수년간의 중단 시도에도 계속 동작함
  • 기본 구조는 HTTP 파일 전송과 TCP 가십 프로토콜의 결합이며, PING/PONG, QUERY/QUERYHIT, PUSH가 검색·응답·방화벽 우회를 맡음
  • 주류에서 사라진 이유는 즉각적 기술 실패가 아니라 당시의 사용자 환경이 사라졌기 때문이며, 네트워크는 축소된 규모로 계속 살아남음

Gnutella가 오래 살아남은 이유

  • Gnutella는 많은 사람이 잊은 파일 공유 프로토콜이지만, 탈중앙 기술을 이해하려 하지 않은 일반 사용자 수백만 명이 실제 문제를 해결하려고 쓴 사례임
  • 사용자는 토큰 가치 상승 같은 동기가 아니라 MP3 다운로드 때문에 Gnutella 네트워크에 참여했고, 네트워크는 폭발적으로 성장한 뒤 거의 10년간 안정기에 머물렀으며 이후 줄어든 사용량으로 긴 꼬리를 유지함
  • Gnutella는 LimeWire 같은 더 눈에 띄는 프로젝트 아래 숨은 구성 기술이었고, 현대 플랫폼의 폐쇄형 정원 모델 때문에 파일시스템 자체를 기억하지 못하는 인터넷 사용자도 많아짐
  • Gnutella 프로젝트는 AOL이 취소한 내부 데모가 공개로 유출되면서 시작됐고, 서버 없는 탈중앙 설계 때문에 일단 공개된 뒤에는 되돌리기 어려웠음
  • Gnutella는 중단 시도에도 불구하고 수년간 동작했고, 원본 Gnutella.exe 사본도 archive.org에서 찾을 수 있음
  • 실패한 프로토콜로 보기 어려운 이유는 분명함
    • 수백만 명의 동시 활성 사용자까지 확장됐고, 약 10년간 주류 사용 사례로 번성함
    • 주류에서 사라진 이유는 프로토콜 자체의 즉각적 실패가 아니라, Gnutella가 태어난 사용자 환경이 사라졌기 때문임
    • 오늘날에도 축소된 규모로 계속 동작함

채택을 만든 역사적 조건

  • 2000~2001년 무렵 미국 소비자의 인터넷 보급률이 50%에 도달했고, 인터넷은 마니아용 도구에서 일상적 기반으로 바뀌는 중이었음
  • 음악 파일 공유가 흔해진 배경에는 여러 조건이 동시에 작용함
    • 음악 산업이 바뀌는 소비자 선호에 적응하지 못함
    • MP3 플레이어와 솔리드 스테이트 저장장치가 저렴해지고 널리 퍼짐
    • 저속 전화 접속 인터넷에서는 음악 스트리밍이 현실적이지 않았음
    • 디스크 공간, 디렉터리, 백업, 다운로드 파일을 직접 관리하는 일이 당시 일반 컴퓨터 사용자에게도 받아들일 만했음
  • 이런 조건은 2010년대 초까지 이어진 파일 공유의 황금기를 만들었고, LimeWire는 당시 경험을 대표하는 이름으로 남음
  • Gnutella는 단일 장애점이 없어서 죽이기 어려웠고, 기본 프로토콜은 단순했지만 명세에 포함된 선택적 확장 덕분에 쉽게 확장될 수 있었음

프로토콜의 기본 성격

  • Gnutella는 대다수 사용자에게 파일 전송 도구였지만, 핵심적으로는 블롭(blob)을 찾는 P2P 검색 엔진에 가까움
  • 원리상 간이 DNS, 전역 키/값 메타데이터 조회 테이블, Unreal Tournament 리그 매칭 서비스 같은 용도로도 쓸 수 있었지만, 실제 역사에서는 검색어와 일치하는 파일 다운로드, 특히 MP3 다운로드로 기억됨
  • Gnutella 0.6 초안 명세는 공유 대상 리소스가 다른 리소스로의 매핑, 암호화 키, 모든 형식의 파일, 키 지정 가능한 리소스의 메타정보 등 무엇이든 될 수 있다고 밝힘
  • 일반적인 사용 흐름은 다음과 같았음
    • LimeWire, BearShare, GTK-Gnutella 같은 Gnutella 클라이언트를 실행함
    • 클라이언트가 인터넷 어딘가의 몇몇 피어에 연결함
    • 사용자가 LinkinPark.mp3.exe 같은 검색어를 입력함
    • 질의가 피어에서 피어로 네트워크 바깥쪽으로 퍼짐
    • 전 세계 임의 컴퓨터에서 결과가 천천히 돌아옴
    • 사용자는 파일명을 보고 가짜 여부를 추정하고, 연결 속도를 비교하고, 바이러스가 없기를 기대함
    • 파일을 고르면 클라이언트가 사용자 컴퓨터에서 HTTP로 직접 조각을 내려받음
  • 잘못된 파일을 받다가 새 콘텐츠를 우연히 발견하거나 악성코드를 받을 수도 있었고, 이런 탐색적 채집 행동은 추천 엔진의 등장과 함께 사라짐
  • 클라이언트는 보통 네 가지 주요 기능을 제공함
    • 질의 관리자: 수천 피어에 걸쳐 느리게 퍼지는 검색을 처리함
    • 파일 관리자: 공유할 디렉터리나 경로, 다운로드 파일 저장 위치를 지정함
    • 전송 관리자: 양방향 파일 전송의 재개, 분할, 관리를 처리함
    • 부가 기능: IRC 채팅, 게시판, 검색 질의 모니터, 특정 호스트 탐색 같은 기능이 포함됐지만, 다수는 프로토콜 자체의 일부가 아니었음
  • Gnutella 생태계에는 LimeWire 같은 시장 선도자가 있었지만 여러 클라이언트가 공존했고, 독립 개발자가 클라이언트를 처음부터 작성할 수도 있었음
  • Gnutella Bun Client 구현 과정에서는 명세에 없는 내용, 문서화되지 않은 기능, 추가 명세로 흩어진 기능이 많았고, 프로토콜은 유기적으로 진화함

HTTP와 가십 프로토콜의 결합

  • 개인 컴퓨터에서 HTTP 서버를 띄워 IP 주소를 알려주면 파일 공유가 가능해 보이지만, 오늘날에는 NAT, 방화벽, 가정용 ISP 정책 등으로 인바운드 TCP 포트를 공개하기 어려움
  • 20년 전에는 로컬 머신에서 작은 HTTP 서버를 실행하고 공인 IP로 노출하는 일이 지금보다 더 흔히 가능했음
  • Gnutella는 이 환경을 활용해, 가십하는 피어들의 메시 안에서 각 참여자가 파일을 호스팅할 수 있게 했음
  • LimeWire로 파일을 내려받는 전송 단계는 curl이나 wget으로 파일을 받는 것과 유사했음
  • HTTP 서버만으로는 P2P 파일 공유 네트워크가 되지 못함
    • 당시에도 ISP가 안정적인 정적 IP를 제공하지 않는 경우가 많았음
    • 오늘 공유한 IP 주소가 내일 바뀔 수 있었음
    • http://74.6.231.21:4000 같은 임의 URL은 검색 엔진에 색인되지 않았을 가능성이 높고, 노트북을 닫으면 오프라인이 됨
  • Gnutella 클라이언트는 HTTP 서버와 함께 TCP 기반 가십 프로토콜을 실행함
    • 다른 Gnutella 참여자와 공유 디렉터리를 HTTP로 제공하는 피어들의 메시 안에서 자신의 존재를 알림
    • 피어 주소, 대역폭, 지연시간, 검색 질의 같은 정보가 메시를 통해 이동함
    • 방화벽을 다루기 위한 도구도 있었지만, 현대 NAT 문제를 해결하려면 나중에 확장이 필요했음
  • Gnutella 노드의 기본 역할은 세 가지임
    • 로컬 HTTP 서버로 원하는 사람에게 파일을 전송함
    • 가십 메시를 통해 사용 가능한 파일을 검색하고 알림
    • 경우에 따라 방화벽을 우회하기 위한 기법을 사용함
  • Gnutella에는 중앙 진입점이나 사용자 레지스트리가 없으므로, 메시 안에 들어간 뒤에는 새 피어, 인바운드 검색 질의, 기타 네트워크 트래픽을 발견하게 됨

부트스트래핑

  • 부트스트래핑은 초대받지 않았고 정문도 없는 P2P 메시 안으로 처음 들어가기 위해 몇 개의 시작 피어를 찾는 과정임
  • Gnutella 전역 네트워크는 참여자 IP 주소의 혼합체이며, 주 네트워크에 붙어 있는 신뢰 가능한 피어 하나만 연결해도 많은 사용자 집합의 네트워크 트래픽을 보기 시작함
  • 시간이 지날수록 PONG 메시지를 통해 더 많은 피어를 찾고, 피어 목록은 재접속을 위해 디스크에 저장됨
  • IP 주소 변경이나 오프라인 상태 때문에 저장된 피어 목록은 시간이 지나며 일부가 실패하고, 클라이언트는 유효한 피어를 찾을 때까지 목록을 따라 시도함
  • 처음 네트워크에 참여하거나 오랫동안 접속하지 않았다가 돌아오는 경우에는 저장된 목록만으로 충분하지 않을 수 있어 부트스트래핑이 필요함
  • 가장 흔한 방식은 Gnutella Web Cache(GWebCache)
    • 자원봉사자가 운영하는 독립 웹 서버들의 연합이며, 보통 CGI나 PHP 스크립트 형태의 작은 웹 애플리케이션임
    • 정보를 자발적으로 제공한 Gnutella 참여자의 IP 주소를 기록함
    • 현재 서버가 내려갈 경우를 대비해 다른 GWebCache 서버의 IP나 도메인을 기록함
    • 대체 GWebCache 서버 목록을 제공함
    • 현재 알려진 Gnutella 네트워크 참여자 IP 주소 목록을 제공함
  • 일부 Gnutella 클라이언트는 캐시 서버에 자동으로 접속하고, 일부는 IP를 설정 파일이나 설정 메뉴에 복사해 넣어야 함
  • 시작 피어에 연결한 뒤에는 네트워크 메시 내부에서 더 많은 피어를 간접적으로 수집하게 되므로, 캐시 의존도는 낮아짐
  • GWebCache는 중앙 병목점이 아님
    • 서로 관련 없는 GWebCache 서버가 여럿 존재함
    • GWebCache 없이 클라이언트를 부트스트랩하는 방법도 여럿 있음
    • GWebCache가 없어도 Gnutella는 덜 편리한 형태로 생존할 수 있음
  • 부트스트랩 목록 요청 예시는 다음과 같음
    • URL 끝에 ?get=1&client=TEST&version=1를 붙이면 목록을 가져올 수 있지만, 너무 많이 요청하면 빠르게 속도 제한됨
http://cache.jayl.de/g2/gwc.php http://gweb.4octets.co.uk/skulls.php http://midian.jayl.de/g2/bazooka.php http://p2p.findclan.net/skulls.php http://skulls.gwc.dyslexicfish.net/skulls.php
  • 출력 예시는 다음과 같음
H|106.107.193.27:23459|88579 H|182.233.59.26:23464|88581 U|http://bj.ddns.net/skulls/skulls.php|208999 U|http://scissors.gwc.dyslexicfish.net:3709/|341201
  • H로 시작하는 항목은 피어이고, U로 시작하는 항목은 나중에 사용할 수 있는 중복 캐시 서버임

핵심 메시지 타입

  • Gnutella는 TCP 기반 프로토콜이며, 인바운드 연결을 받는 피어에 연결하면 먼저 핸드셰이크가 발생함
  • 클라이언트는 GNUTELLA CONNECT/0.4 또는 GNUTELLA CONNECT/0.6를 보내고, 상대가 긍정 응답을 보내면 연결이 수립되어 바이너리 Gnutella 메시지가 흐르기 시작함
  • 모든 바이너리 메시지는 23바이트 헤더로 시작함
    • 헤더에는 메시지 ID, 페이로드 타입, TTL, 홉 수, 페이로드 길이가 들어 있음
    • TTL은 메시지의 남은 생명이고, 홉 수는 이미 이동한 거리임
    • TTL + Hops는 메시지가 원래 의도한 도달 범위를 나타냄
  • 실질적인 핵심 메시지는 다섯 가지임
    • PING: 살아 있는 피어를 탐색함, 페이로드 타입 0x00
    • PONG: PING에 응답하며 IP 주소, 포트, 공유 통계를 담음, 페이로드 타입 0x01
    • QUERY: 사용자가 시작했거나 근처 피어가 시작한 검색 요청임, 페이로드 타입 0x80
    • QUERYHIT: QUERY에 대한 긍정 응답이며 파일 결과 레코드와 다운로드용 연결 정보를 포함함, 페이로드 타입 0x81
    • PUSH: 방화벽 뒤의 업로더를 위한 우회책이며, 파일 보유자에게 다운로더 쪽으로 다시 연결하라고 요청함, 페이로드 타입 0x40
  • BYE 메시지도 있지만 엄격히 필수는 아님
  • 프로토콜 메시지는 확장 데이터 필드를 지원해, 클라이언트가 전체 네트워크를 깨뜨리지 않고 기능을 추가할 수 있음
  • GTK-Gnutella는 작은 핵심 프로토콜에는 없던 TLS, IPv6, UDP 같은 기능을 지원함

프로토콜 확장

  • 다섯 메시지 타입만 구현해도 동작하는 Gnutella 클라이언트를 만들 수 있을 가능성이 있지만, 명세는 거의 30년 전 것이고 생태계는 멈추지 않았음
  • Gnutella는 오래된 패킷 안에 새 아이디어를 넣을 여지를 남겼음
  • GGEP(Gnutella Generic Extension Protocol) 는 일반 메시지 안에 확장 데이터를 넣는 범용 공간을 제공함
  • HUGE(Hash/URN Gnutella Extensions) 는 클라이언트가 파일명을 기준으로만 찾는 대신 SHA 해시로 파일을 식별할 수 있게 함
  • XML 확장 페이로드 지원도 전해지지만, 명세가 이를 과거형으로 다루고 현대 네트워크 트래픽에서는 관찰되지 않았음
  • 원래 설계는 작았지만 계속 늘어날 만큼의 유연성을 갖고 있었음

검색과 전송의 동작 방식

  • PING/PONG 메시지는 노드 사이를 오가는 심장박동 역할을 함
    • PING은 일반 23바이트 헤더 외에는 필수 페이로드가 없지만, 선택적 GGEP 확장 데이터를 담을 수 있음
    • PONG은 응답한 servent의 포트, IPv4 주소, 공유 파일 수, 공유 킬로바이트 수를 담음
    • 노드는 PONG에 붙은 IP/포트 정보를 모아 메시 안에서 더 잘 연결된 구성원이 되고, 이후 세션을 위해 이 정보를 저장함
  • QUERY/QUERYHIT은 PING/PONG과 비슷하게 동작하지만, 피어 광고 대신 검색 트래픽을 운반함
    • QUERY는 최소 속도, 즉 전송 대역폭 필드를 담고, 그 뒤에 NUL로 끝나는 검색 문자열이 붙음
    • 예: beethoven.mp3
    • QUERY 메시지는 발신자에게서 바깥쪽으로 홍수처럼 퍼지고, QUERYHIT 메시지는 결과가 있을 경우 발신자 쪽으로 돌아옴
    • QUERYHIT에는 응답자의 IP 주소, 포트, 속도, 결과 집합이 들어 있음
    • 각 결과에는 파일 인덱스, 파일 크기, 파일명, 선택적 메타데이터나 확장이 포함됨
    • 파일 인덱스는 이후 HTTP로 파일을 요청할 때 사용됨
  • Gnutella의 플러드 라우팅 특성 때문에 결과는 천천히 도착했고, 완료까지 몇 분이 걸리기도 했음
  • LimeWire 엔지니어들은 이를 더 확장성 있게 처리하기 위해 동적 질의 라우팅을 고안함
    • 블룸 필터와 영리한 네트워크 토폴로지를 활용함
    • 이 고급 구성 덕분에 플러드 라우팅 문제 없이 수백만 사용자 규모로 확장할 수 있었음
    • 오늘날에도 대부분의 주류 클라이언트가 이 시스템을 구현함

PUSH와 방화벽

  • PUSH 메시지는 일부 HTTP 서버가 방화벽을 빠져나오도록 돕는 우회책이지만, 모든 경우를 해결하지는 못함
  • 일반적인 HTTP처럼 클라이언트가 서버에 접속하는 대신, 서버에게 클라이언트 쪽으로 접속해 달라고 요청하는 방식에 가까움
  • PUSH 메시지는 업로더가 다운로더를 찾는 데 필요한 servent 식별자와 기타 식별자를 담음
  • 클라이언트가 직접 연결할 수 없으니 상대가 다시 연결해 파일을 보내 달라고 요청하는 형태임
  • 자세한 내용은 Gnutella 명세에서 확인할 수 있음
  • 현대 클라이언트는 이런 문제를 더 자연스럽게 다루기 위해 추가 기법과 UDP 확장을 사용함

남은 의미와 참고 자료

  • Gnutella는 좋은 초기 설계 덕분에 수백만 동시 사용자로 확장됐고, 차단을 피했으며, 외부 도움 없이 수십 년간 온라인 상태를 유지함
  • 실제 트래픽이 들어오자마자 무너진 네트워크가 아니라, Y2K 문화의 일부였던 네트워크가 여전히 사라지지 않았다는 사실이 설계의 견고함을 드러냄
  • Gnutella가 희미해진 진짜 이유는 그것을 만든 세계보다 오래 살아남았기 때문이라는 결론에 가까움
  • 네트워크 자체는 프로토콜 관련 자료를 호스팅하던 사이트들보다 오래 살아남았음
  • 참고 링크

Read Entire Article