#keywords WOL,wake,on,lan,UDP,magic,packet #title Wake on LAN (WOL) [wiki:Home 대문] / [wiki:CategoryProgramming 프로그래밍], [wiki:CategoryNetwork 네트웍] / [wiki:WakeOnLAN Wake on LAN (WOL)] ---- == [wiki:WakeOnLAN Wake on LAN (WOL)] == * 작성자 조재혁([mailto:minzkn@minzkn.com]) * 고친과정 2011년 3월 8일 : 처음씀 [[TableOfContents]] === Magic packet의 생성 === * [wiki:WakeOnLAN Wake on LAN (WOL)]을 UDP상에서 구현 하는 경우 UDP packet의 첫 6 byte는 FFH로 채워지고 이후 대상 PC의 Ethenet MAC address 6 byte가 16회 반복시켜서 UDP Magic packet을 만들수 있습니다. 만약 MAC address가 "{{{44:87:FC:8F:BB:B4}}}" 라고 한다면 다음과 같은 UDP packet을 만들어 Broadcast나 Unicast로 송출하면 됩니다. {{{#!plain 00000000 FF FF FF FF FF FF 44 87 | FC 8F BB B4 44 87 FC 8F [......D.....D...] 00000010 BB B4 44 87 FC 8F BB B4 | 44 87 FC 8F BB B4 44 87 [..D.....D.....D.] 00000020 FC 8F BB B4 44 87 FC 8F | BB B4 44 87 FC 8F BB B4 [....D.....D.....] 00000030 44 87 FC 8F BB B4 44 87 | FC 8F BB B4 44 87 FC 8F [D.....D.....D...] 00000040 BB B4 44 87 FC 8F BB B4 | 44 87 FC 8F BB B4 44 87 [..D.....D.....D.] 00000050 FC 8F BB B4 44 87 FC 8F | BB B4 44 87 FC 8F BB B4 [....D.....D.....] 00000060 44 87 FC 8F BB B4 [D..... ] }}} === [wiki:WakeOnLAN Wake on LAN (WOL)] packet을 수신하였는데도 켜지지 않는 경우 === 완벽하게 [wiki:WakeOnLAN Wake on LAN (WOL)]을 지원하는 경우는 상관없으나 메인보드로의 전원을 인가하는 방법에 따라서 일부 전원이 인가하여 1회 부팅후 커널등에 의해서 [wiki:WakeOnLAN Wake on LAN (WOL)]을 동작시킬수 있도록 매번 설정해야 동작하는 경우가 있습니다. 즉, 정전등에 의해서 완전히 전원이 소실된 경우 적어도 한번은 부팅후 정상적인 shutdown 절차를 밟아야만 [wiki:WakeOnLAN Wake on LAN (WOL)]이 동작하도록 설계된 H/W 가 많아서 이를 정확히 확인후 사용하시는게 좋습니다. 요즘 PC들은 대부분 이에 속합니다. === 예제소스 === * 예제 소스 : 아래의 예제는 여러개의 NIC를 가진 다중 IP환경에서에 대한 고려는 빠져있습니다. 실제 범용적인 구현을 위해서는 다중IP에 대한 bind 및 개별 IP별로 broadcast를 구현하는것이 좋다는 점 유의 하면서 보시면 좋을듯 합니다. {{{#!enscript c /* Copyright (C) JAEHYUK CHO All rights reserved. Code by JaeHyuk Cho */ #include #include #include #include #include #include #include #include int main(int s_argc, char **s_argv); int main(int s_argc, char **s_argv) { static const unsigned char s_target_mac[6] = { /* 이 부분을 자신이 깨우고자 하는 PC의 Ethernet MAC address로 채워주면 되겠습니다. */ 0x44u, 0x87u, 0xfcu, 0x8fu, 0xbbu, 0xb4u }; int s_socket; struct sockaddr_in s_sockaddr_in; unsigned char s_magic_packet[ 6 + (6 * 16) ]; int s_repeat; ssize_t s_send_bytes; int s_value; /* UDP socket open */ s_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if(s_socket == (-1)) { perror("socket"); return(EXIT_FAILURE); } /* bind */ (void)memset((void *)(&s_sockaddr_in), 0, sizeof(s_sockaddr_in)); s_sockaddr_in.sin_family = AF_INET; s_sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY); s_sockaddr_in.sin_port = htons(0); if(bind(s_socket, (const struct sockaddr *)(&s_sockaddr_in), (socklen_t)sizeof(s_sockaddr_in)) == (-1)) { perror("bind"); (void)close(s_socket); return(EXIT_FAILURE); } /* broadcast socket option enable (Broadcast를 위해서는 SO_BROADCAST가 설정되어야 합니다.) */ s_value = 1; (void)setsockopt(s_socket, SOL_SOCKET, SO_BROADCAST, (const void *)(&s_value), (socklen_t)sizeof(s_value)); /* WOL packet build (repeat 6) */ (void)memset((void *)(&s_magic_packet[0]), 0xff, (size_t)6u); /* 첫 6byte는 FFH로 채웁니다. */ for(s_repeat = 0;s_repeat < 16;s_repeat++) { /* 깨우고자 하는 MAC address를 16회 반복하여 채웁니다. */ (void)memcpy((void *)(&s_magic_packet[6 + (s_repeat * 6)]), (const void *)(&s_target_mac[0]), sizeof(s_target_mac)); } /* broadcast socket address structure set */ (void)memset((void *)(&s_sockaddr_in), 0, sizeof(s_sockaddr_in)); s_sockaddr_in.sin_family = AF_INET; (void)inet_pton(s_sockaddr_in.sin_family, "255.255.255.255", (void *)(&s_sockaddr_in.sin_addr)); s_sockaddr_in.sin_port = htons(2304); /* any port ... (사실상 port는 아무거나 상관하지 않습니다. 다만 다른 응용프로그램들에게 방해가 되지 않는 포트를 선택하는것 뿐입니다.) */ /* send */ s_send_bytes = sendto( s_socket, (const void *)(&s_magic_packet[0]), sizeof(s_magic_packet), 0, (const struct sockaddr *)(&s_sockaddr_in), (socklen_t)sizeof(s_sockaddr_in) ); if(s_send_bytes == ((ssize_t)(-1))) { perror("sendto"); (void)close(s_socket); return(EXIT_FAILURE); } (void)fprintf(stdout, "WOL packet : %ld bytes (Target %02X:%02X:%02X:%02X:%02X:%02X)\n", (long)s_send_bytes, (unsigned int)s_target_mac[0], (unsigned int)s_target_mac[1], (unsigned int)s_target_mac[2], (unsigned int)s_target_mac[3], (unsigned int)s_target_mac[4], (unsigned int)s_target_mac[5] ); /* socket close */ (void)close(s_socket); return(EXIT_SUCCESS); } /* vim: set expandtab: */ /* End of source */ }}} === 예) libvirt (KVM+QEMU) 로 구축된 가상 서버를 WOL 패킷을 수신하여 해당하는 MAC 주소의 Guest 기동해주는 스크립트 === {{{#!plain #!/bin/bash # listen to udp port 2304 for packets, check if it is a magic packet netcat -dkn -l 2304 -u | stdbuf -o0 xxd -c 6 -p | stdbuf -o0 uniq | stdbuf -o0 grep -v 'ffffffffffff' | while read do echo "Got triggered with $REPLY" mac="${REPLY:0:2}:${REPLY:2:2}:${REPLY:4:2}:${REPLY:6:2}:${REPLY:8:2}:${REPLY:10:2}" # loop through libvirt machines for vm in $(virsh list --all --name) do # Get the MAC address and compare with the magic packet vmmac=$(virsh dumpxml $vm | grep "mac address" | awk -F\' '{ print $2}') if [ "$vmmac" = "$mac" ] then state=$(virsh list --all|awk -v vm=$vm '{ if ($2 == vm ) print $3 }') echo "Found $vm with $mac in $state state" # Dependent on the state, resume or start [ $state == "paused" ] && virsh -q resume $vm && virsh domtime --domain $vm --now [ $state == "shut" ] && virsh -q start $vm fi done done }}} === 참고문헌 === * [^http://support.amd.com/TechDocs/20213.pdf] * [^http://www.intel.com/support/kr/network/sb/cs-000084.htm]