Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

Avahi

Avahi는 LGPL(GNU Lesser General Public License) 기반의 Zeroconf(Zero configuration networ)소프트웨어로 mDNS(Multicast DNS)와 DNS-SD service discovery 구현체다.

Avahi를 이용하면 (mDNS와 DNS-SD 기술을 이용)로컬 네트워크 상에서 서비스와 호스트를 찾거나 등록할 수 있다. 예를 들어 로컬 네트워크에 프린터가 있다면, 다른 설정 없이 즉시 프린터를 찾을 수 있다. 로컬 네트워크에 파일서버나 채팅서버가 있다면, Avahi를 이용해서 파일을 공유하고 채팅 서비스를 즐길 수 있다. 호스트 이름, 포트번호 등을 관리자에게 물어서 설정할 필요도 없다. IoT로 확장을 해보자면, 기기와 기기, 기기와 애플리케이션이 설정없이 네트워크로 연결되고 서비스를 제공할 수 있는 환경을 만들어 준다.

Avahi는 애플의 Zeroconf, mDNS/DNS-SD 그리고 IPv4LL의 구현체인 Bonjour 프레임워크의 또 다른 구현이다.

Avahi는 Python, ,ruby, Mono 등 다양한 언어와 바인딩된다. 대부분의 리눅스와 *BSD 배포판에 탑재돼 있으며, 모듈화 가능한 구조로 GNOME의 Virtual File System과 KDE의 Input/Out 아키텍처에서 널리 사용하고 있다.

mDNS/DNS-SD

Avahi는 mDNS/DNS-SD 구현체다. mDNS를 이용하면 로컬 네트워크에 참여한 호스트를 자동으로 찾을 수 있다. 하지만 어떤 종류의 서비스인지는 확인할 수 없다. 예컨데 로컬 네트워크에서 프린트 서버를 찾거나 오디오 서버, (TV 와 같은)멀티미디어 기기 등을 찾으려고 한다면 호스트 이름만으로는 찾는데 어려움이 있다. DNS-SD를 이용하면 서비스 타입(service type)을 설정하는 것으로 서비스 검색이 가능하다.

DNS-SD는 DNS의 PTR(pointer record)를 이용해서 service type을 설정한다. 원래 DNS에서 PTR은 reverse DNS lookup을 위해서 사용하는 레코드인데, SD(Service discovery)를 위해서 필드를 전용해서 사용한다. 형식은 <Service>.<Domain> 이다. 오디오 서비스 라면 _audio._tcp 대략 이런식으로 만들 수 있을 거다.

Archtectural Overview

Avashi의 핵심은 "기기를 쉽게 사용할 수 있게"하는데 있다. 이를 위한 메커니즘으로 publishing, discovering, using IP-based services를 제공한다. 제공하는 모든 메커니즘은 zero-configuration을 목표로 한다.
  • Publication(advertising a service) : 네트워크에 특정 서비스를 제공하는 노드가 참여하면, 이 노드는 자동으로 자신의 서비스를 네트워크에 참여하는 다른 모든 노드에 광고한다. 예를들어 오디오 플레이 서비스를 제공하는 스피커가 네트워크에 참여하면, 이 스피커는 주변의 모든 PC 혹은 모바일앱에 오디오 플레이 서비스가 준비됐음을 알린다.
  • Discovery : 주변에 사용할 수 있는 서비스가 있는지 확인한다.
  • Resolution : 서비스를 제공하는 노드의 이름과 IP 주소, 포트 번호를 확인할 수 있다.

mDNS/DNS-SD arhitecture

  • mDNS Resolver : 애플리케이션의 요청을 받아서 DNS resolve를 한다. 즉 호스트나 서비스를 찾아서 그 정보를 애플리케이션에 돌려주는 일을 한다.
  • mDNS Responder : DNS resolve 요청이 왔을 때, 응답을 한다.
  • mDNS DB : 노드의 IP, 호스트 이름, 서비스 타입, 포트 정보 등을 저장한다. 또한 resolv된 다른 노드들의 정보들을 저장한다.

Publication

노드가 서비스를 퍼블리쉬(publish)하기 위해서는 멀티캐스트 채널에 join한 노드들의 mDNS DB에 등록이 돼야 한다. mDNS는 DNS를 그대로 사용한다. 등록정보들을 DNS record로 관리한다는 이야기다. 사용하는 레코드들은 서비스(SRV) 레코드, pointer(PTR)레코드, 그리고 text(TXT)레코드다.

Service Records

SRV 레코드는 서비스를 사용하는 클라이언트에게 상세한 서비스 정보를 알려주기 위해서 사용한다. 인스턴스 이름, 서비스 타입, 포트 번호등의 정보를 클라이언트에게 알려준다. SRV 레코드의 중요한 특징은 "서비스 이름"을 키로 사용할 수 있는데 있다. 예를들어 IP 주소나 호스트 이름이 변경되더라도 서비스 이름은 그대로 남기 때문에, 서비스 이름을 이용해서 노드를 찾을 수 있다 .

SRV에 저장되는 정보의 형식은 다음과 같다. 각 필드는 공백문자로 구분할 수 있다.
PrintsAlot._printer._tcp.local. 120 IN SRV 0 0 515 blackhawk.local.
첫번째 필드에는 서비스 이름이 저장된다. <Instance Name>.<Service Type>.<Domain> 형식으로 저장된다. 위의 정보를 이용해서, 로컬 네트워크에서 프린트 서비스를 제공하는 노드이며, 서비스의 이용 포트번호는 515 임을 알 수 있다. 120은 time-to-live로 캐쉬 기간 설정에 사용한다. 두 개의 0은 weight와 priority 이다. mDNS에서는 사용하지 않는다.

Pointer Records

PTR 레코드는 Discovery를 위해서 사용한다. 서비스 타입을 이용해서, 서비스를 제공하는 노드를 찾을 수 있다. 서비스 타입이기 때문에, 동일한 타입의 여러 기기들을 찾을 수 있다. 집안에 오디오 서비스가 가능한 스피커TV 두개의 노드가 있다면 함께 검색된다.

PRT 레코드는 서비스 타입과 도메인 정보만 가지고 있다. 형식은 <Service Type>.<Domain>이다. SRV 레코드와 중복된다. SRV와 다른 점이라면 인스턴스 이름이 없다는 정도다.

text Records

서비스 노드에 대한 추가적인 정보를 제공한다. 로컬 네트워크를 기반으로 하는 멀티 플레이어 게임에서 맵의 이름을 공유하기 위해서 혹은 채팅 프로그램에서 유저의 상태를 알리기 위한 용도 등으로 사용할 수 있다.

Discovery

서비스 Discovery는 PTR 레코드에 대해서 검색하는 걸로 시작한다. 주변에 이미지를 출력할 수 있는 기기를 찾기를 원한다면 _image._tcp와 같이 서비스 이름으로 검색하면 된다. 검색을 시작하면 멀티캐스트 채널로 'DNS 요청를 보내고, 각 노드는 자신의 서비스 타입을 반환한다.

Resolution

서비스를 한 번 찾은 후에는 서비스 노드가 네트워크를 이탈하지 않는 한은 언제든지 사용할 수 있어야 한다. 예를들어 TV를 찾았다면, TV의 IP가 변경되거나 호스트 명이 변경되더라도 같은 멀티캐스트 채널이 붙어있다면, TV의 서비스를 이용할 수 있어야 한다. IP가 변경됐다고 혹은 호스트 명이 변경됐다고 해서 서비스를 다시 찾는 일은 없어야 할 것이다.

이를 위해서는 서비스를 resolution 하기 위한 키(key)가 필요하다. 확실한 건 IP나 호스트 네임이 키가 될 수 없다는 거다. 대신 서비스 이름을 키로 사용한다. 멀티캐스트 채널에 join한 노드의 IP와 호스트 정보를 획득했다면, 서비스 이름을 요청할 수 있다. 이 서비스 이름은 변하지 않기 때문에, IP 정보등이 변경되더라도 서비스 이름을 이용해서 노드를 찾을 수 있다.

Avahi 테스트

뭐든지 삽질을 해봐야 직성이 풀리는 스타일이라서, 직접 설치해서 테스트해보기로 했다. Discovery, Publish, Resolution을 중점적으로 테스트 한다.

테스트 환경

  • VirtualBox 4.3
  • 호스트 운영체제 : Ubuntu 14.04 Desktop
  • 게스트 운영체제 : Ubuntu 13.10 Server x 3
3 개의 리눅스 게스트 운영체제에 avahi 소프트웨어를 설치해서 테스트했다. 구성은 다음과 같다.

Avahi 소프트웨어 설치

avahi-daemon(mDNS/DNS-SD 데몬 프로그램)을 설치한다.
# apt-get install avahi-daemon
패키지를 설치하면, avachi 데몬이 실행된다. netstate로 데몬을 확인한다.
# netstat -nap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
udp        0      0 0.0.0.0:5353            0.0.0.0:*                           569/avahi-daemon: r
udp6       0      0 :::5353                 :::*                                569/avahi-daemon: r

5353 포트bind 된 프로세스를 확인할 수 있다. 5353 포트는 mDNS가 사용하는 포트다. 확인해보자.
# cat /etc/services | grep 5353
mdns            5353/tcp                        # Multicast DNS
mdns            5353/udp

avahi-utils를 설치한다.
# apt-get install avahi-utils
avahi를 테스트하고 관리하기 위한 몇 가지 프로그램들이 설치된다.
  • avahi-resolve
  • avahi-browse
  • avahi-publish
  • avahi-set-host-name

Discovery

3개의 게스트 운영체제를 모두 실행 한후 테스트를 진행했다. 보기 쉽게 IPv6 주소는 필터링 했다.

host-1에서 검색
host-1# avahi-browse --all
+   eth0 IPv4 host-2 [08:00:27:51:a6:88]                    Workstation          local
+   eth0 IPv4 host-3 [08:00:27:61:10:b4]                    Workstation          local
+   eth0 IPv4 host-1 [08:00:27:8f:dc:e1]                    Workstation          local
+   eth0 IPv4 home [0a:00:27:00:00:00]                      Workstation          local
모든 호스트들이 검색 된다.

host-3를 shutdown 하면, host-3가 채널에서 빠졌다는 것을 알려준다.(+는 새로 추가, -는 탈퇴)
+   eth0 IPv4 host-2 [08:00:27:51:a6:88]                    Workstation          local
+   eth0 IPv4 host-1 [08:00:27:8f:dc:e1]                    Workstation          local
-   eth0 IPv4 host-3 [08:00:27:61:10:b4]                    Workstation          local

당연하지만 host-3를 실행하면 자동으로 채널에 추가된다.
-   eth0 IPv4 host-3 [08:00:27:61:10:b4]                    Workstation          local
+   eth0 IPv4 host-3 [08:00:27:61:10:b4]                    Workstation          local

dns name resolve를 테스트 했다.
# avahi-resolve --name host-2.local
host-2.local    192.168.56.32

# avahi-resolve --name host-1.local
host-1.local    192.168.56.31

ip로 dns 이름을 찾는다.
# avahi-resolve --address 192.168.56.32
192.168.56.32   host-2.local

Publish & Resolution

avahi-publish를 이용해서 호스트 혹은 서비스의 변경된 정보를 주변의 호스트들에게 알릴 수 있다.

Audio 스피커 서비스를 예로 들어보자. 이번에 구입한 오디오 스피커는 mDNS/DNS-SD를 지원한다. 로컬네트워크에 묶여 있다면, mDNS/DNS-SD 클라이언트가 설치된 모바일 앱 혹은 PC 애플리케이션을 이용해서 자유롭게 오디오 스피커로 음악 파일을 재생할 수 있다. 대략 아래의 시나리오가 될테다.

avahi-publish를 이용해서 위 시나리오를 테스트했다.
host-3# avahi-publish -s "Audio server for $(hostname)" _audio._tcp 6600 "Audio speaker"
Established under name 'Audio server for host-3.local'
  • host-3가 오디오 스피커 서버 역할을 한다.
  • 서비스 타입은 _audio._tcp다.
  • 서비스 포트는 6000 번이다.
  • "Audio speaker"은 description이다. 애플리케이션에서 기기를 묘사하기 위한 용도로 사용할 수 있을 거다.
  • 실행을 하면, 멀티캐스트 채널에 데이터가 뿌려진다.
서비스 정보를 받는 측이다. PC 애플리케이션 혹은 모바일 앱이라고 보면 되겠다.
host-1# avahi-browse _audio._tcp --resolve 
$ avahi-browse _audio._tcp --resolve
=   eth0 IPv6 Audio server for host-3.local                 _audio._tcp          local
   hostname = [host-3.local]
   address = [fe80::a00:27ff:fe61:10b4]
   port = [6600]
   txt = ["Audio speaker"]
=   eth0 IPv4 Audio server for host-3.local                 _audio._tcp          local
   hostname = [host-3.local]
   address = [192.168.56.33]
   port = [6600]
   txt = ["Audio speaker"]
이들 앱은 오디오 스피커에 음악 파일을 보내는게 목적이므로, 실행되면 먼저 멀티캐스트 채널에 _audio._tcp 서비스가 있는지를 찾을 것이다. 오디오 서비스가 publish 한 정보들, 포트번호, description(text), 주소를 찾을 수 있다. 이제 애플리케이션은 192.168.56.33:6600 으로 플레이할 음악파일을 보내면 된다.

avahi service 설정

서비스를 등록해 보자. 서비스 등록 파일의 위치는 /etc/avahi/services/이다. 여기에 <서비스이름>.service 으로 DNS-SD 서비스 데이터를 위한 XML 파일을 만들면 된다. 노드는 하나 이상의 서비스를 가질 수 있다.

나는 host-2에 서비스를 설정하기로 했다. host-2는 멀티미디어 노드로 http, audio(음악파일 플레이) 서비스를 제공한다.

다음은 HTTP 서비스다.
host-2 # cat /etc/avahi/services/http.service
<?xml version="1.0" standalone='no'?>
 <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
 <service-group>
   <name replace-wildcards="yes">HTTP Server at %h</name>
   <service>
     <type>_http._tcp</type>
     <port>8081</port>
     <txt-record>HTTP Server</txt-record>
   </service>
 </service-group>

host-1에서 확인했다.
host-1 # avahi-browse _http._tcp --resolve
+   eth0 IPv6 HTTP Server at host-2                         Web Site             local
+   eth0 IPv4 HTTP Server at host-2                         Web Site             local
=   eth0 IPv6 HTTP Server at host-2                         Web Site             local
   hostname = [host-2.local]
   address = [fe80::a00:27ff:fe51:a688]
   port = [8081]
   txt = ["HTTP Server"]
=   eth0 IPv4 HTTP Server at host-2                         Web Site             local
   hostname = [host-2.local]
   address = [192.168.56.32]
   port = [8081]
   txt = ["HTTP Server"]

다음은 Audio 서비스 설정이다.
host-2 # cat /etc/avahi/services/audio.service
<?xml version="1.0" standalone='no'?>
 <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
 <service-group>
   <name replace-wildcards="yes">Audio Server at %h</name>  
   <service>
     <type>_audio._tcp</type>
     <port>6789</port>
     <txt-record>path=/path/to/bo/folder</txt-record>
     <txt-record>u=anonymous</txt-record>
     <txt-record>p=123456</txt-record>
   </service>
 </service-group>

host-1에서 확인했다.
host-1 # avahi-browse _audio._tcp --resolve
=   eth0 IPv6 Audio Server at host-2                        _audio._tcp          local
   hostname = [host-2.local]
   address = [fe80::a00:27ff:fe51:a688]
   port = [6789]
   txt = ["p=123456" "u=anonymous" "path=/path/to/bo/folder"]
=   eth0 IPv4 Audio Server at host-2                        _audio._tcp          local
   hostname = [host-2.local]
   address = [192.168.56.32]
   port = [6789]
   txt = ["p=123456" "u=anonymous" "path=/path/to/bo/folder"]

DNS SRV Service Types 목록

DNS SRV(RFC 2782) 서비스 타입 목록은 http://www.dns-sd.org/ServiceTypes.html 에서 확인할 수 있다. 리눅스 시스템이라면 /usr/share/avahi/service-types 에서도 확인할 수 있다.

참고