2017년 12월 27일 수요일

SSDP msearch 구현 - C언어

SSDP msearch 구현


알아둘 사항
  • msearch 패킷을 날리기 위해 multicast join 할 필요는 없음

동작 방식
  • SSDP M-SEARCH 패킷을 multicast 주소인 239.255.255.250:1900 으로 송신
  • M-SEARCH 패킷에 명시된 제한시간 (MX header field) 만큼 response 수신

socket programming
  • IPv4
  • datagram socket
  • select (multiplex)

Linux 환경 + C 언어


함수 설명
  • tick_milli() - time milliseconds
  • create_ssdp_sender() - socket_sender_t 생성 및 socket 생성
  • release_ssdp_sender() - socket_sender_t 제거
  • send_msearch() - msearch 패킷 전송
  • pending_ssdp_sender() - msearch 응답 기다림
  • receive_ssdp_response() - 패킷 수신


#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>

static const char * SSDP_HOST = "239.255.255.250";
static const int SSDP_PORT = 1900;

static unsigned long tick_milli(void) {
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0) {
return 0;
}
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}

typedef void (*__ssdp_handler_cb)(struct sockaddr *, const char *);

/* ssdp sender */
typedef struct _ssdp_sender_t
{
    int sock;
fd_set read_fds;
__ssdp_handler_cb handler;
} ssdp_sender_t;

static ssdp_sender_t * create_ssdp_sender(void) {
ssdp_sender_t * sender = (ssdp_sender_t*)malloc(sizeof(ssdp_sender_t));
memset(sender, 0, sizeof(ssdp_sender_t));
sender->sock = socket(AF_INET, SOCK_DGRAM, 0);
assert(sender->sock >= 0);
FD_ZERO(&(sender->read_fds));
FD_SET(sender->sock, &(sender->read_fds));
return sender;
}

static void release_ssdp_sender(ssdp_sender_t * sender) {
close(sender->sock);
free(sender);
}

static void send_msearch(ssdp_sender_t * sender, const char * type, int mx)
{
static const char * SSDP_FMT =
"M-SEARCH * HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"ST: %s\r\n"
"MX: %d\r\n"
"MAN: %s\r\n"
"User-Agent: UPnP/1.0 Sample/1.0 Linux/1.0\r\n"
"\r\n";

struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);

char buffer[4096] = {0,};
snprintf(buffer, sizeof(buffer), SSDP_FMT,
SSDP_HOST, SSDP_PORT, type, mx, "\"ssdp:discover\"");

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(SSDP_HOST);
addr.sin_port = htons(SSDP_PORT);

sendto(sender->sock, buffer, strlen(buffer), 0, (struct sockaddr *)&addr, addr_len);
}

static int pending_ssdp_sender(ssdp_sender_t * sender, unsigned long wait_milli) {
fd_set fds = sender->read_fds;
struct timeval timeout;
timeout.tv_sec = wait_milli / 1000;
timeout.tv_usec = (wait_milli % 1000) * 1000;
return select(sender->sock + 1, &fds, NULL, NULL, &timeout);
}

static void receive_ssdp_response(ssdp_sender_t * sender) {
char buffer[4096] = {0,};
struct sockaddr_in addr = {0,};
socklen_t addr_len = sizeof(addr);
int len = recvfrom(sender->sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&addr, &addr_len);
assert(len > 0);
printf("[RECV]\n");
printf("%s", buffer);
if (sender->handler) {
sender->handler((struct sockaddr *)&addr, buffer);
}
}

int main(int argc, char *argv[])
{
int count = 0;
int mx = 5;
unsigned long tick;
ssdp_sender_t * sender = create_ssdp_sender();
send_msearch(sender, "upnp:rootdevice", mx);
tick = tick_milli();
while (tick_milli() - tick < mx * 1000) {
if (pending_ssdp_sender(sender, 10) > 0) {
count++;
receive_ssdp_response(sender);
}
}
release_ssdp_sender(sender);
printf("count := %d\n", count);
printf("[DONE]\n");
    
    return 0;
}



2017년 12월 14일 목요일

UPnP 소개

UPnP 소개


Apple 의 Bonjour (avahi) 에 맞서는 UPnP (Universal Plug n Play) 스펙


UPnP spec v1.1
http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.1.pdf

크게 SSDP 와 UPnP 모듈로 구성

SSDP


zero configuration network 에 속하는 디바이스 검색 프로토콜

Multicast 로 notify 또는 search 패킷을 전송하는 식으로 망내 디바이스를 자동으로 검색

SSDP multicast group 주소는 239.255.255.250 와 1900 포트

SSDP 프로토콜의 역할은 디바이스 종류 및 존재 유무 정도의 간단한 정보를 제공하고 디바이스의 세부 정보를 취득할 수 있는 링크를 제공함으로써 UPnP 세부 기능을 사용할 수 있게 함

SSDP 패킷은 HTTP header format 을 따름

SSDP 패킷 종류

  • notify (multicast) - Device 시작 종료 갱신 등의 이벤트 발생시
  • msearch (multicast) - 망내 Device 검색
  • msarch response (unicast) - msearch 패킷의 ip 주소로 unicast 로 전송
    • datagram 프로토콜 상 전송자 ip 주소가 포함되어 SSDP 패킷 안에 정의할 필요 없음

주요 SSDP 필드

  • Location - UPnP device description 주소 (http link)


UPnP


크게 UPnP device 와 UPnP controller 두가지 역할이 있음
  • UPnP device - 프린터, 디스플레이, 미디어 서버 등의 명령 수행 주체
  • UPnP controller - 모바일 기기 등의 원격으로 명령을 내리는 주체

주요 구성 프로토콜

  • HTTP - UPnP 동작은 기본으로 HTTP 통신에 대부분 의존
  • XML - XML 자체 언어적 측면 및 확장성 등을 이용함
  • SOAP - Action invoke 명령 전달/수행 시 (XML 확장)
  • URL - resource 정보 등은 URL 프로토콜을 활용
  • UUID - device 의 unique 한 id 를 생성하는데 사용

구현상 고려할 점

  • 주요 구성 프로토콜들 하나하나가 general 한 목적의 포로토콜들이라 기존에 구현된 라이브러리 사용시 덩치가 커져 포팅이 어려울 수 있음
  • UPnP 프로토콜에서 사용하는 기능만 사용하기 위해 직접 구현하려면 기존에 구현된 기능을 다시 구현하는 노력이 들어감


UPnP device


UPnP device 는 device description 및 SCPD (service control protocol descriptor) 를 제공하여 UPnP controller 가 자신에게 내릴 수 있는 명령 또는 제공하는 서비스 종류 등을 알림

위 정보는 HTTP server 로 알리며 XML 양식을 따름

UPnP service


UPnP device 는 다수의 UPnP service 를 포함하거나 하지 않을 수 있으며 UPnP service 가 UPnP controller 로 부터 내린 명령을 처리하는 set 를 포함


UPnP action


UPnP service 는 UPnP action 을 포함하며 프로그램의 함수와 유사함 (RPC)
UPnP controller 는 SCPD 에 명시된 action 목록을 보고 call 가능한 action 을 확인하고 필요시 action 을 call

action call (invoke) 는 SOAP 프로토콜을 따름

DLNA

원격 Media 재생 환경 구성을 위해 반드시 구현하고 따라야 하는 UPnP device 와 service, action 들을 규격화하고 있는 프로토콜

크게 Media server 와 controller, render 의 UPnP device 가 나뉘고 각 기기 마다 제공해야 되는 service 와 action 들이 명시되어 있음


결론


XML, HTTP server 등의 무거운 프로토콜들을 사용하면서 의존 라이브러리를 사용하고 싶을 수도 있지만 임베디드 환경에 적용하기 위해선 의존성을 최대한 줄이는 것이 좋아 고민이 있음



UPnP 용어 정리

용어 정리 https://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf https://openconnectivity.org/upnp-specs/UPnP-arch-DeviceArchitecture...